carry::access, deployed on Sui testnet. It holds the access policy, mints Receipt proof objects, and recomputes the authorization verdict on-chain.
Objects
AccessPolicy
A shared object: the agent × namespace grants table, plus the receipt-chain head (
receipt_count, chain_head).OwnerCap
An owned capability. Only the holder can change grants on its policy.
Receipt
An owned,
Display-enabled proof object minted on each anchor.AccessPolicy
is_allowed returns true only where an agent was explicitly granted a namespace. An agent nobody configured, or a namespace nobody defined, reads nothing — a permissive default is how a typo silently widens what a model can see. receipt_count and chain_head track the tamper-evident receipt chain.
Granting and revoking
Only theOwnerCap holder can change access:
is_allowed is a public, read-only function — anchor_receipt calls it on-chain to recompute a proof’s verdict, and the walletless verifier calls it via devInspect (no gas, no signing) to re-check any proof against the live policy.
Anchoring a receipt
anchor_receipt is the heart of the proof layer. It recomputes the verdict, extends the hash chain, mints the Receipt, and transfers it to the signer:
anchor_receipt takes the policy as &mut and is callable by anyone — the receipt chain is append-only. Only set_access requires the OwnerCap.Display metadata
Ininit, the module claims a Publisher and registers a Display<Receipt> so wallets and explorers render each proof as a “Carry Proof #” rather than a raw object.
Tests
The Move tests assert the behavior that matters:blake2b256_abc_golden— pins the hash so it matches the TypeScript verifier byte-for-byte.gate_denies_by_default_and_honours_grants— nothing is readable until granted; an unknown agent and an undefined namespace both resolve to denied.anchor_mints_receipt_and_chains— anchoring creates aReceipt, incrementsseq, and chains correctly.