feat(proxy): add loopback OpenAI Codex OAuth adapter - #91199
Conversation
andrexibiza
left a comment
There was a problem hiding this comment.
Reviewed exact head bb23a98be19ce32d1c6ca3a6abf4306acf7d59db against exact current main/base 8794e5a21c980a0f26532cb4883284b786cb3f25.
The transport work is strong: I checked the exact upstream allowlist, exact trusted Codex base-url gate, adapter-owned header stripping/overlay, raw query preservation, body/SSE byte preservation, retry cleanup, prepare/cancellation cleanup, pool-backed 401 coalescing, 429 rotation, and the loopback checks at both CLI and server entry points. The focused tests cover the right failure directions rather than only the happy path.
There is one stop-the-line authority-boundary issue on the other side of the shape:
Loopback proves network location, not caller authority
OpenAICodexAdapter.loopback_only prevents LAN/public exposure, but create_app() still accepts every local caller and replaces whatever bearer it supplied with the profile owner's ChatGPT OAuth credential. On a multi-user host, 127.0.0.1/::1 is machine-local, not UID-local: another OS user or service on the same machine can connect to this TCP listener even though it cannot read the owning Hermes profile's credential files. That makes this proxy a confused-deputy path from local reachability to subscription-spending authority.
The new docs state the behavior directly: “It accepts any inbound bearer and spends the OAuth account owned by the profile that started it.” That is not equivalent to an authenticated local client. The existing Hermes API-server surface also treats bearer identity separately from bind location, and the prior Codex-proxy lineage already exposed this missing half:
- #62297 (
@darkyy92) implemented native Responses/pool-backed Codex with owner-only per-client keys specifically for multi-user Hermes hosts. - #62510 (
@dtownsel) implemented constant-time downstream bearer enforcement and stripped/replaced that bearer before forwarding, although its older token-file/refresh implementation had separate review defects. - #54877 (
@blazing-mj) is earlier adjacent/superseded shared-pool/Cloudflare-header work with a different Chat-Completions translation choice.
#91199 is the strongest current-main consolidation and should become the canonical owner, but it should not discard the downstream-authority half of that lineage.
Required fix: keep the hard loopback-only Codex bind, and require a downstream credential/identity before attaching the ChatGPT bearer. A file-backed bearer is fine if the file itself is proven owner-only/regular (or use another OS-owned local identity mechanism); compare secrets in constant time; never forward the downstream credential. Add an adversarial witness that starts the proxy under principal/user A, attempts /v1/responses without/wrong authority (representing another local principal), and proves no upstream request is made; then prove the authorized client succeeds. The invariant is: network locality may constrain reachability, but only authenticated local authority may spend the profile-owned subscription.
Topology / landing evidence
Please explicitly interlock/supersede #62297, #62510, and #54877 in the PR body rather than leaving three older open implementations looking like independent competing owners, and preserve the contributors' credit for the pieces carried forward. They are not all duplicates: #62297 is the closest superseded native-Responses/client-auth implementation, #62510 is complementary downstream-auth precedent with older defects, and #54877 is superseded/adjacent translation-era work.
Exact-head hosted evidence is not green yet: CI 32433967389, Docker 32433966978, and Nix 32433967017 are all action_required with no jobs created, so the local 46-test/live-canary receipts are useful but cannot substitute for exact-head hosted runs. After the authority fix, require fresh CI/Nix/Docker on the resulting exact SHA.
Once the downstream-authority boundary is present, the rest of this current-main shape looks materially better than the older Codex-proxy branches.
Strong hardening alongside the new adapter. Highlights: OAuth credentials refuse any upstream except the pinned Codex base URL (hermes_cli/proxy/adapters/codex.py The 401 coalescing path in Suggestions:
|
|
I opened a stacked security follow-up against this PR’s exact head: BELGARATHbb#2 It adds downstream bearer authentication for |
Summary
openai-codexsubscription-proxy adapter withcodexaliasSecurity findings closed
ChatGPT-Account-IDspoof when JWT claim is absentVerification
%252f/%2Fquery preservationgit diff --checkpassedPROXY_GPT_SOL_READYCONTROLLER_DELEGATION_PASSLOCAL_ROUTE_VERIFIED_BY_GPT_SOLOperational boundary
Codex proxy is hard-blocked from non-loopback binds. It has no inbound authentication and must remain on
127.0.0.1/::1.