Five ways to read this feed, simplest first. Each opens on its own; nothing here needs the one above it. Everything is collapsed — open what you need.
The public endpoints take no key and open to anyone. You can integrate against them, and against the track record, before paying a cent — this is the free tier the access model serves.
$ curl -s /api/public/tape?limit=3
$ curl -s /api/public/record
$ curl -s /api/public/status
$ curl -s '/api/public/proof?ts=0'
There is no signup and no dashboard login. You buy a pass, you are shown one key, and that same key works on every paid route below — Direct API, MCP, P2P and the SDK. The free tier above needs no key at all.
# poll every few seconds; stop when status leaves "pending"
$ curl -s /api/pass/status/p_REPLACE_WITH_PURCHASE_ID
# the first read after activation carries it, and only that read:
# "claim": { "issued": true, "token": "mapt_…", "shownOnce": true }
# every later read returns no token at all.
Same response shapes as the public endpoints, so one parser reads either tier. since takes the cursor from the previous answer and returns only what is newer.
$ PASS=mapt_replace_with_your_token
$ curl -s -H "Authorization: Bearer $PASS" /api/live/tape?limit=20
$ curl -s -H "Authorization: Bearer $PASS" '/api/live/tape?since=0'
$ curl -s -H "Authorization: Bearer $PASS" /api/live/status
The token is a bearer credential: whoever holds it has the access. Bind a key and the pass follows the key instead. Two calls, and the text you sign is shown to you first.
$ curl -s -X POST /api/pass/claim/challenge \
-H 'content-type: application/json' \
-d '{"purchaseId":"p_0000000000000000000000000000dead","keyKind":"evm","address":"0x0000000000000000000000000000000000000000"}'
# sign the returned .challenge string verbatim, then:
$ curl -s -X POST /api/pass/claim/verify \
-H 'content-type: application/json' \
-d '{"keyKind":"evm","nonce":"REPLACE_WITH_NONCE","signature":"0xREPLACE_WITH_SIGNATURE"}'
$ curl -s -X POST /api/pass/claim/challenge \
-H 'content-type: application/json' \
-d '{"purchaseId":"p_0000000000000000000000000000dead","keyKind":"trac","address":"testtrac1replace_with_your_address"}'
# sign .challenge verbatim: ed25519 over the raw bytes, no envelope, no pre-hash
$ curl -s -X POST /api/pass/claim/verify \
-H 'content-type: application/json' \
-d '{"keyKind":"trac","nonce":"REPLACE_WITH_NONCE","signature":"REPLACE_WITH_HEX","publicKey":"REPLACE_WITH_64_HEX"}'
REST polling is stateless and holds no reader slot. A stream does: the SDK's WebSocket and the MCP feed tools open a reader session, and a pass has a fixed number of them.
The pass gate and its session lease live on the pass service and are reached with your bearer token, not through this site — the browser has no route to them, on purpose: a gate that resolved a pass from a public address alone would be an open door.
loading /verify.mjs…
$ curl -O /verify.mjs
$ node verify.mjs
{
"mcpServers": {
"much-alpha": {
"type": "streamable-http",
"url": "http://127.0.0.1:8791/mcp",
"headers": { "Authorization": "Bearer mapt_replace_with_your_token" }
}
}
}
$ npm install /sdk/much-alpha-sdk.tgz
loading…
$ printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| node ./node_modules/much-alpha-sdk/bin/much-alpha-mcp.mjs
The SDK is a convenience over a public transport. If you would rather not run our package, run your own Trac Intercom peer and read the frames yourself: the identifiers below are served by this site so a copied snippet cannot go stale.
# your own peer, over its local bridge, told you its public key
$ curl -s -X POST http://127.0.0.1:8787/auth/agent/verify \
-H 'content-type: application/json' \
-d '{"token":"mapt_replace_with_your_token","peerPubkey":"REPLACE_WITH_64_HEX"}'
# -> { "authorized": true, "channel": "…", "invite": "…",
# "epoch": { "id": …, "key": "…" }, "inviteExpiresAtMs": … }
One channel is open to anyone: it carries a periodic cleartext announcement of what this service is and how to get into the gated one. It is an ephemeral frame, not a record — nothing on it is committed, hashed or scored.
loading…
The signal channel is invite-required and owner-write. The gatekeeper issues the invite, and only against an active pass: you sign a challenge with the key your pass is bound to, and send your own peer's public key so the invite can be addressed to it.
A join is answered only once the topic is genuinely announced on the swarm, which during peer bootstrap can take seconds. Announced means discoverable, not merely registered — so a reply that says registered, and a reply that never comes, are both retries. Neither is a joined channel, and neither is an error.
| reply | what it means | what to do |
|---|---|---|
| joined · announced:true | on the swarm | joined |
| joined · announced:false | registered, not announced | retry |
| no reply before your timeout | still bootstrapping | retry |
| error · …already… | this peer is already in | joined |
| error · anything else | the invite did not carry | get a fresh invite |
Treating a timeout as success is the one failure mode worth naming: it leaves a client believing it is subscribed to a channel it was never announced on, and the feed simply looks empty for ever.
loading…
Every message on the signal channel carries one sealed record. Read it from the event's message field and pass that whole object to your decrypt step — the envelope around it has an id and a ts of its own, and they are not the record's.
loading…
What comes out is the canonical record — the same bytes the commit leaf was built from, so a frame you received live and a record you read off the ledger a week later hash to the same thing.
# one process holds the epoch key, everything else reads its socket
$ node ./node_modules/much-alpha-sdk/bin/much-alpha-feed.mjs
$ node ./node_modules/much-alpha-sdk/quickstarts/ws.mjs
import { openFeed } from 'much-alpha-sdk';
const feed = await openFeed({ mode: 'auto' });
feed.on('record', (r) => r.type === 'call' && console.log(r.instrument, r.direction, r.threshold, r.deadline_utc));
feed.serve(); // ws://127.0.0.1:8790
feed.latest({ type: 'call', limit: 10 });