Skip to main content
Carry is built around one non-negotiable rule: gate before generation. Enforce the agent × namespace policy at retrieval — the model only ever sees allowed memory — so the receipt under every answer is honest by construction.

The big picture

Three layers

1 · Policy & gate

A typed access policy — which agent can read which namespace — enforced at retrieval, before the model is called.

2 · Storage

Every memory is a real content-addressed blob on Walrus, optionally encrypted through Seal + MemWal.

3 · Proof

The policy and a tamper-evident receipt chain live on Sui — the verdict is computed on-chain, not asserted by the app.

Typed contracts

The system is designed around a few typed contracts in @carry/core — get the boundaries right and the rest composes.

The recall loop, step by step

This is what POST /api/chat does — and every step assumes the model might be wrong about what it’s allowed to see.
1

Trigger

An agent receives a query.
2

Gate

recall(agentId, query, memories, policy) returns only the memories in namespaces this agent is allowed to read, plus the namespaces that matched the query but were blocked by policy. Blocked content is never loaded into the prompt. The on-chain AccessPolicy is the authoritative allow/deny — recomputed on anchor and re-checkable by anyone via devInspect.
3

Verify

Each allowed memory’s Walrus blob is re-checked against the aggregator (GET /v1/blobs/{id}). “Verified” means the blob genuinely resolves on-chain — it is not a flag set by the app.
4

Generate

The gated memories (and only those) are passed to the agent’s model. With no allowed memory, the agent truthfully says it can’t access what it needs.
5

Receipt

buildReceipt(...) assembles the Answer Receipt: used memories with authorization + verification status, the blocked namespaces, and the source agent.
6

Anchor (optional)

The receipt is stored to Walrus and anchored on Sui as a Receipt object — chained by blake2b256 — for tamper-evident provenance.

The contrast that sells it

Revoke agent-b × health, ask the allergy question again, and the gate returns zero memories plus blocked: ["health"]. The model never sees the health memory, the answer is an honest refusal, and the receipt proves the block.
Because the gate runs server-side before getLLM().complete(...), there is no code path where a disallowed memory reaches the model — even by accident.

Cross-model by design

Agent A runs on OpenAI GPT-4o, Agent B on Anthropic Claude. Both share the same gated memory and the same proof format. The proof travels with the answer, not the vendor — which is what makes “memory you can trust” portable across providers.