A lightweight HTTP proxy for Codex CLI’s ChatGPT subscription backend, exposing /responses and more. Requires auth.json.
Find a file
2026-08-22 18:43:14 +09:00
src feat: handle SIGTERM 2026-08-22 18:43:14 +09:00
.gitignore initial commit 2026-08-17 14:54:55 +09:00
AGENTS.md chore: link agents.md to readme 2026-08-22 16:45:58 +09:00
Cargo.lock update cargo files 2026-08-22 15:59:49 +09:00
Cargo.toml update cargo files 2026-08-22 15:59:49 +09:00
config.example.toml feat: path prefix, defaults to /v1 2026-08-22 16:45:38 +09:00
LICENSE initial commit 2026-08-17 14:54:55 +09:00
README.md feat: pdate header filtering 2026-08-22 18:02:22 +09:00

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/compact
  • GET {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_refresh fallback.
  • Reload auth.json under a process-wide refresh lock before rotating credentials.
  • Preserve fields omitted by the OAuth refresh response and preserve unknown JSON fields.
  • Atomically replace auth.json with mode 0600 on 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.