Web UI¶
kew serve opens a unified cockpit — a left-rail shell framing all governance views in a SvelteKit SPA. Everything runs on localhost by default and reads your existing run history; no account, no external service.
pip install 'kew-cli[serve]'
kew serve # → http://127.0.0.1:8765 — prints a tokenized link to open /inbox
See kew serve reference for every flag and example.
Cockpit shell¶
The left rail gives you six destinations:
| Destination | What it does |
|---|---|
| Inbox | Approve or deny holds; see fleet status and active budget caps. Sign in here. |
| Runs | Read-only fleet history — every dispatched run, status, model, and cost. |
| Budget | Edit spend caps live (CSRF-protected writes). Changes take effect immediately. |
| Scorecard | Cost-per-model table: merge rate, review rounds, and $/merged PR. |
| Epics | DAG live view — epic progress, issue states, dependency graph. |
| Loops | Active loop status and history. |
Signing in¶
kew serve prints a tokenized link at startup — http://127.0.0.1:8765/inbox#token=…. Open it in a browser: the token in the URL fragment is exchanged once for an HttpOnly session cookie (never sent to any log). After that the URL strips to /inbox and all write surfaces unlock.
If you navigate to the cockpit without the token fragment, /inbox shows a prompt to open the link from your terminal.
Inbox¶
The Inbox is the write heart of the cockpit: needs-approval holds with Approve / Deny controls, a fleet stop button, and a read-only budget summary with a link to /budget for editing.
Both approval actions are audited identically to CLI approval (source="web", policy-bound), so the web surface does not weaken the audit trail.
Runs¶
Read-only table of every dispatched run: issue number, title, model, cost, start time, and status. Shows an offline state when kew serve is unreachable.
Budget¶
Editable spend caps. Each field shows its kew.toml base value and any active runtime override. Save writes the new value; Revert removes the override and restores the kew.toml value.
Every write requires a CSRF token (X-Kew-CSRF header read from the kew_csrf cookie) and passes an Origin check, so the budget endpoint cannot be driven by cross-site requests.
Scorecard¶
Cost-per-model table for the last N days (default 30). Columns: model name, run count, merge rate (merged / opened), average review rounds, and $/merged PR — the cost-efficiency signal for choosing between models. Local models (lemonade/*) show "local · free" in place of a dollar figure.
Authentication (in brief)¶
- A random token is printed at startup in the
#token=URL fragment (never sent to the server or any log); the browser exchanges it once for an HttpOnly,SameSite=Strictsession cookie. - Writes additionally require a CSRF token (
X-Kew-CSRF) and pass an Origin check; every request is validated against a Host-header allowlist (DNS-rebinding defense). - Loopback by default. Binding a non-loopback address requires explicit
--expose(or[flight_recorder].expose = true); when exposed, all API reads require a session too.
Full model — token storage, rotation, expose rules, actor honesty — in Security › Approval inbox and the kew serve reference.
Security posture¶
- Localhost-first. Default bind is
127.0.0.1; the read UI is local-only unless you opt in. - No TLS. kew does not terminate TLS — front it with a reverse proxy (nginx, Caddy) for HTTPS, and enforce an auth proxy when exposing beyond the machine.
- Single-operator. Web approvals are attributed to the OS user running
kew serve(the token proves possession, not distinct identity) — the same trust model as CLI approval on the host.
Related¶
kew serve— command reference (flags, API routes, examples).kew stop/kew approve— CLI equivalents for inbox actions.- Security — security model for the inbox auth.
- TUI — in-terminal equivalent.