Cryptographic verification for AI agents
Atrib adds signed receipts to every tool call, every response, every decision. Ed25519 signatures. Merkle chain. Publicly verifiable by anyone — no trust required.
"creator_key": "did:key:z6Mk...3f2a",
"signature": "SigEd25519 2026 Ed...",
"record_hash": "MerkleNode sha256:7b3...",
"prev_hash": "sha256:a1c...",
"context_id": "session-f8j2k",
"tool_name": "mcp__database__query",
"action""."": "tool_call",
"producer": "@atrib/mcp v0.9.1"
Every action, provably authored. Ed25519 over JCS-canonicalized JSON. No invented crypto — only RFCs you already trust.
Every action, linked to what caused it. Record hash committed to an append-only Merkle log. C2SP tlog-checkpoints. A causal graph, by construction.
Every chain, readable by anyone. Settle against a policy with sensible defaults. Single env-var overrides. Run your own Trust Authority — nothing phones home.
Ask a question. Watch Atrib sign the database query and return a verifiable receipt.
import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { atribMiddleware } from '@atrib/mcp';
const { text, toolCalls, receipts } = await generateText({
model: openai('gpt-4o'),
system: 'Database assistant.',
tools: { query_database },
middleware: [atribMiddleware()],
prompt: "Show me the first 3 users",
});
// receipts[0] contains the signed receipt
console.log(receipts[0]);
// { id: "rcpt_abc123", signature: "SigEd25519...", tool: "query_database", ... }
const chain = await fetch(`/api/trace/${receipts[0].id}`);
// → { source: "atrib-graph.fly.dev", chain: [...] }
MCP defines how agents call tools. It does not define who is allowed to call them, whether the message was tampered with, or whether the tool definition you received is the real one. JSON-RPC messages travel unsigned. Man-in-the-middle attacks can modify parameters, inject payloads, replay requests. Compromised agents cannot be revoked.
Atrib is the fix. One line of middleware. A signed receipt for every response.
One package. Every framework. Every rail. The grid grows without a breaking change.
pnpm add @atrib/mcp
server.use(atrib())
Receipts flow out, settle, audit, and return as the next trace. The loop closes by itself. You did not write a single line to make that happen.