Skip to content

The interface

voice-web is a React app built by Vite and served by a small node:http server, deployed as a web-hosted ankka service. Its descriptor mounts the service voice at /api, so the browser's request for /api/public/issues reaches voice as /public/issues at the interface's own address, with the Authorization header unchanged and X-Ankka-Caller: internet set by the proxy. The interface calls these routes and nothing else; it has no state and no database.

Pages

Path Who Shows
/, /search a reader search over issues, sources and options
/issues/{issue} a reader the issue: contributions, questions, evidence, claims, options, the aggregate, investigations, actions, decisions, outcomes, chronology
/issues/{issue}/evidence/{evidence}, /issues/{issue}/claims/{claim}, /sources/{source} a reader an evidence card, a claim's explanation, a source card
/me a visitor start a session with the invite code; the two notices
/me/recover a participant recover a session with the recovery credential
/me/conversations, /me/conversations/{conversation} a participant the conversations; one conversation with its turns, stream and proposals
/me/concerns/{concern}/share a participant the four modes, the exact text, submission
/me/contributions/{contribution} a participant a contribution's state, review outcome, issue suggestion, withdrawal
/me/issues/{issue}/express a participant priority, trade-offs, considerations
/me/inbox a participant updates, with the stream for an open tab
/me/data a participant what Voice holds, and the erasure request
/staff staff sign in with a token from the staff issuer
/staff/queue, /staff/reviews, /staff/erasures a representative, a reviewer, an operator the work queue, the review list, the erasure requests and receipts
/staff/issues/{issue}, /staff/issues/{issue}/evidence, /staff/issues/{issue}/research, /staff/issues/{issue}/actions a representative, a reviewer the dashboard, evidence curation, a research run, recording an action
/staff/investigations/{investigation} a representative an investigation and its tasks

Tokens

A participant's session token and a staff token are held in memory for the tab, never in a cookie or in storage. The client sends the citizen's token to /api/me routes only, the staff token to /api/staff routes only, and nothing to /api/public. A staff person pastes a token from the installation's issuer on /staff; the role it carries is the issuer's, and every route checks it again.

Refusals and states

Every error is read as a refusal by its rule, never by its status alone, so a page can say what was refused and what to do instead. The seven state labels are read from the Voice-State-Labels header the issue and chronology routes send, and rendered from that vocabulary, not the app's own. Every sealed field reaches a page through one component, which shows the value or the text "redacted or unavailable" and nothing else.

Streams

A conversation page holds the conversation's stream and an inbox page the inbox's. The gateway closes a stream at fifteen seconds; the client reconnects, passing the last sequence it saw as its own cursor, because the platform's stream sends no event ids.

Run it

cd web && npm ci && npm run typecheck && npm test
just web

just web runs ankka local web --service voice=http://127.0.0.1:9000 -- npm run dev: the app on 3000, behind the same proxy a cluster runs, with voice named explicitly. The suite tests the client, the paths, the session store, the label parsing and each page's model with an injected fetch; the one thing it cannot see is the browser's own fetch, which the client binds to the global on purpose. Build the image with docker build -t voice-web:<version> web.