System overview
One Bun process, three dependencies, seven moving parts. The whole router is small enough to read in an afternoon.
The router runs as a single long-lived process on Bun: native TypeScript execution with no build step, built-in SQLite via bun:sqlite, and fast HTTP streaming. The dependency list is hono for HTTP, viem for signatures and chain transactions, and zod for schemas. That is the whole list. There is no Kubernetes, no Redis, no Postgres, no message queue: one process, one cron loop inside it, one container.
Seven moving parts
Inside that process, seven components do the work. Each is a module, not a service, so the boundaries below are function calls.
- Gateway with x402 middleware. An OpenAI-compatible API (
/v1/chat/completions), so switching to the market is abase_urlswap. The middleware turns an unpaid request into a402 Payment Requiredthat carries its own payment instructions. - In-memory quote book. Providers stream signed standing quotes with 5 to 30 second TTLs, refreshed roughly every 2 seconds. See the quote book.
- Auction engine. Second-score clearing over the live book, in memory, in under 1 ms. No auction round trip is added to any request.
- Metering and streaming proxy. Tokens stream through the router, which counts usage and measures each provider's real time-to-first-token. Latency in the auction score is measured, never self-reported.
- Verification pipeline. Canary fingerprint probes (paid requests, indistinguishable from customer traffic) catch model substitution; results feed provider standing.
- Settlement service. Verifies payments in about 100 ms, starts inference optimistically while settlement runs concurrently, and closes hourly epochs with netted payouts and refunds.
- Chain adapters. Every chain touch goes through a five-operation interface (quote, verify, settle, payout, anchor). The auction, metering, and reconciliation never import a chain SDK.
The module map
The code follows the same shape. These are the files that matter in router/src/:
| Module | What it does |
|---|---|
server.ts | Request orchestration: auction, 402, verify, stream, bill |
auction.ts | Scoring plus second-score clearing (the product) |
sessions.ts | Agentic-loop continuity: cache-aware sticky routing, locked session rates, budget draw-down |
quotes.ts | Signed standing-quote book: TTL, capacity, measured TTFT |
payments.ts | x402 flow, optimistic-start bad-debt caps |
proxy.ts | SSE passthrough with token metering and latency timing |
payouts.ts | Hourly epochs: netted payouts, refunds, on-chain anchor |
records.ts | SQLite: usage records, quote log, payouts, replay nonces |
settle/adapter.ts | The chain boundary: quote, verify, settle, payout, anchor |
Two invariants
Two rules hold everywhere in the codebase, and most of the system's trustworthiness reduces to them.
Money is integers. Every amount is integer USDC base units (6 decimals); prices are base units per million tokens. Floats never touch a balance, so sums either tie out exactly or fail loudly. The reconciliation invariant (every payment, refund, and payout nets to zero per epoch) only works because there is no rounding to hide in.
Every quote is a signed commitment. A quote is verified against the provider's key on ingest and logged, which makes it non-repudiable in a dispute. The book contains promises, not indications. See tamper evidence for what that buys.
- router/src/server.ts route wiring and request orchestration
- router/src/auction.ts the scoring and clearing core
- router/src/settle/adapter.ts the five-operation chain boundary