INTERFACE

War Room

Every view, every control, every transport. The browser UI and the macOS desktop shell share one backend.

The views

Ten rooms in one War Room. Each view names its controls and exactly what they do. Nothing here hides a second backend: the browser tab and the desktop shell are two windows into the same services.

Deliberate

Streams a multi-seat session over /ws/deliberate.

Between rounds, when the server reports awaiting_input, the operator can send an intervention into the proceeding. The stream speaks in typed events, so the UI always knows which act it is in:

session_started round_started seat_started seat_chunk seat_complete convergence_scored round_divergence round_complete awaiting_input intervention_received synthesis_started synthesis_complete session_saved done error

Direct Fire

One seat, one prompt.

A single-seat focused prompt for when you want one mind on the question, not the whole council.

History

The record of proceedings.

Saved sessions, their synthesis, their lineage, and their exports. What the council said stays findable.

Outbox

Reads signed Gateway directives.

This is where signed directives surface. An empty Outbox means there is no signed directive from the configured Gateway. It is not an error, it is a quiet gauntlet.

Watch

Sentinels, fires, and the watch plane.

Shows registered Sentinels, their fires, and watch-plane status. It stays readable while action production is disabled, because observing is not acting.

Discover

Scans exact transports, nothing more.

Three probes: non-empty API-key environment variables (names only, never values, no billable call), supported local CLI binaries, and reachable local runtimes. Unavailable rows stay visible for setup guidance but are disabled in selectors. Rescan refreshes the browser-cached result. There is no background polling.

Cabinets

Reads and edits local cabinet YAML.

The server is the YAML-validity authority, and it refuses to overwrite built-ins. Cabinet keys must match ^[a-z0-9][a-z0-9_-]{0,63}$.

Drift

Compares normal versus blind reruns.

Same question, run with and without context, so you can see what the context actually bought. Notifications are delivered via COUNCIL_WEBHOOK_* environment variables.

Librarian

An optional tab, never a dependency.

Proxies a separately configured local Librarian service. If that service is offline, Council and Gateway carry on untouched. Memory is a guest here, not a load-bearing wall.

Settings

The bases and the token.

API base, WS base, Gateway base, Librarian base, auth token, and Council root, all stored in local storage. Prefer loopback: a non-loopback endpoint can leak the token.

The transport matrix

A seat is bound to exactly one transport, and that binding is never silently switched. These are the built-in transport IDs and what each one actually talks to.

Transport ID
What it carries
grok_build
Grok Build CLI, the authenticated grok CLI.
grok_hermes
Grok via the Hermes adapter.
grok_api
xAI API, keyed by XAI_API_KEY.
claude_code
Claude Code CLI.
claude_api
Anthropic Messages API, keyed by ANTHROPIC_API_KEY.
codex_cli
Codex CLI.
openai_api
OpenAI Responses API, keyed by OPENAI_API_KEY.
gemini_agy
Gemini via the agy CLI.
gemini_vertex
Vertex AI, via Google ADC plus VERTEX_PROJECT, VERTEX_LOCATION set to global, and VERTEX_GEMINI_MODEL.
gemini_cli
Legacy Gemini CLI.
nvidia/nim
Keyed by NVIDIA_API_KEY.
nous
Keyed by NOUS_API_KEY.
deepseek
Keyed by DEEPSEEK_API_KEY, or a configured NIM or Nous model.

Seat ID rules

  • One seat ID equals one transport. The binding is never silently switched.
  • Legacy IDs stay readable: grok, grok_cli, hermes_cli, claude, gpt, gemini, agy_cli.
  • Custom slugs via ~/.config/council/providers.toml cannot redefine reserved built-in IDs.
  • Gateway adapters exist only for grok_api, claude_api, claude_code, openai_api, codex_cli, gemini_vertex, gemini_cli, and nvidia/nim. Everything else is Direct-only.

The REST surface

The whole spoken surface of the Council backend, plus the one WebSocket. Authentication is a bearer token when COUNCIL_AUTH_TOKEN is set; the WebSocket carries that token via a negotiated subprotocol, and Gateway may require X-Gateway-Auth.

Route
Purpose
GET /api/health
Liveness and health of the backend.
GET /api/discover
The transport discovery scan behind the Discover view.
GET /api/cabinets
List local cabinets.
POST /api/cabinets/save
Save cabinet YAML, validated by the server.
POST /api/deliberate
Start a deliberation over plain HTTP.
GET /api/sessions
Saved sessions for the History view.
GET /api/precedent
Precedent lookup.
/api/drift/*
Drift comparison routes.
/api/mapmaker/*
Mapmaker routes.
/api/meta-review/*
Meta-review routes.
/api/librarian/*
Proxy routes to the optional local Librarian service.
GET /ws/deliberate
The streaming deliberation WebSocket behind the Deliberate view.

Real receipts

Not mockups. These frames come from an actual proceeding on the actual UI.

Animated capture of a real War Room deliberation session with five seats across two rounds, ending in a filed ruling.
A real proceeding: five seats, two rounds, a filed ruling.
Poster frame of the same War Room session.
Poster frame of the same session.
The filed chair ruling shown beside the seats in the War Room.
The filed chair ruling, beside the seats.

Persistence caps

Storage has hard edges, and the edges are numbers. Chat patches use optimistic concurrency via If-Match, so two writers cannot silently trample each other.

Field
Cap
Chat file
2 MiB per file.
Assistant message
64 KiB.
Source snippet
1 KiB.
Sources per turn
20.
User content
8192 B.
Title
120 B.
Cabinet name
64 B.
client_msg_id
64 B.

Fine print, read before trusting

  • A detected CLI binary is not proof its login is still valid. The first real seat call is.
  • Discovery and routing YAML never prove model entitlement.
  • The War Room blocks a Governed start when the cabinet references a Direct-only transport.
  • COUNCIL_DEV_NO_AUTH=1 must never run on a network-accessible service.
  • Do not delete or rewrite historical sessions without a migration plan. New fields need serde defaults.