Skip to main content
The gate is Carry’s core security primitive. It enforces an agent × namespace policy at retrieval time, before the model is ever called.

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

A Policy maps each agent and namespace to a boolean:
In-process (mock / fallback) the policy is default-deny with explicit grants. On-chain, the policy is default-allow with explicit revokes — 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

The function returns two things:
  • 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.
This logic is pure, framework-free, and unit-tested.

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:
readIsAllowed fails closed: if the on-chain policy can’t be read (RPC error), the namespace is treated as not allowed. For an access gate, denying on uncertainty is the safe default.
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

Revoking agent-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.