feat(llm-proxy): mission containers reach their models through a key-adding proxy (flagged)
deploy / test (push) Failing after 1m52s
deploy / build (push) Skipped

Container-tier missions held the platform's provider keys in their environment,
readable by an agent with Bash and public egress. With CLAWMATES_LLM_PROXY=1
(and CLAWMATES_LLM_PROXY_SECRET), a container instead holds a per-mission token
where each key was, ANTHROPIC_BASE_URL points at the server's proxy on :8089
(not published, not routed by Traefik), and the GLM/Kimi hops' base URLs are
rewritten in the mission's config copy. The proxy verifies the token
(HMAC(secret, mission_id) — stateless, survives redeploys), refuses it unless
the mission is running, swaps in the real credential and streams the response.

Spiked first on gw-04: Claude Code on a subscription OAuth token, given only a
placeholder and a base URL, sent nothing but POST /v1/messages there and
answered once the placeholder was swapped. Off by default; no behaviour change
until enabled.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-09-23 12:35:01 -05:00
co-authored by Claude Opus 5.5
parent 60a1fef5e5
commit 72fb0dcb6f
5 changed files with 415 additions and 2 deletions
+3
View File
@@ -452,6 +452,9 @@ async fn run() -> Result<(), String> {
// evaluator skip a judge at 95% for the fallback. Ten minutes: the windows
// are hours and days long, and each poll is one tiny GET per provider.
cm_api::judge_quota::spawn_poller(std::time::Duration::from_secs(600));
// Mission containers reach their models through this, holding a
// per-mission token instead of provider keys. Off unless configured.
cm_api::llm_proxy::spawn(pool.clone());
// Nightly: check upstream for newer dev-tool releases (claude/kimi/ollama).
cm_api::tool_versions::spawn_latest_checker(
pool.clone(),