mirror of
https://github.com/Comfy-Org/github-workflows.git
synced 2026-11-05 00:17:37 +00:00
[PR #72] [MERGED] feat(groom): add key-broker.mjs — localhost header-injecting API-key proxy + unit tests (BE-4419) #81
Labels
No labels
cursor-review-catalog-drift
groom
groom
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Comfy-Org/github-workflows#81
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/Comfy-Org/github-workflows/pull/72
Author: @mattmillerai
Created: 7/24/2026
Status: ✅ Merged
Merged: 8/4/2026
Merged by: @mattmillerai
Base:
main← Head:matt/be-4419-key-broker📝 Commits (5)
94d57b8feat(groom): add key-broker.mjs — localhost header-injecting API-key proxy + unit tests (BE-4419)1241e1ffix(groom): harden key-broker stream teardown, timeouts, and config guards (BE-4419)55081e2test(groom): add regression test for stdin key trimming (BE-4419)dda9133Merge remote-tracking branch 'origin/main' into matt/be-4419-key-broker8f3c8adMerge remote-tracking branch 'origin/main' into matt/be-4419-key-broker📊 Changes
3 files changed (+639 additions, -1 deletions)
View changed files
📝
.github/groom/README.md(+51 -1)➕
.github/groom/key-broker.mjs(+214 -0)➕
.github/groom/tests/test_key_broker.py(+374 -0)📄 Description
ELI-5
The groom agent that cleans up code runs on a shared runner where it can read any file and any process's environment. If we handed it the real Anthropic API key, it could just read the key straight out of
/proc. So instead we run a tiny local doorman (key-broker.mjs): it holds the real key, the agent gets a fake one, and the agent points the Claude CLI at the doorman (ANTHROPIC_BASE_URL=http://127.0.0.1:<port>). Every real request passes through the doorman, which swaps the fake key for the real one before sending it upstream. The key only ever arrives on the doorman's stdin — never in its environment or command line — so it can't be recovered from/proc. This PR adds the doorman script and its tests only; wiring it intogroom.ymlis a sibling ticket (BE-4311).What this adds
.github/groom/key-broker.mjs— a dependency-free (node:http/node:https, node ≥18, ESM) localhost proxy that reads the real key from stdin's first line, listens on127.0.0.1:<port>, answers the CLI'sHEAD/GET /connectivity probe locally with200, forwards/v1/*to the upstream (streaming both directions so SSE works) after stripping inboundx-api-key/authorization/hostand injecting the realx-api-key+ upstreamhost, returns404for anything else, and502(static body) on an upstream connection error. Config viaGROOM_BROKER_PORT(default8199) andGROOM_BROKER_UPSTREAM(defaulthttps://api.anthropic.com, overridable for tests). It never logs headers/bodies — at mostmethod path -> status..github/groom/tests/test_key_broker.py— aunittestintegration suite that boots the real script undernodeagainst a local fake upstream (skipped whennodeis absent). Discovery picks it up automatically via the existingtest-groom-scripts.yml.key-broker.mjssection in.github/groom/README.md(what it's for, the stdin-not-env rule and why, the env knobs, and the note thatgroom.ymlwiring lands in BE-4311).Tests
Covers every case the ticket enumerates, plus a startup guard:
x-api-key/authorization, and round-trips a non-200 upstream status and body;HEAD /→200, not forwarded;GET /anything-else→404, not forwarded;/proc/<pid>/{environ,cmdline}do not contain the real key (Linux-only, skipped elsewhere — so it exercises in CI onubuntu-latest, skips on the macOS dev box);502and the broker stays alive for a subsequent request;python3 -m unittest discover -s .github/groom/tests -p 'test_*.py' -v→Ran 66 tests ... OK (skipped=1)locally (the 1 skip is the Linux/procassertion on macOS).Acceptance criteria
ubuntu-latestshipsnode, so thenode-gated class runs there and the/procassertion is live).grep -c ANTHROPIC .github/groom/key-broker.mjs→0(the key is never read from env/argv; the only env reads areGROOM_BROKER_PORT/GROOM_BROKER_UPSTREAM). The comments were deliberately worded to avoid the provider token so this stays literally 0..github/workflows/groom.yml(nor any workflow file —test-groom-scripts.ymlalready triggers on.github/groom/**andubuntu-latest's preinstalled node is sufficient, so nosetup-nodestep was needed).Judgment calls / notes
key-broker.mjsfrom an earlier interrupted attempt was on the branch; I reviewed it adversarially, rebased the work onto the currentorigin/main, and kept the sound implementation. The one substantive fix: the residue's header comment falsely claimedgrep -c ANTHROPIC"is 0" while its own comments contained the token (actual count was 3) — reworded so the count is genuinely 0.req/res/upstreamReserrorhandlers) beyond the ticket's enumerated branches: a CLI that aborts mid-stream would otherwise throw an unhandled streamerrorand crash this long-lived proxy. These no-op/teardown handlers don't change the specified happy path.server_bindto skipHTTPServer'ssocket.getfqdn()reverse-DNS lookup, which hung ~35s per test on the dev box (and is a general flake risk); the suite now runs in ~3s.404/502/startup-diepaths are a proxy's correct protocol behavior, not a user-facing capability denial (no "X not supported" dead-end, no product surface to probe), so the falsification discipline doesn't apply here.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.