Overview
How the pieces fit
A cell is the unit of isolation. Inside it a router stores every job before it moves, a directory issues and revokes seat tokens, and a database belongs to that cell alone. Seats sit outside the cell and connect in.
A tenant is a cell
Nothing is shared between tenants except the host. A seat is a process on some machine with a token; the router verifies the token, stores the job, and pushes it over a stream the seat holds open.
┌──────────────── tenant cell ────────────────┐
agent seat ──▶│ │◀── browser seat (PC)
(any host) │ router ── stream ──▶ seat │
│ │ │◀── executor seat (host)
mail seat ───▶│ │ durable queue (SQLite, cell-owned) │
(adapter) │ ▼ │◀── test seat (CI)
│ directory (tokens, revocation snapshot) │
└───────────────────────────────────────────────┘
The life of a job
A job is stored before it is delivered, delivered once per acknowledgement, and its result returns to the seat that asked — not to whoever happens to be listening.
caller ──① POST /agents/caller/a2a──▶ router ──② store──▶ SQLite
│
├──③ SSE job──────▶ seat (adapter)
│ │ execute
│◀──④ POST result─────┘
└──⑤ SSE egress───▶ caller
│
⑥ ack ────┘
(no ack → retry until TTL)
Four kinds of seat
The router does not know what is behind a seat. What differs is where the seat runs, what identity it holds, and what it is allowed to return.
person ─┬─ agent seats anywhere model-driven work
├─ mail seat adapter host reads mail, returns drafts only
├─ browser seat owner's PC real session; personal = metadata only
└─ executor seat owner's host approved commands, bound to the task
test seat: CI, deterministic suite
Where it runs
The same cell shape covers a hosted tenant and a cell on a customer's own host. A PC seat needs no inbound port: it connects out, so a laptop behind NAT is a normal seat.
(a) hosted tenant VPS ─ cell(router, directory, db) ─ web console
(b) customer host their server ─ cell ─ their seats
(c) PC seats laptop/PC ── outbound stream ──▶ cell (no open port)
(d) tests CI runner ── suite ──▶ failure bundle ──▶ dev agent
Private and public routers
Private and public are two deployment profiles of the same router source, not two products. The contract is shared — API, envelope, correlation, origin return, idempotency — and the differences live in the transport: authentication, registration and address policy.
A public router faces the internet: verified TLS, seat tokens, tenant and address policy, request quotas. A private router sits on the owner's machine or inside the network: loopback only, no auth, seats register themselves and hold an address lease.
internet
│ verified TLS · seat tokens · tenant/address/quota
┌─────▼──────┐
│ public cell│ router · directory · db · web console
└─────▲──────┘
│ r2r link: signed envelope, path fixed in advance,
│ receipts returned to the origin
┌─────▼──────┐
│ private │ loopback · no auth · auto-registered seats
│ router │ (owner's machine or inside the LAN)
└─────┬──────┘
│
local seats: executor · browser · codex · opencode
What an adapter does
An adapter is the only place where a message becomes a system action. It holds a seat token, keeps a stream open, receives a job, decides whether the request is allowed, executes it, and returns a result bound to the seat that asked.
The router never interprets the body. It stores, delivers, retries and records; the adapter owns the interpretation, the permission check and the record of what it touched.
seat token ─▶ [ transport ] SSE in · POST result / egress ack out
│
▼
[ policy ] origin allowlist · domain list · content policy
│
▼
[ executor ] system API · browser step · mail draft
│
▼
[ result ] status · data · activities ──▶ origin seat
│
↺ reconnect with backoff · renew the token · retry once on a transient code
Adapters share one shape
The loop above is identical for every adapter; only the executor changes. That is why a new integration is a new executor, not a new protocol.
mail Gmail push / IMAP poll ─▶ triage seat ─▶ proposal ─▶ human ─▶ draft
browser step list ─▶ ego lite on the owner's PC ─▶ page data / screenshot
executor task ─▶ approved command on the host ─▶ result bound to the task
engine job ─▶ codex / opencode session ─▶ result bound to the conversation
router envelope ─▶ next router ─▶ receipt back along the same path