Phase 1 of the MCP Control Plane Web Console (#631) defines the model that
future gated writes must pass through, and enables none of them.
The read-only MVP (#426-#436) ships with no authentication; protection comes
from network placement alone (#435). That is adequate while every route is a
GET and inadequate the moment Phase 2 wires a write. This lands the authority
first, so no write can later be added without something to check it against.
webui/console_authz.py
Identity sources (none / local-dev / access-proxy), a four-role matrix
(viewer, operator, controller, admin), the privileged-action list, and a
fail-closed authorize(). Every action maps to a task_key in
task_capability_map, so the console cannot invent an authority the MCP layer
does not already define. Roles are always server-side configuration, never a
client assertion. Deny reasons are closed and enumerated; there is no
implicit allow branch, and even an allow reports execution_enabled=false
while ACTIVE_PHASE is 1.
webui/console_redaction.py
One redaction pass for API payloads, rendered HTML, logs, and audit records.
Reuses gitea_audit.redact as the shared authority rather than forking it,
then adds console patterns for keychain references, credential assignments,
PEM private-key blocks, and JWTs. Never raises: an unredactable value
degrades to the placeholder rather than being emitted raw.
webui/console_audit.py
Console-side audit records, which gitea_audit cannot supply: it records MCP
mutations and carries no console actor, identity source, correlation id, or
retention class, and an authorization denial is not a mutation at all. The
two are additive and join on correlation.request_id. Records are redacted at
build time, re-scanned at write time, and dropped rather than persisted if
they still trip a detector. Retention is per-record; an unknown action is
retained as privileged rather than standard.
webui/app.py
Attaches an authorization block to the existing preview and attempt routes
and records the decision. The terminal outcome is unchanged - gated_actions
still fails closed for every action - so this cannot loosen anything. Adds
GET /api/console/security-model publishing the three policies as JSON.
Probe authentication is deliberately declarative in this slice:
probe_auth_required() reports operator intent and no route consults it. The
documentation says so plainly and a regression test pins the not-enforced
status, so wiring it in Phase 2 is a deliberate change rather than a silent
one. An operator who sets the variable believing it protects a probe would be
worse off than one who knows it does not.
Tests: tests/test_webui_console_authz_audit.py - 75 passed, 93 subtests,
covering each acceptance criterion and each test the issue requires
(redaction units, default-deny for unauthenticated write stubs, audit record
creation for a simulated privileged preview).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Add the declarative worker registry that epic #797 makes the source of
truth for the scheduled multi-LLM worker fleet.
Providers and configured workers are modelled as separate entities so a
provider can be listed with no worker configured, and so provider facts
are not copied into every worker record. A worker records provider,
model, project, role, namespace, profile, workflow, schedule, timeout,
enabled state, and scheduler metadata.
Validation fails closed: unknown fields are refused rather than ignored,
so a typo cannot silently disable a timeout; a worker naming an
undeclared provider is rejected; worker ids, provider ids, and
LaunchAgent labels must be unique.
Persistence is atomic (temp file in the same directory, fsync, replace).
Every superseded document is retained as a numbered revision, and
rollback republishes a chosen revision as a new head, so history stays
append-only and a rollback is itself reversible.
The credential-rejection guard is extracted to webui/registry_safety.py
so both registries share one implementation instead of two copies of a
security check; project_registry.py keeps identical behaviour.
Scope: data model, validation, persistence only. No routes, scheduler,
process control, or provider probing - those are #799/#800/#804/#805.
The workers array ships empty because populating it is #808.
Tests: tests/test_webui_worker_registry.py, 44 cases.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Add consolidated MVP route/read-only tests, path-filter helpers, and
scripts/test-webui plus scripts/ci-webui-check for Jenkins path-filtered
runs on PRs touching web UI surfaces.
Closes#436
Conflict resolution left webui/lease_loader.py importing ISSUE_LOCK_FILE
from merged_cleanup_reconcile (not exported); import from
issue_lock_provenance instead. Validate WEBUI_HOST before loading the
full app stack so 0.0.0.0 refuse exits immediately.
Addresses reviewer findings on PR #456 / issue #435.
Add bind-host assessment that refuses 0.0.0.0/:: without override,
warns on non-loopback binds, and documents internal-only MVP serving.
Health endpoint exposes deployment metadata; docs cover Access/VPN/WARP
and runtime env assumptions without embedding secrets in the client.
Closes#435
The conflict-resolution rebase incorrectly kept ISSUE_LOCK_FILE in the
merged_cleanup_reconcile import, but that symbol was moved to
issue_lock_provenance on master. This broke webui import collection.
Fixes the reviewer-reported regression on PR #448.
Adds read-only /runtime and /api/runtime routes showing active profile,
identity, config model, git sync vs remote master, shell health,
workflow/schema hashes, and stale-runtime warnings with #420 guidance.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
When Gitea credentials are missing or the queue fetch fails closed,
show "Not loaded" instead of "No open items." and keep pagination
marked unavailable. Successful empty inventories still show the empty
state with complete pagination proof.
Closes#458
Adds read-only /leases and /api/leases routes for issue claim inventory,
reviewer PR lease comments, duplicate PR/branch warnings, and local issue
lock visibility without acquire/release mutations.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Adds read-only /worktrees and /api/worktrees routes that scan branches/
directories, classify worktree state, flag missing preserved worktrees from
the issue lock, and surface the canonical cleanup prompt without deletion.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Expose /audit and /api/audit for local validator previews using
final_report_validator and review schema checks. Operators can paste
reports, auto-detect task kind, and get structured findings with
suggested next prompts and issue-comment drafts.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Expand queue dashboard tests for stale/duplicate badges, title-linked
issues, and nav coverage; drop unused import in queue_loader.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Adds read-only /queue and /api/queue routes that load open PRs and issues
from the default registry project via gitea_auth, surface pagination proof,
and classify items with claimed/blocked/in-review/duplicate badges.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Surface short copy/paste operator prompts derived from canonical workflow
files with workflow path and SHA-256 citations. Adds /prompts, /prompts/{id},
and /api/prompts with one-click copy.
Closes#428
Expose short copy/paste operator prompts on /prompts derived from canonical
workflow files with workflow path and SHA-256 hash. Adds JSON export at
/api/prompts, copy buttons, and tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Starlette read-only MVP with shared layout, /health JSON liveness, and
route stubs for projects, prompts, runtime, audit, worktrees, and leases.
Includes scripts/run-webui, docs/webui-local-dev.md, and tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>