The authorization path
A boundary is only worth what enforces it. Nothing in this path is Limen: the check runs in contract code on the ledger, and it runs whether or not this repository still exists.
What runs before a token moves
The agent signs an envelope with its own key and pays its own fee. No owner signature is anywhere near it. The network invokes __check_auth on the smart account, and the account’s own code decides.
If any attached policy refuses, the invocation traps. The transaction still reaches a ledger and still burns a fee — which is what makes a refusal checkable by anyone rather than a claim this application makes about itself.
The structure of an account
The relationship between signers, context rules and policies is the thing most often read backwards. A policy is attached to a rule, not to a signer, and a signer’s authority is exactly the union of the rules that name it.
Who enforces this
Three deployed contracts, all of them OpenZeppelin code that Limen configures rather than writes. Their addresses are read from the deployments file.
| Contract | Address | Deployed |
|---|---|---|
| ed25519 verifier | CA3ZVES4…5HPGZX | 41c2459f…1e78 |
| WebAuthn verifier | CCC4T3F7…2IAKWY | f95ff610…be50 |
| spending limit policy | CDWPYL45…TH4CSE | 8935d56a…4fe4 |
Revoking, and who can
Removing a context rule requires the account to authorize itself. The agent’s rule is a CallContract rule scoped to a token contract, which does not match a call to the account — so the agent’s attempt to remove its own boundary traps. The owner’s Default rule does match.
This is a property of the contract, not of this application declining to draw a button. Both halves are on record:
| Attempt | Outcome | On ledger |
|---|---|---|
| the agent removing its own rule | UnvalidatedContext#3002 | fca28f06…ae4e |
| the owner removing it | rules after: 0 | 8bfb1cc5…781c |
| the permitted call, repeated afterwards | ContextRuleNotFound#3000 | 631f211b…9ef3 |
The last row fails because the rule is gone, not because a limit was reached. Those are different claims, and only one of them is evidence the boundary worked — which is why ContextRuleNotFound#3000 is deliberately absent from BOUNDARY_REFUSAL_CODES.
ProvenanceThese three transactions are from node packages/chain/scripts/acceptance.mjs run, recorded 2026-08-05 on smart account CBYJPU…5GKW. Re-read from the chain after the write, not assumed. Rule 1 is gone; the owner's Default rule 0 remains.