A lightweight HTTP proxy for Codex CLI’s ChatGPT subscription backend, exposing /responses and more. Requires auth.json.
Find a file
2026-09-21 11:40:54 +09:00
src feat: auto fetch codex version when omitted in config 2026-09-21 11:24:39 +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 chore: update dependencies and bump version 2026-09-21 11:40:54 +09:00
Cargo.toml chore: update dependencies and bump version 2026-09-21 11:40:54 +09:00
config.example.toml feat: auto fetch codex version when omitted in config 2026-09-21 11:24:39 +09:00
LICENSE initial commit 2026-08-17 14:54:55 +09:00
README.md feat: auto fetch codex version when omitted in config 2026-09-21 11:24:39 +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.

Omit client_version to automatically use the latest stable Codex CLI release from GitHub. The proxy resolves the version before starting the HTTP service and checks for updates every hour. Only stable rust-vX.Y.Z releases are considered; drafts, prereleases, and other packages are ignored. Initialization and version updates produce INFO logs. New versions apply to subsequent requests without restarting the proxy.

Automatic discovery requires GitHub access at startup. If the initial lookup fails, startup fails; if a later check fails, the proxy logs a warning and keeps the last known version until a subsequent hourly check succeeds. Set client_version = "0.155.1" explicitly to pin a version and disable all GitHub checks. The active version supplies the default version header and /models client_version query parameter; caller-provided values still take precedence.

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.