
The three layers side by side, with the module each one lives in. Layer 2 is the source of truth because the USDC sits inside the contract, so the cap is enforced by code rather than by a server that could be bypassed. Layers 1 and 3 are a pre-check and a hosted screen around it.
Three independent enforcement layers
1
Server policy engine (pre-check)
The fast first gate: daily cap, auto-approve ceiling, payee allowlist, freeze, and
the human-approval requirement. Resets at 00:00 UTC. This is a check, not the
source of truth: it is the fallback, not the guarantee.
2
On-chain policy vault (the source of truth)
Each agent can get its own
AgentSpendPolicy contract on Arc that holds the USDC
and enforces the same policy on-chain. An over-limit pay() reverts on Arc
with a typed error you can read on the explorer, not a server “no”. The human
owner can override an approved payment, adjust limits, freeze, or withdraw.3
Circle Agent Wallet (hosted screening)
Optionally, the agent’s USDC lives in a Circle Developer-Controlled Wallet whose
hosted policy engine screens every transfer at the wallet layer (sanctions,
address allow/block, freeze). A screening denial is authoritative.
executeInstruction routes a payment vault, then Circle, then direct, each
additive with a fallback, so one layer hitting an infra error never fabricates a
success, and a real policy rejection always stops the payment.
The money shot: an on-chain revert
The difference between a demo and a product is where the “no” comes from. Here it comes from the chain:Owner vs operator. The vault’s
owner is a human wallet (sets policy, overrides,
freezes, withdraws). The operator is the agent’s signer (may only pay within
policy). They are genuinely separate keys, and the agent can never change its own limits.What each layer actually enforces
See it on the explorer
Provision a vault from the Permissions screen, then watch an over-limit payment
revert on Arc testnet.
