Architecture

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_blocks
4  verify Merkle inclusion proof  // bundle ∈ root
5  // 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.
Rollout order: Avalanche Fuji → Avalanche C-Chain → Base (where x402 lives) → Ethereum mainnet (where ERC-8004 identity lives) → Optimism, Arbitrum.
05 — Address parity

One address, every chain

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.