Files
stack/docs/plans/2026-10-11_webui-preview.md
T

5.2 KiB

WebUI preview behind sign-in (2026-10-11)

Status: written by Sage, lead. It follows docs/plans/BRIEF-TEMPLATE.md. One row. Amended the same evening for Jason's Q91 answer "A" (lead decision 93): a read-only preview at its own hostname, behind Pocket ID forward auth limited to Jason's group, the old build left up. Target: Jason signs in on 2026-10-12, by 2026-10-13T05:00Z at the latest. The service script is cut from this row for that date; it is a follow-up.

WebUI preview: a signed-in, read-only Console behind the edge proxy

Problem

Jason asked for an ETA for the refactor WebUI at mosaic.woltje.com. Today the Console can't be reached from anywhere but the host it runs on, by design:

  • startServer refuses to bind to a non-loopback address (packages/webui/src/serve.mjs:82) and answers 403 to any request whose Host isn't loopback on its own port (serve.mjs:92). A reverse proxy that forwards the public Host is refused.
  • The Origin check (serve.mjs:96) compares against http://<Host>, so a browser on https://<public name> is refused as cross-origin.
  • There is no sign-in. Anything that connects to the port reads what a reader capability reads (packages/webui/README.md, the paragraph on the bus views), and the README forbids a public proxy.
  • Two routes write: POST /api/seen and /api/reply go to the board.
  • Nothing runs the Console as a service. It is a foreground serve.

Mos approved these cuts for the preview (2026-10-11): sign-in at the edge through Pocket ID forward auth, not login code in the Console; a preview hostname, so the old build at mosaic.woltje.com stays; read-only; not held for S7 (row 42) or Gate E (row 5). ops-01 owns the Pocket ID client (limited to Jason's group), the DNS name, TLS, the proxy and the route from the edge to this host. The route opens once rev-sol-01 passes ops-01's proposal (Q91 A).

Owner and reviewer

Owner: Dewey, who owns the WebUI, starting 2026-10-11 evening. Reviewers: Darkwing and Filbert, ahead of their own rows.

Files owned

  • packages/webui/src/serve.mjs, packages/webui/src/cli.mjs, packages/webui/README.md and packages/webui/tests/
  • agents/dewey/work/queue-<row>/

What ships

A preview mode, off by default. Without its flags the Console behaves exactly as today, and the existing tests show it.

  • serve --public-host NAME --auth-header HEADER --allow-user ID, all three or none. NAME is a hostname, no port or scheme. The bind stays loopback; nothing here listens beyond it.
  • In preview mode a request's Host must be NAME, or loopback on the Console's port as today. An Origin, when sent, must be https://NAME for a NAME request, and the loopback form for a loopback request.
  • A NAME request must carry HEADER with a value equal to ID. Missing or different answers 401 with no bus read made. Loopback requests are unchanged, so the operator's own browser on the host still works.
  • In preview mode every POST answers 403 read-only, whatever the Host. /api/seen and /api/reply don't reach the board.
  • Tests for each refusal: wrong Host, wrong Origin, missing header, wrong user, a POST to each write route, and a partial flag set refused at start. A test that the loopback path is unchanged in preview mode.
  • The minimum for the first sign-in is the inbox, tasks, agents and trails (/api/bus/*) read-only. The other GET routes stay as they are. The board panel reads the board already running on 7331; if it is down the panel shows its existing error and the bus views still work.
  • The README replaces "Do not expose either unauthenticated server through a public proxy" with what the preview mode requires of the proxy: it strips HEADER from every client request and sets it only after Pocket ID signs the user in, it preserves Host, it terminates TLS, and it is the only thing that reaches the port from off the host. It says plainly that any local process can still connect and send HEADER, the same exposure the README already describes for the port.
  • The packet runs the Console in preview mode with curl against loopback, showing each refusal and one allowed read, with no network beyond the host.

Out of scope

  • OIDC or login code in the Console; Pocket ID config; DNS, TLS, the proxy and the route from the edge (ops-01).
  • Resolving decisions or replying from the browser. REQ-DEC-3 keeps resolution in the CLI; changing that is a ruling and its own row.
  • Live-seat conversations (CHAT-07) and any board change.
  • A container image, CI deploy, or changes to the bus unit.
  • A service script and unit template (scripts/webui-service.sh on the bus-service.sh pattern). That is a follow-up row.
  • Starting it on dragon-lin. After the row lands, Sage starts the preview as a transient user unit (systemd-run --user, loopback port 7332; 7330 holds the operator's own Console). The edge reaches it once rev-sol-01 passes ops-01's route.
  • The group check. Pocket ID and the proxy limit sign-in to Jason's group; the Console checks the header against the one user id.

Gate

Darkwing and Filbert approve on the row's issue naming the candidate manifest. The webui, control-board and cli node suites and every scripts/test-*.sh green on Sage's gate rerun.