- Rust 100%
| src | ||
| .gitignore | ||
| AGENTS.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| config.example.toml | ||
| LICENSE | ||
| README.md | ||
Codex subscription proxy
This is a small HTTP proxy for the ChatGPT subscription backend used by Codex
CLI. It manages auth.json and exposes:
POST {prefix}/responses(SSE response streaming)POST {prefix}/responses/compactGET {prefix}/models
The prefix is configurable and defaults to /v1.
Response bodies, statuses, and errors from the upstream Codex backend are passed through without modification. Hop-by-hop headers, cookies, upstream infrastructure details, and browser policies tied to the upstream origin are removed from response headers.
Run
Copy config.example.toml, set a strong proxy access_token, and point
auth_file at the file-backed Codex CLI auth.json. Then run:
codex-subscription-proxy --config ./config.toml
Call the proxy with its own bearer token:
curl -N http://127.0.0.1:8787/v1/responses \
-H 'Authorization: Bearer replace-with-a-long-random-secret' \
-H 'Content-Type: application/json' \
--data-binary @request.json
The proxy replaces that local authorization header with the current ChatGPT
bearer token and adds the account/workspace header from auth.json.
Architecture
Each request follows a short, stateless forwarding path: authenticate the local
bearer token, read a bounded request body, reload/refresh the Codex
credentials, sanitize and replace authentication headers, and call the matching
upstream route. Unary response bodies are passed through directly; the
Responses SSE body is streamed as it arrives. Response headers are sanitized
before being returned. An upstream 401 invokes one guarded auth
reload/refresh and one replay of the buffered request.
The auth manager is the only component that writes auth.json. Its async lock
prevents two requests in this process from rotating the same refresh token
concurrently. It re-reads the file after taking the lock so a credential update
made by Codex CLI or another process can be adopted before it calls the token
authority.
Credential behavior
The proxy follows the Codex CLI refresh policy found in the adjacent source clone:
- Refresh a parseable access-token JWT when it has five minutes or less remaining.
- If the access token is opaque/unparseable, use Codex's eight-day
last_refreshfallback. - Reload
auth.jsonunder a process-wide refresh lock before rotating credentials. - Preserve fields omitted by the OAuth refresh response and preserve unknown JSON fields.
- Atomically replace
auth.jsonwith mode0600on Unix after a successful refresh. - On an upstream
401, reload/refresh and retry the request once.
This MVP supports Codex's file-backed, managed ChatGPT auth (auth_mode = "chatgpt") only. It does not read OS keyrings, proxy WebSockets, keep a
Cloudflare cookie jar, validate Responses request JSON, or expose other ChatGPT
backend routes. Request bodies are bounded and buffered to permit one
authentication retry; SSE response bodies are streamed directly. The ChatGPT
subscription backend is internal and implementation-specific, so its behavior
can change independently of this project.
Keep the default loopback bind unless TLS and suitable access controls are provided by a trusted front end. Anyone holding the configured proxy access token can consume the ChatGPT account's Codex allowance.