Space stone

Gateway

Governs every call it carries. The routing, metering, watch, and signing plane of the gauntlet. An OpenResty edge plus a Rust sidecar, whispering over a unix socket, signing everything it touches.

edge 127.0.0.1:18080 signing Ed25519 over RFC 8785 JCS default routing Direct

The discipline

Gateway is the local routing, metering, watch, and signing plane. Architecturally it is an OpenResty edge in front of a Rust sidecar, and the two talk over a unix socket with mode 0660. Loopback only: the edge listens on 127.0.0.1:18080. The design runs on two axes, and one Ed25519 audit ledger spans both as a tamper-evident hash chain.

Axis 1: routing and accounting

The opinionless pipe

Forwards, translates, accounts.

An opinionless pipe. It forwards calls, translates provider formats via the Rosetta layer, accounts for tokens and cost, caches, and enforces budgets. It does not editorialize on your prompts.

Axis 2: content trust

The 7-stage decontaminator

Rejects or passes, never rewrites.

A reject-or-pass firewall for structural attacks. It never mutates payloads and never performs semantic analysis. A call comes out the far side exactly as it went in, or it does not come out at all.

One ledger, both axes

Every governed event on either axis lands in a single Ed25519-signed audit ledger: an append-only, tamper-evident hash chain. Routing decisions and trust decisions share one spine, so the record of what the stone carried cannot be quietly rearranged.

Direct vs Governed

Direct is the default: Council calls the provider API or your authenticated CLI itself, and Gateway is not in the path. Governed is an explicit per-seat opt-in that puts the stone between the seat and the provider, adding metering, a budget limit, and a signed audit-ledger record. Gateway is not a maturity ladder, and it needs no arming for normal calls.

Direct

The default. No stone in the middle.

Council talks to the provider API or your authenticated CLI itself. No metering, no budget limit, no ledger record from Gateway. This is how normal calls run.

Governed

Explicit per-seat opt-in.

Select Governed via Gateway for a seat, or set COUNCIL_VIA_GATEWAY=1. The call gains metering, a budget limit, and a signed audit-ledger record.

COUNCIL_VIA_GATEWAY=1

Opt inEnvironment switch that routes Council calls through Gateway. Per-seat selection in the War Room does the same thing for one seat at a time.

Where Governed can reach

Gateway adapters exist only for these transports:

  • grok_api
  • claude_api
  • claude_code
  • openai_api
  • codex_cli
  • gemini_vertex
  • gemini_cli
  • nvidia/nim

Everything else is Direct-only

Governed mode blocks cabinets that reference non-adapter transports. It never silently substitutes another path. If a seat asks the stone to carry a call it has no adapter for, the stone refuses, out loud.

Endpoints

Three families: model calls, ops surfaces, and the admin plane. All on 127.0.0.1:18080.

Model calls

Endpoint
What it does
POST /v1/chat/completions
Chat completions passthrough.
POST /v1/responses
Responses API passthrough.
POST /v1/messages
Messages API passthrough.
POST /v1/batches
Batch operations. The X-Batch-Op header selects create, retrieve, cancel, or list.

Ops

Endpoint
What it does
GET /health
Liveness. Exempt from the rate limit.
GET /metrics
Prometheus metrics. Unauthenticated, localhost-only, and contains no PII and no keys.

Admin

Endpoint
What it does
POST /admin/keys
Mint keys. Requires the bootstrap token or an admin key.
POST /admin/keys/revoke
Revoke a key. No self-revoke.
POST /auth/rotate
Ledger key rotation, recorded as a ceremony event.
GET /ledger/verify
Verify the audit ledger. Admin-tier X-Admin-Key required.
GET /ledger/export
Export the audit ledger. Admin-tier X-Admin-Key required.
/ledger/record
NOT exposed. The ledger is written by the stone, not by callers.
/budget/check
Check spend against the budget for a key.
/budget/record
Record spend against the budget for a key.
/guard/sovereignty
Exists, but is never invoked internally.
/guard/tool
Bounds READ_ONLY tool use.

Ingress headers

How a caller talks to the stone. One is required, one is privileged, and the rest shape routing, budgets, and locality.

Header
Meaning
Authorization / X-API-Keyrequired
Caller identity. One of the two is required on every call.
X-Sensitivity-Leveldefault: GREEN
GREEN is the default. YELLOW is logged-only and has no routing effect today. RED forces local.
X-Council-Role
Passthrough tag. Carried, not enforced.
X-Council-Transport-IDprivileged
Requires service_role=council and a key id equal to COUNCIL_GATEWAY_KEY_ID. Anything else gets 403 ERR_COUNCIL_TRANSPORT_IDENTITY.
X-Routing-Strategy
One of quality, balanced, economy, or speed.
X-Budget-Key
Selects which budget the call spends against.
X-Sovereign-Mode: true
Forces all-local routing.
Idempotency-Key
Deduplicates retried requests.

The ledger

An append-only hash chain, signed with Ed25519 over RFC 8785 JCS canonical bytes. Tamper-evident by construction: rewrite history and the chain stops adding up.

Fact
Detail
LEDGER_SIGNING_KEY_PATHdefault: $HOME/.irin/ledger_key.pem
32 raw bytes despite the .pem name, file mode 0600. Startup is fail-closed: no key, no stone.
Rotation
Via POST /auth/rotate with a dual-signing window. Never replace the key in place while running, and never use ephemeral keys.
Write retries
3 attempts at 50, 200, and 500 ms. After that the stone emits event=ledger_commit_failed: observable, not fatal.
make -C gateway ledger-verify

VerifyChecks the hash chain of the audit ledger.

make -C gateway ledger-fsck

Deep checkFull filesystem check: chain, signatures, key trust, and schema. Exit code 0, 1, or 2 tells you how the stone is feeling.

Fine print, stone-cold

The honest limits. These are quoted from the product's own boundaries, and they stay prominent because they are the point.

Threat model and trust

  • The threat model is an authenticated localhost single-tenant proxy. Gateway is not designed for network exposure, and there is no multi-tenant isolation.
  • The ledger stores hash fingerprints only: no raw prompt content, and no provider request-ID cross-references.
  • Gateway does not classify: no DLP or PII detection, no sovereignty scoring, no semantic analysis.

Bodies, budgets, and state

  • Body cap is 2MB: a 413 at nginx, roughly 1.5MB for vision payloads after base64, and no spill-to-disk.
  • Budget default is $10 per 24 hours per key.
  • Without GATEWAY_DURABLE=1, state is in-memory, with the data-loss risk that implies.

Streaming, proxies, and backups

  • Streaming skips the cache. claude-cli returns 501 because there is no SSE. NVIDIA streaming usage is a byte-length estimate.
  • The CLI proxies in tools/ bind all host interfaces for Docker Desktop and must not be exposed.
  • Backup requires both the state volume and the signing key. Never run docker compose down -v.