/console, with no client-side JavaScript. The server, the gateway (on its
own port) and reversi console can all serve it.
Modes
The console is off by default and serves nothing until you choose a mode.reversi.config.yaml (console: { mode: login }; for open, role:), and for the server
REVERSI_CONSOLE_ROLE=auditor|approver|operator picks the open-mode role.
Open mode is for a laptop. It has no login, so it is fenced:
- Only requests whose
Hostislocalhost,127.0.0.1or::1are answered, anyOriginmust equal the host, andSec-Fetch-Sitemust besame-originornone. Anything else gets a 403 page (tests cover foreign hosts and origins). - The server and the gateway refuse to start in open mode if they listen on a non-loopback address (
HOSTfor the server,listen.hostfor the gateway; the Docker image setsHOST=0.0.0.0, so useloginthere). They also refuse theadminrole. - It is labelled “open console, no login” in the header.
Pages
Run detail
- Auditor report (HTML and PDF) and a signed evidence bundle (needs a signing key). Replay with a change appears when capture is on: change one argument and see which decisions would differ, in a sandbox that executes nothing.
- Resolve (operator): for a step stuck at
intendedoruncertain, choosesucceededorfailedand write how you checked. Recorded with your name. - Rollback plan: each action, the compensation tool, whether it needs approval, residue, and whether the undo is read back.
- Run the rollback / Retry the rollback (operator): shown when a compensator is configured. It runs in the background, the page refreshes every 2 s, and the result lists compensated, failed and unverified steps. The checkbox “also approve compensations that need approval” works only if you hold the approver role too. Without a compensator the page says how to run it elsewhere.
- Escalate to on-call (operator): shown when a compensation failed or a step is unrecoverable. Writes an
escalatedrecord and sends a critical notification.
Approvals
Each row shows why approval is needed, the who may decide column (the required roles or groups, “any approver”, the quorum progress such as1/2, an escalated badge) and argument names only, never values. Approve and deny take a note.
A requester cannot approve their own request, and the server applies the same role, group and quorum rules as the API.
Break-glass (admin) overrides a pending request with a reason of at least 10 characters and an evidence reference such as
a ticket; it is marked break-glass on the approvals page and in the decision note. Delegation hands your approval authority to someone for 1 to 2160 hours;
the delegate decides as themselves on your behalf. It appears only when the engine services are present.
Manifests
Needs a registry folder (created byreversi manifests publish, or by reversi apply) as the manifests: setting. With
plain YAML files the console shows manifests but they are read-only, and the page says so. With a registry:
Every save is a new version; history shows the author, approver and signature. An approve or restore reloads the running
configuration, and if the new configuration does not load the page says so and the previous one stays active. Drafts from
MCP tools come with a confidence level and notes; annotations are the server’s claims and only ever produce drafts.
A manifest’s
approval: object (roles, quorum, timeout, escalate_to) is kept as written by save, approve, deprecate and restore.
Policy
Needs a policy versions folder (reversi policy publish) as the policy: setting; a single policy file is shown
read-only. The rule builder (tool, effect, environment, tenant, data class, one argument condition, approver groups, quorum)
adds a rule to the editor. Any console role can validate (runs the policy’s own tests, shows a diff against the active
version) and simulate against the last 7 days of captured calls (needs capture on): newly blocked, newly needing
approval, newly allowed. Publish and activate, publish without activating, and activate an older version (which is how
you roll back) need admin. Versions list the author, tests passed and note, with diffs between neighbours.
Settings
What needs which role
Buttons the role cannot use are hidden and the POST is refused anyway.
Security
- The session cookie is signed (HMAC-SHA-256),
HttpOnly,SameSite=Strict, scoped to/console, andSecurewithREVERSI_SECURE_COOKIES=1. It lasts 8 hours and ends on restart unless you setREVERSI_SESSION_SECRET. - Every action is a POST with a per-session CSRF token. The login form body is capped at 16 KB.
- All values are HTML-escaped and responses carry a strict
Content-Security-Policy(default-src 'none',form-action 'self'). - Roles are checked on every page.

