codex-oauth-proxy is a Codex OAuth-backed proxy for the Codex CLI and clients
that can use OpenAI-compatible Responses or Chat Completions wire formats. It
forwards supported Codex traffic to ChatGPT/Codex backend endpoints using
existing Codex OAuth credentials stored on disk.
Codex CLI is the primary target. The proxy also provides API-only managed-user endpoints and a small OpenAI-compatible public API surface for custom agents.
go build -o codex-oauth-proxy ./cmd/serverdocker run --rm -p 8317:8317 \
-v "$PWD/config.yaml:/codex-oauth-proxy/config.yaml:ro" \
-v "$PWD/auths:/root/.codex" \
zendext/codex-oauth-proxy:latestdocker compose up -dCreate config.yaml from config.example.yaml.
| Field | Default | Description |
|---|---|---|
host |
127.0.0.1 |
Bind host. Use 0.0.0.0 in containers. |
port |
8317 |
Bind port. |
auth-dir |
~/.codex |
Codex auth directory. Supports official auth.json and flat token JSON files. |
debug |
false |
Enable masked request/proxy debug logs. |
admin-api-key |
empty | Enables /v0/management when set. |
database.path |
empty | SQLite path. Empty resolves to <auth-dir>/codex-oauth-proxy.db. |
usage.enabled |
true |
Track per-user token usage for managed API keys. |
usage.debug-openai-response |
false |
Log safe usage metadata from upstream responses. |
allow-fast-mode |
false |
Allow Codex Fast mode. When disabled, Fast tier metadata is hidden and service_tier: "fast" or "priority" requests return 400. |
proxy-url |
empty | Optional outbound proxy. Use direct or none to bypass proxy settings. |
request-retry |
3 |
Upstream retry attempts for retry-aware calls. |
codex-base-url |
https://chatgpt.com/backend-api/codex |
Codex upstream base URL. |
chatgpt-base-url |
https://chatgpt.com/backend-api |
ChatGPT backend base URL for Codex compatibility calls. |
codex-user-agent |
empty | Optional upstream User-Agent override. |
codex-beta-features |
empty | Optional x-codex-beta-features fallback. |
codex-refresh-token-url |
empty | Optional OAuth refresh endpoint override. |
Expired OAuth tokens are refreshed automatically and written back to the same auth file.
Configure Codex CLI to use the proxy as a Responses provider. Use
supports_websockets = true for realtime routes and
requires_openai_auth = false so Codex sends the managed user API key from
COP_API_KEY to this proxy. The chatgpt_base_url value below is for Codex
client compatibility only; /backend-api/* is not a public API surface.
model_provider = "proxy"
chatgpt_base_url = "http://127.0.0.1:8317/backend-api/"
[model_providers.proxy]
name = "OpenAI using LLM proxy"
base_url = "http://127.0.0.1:8317/v1"
env_key = "COP_API_KEY"
wire_api = "responses"
supports_websockets = true
requires_openai_auth = falseSet admin-api-key to enable /v0/management. Generated user API keys
authenticate proxy routes and /v0/user; set COP_API_KEY when running Codex
through this proxy.
Usage tracking stores 10-minute UTC buckets for managed user API keys and keeps
30 days of bucket data. User totals are exposed at /v0/user/usage/today;
5-hour and 7-day management snapshots are exposed at /v0/management/usage.
Model breakdowns include model, reasoning_effort, and service_tier
(standard or fast). Dashboard-oriented timeseries are exposed at
/v0/management/usage/timeseries; they read the same 10-minute buckets and can
aggregate them with step=10m, 30m, 1h, 6h, or 1d.
Set debug: true for masked request/proxy logs. Add
usage.debug-openai-response: true for request IDs, model, key metadata, and
token summaries; secrets and response bodies are not logged.
OpenAI-compatible clients can also point their base URL at
http://127.0.0.1:8317/v1 and send POST /v1/chat/completions with a managed
user API key. Chat requests are translated to upstream Responses requests; both
streaming and non-streaming calls are supported.
./codex-oauth-proxy --config config.yaml
./codex-oauth-proxy serve --config config.yamlThe same binary includes a local admin CLI. It calls the running proxy at
http://127.0.0.1:8317 by default through a loopback-only local admin route and
does not require --config or the configured admin-api-key.
./codex-oauth-proxy admin users list
./codex-oauth-proxy admin users create alice
./codex-oauth-proxy admin users reset-key usr_xxx
./codex-oauth-proxy admin usage snapshot
./codex-oauth-proxy admin usage timeseries --window 7d --step 1h --group-by userUse --url for a non-default local instance:
./codex-oauth-proxy admin users list --url http://127.0.0.1:8318All admin commands support --json for scripting. User creation and key reset
print the plaintext user API key once.
Public API routes:
GET /GET /healthzPOST /v0/management/usersGET /v0/management/usersGET /v0/management/users/{user_id}PATCH /v0/management/users/{user_id}POST /v0/management/users/{user_id}/api-key/resetGET /v0/management/usageGET /v0/management/usage/timeseriesGET /v0/user/api-keyPOST /v0/user/api-key/resetGET /v0/user/usage/todayGET /v1/modelsPOST /v1/chat/completionsPOST /v1/responsesGET /v1/responsesPOST /v1/responses/compactPOST /v1/alpha/searchPOST /v1/images/generationsPOST /v1/images/editsPOST /v1/memories/trace_summarizePOST /v1/realtime/callsGET /v1/realtime
Image generation is available through /v1/responses by using the upstream
Responses API image_generation tool, normally with stream: true. The proxy
also exposes the narrow image compatibility routes /v1/images/generations and
/v1/images/edits. Other raw /v1/images/* routes and /backend-api/* paths
are not public API and should not be used by general clients.
Protected proxy routes require a managed user API key:
curl http://localhost:8317/v1/models \
-H 'Authorization: Bearer cop_...'Create a managed user and one generated API key:
curl -X POST http://localhost:8317/v0/management/users \
-H 'Authorization: Bearer admin-change-me' \
-H 'Content-Type: application/json' \
-d '{"name":"alice"}'The plaintext user API key is returned only by user creation and key reset
responses. List/detail responses only return key metadata such as masked_key.
An optional Grafana dashboard bundle is available under observability/.
export CODEX_OAUTH_PROXY_ADMIN_API_KEY="admin-change-me"
docker compose -f docker-compose.yml -f observability/docker-compose.dashboard.yml up -dOpen Grafana at http://localhost:3000. The dashboard calls
/v0/management/usage/timeseries through the configured admin API key.
If Grafana shows No data, make sure observability/grafana is readable by the
Grafana container and recreate the Grafana volume after provisioning changes.
The proxy usage database is separate from the Grafana volume.
gofmt -w .
go test ./...
go build -o test-output ./cmd/server && rm test-outputPushing a version tag that starts with v runs
.github/workflows/release.yml.
git tag v0.1.0
git push origin v0.1.0The workflow:
- runs
go test -count=1 ./... - builds
codex-oauth-proxy-linux-amd64 - uploads the binary and its SHA-256 file to the GitHub Release
- builds and pushes a
linux/amd64Docker image to Docker Hub
Required GitHub repository secrets:
DOCKERHUB_USERNAMEDOCKERHUB_TOKEN
Docker Hub image tags are published under zendext/codex-oauth-proxy with the
release tag, semver aliases, and latest.
Licensed under the Apache License, Version 2.0. See LICENSE.