How Membrane turns an agent action into an anchored proof.
Membrane is a thin layer with a precise job: bind the parts of a consequential agent
action into one signed bundle, commit it, and anchor it on-chain — so anyone can
verify it later without trusting Membrane, the agent, or its operator.
Apache-2.0solc 0.8.2438/38 contract tests passing
01 — Overview
A trust layer, not a chain
Membrane is not a blockchain, a wallet, a memory store, or a payment rail. It is the
connective tissue that binds those things into a verifiable record. The design goal is
narrow and deliberate: produce one tamper-evident artifact per consequential agent
action, and make that artifact verifiable by anyone, offline.
The agent ecosystem in 2026 has rails (x402, Circle, Stripe), identity (ERC-8004, Kite
Passport, Skyfire KYA), communication (A2A), and a tool-call layer (MCP). What it does
not have is a layer that says: this payment was made because of this intent,
under this policy, by this identity, producing this output, and written into this
memory entry — all in one place, all verifiable. That is the seat Membrane
occupies.
Three properties hold throughout
No trust in Membrane. A verifier checks signatures and Merkle proofs against public chain state. Membrane the company is never a trusted party in verification.
Protocol-agnostic at the input. Any signed claim — an x402 receipt, an AP2 mandate, an ACP token, an MCP tool-call envelope — is an equivalent input.
Opinionated at the output. The output is always one thing: a Membrane attestation bundle with a fixed, documented shape.
02 — The proof bundle
Anatomy of an attestation
Each consequential agent action produces one MembraneAttestation.
It is a flat, content-addressed object: every field is a hash or a signed reference, so
the bundle is small, portable, and cheap to verify. Payloads (the actual memory text, the
actual response body) can be carried encrypted or kept off-bundle — only their commitments
are required.
intent_anchorhash · signed
The authorizing intent — an AP2 Intent Mandate or a Membrane-native intent. Captures what the user actually asked for, with its price and scope limits, signed before anything moves.
policy_idhash
The spending / behavioural policy that was active for this action — a declarative spending_policy.yaml, or an OPA / Cedar policy fed through an adapter.
issuerCAIP-10 · signed
The agent identity, referenced — not re-invented. An ERC-8004 agent DID, a Kite Passport, or a Skyfire KYA credential. Membrane consumes identity; it never issues it.
parent_callhash
The hash of the tool-call envelope — an MCP tool call ID or an A2A message ID — that this action corresponds to.
payment_receipthash · optional
A receipt from whatever rail moved value: an x402 receipt, an AP2 Payment Mandate, an ACP shared payment token, or a Stripe / Circle webhook. Optional — not every action spends money.
response_hashkeccak256
A content-addressed hash of the output the action produced. Proves the result was not altered after the fact.
memory_entryhash · commitment
The commitment to the memory entry written as a result of this action — with its provenance. This is the field no existing memory framework emits.
anchor{ chain, root, block }
The chain anchor: the CAIP-2 chain id, the Merkle root the bundle was committed under, and the block it landed in.
Mem0, Zep and Letta give your agent a memory. Membrane gives that memory a receipt — the memory_entry commitment, bound to the intent and policy that produced it.
03 — Verification
How a bundle is verified
Verification is the heart of the product. The local verifier, kwi-verify,
is chain-config-driven and does its work entirely against public chain state — no
Membrane service is in the path.
verification flow
1 read bundle.anchor.caip2// which chain was this anchored on?2 resolve chain → rpc + MembraneAnchor address
3 confirm root is on-chain AND past finality_blocks4 verify Merkle inclusion proof // bundle ∈ root5// optional: cross-check Avalanche home registry
6 verify issuer signatures // ERC-8004 / Passport / KYA / AP2→ green / red. no trust in Membrane required.
Step 3 is load-bearing: a root is never trusted before it is past the chain's finality
window, and that window differs sharply between chains — roughly one block on Avalanche,
far longer on Ethereum mainnet. The verifier encodes a per-chain
finality_blocks value so two verifiers never disagree.
04 — Multi-chain model
Avalanche by home, portable across the EVM
Membrane uses a hub-and-spoke topology. A single canonical registry —
MembraneRootRegistry — lives on Avalanche C-Chain and is
the single place to look up any Membrane anchor on any chain. A byte-identical anchor
contract — MembraneAnchor — deploys to the same address
on every supported EVM chain. An action anchors on whichever chain it actually happened.
Meets developers where they are. An agent paying an x402 endpoint on Base anchors on Base — it never has to touch Avalanche to produce a proof.
But keeps a single source of truth. Every spoke anchor back-references the Avalanche home registry, so there is always one canonical index.
Bridges are never in the trust path. The verifier reads each anchor on its own home chain, directly. Cross-chain messaging is an optional, pluggable convenience — never a trust dependency. An audit layer whose guarantees can be undermined by a bridge hack is not an audit layer.
Avalanche ICM is the native glue for Membrane's own multi-L1 future — and for the eventual Membrane-dedicated Avalanche L1.
MembraneAnchor is deployed via CREATE2
with a fixed salt and byte-identical creation bytecode, so it lands at the
same address on every EVM chain. 0xMembrane…
becomes a constant a developer can hardcode regardless of chain — a real
developer-experience unlock, and worth the engineering discipline it demands.
What byte-identical requires
A pinned compiler. solc 0.8.24, fixed optimizer settings — a floating version breaks reproducibility.
The lowest-common-denominator EVM. Compiled for the paris target, avoiding the PUSH0 opcode that some L2 and alt-EVM chains still do not support.
No metadata hash. The metadata CBOR blob embeds a hash of the source layout that varies with file paths and comments; stripping it makes the bytecode a pure function of the logic.
Zero external dependencies. The Merkle library is an internal, inlined library — nothing is imported, so the creation bytecode is fully self-contained and reproducible.
06 — The contracts
Two contracts, ~250 lines
The on-chain footprint is deliberately tiny. The less logic on-chain, the cheaper it is
to audit once and trust on every chain.
MembraneAnchor
Makes a Merkle root tamper-evident and timestamped. Immutable — no proxy, no upgrade path; new behaviour ships as a V2. Permissionless — anyone may anchor; a root is meaningless unless it commits bundles signed by recognised issuers. Deployed byte-identically to every EVM chain.
immutable · permissionless · ~30 lines
MembraneRootRegistry
The canonical cross-chain index — Avalanche only. Registration is restricted to an owner-managed registrar set; this is anti-griefing, not a trust gate. A bundle verifies against its spoke chain's anchor whether or not the registry ever indexed it.
avalanche-only · owner + registrars
Both contracts compile under solc 0.8.24 / paris with 38 / 38 unit + fuzz tests passing. A formal audit (Trail of Bits / NCC / Cure53 tier) is scheduled before mainnet anchors. See the developer docs for addresses and the build.
07 — Threat model
What Membrane defends against
The new attack surface in 2026 is the agent itself — its memory, its tool calls, its
payment authority. Membrane is not a sandbox or a firewall; it is the layer that makes
attacks detectable and attributable after the fact, and prevents a class
of them by binding provenance at write time.
Threat
How Membrane addresses it
Memory poisoning — a malicious page injects a false "fact" into agent memory
Every memory write carries a provenance attestation; entries written under low-trust provenance can be flagged or quarantined, and audited retroactively.
Response tampering — an output is altered after it is produced
The response_hash is content-addressed and bound into the signed bundle; any later change breaks verification.
Unauthorised spend — an agent pays outside its mandate
The payment receipt is bound to the intent_anchor and policy_id; a spend with no matching authorisation fails verification.
Audit-trail tampering — logs edited after an incident
Bundles are Merkle-committed and chain-anchored; a log entry cannot be altered after the fact without detection.
Bridge compromise — a cross-chain message bridge is hacked
Bridges are never in the verification trust path; the verifier reads each anchor on its own home chain directly.
08 — Standards posture
Membrane consumes; it does not compete
Membrane's position is to be protocol-agnostic at the input and opinionated only at the
output. It is designed to sit alongside the standards being decided right now, not to
replace them.
x402, AP2, ACP — payment receipts and mandates are equivalent inputs. AP2's Intent / Cart / Payment Mandate triple maps directly onto Membrane attestation types; Membrane adds the memory and response binding AP2 leaves open.
ERC-8004, Kite Passport, Skyfire KYA — identity is referenced by CAIP-10, never re-invented. ERC-8004 is the noun; Membrane is the verb.
MCP, A2A — tool-call envelopes and agent-to-agent messages are hashed into the parent_call field; a Membrane MCP server makes attestation a one-server add for any MCP-aware agent.
The result: whoever wins the rails war, whoever wins the identity war — Membrane is still
the layer that binds the pieces into something a user, an auditor, or a regulator can
verify.
Build on the proof layer.
Read the developer docs, run the verifier against a sample bundle, or explore the contracts on GitHub.