Skip to main content
Enforcement is in the Move contract, not in a server. Every gated agent action routes through policy::check_and_consume, which asserts the policy is live and the spend is in scope before any coin leaves the vault. A failed assertion aborts the whole transaction. No coin moves, and on a dry-run no gas is spent.

The gate

Order matters: liveness first (right policy, not revoked, not paused, not expired), then scope, then caps. The first failing assertion is the abort.

Abort codes

Each abort code maps to a stable reason. decodePolicyAbort reads the abort out of a thrown error or a dry-run failure status and returns the reason.

Denied without spending gas

A denied action aborts on-chain and emits no event, so a chain indexer never sees it. The agent records the denial itself: dry-run the transaction with client.devInspectTransactionBlock, decode the policy abort, log it, and skip submitting. guardedSubmit does exactly this — dry-run, and on a policy abort record the denial and return without submitting; otherwise submit for real. A non-policy dry-run failure (DeepBook liquidity, gas) is not a policy decision. decodePolicyAbort returns null for it and guardedSubmit surfaces it rather than recording a denial.

Pause and revoke

Both are owner operations signed with the OwnerCap. Both take effect on the next agent action.
  • Pause sets paused. The next action aborts agent_paused. Unpause clears it and the agent resumes.
  • Revoke sets revoked and emits PolicyRevoked. The next action aborts policy_revoked. There is no path back. killAndDrain revokes and sweeps every vault asset to the owner in one transaction.

Next: audit trail

The on-chain events every decision emits, and how denials reach the Chronicle.