> ## Documentation Index
> Fetch the complete documentation index at: https://docs.altheia.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# On-chain enforcement

> policy::check_and_consume is the gate. A denied action aborts in the contract before any coin moves.

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

```move theme={null}
public(package) fun check_and_consume<T>(
    policy: &mut Policy,
    cap: &AgentCap,
    amount: u64,
    target_package: address,
    clock: &Clock,
) {
    assert!(agent::policy_id(cap) == object::id(policy), EWrongPolicy);
    assert!(!policy.revoked, EPolicyRevoked);
    assert!(!policy.paused, EPolicyPaused);
    assert!(now < policy.expires_at_ms, EPolicyExpired);
    assert!(policy.allowed_packages.contains(&target_package), EPackageNotAllowed);
    // for a capped (budget) asset: enforce per_tx + per_day, then record the spend
    // ...
    audit::emit_allowed(policy.agent_id, policy.version, amount, target_package, now);
}
```

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.

| Move error             | Code | Reason code             | Meaning                                                   |
| ---------------------- | ---- | ----------------------- | --------------------------------------------------------- |
| `EPolicyRevoked`       | 1    | `policy_revoked`        | The owner revoked the agent. Terminal.                    |
| `EPolicyExpired`       | 2    | `policy_expired`        | `expires_at_ms` has passed.                               |
| `EPolicyPaused`        | 3    | `agent_paused`          | The owner paused the agent. Reversible.                   |
| `ECapExceededPerTx`    | 4    | `over_per_tx_cap`       | Amount exceeds `per_tx_cap`.                              |
| `ECapExceededPerDay`   | 5    | `over_per_day_cap`      | Would push the rolling 24h total over `per_day_cap`.      |
| `EPackageNotAllowed`   | 6    | `package_not_allowed`   | Target package or pool is not in `allowed_packages`.      |
| `EWrongPolicy`         | 7    | `wrong_policy`          | The cap's `policy_id` does not match this policy.         |
| `ENotAllowedAction`    | 8    | `action_not_allowed`    | Action is not in `allowed_actions`.                       |
| `EActionConfigMissing` | 9    | `action_config_missing` | A required per-action config (e.g. swap floor) is absent. |
| `EAssetNotAllowed`     | 10   | `asset_not_allowed`     | Transfer-out of an asset with no cap entry.               |

## 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.

<Card title="Next: audit trail" icon="arrow-right" href="/concepts/audit-trail" horizontal>
  The on-chain events every decision emits, and how denials reach the Chronicle.
</Card>
