Gate before generation
The single design rule: gate before generation. If access were checked after retrieval (or after generation), a blocked memory would already be in the prompt — and “the agent shouldn’t have used that” becomes an apology, not a guarantee.
The policy
APolicy maps each agent and namespace to a boolean:
is_allowed returns true unless a namespace was explicitly turned off for that agent. Either way, the gate consults a single source of truth before every recall.
How recall gates
memories— only the allowed, query-matching memories. These are the only memories passed to the LLM.blockedNamespaces— namespaces that matched the query but were denied. These are surfaced on the receipt, never loaded into the prompt.
The chain is the source of truth for the verdict
The per-answer gate runs in@carry/core for speed. But the authoritative allow/deny lives in the on-chain AccessPolicy: anchor_receipt recomputes the verdict against it, and the walletless verifier reads it back with the same is_allowed, via devInspect — no signing, no gas, fail-closed:
devInspectTransactionBlock is a read-only simulation — no gas, no signing — so anyone can recompute the verdict against the live policy without trusting Carry.
Revocation is a real state change
Revokingagent-b × health flips a boolean on the shared AccessPolicy object (a Sui transaction signed by the policy owner). The next recall reads the new state and returns zero memories for that namespace. There is no cache to invalidate and no prompt to scrub — the memory is simply never fetched.