Files
Gitea-Tools/docs/webui-restart-console.md
T
sysadminandClaude Opus 4.8 9b8e315b49 docs(webui): document the read-only restart console surface (#667)
Records the two GET routes, what each panel consumes, and the three
properties the surface is held to: an unreadable source reports
unavailable rather than green, authorization is probed with
for_execution=True so a Phase 1 refusal is never shown as an allow, and
the control-plane database is opened mode=ro so reading status never
creates it.

Refs #655 #642 #658 #661 #662 #663 #633 #652 #653 #664

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-25 17:26:30 -04:00

4.7 KiB

Web Console: restart status, impact preview, and approval state (#667)

Phase 1 of the console restart surface. It consumes the #655 coordinator substrate and displays it. It performs no restart, reload, drain, approval, or process action, and it registers no write endpoint.

Issue #667's rollout is explicit — status views first, write approval after the backend gates are green — and this change delivers only the status half.

Surfaces

Path Method Purpose
/runtime/restart GET Restart status page
/api/v1/system/restart/status GET Same snapshot as JSON

Both accept an optional restart_class query parameter (default full_mcp_restart). An unrecognised class is not an error: the coordinator resolves it as unknown and fails closed, and the page shows the resulting deny.

Neither path accepts POST; a write attempt returns 405, and a test asserts it.

What it shows

  • Impact preview (#658) — verdict, blast radius, affected sessions, leases, critical sections, mutations, and the counts behind them, evaluated dry_run=True against live control-plane state.
  • Drain proof (#661) — verification of a supplied proof: valid, clean, expired, tampered, and the reasons behind a refusal.
  • Post-restart reconcile (#662) — the most recent completion proof, its overall status, and which dimensions still require follow-up.
  • Restart classes (#663) — the least-privilege matrix, with you may request and you may execute computed for the viewing role rather than for a generic operator.
  • Approval controls (#633) — the authorization state of system.restart_namespace and system.reload_namespace.
  • Break-glass (#664) — declared and marked unavailable; see below.

Three rules this surface holds itself to

A status page that is wrong is worse than one that is missing, because an operator acts on it. Three properties are enforced by tests, and each was verified by reverting the guard and watching a test fail.

An unreadable source reports unavailable, never green

Every source carries its own SourceStatus. Nothing substitutes a default, placeholder, or self-comparison for a reading that failed. An unreadable control-plane database yields inventory_complete: false, which the coordinator itself turns into a fail-closed verdict, and the page says the blast radius is unknown rather than showing an empty affected-sessions table.

An absent drain proof is reported as absent — not as a pass. The #661 gate authorizes a restart only against a valid, unexpired, clean proof, so no proof is precisely the state that gate denies on.

Authorization is asked the way execution would ask it

Every probe passes for_execution=True.

Asked without it, an admin is allowed for system.restart_namespace. On a control surface that reads as a live button. Asked the way an execution attempt would ask, the same principal is refused phase_not_active, because the console is in Phase 1 and the action is Phase 2. This surface reports the second answer.

execution_enabled is therefore false for every action and every role today, and a test asserts that across the whole role matrix.

The control-plane database is opened read-only

ControlPlaneDB() creates directories and runs migrations on construction — a write. This surface never constructs one. It opens the sqlite file with mode=ro, exactly as webui/inventory.py does, and treats a missing file as missing authority rather than as an empty inventory.

The test that protects this points at a path inside a directory that already exists, so a read-write connect would really create the file. A nested missing-directory path would have passed for the wrong reason.

Break-glass is declared, not offered

The break-glass workflow (#664) is not available on this branch's base. The panel is rendered to operator-class roles as unavailable, naming the issue that tracks it. It is not silently omitted, because an operator who has been told a governance path exists needs to see that it is not wired here; and it is not rendered as a control, because there is nothing behind it.

Unprivileged viewers see only a note that the surface is operator-class.

Redaction and escaping

Every interpolated value passes through _esc (html.escape(..., quote=True)). Free-form text and anything that can carry a filesystem path additionally passes through webui.inventory.scrub_text, which redacts credential-shaped tokens inside a string rather than only at its start. The impact payload is passed through webui.inventory.scrub before rendering.

Linkage

Parent #655 · extends #642 · consumes #658, #661, #662, #663 · RBAC #633 · console #631 · vision #652 · roadmap #653 · break-glass #664.