Tabularium
Memory for AI agents that survives sessions, plus a signed ledger of what they buy. Paid per call in USDC over x402.
Live on Base mainnet at https://tabularium.matt-3e0.workers.dev (real USDC). Listed on the x402 Bazaar and the
MCP Registry (io.github.SQLazy/tabularium, npm tabularium-mcp).
Identity is the paying wallet: no sign-up, no API key. Every write is appended to a hash chain whose head is signed
by the server, so an agent can prove its history and walk away with it.
Quickstart
From Claude Code or any MCP client (needs a wallet with a few dollars of USDC on Base, no ETH):
claude mcp add tabularium --env TABULARIUM_PRIVATE_KEY=0x... -- npx -y tabularium-mcp
Then ask your agent to remember something, or use the load_memory prompt. Spending is capped at $1 per session by
default (TABULARIUM_MAX_SPEND_USD). Use a dedicated wallet.
From code, with any x402 v2 client: see examples/agent-memory.mjs (about 30 lines).
For language models: https://tabularium.matt-3e0.workers.dev/llms.txt explains when it fits, when it does not, prices and exact formats.
Use it for: memory across sessions and machines, hand-offs between agents, proof of what was stored and when,
and an auditable record of what an agent spent (tabularium_pay). Not for: secrets unless encrypted first (the
operator can read values), large files (64 KB per value), or anything that must be free.
The menu (four calls, fixed prices)
| Call | Price | What |
|---|---|---|
POST /write {key,value} |
$0.003 | Append a value. First write opens a 30-day workspace. |
GET /read?key= |
$0.002 | Latest value + its chain entry. |
POST /renew |
$0.25 | Extend by 30 days (from current expiry, so early renewal wastes nothing). |
GET /export?since= |
$0.01 | Entries + values + signed head, verifiable offline. |
Free: GET /llms.txt (a plain-text guide for language models: when it fits, when not, exact formats), GET /openapi.json
(routes, schemas and prices for agent wallets and x402 explorers such as x402scan and AgentCash), GET / and /menu
(prices, policy, public signing key), GET /head?wallet= (signed seq + hash, no values).
GET /stats: public aggregates of settled calls (paying wallets, 14-day return rate, calls, revenue). Wallets appear only
as counts; keys and values are never recorded. Every payment is public on-chain anyway.
Prices were set on 2026-10-26, before launch, to cover the Coinbase facilitator's fee ($0.001 per settled payment after the first 1,000 a month). A typical session (10 reads, 20 writes) costs about $0.08.
Writes accept an optional idempotency_key (same id + same body within 7 days returns the original entry with replayed: true; same id + different body is 409). Use it on every retry: x402 settles after the handler runs, so a failed settlement can follow a committed write.
Policy: 30-day term, 90-day grace (reads/export/renew work, writes return 409), then purged.
64KB value cap. One signed payment buys one call: a reused payment gets 409 and is not charged. renew and export
settle before running (a free pre-check rejects a missing workspace first). See SECURITY.md.
Keys match ^[A-Za-z0-9._:/-]{1,128}$. 4xx/5xx responses are never charged
(the x402 middleware settles only when status < 400).
Use it from an agent (MCP)
mcp/ is a stdio MCP server exposing the menu as tools (the four paid calls plus menu, head, and local verify), and
tabularium_pay / tabularium_ledger: pay any x402 service and keep a signed receipt of every purchase in the
workspace, so an agent's spending is provable. The agent's wallet pays, with a per-process spend cap. See mcp/README.md.
claude mcp add tabularium --env TABULARIUM_PRIVATE_KEY=0x... -- node /path/to/mcp/server.mjs
Verify without trusting the server
node verifier/verify.mjs export.json <signingPubKeyHex>
Zero dependencies. Checks the chain, the values against value hashes, and the Ed25519 head signature.
Signing keys rotate. /menu publishes the current key and every retired one (signing.retired); an export
verifies against the key that was current when it was exported. Retired keys:
| Public key | Used until | Note |
|---|---|---|
5599f548393f2f2e1e694361a06d76d06fd3dc6cbe2d985d28f3a533025798e3 |
2026-10-26 | Testnet launch key. Its private half was exposed, so treat its signatures as testnet-only. |
Develop
npm install
npm run keygen -- --dev # writes .dev.vars
npx wrangler dev -c wrangler.dev.jsonc --port 8787 # NO paywall, local only
node scripts/e2e.mjs # 25 checks
npm test && npm run typecheck
src/dev-nopay.ts removes the paywall and adds a 5ms async gap in the write path so the
concurrency test bites. It is referenced only by wrangler.dev.jsonc.
Caveat: wrangler hot-reload can serve stale code after a file swap; restart the server before trusting a result.
Deploy
npm run keygen, put the public key inSIGNING_PUBKEY_HEX(wrangler.jsonc), thennpx wrangler secret put SIGNING_KEY_PKCS8. The Worker refuses to sign if the two don't match. To rotate: move the old public key intoRETIRED_SIGNING_KEYS(src/policy.ts), set the new one, and deploy the new secret in the same version:npx wrangler deploy --secrets-file <json with SIGNING_KEY_PKCS8>.- Set
PAY_TOto your receiving address. npx wrangler deploy.NETWORKin wrangler.jsonc picks the chain (basein production,base-sepoliafor testing; the freehttps://x402.org/facilitatorworks on testnet). Localwrangler devruns read.dev.vars, which keeps them on base-sepolia so local tests never spend real money.- Mainnet: set
NETWORK=baseand use a mainnet-capable facilitator (needs its own credentials) viaFACILITATOR_URL.
Lessons / known limits
blockConcurrencyWhileis not a mutex. Concurrent writes forked the chain (UNIQUE constraint failed: log.seq). Mutations now go through an in-isolate promise-chain mutex plustransactionSync. Disabling the mutex makes e2e fail; enabling it passes.- Payments use x402 v2 (
@x402/hono2.28): 402s carry aPAYMENT-REQUIREDheader, clients pay withPAYMENT-SIGNATURE, receipts come back inPAYMENT-RESPONSE. Network ids are CAIP-2 (eip155:84532,eip155:8453);/menuadvertises them underx402. - Payer identity is read only from
PAYMENT-SIGNATURE(the header the middleware verifies): EVMexactvia EIP-3009. A legacyX-PAYMENTheader is ignored, so nobody can pay with one wallet and claim another. - Workers do not let one request await I/O started by another, so the paywall is initialised inside the request that needs it and only the finished instance is shared (a shared in-flight init hung every paid call).
- Name: "Tabularium" is Rome's state archive. Earlier names (Omega, Hypomnema) collided with existing AI-memory projects. The protocol tag "omega/v1" in the hash preimage is kept as a stable domain separator.
- Success test: share of wallets that come back within 14 days.
License
Two licenses, on purpose:
- The service (everything except
mcp/,verifier/andexamples/): FSL-1.1-MIT, the Functional Source License. Read it, audit it, run it for yourself, but do not offer it as a competing commercial service. Each version becomes MIT two years after it is published. - The MCP client (
mcp/), the offline verifier (verifier/) and the examples (examples/): MIT. The code that holds your wallet key, and the code that checks the server, are yours to read, change and reuse freely.