Finishes PRD 0017. The `cred-proxy-block` MCP tool is renamed and
its remediation apply path is repointed at egress-proxy.
- `claude_bottle/supervise.py` — `TOOL_CRED_PROXY_BLOCK` →
`TOOL_EGRESS_PROXY_BLOCK`; `COMPONENT_FOR_TOOL` maps the new
tool ID to `egress-proxy` for audit-log routing.
- `claude_bottle/supervise_server.py` — tool definition renamed
+ description rewritten: "Call when egress-proxy refused your
HTTPS request ... Read the current routes.yaml from /etc/
claude-bottle/current-config/routes.yaml, compose a modified
version, pass the full new file plus a justification." The
syntactic validator dispatches on the new tool ID.
- `claude_bottle/backend/docker/egress_proxy_apply.py` — renamed
from `cred_proxy_apply.py`. Reads routes.yaml from
/etc/egress-proxy/routes.yaml via `docker exec cat`; validates
via `egress_proxy_addon_core.load_routes` (so both sides use
the same parser); writes via `docker cp`; SIGHUPs egress-proxy
with `docker kill --signal HUP`. `EgressProxyApplyError`
replaces `CredProxyApplyError`.
- `claude_bottle/cli/dashboard.py` — wires the new apply +
`discover_egress_proxy_slugs` helper; the operator-initiated
`routes edit <bottle>` verb now writes to egress-proxy with
`.yaml` suffix. Stale follow-up comment about path-aware
filtering removed — PRD 0017 settled that question.
- `tests/integration/test_supervise_sidecar.py` — restores the
approval round-trip test (chunk 2 had switched it to a reject
path because no cred-proxy existed). Approval stubs
`apply_routes_change` so the test focuses on the supervise
queue/response plumbing rather than docker-exec into a real
egress-proxy sidecar (that's covered separately).
- `tests/unit/test_egress_proxy_apply.py` — rewritten against
the new validator; covers JSON shape, missing routes key,
partial-auth-pair rejection (the addon-core parser catches
these before SIGHUP).
- PRDs 0010 + 0014 — status headers updated to
Superseded / Retargeted with a callout block pointing at PRD
0017's migration section. Historical text preserved.
384 unit + integration tests pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
4.2 KiB
PRD 0014: cred-proxy block remediation
- Status: Retargeted by PRD 0017
- Author: didericis
- Created: 2026-05-25
- Retargeted: 2026-05-25
- Parent: PRD 0012
- Depends on: PRD 0013
Retarget notice. The remediation shape this PRD describes (MCP tool → operator approve → SIGHUP a sidecar) is intact, but the sidecar moved: cred-proxy is gone, replaced by egress-proxy under PRD 0017. The MCP tool is now named
egress-proxy-block; the proposed file isroutes.yaml(JSON content) in egress-proxy's route shape (host + path_allowlist + nestedauthblock); the apply path docker-cps + SIGHUPs egress-proxy. The audit-log component label changed fromcred-proxytoegress-proxy. Operator-initiatedroutes edit <bottle>still exists with the same UX, now pointed at the egress-proxy sidecar.
Summary
Wires the cred-proxy block path (PRD 0012 Stuck categories) end-to-end. cred-proxy gains SIGHUP-based hot reload of routes.json. The supervisor, on approval of a cred-proxy-block proposal, writes the new routes.json to the host and SIGHUPs cred-proxy — no restart, no dropped connections. The TUI gains a proactive routes edit <bottle> verb for operator-initiated edits unrelated to a tool call. The cred-proxy audit log (format defined in PRD 0013) is filled in with real entries on every edit.
Problem
See PRD 0012. This PRD specifically addresses: with 0013 in place, the operator can approve a cred-proxy-block proposal but nothing happens — routes.json doesn't change and cred-proxy doesn't notice. This PRD closes the loop.
Goals / Success Criteria
A real cred-proxy block recovers end-to-end: the agent's HTTP request fails with a 403, the agent calls cred-proxy-block with a proposed routes.json and a justification, the operator approves in the TUI, the supervisor writes the new file and SIGHUPs cred-proxy, the agent retries against the now-reloaded proxy and proceeds. In-flight connections to cred-proxy do not drop during the reload.
Non-goals
- Pipelock or capability handling (covered by 0015 and 0016).
- Auto-rotation of expired tokens. The operator decides whether to issue a new token; this PRD just delivers approved config changes to cred-proxy.
Scope
In scope
- SIGHUP reload of
routes.jsonon cred-proxy. ~30 lines added to the server. - Supervisor write path: on operator approval of a
cred-proxy-blockproposal, write the proposedroutes.jsonto the host-side path cred-proxy reads, then send SIGHUP. routes edit <bottle>TUI verb: open the bottle'sroutes.jsonin$EDITOR, write + SIGHUP on save. Not gated on a pending proposal.- cred-proxy audit log entries: every routes edit (from a tool-call approval or from a proactive
routes edit) appends an entry with timestamp, diff, justification (if from tool call), and operator action.
Out of scope
- Restart-based reload as a fallback. SIGHUP only.
- Pipelock equivalents (PRD 0015).
Proposed Design
New services / components
routes edit <bottle>TUI verb. Opens the bottle's currentroutes.jsonin$EDITOR. On save, the supervisor writes the new file and SIGHUPs cred-proxy. Useful when the operator wants to add a route without waiting for an agent prompt.
Existing code touched
- cred-proxy (PRD 0010) — gains a SIGHUP signal handler that re-reads
routes.jsonwithout dropping connections or breaking in-flight calls. - MCP sidecar (PRD 0013) — the
cred-proxy-blockapproval handler stops being a no-op; on approval, calls the supervisor's write+SIGHUP path. cli.py— dashboard subcommand gains theroutes editverb.
Data model changes
None beyond PRD 0013. The audit log format is defined there; this PRD fills it in.
Open questions
- SIGHUP race window. An agent that retries within msec of the SIGHUP may hit old routes once before the reload completes, fail, and retry against the new routes. Assumption is that normal HTTP retry semantics absorb this; worth confirming under real usage rather than designing around it preemptively.
References
- PRD 0010 — cred-proxy.
- PRD 0012 — stuck-agent recovery flow overview.
- PRD 0013 — supervise plane foundation (prerequisite).