Skip to content
Version: Current

Architecture

acumen is one Go binary with one listener. Three request shapes arrive at it and one routing path answers all of them; everything else is a view onto that path.

127.0.0.1:4020 serves the model plane (/v1/*) and the operator surface (/api/*, /console, /ide) from the same process. Nothing binds a public address. The public origin https://ai.tcp.systems is the edge, and the edge forwards only the model plane; the operator routes answer 404 there. That split is why a virtual key can be public while an admin key can exist at all.

POST /v1/chat/completions (OpenAI), POST /v1/messages (Anthropic) and POST /v1/responses (OpenAI Responses) all resolve to the same order: authenticate the key, resolve the model alias, pick candidates from the offer table, then call seats in order until one answers.

The door decides the envelope, not the behaviour. An Anthropic-shaped request that is answered by an Anthropic seat still returns an OpenAI-shaped body, because the reply is converted on the way out. A client that expects content[] blocks will not understand it. See the API reference for the exact shapes.

A seat that fails is cooled down rather than removed, and the same request continues on the next candidate. The same deployment is never retried, so a vendor that fails fast costs one attempt, not two. When every candidate fails the answer is 502 with a code from the error reference, and the cooldown window is the plan default rather than the vendor’s Retry-After.

Seats, keys, the grant ledger, the attempt ledger and console sessions are tables, not files. The process re-reads the seat set every 30 seconds (cmd/acumen/main.go:85), which is why adding a provider is a write plus a wait, not a restart. This is also why a rollback of the binary is safe: the state survives it.

  • A seat change is not instant; the reload window is a real delay.
  • A vendor’s own rate-limit advice is ignored, so a long pause is not honoured.
  • The operator surface has no network boundary of its own. It is loopback-only by construction, and putting it behind anything else is a decision that needs its own record.

The decisions behind these choices are in Decisions.