feat(coord): browser approval console (token-gated, read+decide)#77
Merged
Conversation
Adds GET /console — an operator approval console served by the coordinator: a
static, data-free HTML shell (inline CSS/JS, no CDN) that lists runs paused at
an approval gate and lets an operator approve/reject them from a browser.
Security model (built against a dedicated security review):
- The shell is EXEMPT from auth (like /api/health) because a browser navigation
carries no bearer header. It embeds ZERO run data and ZERO secrets.
- All data reads (/api/runs, /api/runs/{id}) and the mutation
(/api/runs/{id}/approve) go through the EXISTING authed endpoints via fetch()
with an operator-supplied bearer token held only in memory and sent only in
the Authorization header — never in a URL or storage; cleared on unload.
- All dynamic DOM is built with textContent/createElement (never innerHTML), so
a hostile run_id/step_id cannot inject markup or script.
- Approvals still require the per-gate S9 token, typed by the human.
- Response denies framing (X-Frame-Options: DENY + CSP frame-ancestors 'none')
and caching (Cache-Control: no-store).
Scope: approve/reject only. Trigger-arbitrary-workflow is deliberately out of
scope (higher blast radius). +2 security tests (shell served without bearer /
carries no data / has headers; /api/runs still 401s without bearer).
Follow-up to the security re-review's one non-blocking suggestion: the console response now sends a full CSP (default-src 'none'; style/script 'unsafe-inline'; connect-src 'self'; frame-ancestors 'none'; base-uri 'none') instead of just frame-ancestors. The page is fully self-contained same-origin, so this only denies exfiltration/injection avenues — no functional change. String-only edit; the header test still asserts frame-ancestors is present.
|
Bench compare Threshold for regression: ≥ 10% slower mean.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
GET /console— an operator approval console served by the coordinator: a static, data-free HTML shell that lists runs paused at an approval gate and lets an operator approve/reject them from a browser. Read + decide only; no trigger/cancel (higher blast radius, deferred).Security model (built against a dedicated review; adversarial re-review returned GO)
/api/health) because a browser navigation carries no bearer header. It embeds zero run data and zero secrets./api/runs,/api/runs/{id}) and the mutation (/api/runs/{id}/approve) go through the existing authed endpoints viafetch()with an operator-supplied bearer token held only in memory and sent only in theAuthorizationheader — never in a URL or storage; cleared on unload.textContent/createElement(neverinnerHTML) → a hostilerun_id/step_idcannot inject markup or script.X-Frame-Options: DENY+ CSPdefault-src 'none'; …; frame-ancestors 'none'; base-uri 'none'+Cache-Control: no-store.Verification
--features servetest suite green (coordinator 53 incl. 2 new security tests: shell served without bearer / carries no data / has headers;/api/runsstill 401s without bearer). fmt + build clean./api/runs401 without token / lists the paused gate with token.2 files touched region (
crates/llmvm-cli/src/coordinator.rs), +~250 lines. No existing behavior changed.