Expose Codex Desktop's capabilities as standard OpenAI / Anthropic / Gemini APIs, seamlessly connecting any AI client.
Quick Start • Features • Models • Client Setup • Configuration
简体中文 | English
Disclaimer: This project is independently developed and maintained by a single person — built to scratch my own itch. I have my own account pipeline and am not short on tokens; this project exists because I needed it, not to freeload off anyone.
I open-source and maintain this voluntarily. Features get added when I need them; bugs get fixed as soon as I find them. But I am under no obligation to serve any individual user's demands.
Think the code is garbage? Don't use it. Think you can do better? Open a PR and join as a contributor. The issue tracker is for bug reports and suggestions — not feature demands, update nagging, or unsolicited code reviews.
Codex Proxy is a lightweight local gateway that translates the Codex Desktop Responses API into multiple standard protocol endpoints — OpenAI /v1/chat/completions, Anthropic /v1/messages, Gemini, Codex /v1/responses passthrough, and an optional Ollama-compatible /api/chat bridge. Use Codex coding models directly in Cursor, Claude Code, Continue, or any compatible client.
Just a ChatGPT account (or a third-party API relay) and this proxy — your own personal AI coding assistant gateway, running locally.
Download the installer from GitHub Releases:
| Platform | Installer |
|---|---|
| Windows | Codex Proxy Setup x.x.x.exe |
| macOS | Codex Proxy-x.x.x.dmg |
| Linux | Codex Proxy-x.x.x.AppImage |
Open the app, log in with your ChatGPT account. Dashboard at http://localhost:8080.
mkdir codex-proxy && cd codex-proxy
curl -O https://raw.githubusercontent.com/icebear0828/codex-proxy/master/docker-compose.yml
curl -O https://raw.githubusercontent.com/icebear0828/codex-proxy/master/.env.example
cp .env.example .env
docker compose up -d
# Open http://localhost:8080 to log inData persists in
data/. Cross-container access: use host LAN IP (e.g.192.168.x.x:8080), notlocalhost. Uncomment Watchtower indocker-compose.ymlfor auto-updates. To enable the Ollama-compatible bridge in Docker, see Ollama Bridge configuration.
git clone https://github.com/icebear0828/codex-proxy.git
cd codex-proxy
npm install # Backend dependencies
cd web && npm install && cd .. # Frontend dependencies
npm run dev # Dev mode (hot reload)
# Or: npm run build && npm start # Production modeRequires Rust toolchain (for TLS native addon):
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh cd native && npm install && npm run build && cd ..Docker / desktop app ship pre-built addons — no manual compilation needed.
After logging in, open the dashboard at http://localhost:8080 and find your API Key in the API Configuration section:
# Replace your-api-key with the key shown in the dashboard
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-d '{"model":"gpt-5.4","messages":[{"role":"user","content":"Hello!"}],"stream":true}'If you see streaming AI text, the setup is working. If you get 401, double-check the API Key.
- Compatible with
/v1/chat/completions(OpenAI),/v1/messages(Anthropic), Gemini, and/v1/responses(Codex passthrough) - Optional built-in Ollama-compatible bridge, defaulting to
http://127.0.0.1:11434 - SSE streaming, works with all OpenAI / Anthropic SDKs and clients
- Automatic bidirectional translation between all protocols and Codex Responses API
- Structured Outputs —
response_format(json_object/json_schema) and GeminiresponseMimeType - Function Calling — native
function_call/tool_callsacross all protocols - If using custom API Keys, only the OpenAI (
/v1/chat/completions) format is supported.
- OAuth PKCE login — one-click browser auth
- Multi-account rotation —
least_used,round_robin, andstickystrategies - Plan Routing — accounts on different plans (free/plus/team/business) auto-route to their supported models
- Auto token refresh — JWT renewed before expiry with exponential backoff
- Quota auto-refresh — background polling every 5 min; configurable warning thresholds; exhausted accounts auto-skip
- Ban detection — upstream 403 auto-marks banned; 401 token invalidation auto-expires and switches account
- Relay accounts — connect third-party API relays (API Key + baseUrl) with auto format detection
- Web dashboard — account management, usage stats, batch operations; dashboard login gate for remote access
- Per-account proxy routing — different upstream proxies per account
- Four assignment modes — Global Default / Direct / Auto / Specific proxy
- Health checks — scheduled + manual, reports exit IP and latency
- Auto-mark unreachable — unreachable proxies excluded from rotation
- Rust Native TLS — built-in reqwest + rustls native addon, TLS fingerprint matches real Codex Desktop exactly (pinned dependency versions)
- Desktop header replication —
originator,User-Agent,x-openai-internal-codex-residency,x-codex-turn-state,x-client-request-idheaders sent per real client behavior - Cookie persistence — automatic Cloudflare cookie capture and replay
- Fingerprint auto-update — polls Codex Desktop update feed, auto-syncs
app_versionandbuild_number
Codex Proxy
┌──────────────────────────────────────────────────────────┐
│ │
│ Client (Cursor / Claude Code / Continue / SDK / ...) │
│ │ │
│ POST /v1/chat/completions (OpenAI) │
│ POST /v1/messages (Anthropic) │
│ POST /v1/responses (Codex passthrough) │
│ POST /gemini/* (Gemini) │
│ │ │
│ ▼ │
│ ┌──────────┐ ┌───────────────┐ ┌──────────────┐ │
│ │ Routes │──▶│ Translation │──▶│ Proxy │ │
│ │ (Hono) │ │ Multi→Codex │ │ Native TLS │ │
│ └──────────┘ └───────────────┘ └──────┬───────┘ │
│ ▲ │ │
│ │ ┌───────────────┐ │ │
│ └──────────│ Translation │◀─────────┘ │
│ │ Codex→Multi │ SSE stream │
│ └───────────────┘ │
│ │
│ ┌──────────┐ ┌───────────────┐ ┌──────────────────┐ │
│ │ Auth │ │ Fingerprint │ │ Model Store │ │
│ │ OAuth/JWT│ │ Rust (rustls) │ │ Static + Dynamic │ │
│ │ Relay │ │ Headers/UA │ │ Plan Routing │ │
│ └──────────┘ └───────────────┘ └──────────────────┘ │
│ │
└──────────────────────────────────────────────────────────┘
│
Rust Native Addon (napi-rs)
reqwest 0.12.28 + rustls 0.23.36
(TLS fingerprint = real Codex Desktop)
│
┌──────┴──────┐
▼ ▼
chatgpt.com Relay providers
/backend-api/codex (3rd-party API)
| Model ID | Reasoning | Output | Description |
|---|---|---|---|
gpt-5.5 |
low / medium / high / xhigh | text | General-purpose flagship (Plus+) |
gpt-5.4 |
low / medium / high / xhigh | text | Latest flagship (default) |
gpt-5.4-mini |
low / medium / high / xhigh | text | 5.4 lightweight version |
gpt-5.3-codex |
low / medium / high / xhigh | text | 5.3 coding-optimized model |
gpt-5.2 |
low / medium / high / xhigh | text | Professional work & long-running agents |
gpt-5-codex |
low / medium / high | text | GPT-5 coding model |
gpt-5-codex-mini |
medium / high | text | Lightweight coding model |
gpt-oss-120b |
low / medium / high | text | Open-source 120B model |
gpt-oss-20b |
low / medium / high | text | Open-source 20B model |
gpt-image-2 |
— | image | Image-generation backend (Plus+, invoked via the image_generation tool) |
Suffixes: Append
-fastto any chat model for Fast mode,-high/-lowfor reasoning effort. E.g.gpt-5.4-fast,gpt-5.4-high-fast. The image model (gpt-image-2) does not take suffixes.Plan Routing: Accounts on different plans auto-route to their supported models. Models are dynamically fetched and auto-synced.
Dashboard model picker ≠ config file: Changing the model in the Dashboard only affects the UI display and API examples — it does not modify
model.defaultinconfig/default.yamlordata/local.yaml. The actual model used is determined by themodelfield in each client request (Cursor, Claude Code, etc.). Themodel.defaultconfig is only a fallback when the client omits the model field.
Image generation rides on /v1/responses via the built-in image_generation tool; the backend is always gpt-image-2.
Prerequisite: a ChatGPT Plus or higher account (free accounts have the tool silently stripped by upstream, and the model falls back to replying with an SVG snippet).
curl -N http://localhost:8080/v1/responses \
-H "Authorization: Bearer $PROXY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"stream": true,
"input": [{"role":"user","content":"Draw a red circle on white background."}],
"tools": [{"type":"image_generation","size":"3840x2160"}]
}'Tunable fields: size (1024×1024 / 1024×1536 / 1536×1024 / 2048×2048 / 2048×3072 / 3072×2048 / 3840×2160 (4K UHD) / auto; longest edge ≤ 3840 px, pixel budget ≈ 8 MP), output_format (png / jpeg / webp), output_compression (jpeg / webp only), background (auto / opaque), moderation (auto / low), partial_images (0–3). Upstream forces model = gpt-image-2 and rejects n, input_image, mask, input_fidelity, style, response_format. See API.md for the full matrix.
In the stream, the image_generation_call item's result field is a base64-encoded image; revised_prompt contains the final prompt used by the model.
Edit mode (with a reference image): include {"type":"input_image","image_url":"data:image/png;base64,..."} in the user message content array.
Get your API Key from the dashboard (
http://localhost:8080). Use a concrete model ID (defaultgpt-5.4) or any model ID as the model name.
export ANTHROPIC_BASE_URL=http://localhost:8080
export ANTHROPIC_API_KEY=your-api-key
# Switch model: export ANTHROPIC_MODEL=gpt-5.4 / gpt-5.4-fast / gpt-5.4-mini ...
claudeCopy env vars from the Anthropic SDK Setup card in the dashboard (includes Opus / Sonnet / Haiku tier model config).
Recommended models: Opus →
gpt-5.4, Sonnet →gpt-5.3-codex, Haiku →gpt-5.4-mini.
~/.codex/config.toml:
[model_providers.proxy_codex]
name = "Codex Proxy"
base_url = "http://localhost:8080/v1"
wire_api = "responses"
env_key = "PROXY_API_KEY"
[profiles.default]
model = "gpt-5.4"
model_provider = "proxy_codex"export PROXY_API_KEY=your-api-key
codex- Enable Developer Mode: Click menu Help → Troubleshooting → Enable Developer Mode.
- Configure Third-Party Inference: Click the new Developer menu → Configure Third-Party Inference....
- Fill in details:
- Endpoint:
http://127.0.0.1:8080 - API Key: your-api-key
- Model:
gpt-5.4(or an Anthropic-formatted ID likeanthropic/claude-3-5-sonnet-20241022)
- Endpoint:
Alternatively, edit the config file (usually a JSON file in
%APPDATA%\Claude-3p\configLibrary\on Windows, or~/Library/Application Support/Claude-3p/configLibrary/on Mac), adding the following fields:{ "inferenceProvider": "gateway", "inferenceGatewayBaseUrl": "http://127.0.0.1:8080", "inferenceGatewayApiKey": "your-api-key", "inferenceGatewayAuthScheme": "bearer", "inferenceModels": [ { "name": "gpt-5.4" } ] }💡 Troubleshooting (Windows): If Claude Desktop shows
ERR_CONNECTION_REFUSEDwhen using127.0.0.1(andmust use httpswhen usinglocalhost), it means Node.js is only binding to IPv6 by default. Go to the Codex Proxy dashboard settings, change Host to127.0.0.1, or addserver: { host: "127.0.0.1" }todata/local.yamland restart the proxy.💡 LAN Usage Tip: Claude Desktop strictly validates the endpoint and only allows
https://or exactlyhttp://127.0.0.1. If your proxy is on another machine in the LAN (e.g.192.168.x.x), you cannot use it directly via HTTP. Workarounds:
- SSH Tunnel (Easiest): Run
ssh -L 8080:127.0.0.1:8080 user@192.168.x.xon your client machine, then usehttp://127.0.0.1:8080in Claude.- Reverse Proxy: Setup Caddy or Nginx with a valid HTTPS certificate for your LAN IP.
The official client shares configuration with the CLI. Restart the app after editing.
~/.codex/config.toml:
[model_providers.proxy_codex]
name = "Codex Proxy"
base_url = "http://localhost:8080/v1"
wire_api = "responses"
env_key = "PROXY_API_KEY"
[profiles.default]
model = "gpt-5.4"
model_provider = "proxy_codex"
⚠️ If you are logged in via "ChatGPT account", the client might ignore this config. Launching withPROXY_API_KEYenvironment variable set is recommended.
Open Claude extension settings → API Configuration:
- API Provider: Anthropic
- Base URL:
http://localhost:8080 - API Key: your API key
- Settings → Models → OpenAI API
- Base URL:
http://localhost:8080/v1 - API Key: your API key
- Add model
gpt-5.4
- Settings → AI Provider → OpenAI Compatible
- API Base URL:
http://localhost:8080/v1 - API Key: your API key
- Model:
gpt-5.4
- Cline sidebar → gear icon
- API Provider: OpenAI Compatible
- Base URL:
http://localhost:8080/v1 - API Key: your API key
- Model ID:
gpt-5.4
~/.continue/config.json:
{
"models": [{
"title": "Codex",
"provider": "openai",
"model": "gpt-5.4",
"apiBase": "http://localhost:8080/v1",
"apiKey": "your-api-key"
}]
}aider --openai-api-base http://localhost:8080/v1 \
--openai-api-key your-api-key \
--model openai/gpt-5.4- Settings → Model Services → Add
- Type: OpenAI
- API URL:
http://localhost:8080/v1 - API Key: your API key
- Add model
gpt-5.4
Enable it in Dashboard → Settings → Ollama Bridge, then use the default Ollama base URL:
| Setting | Value |
|---|---|
| Base URL | http://localhost:11434 |
| API Key | Not required; the bridge uses the Codex Proxy key internally |
| Model | gpt-5.4 (or any model ID) |
curl http://localhost:11434/api/tags
curl http://localhost:11434/api/chat \
-H "Content-Type: application/json" \
-d '{"model":"gpt-5.4","messages":[{"role":"user","content":"Hello!"}],"stream":true}'The Ollama API has no authentication. The bridge listens on
127.0.0.1by default; do not expose it to the public internet or untrusted LANs.
| Setting | Value |
|---|---|
| Base URL | http://localhost:8080/v1 |
| API Key | from dashboard |
| Model | gpt-5.4 (or any model ID) |
SDK examples (Python / Node.js)
Python
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8080/v1", api_key="your-api-key")
for chunk in client.chat.completions.create(
model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], stream=True
):
print(chunk.choices[0].delta.content or "", end="")Node.js
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "http://localhost:8080/v1", apiKey: "your-api-key" });
const stream = await client.chat.completions.create({
model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content || "");
}All configuration in config/default.yaml:
| Section | Key Settings | Description |
|---|---|---|
server |
host, port, proxy_api_key |
Listen address and API key |
api |
base_url, timeout_seconds |
Upstream API URL and timeout |
client |
app_version, build_number, chromium_version |
Codex Desktop version to impersonate |
model |
default, default_reasoning_effort, inject_desktop_context |
Default model and reasoning config |
auth |
rotation_strategy, rate_limit_backoff_seconds |
Rotation strategy and rate limit backoff |
tls |
proxy_url, force_http11 |
TLS proxy and HTTP version |
quota |
refresh_interval_minutes, warning_thresholds, skip_exhausted |
Quota refresh and warnings |
session |
ttl_minutes, cleanup_interval_minutes |
Dashboard session management |
ollama |
enabled, host, port, version, disable_vision |
Ollama-compatible bridge |
ollama:
enabled: false # true = start the built-in Ollama-compatible listener
host: 127.0.0.1 # localhost-only by default
port: 11434 # Ollama default port
version: "0.18.3" # value returned by /api/version
disable_vision: false # true = do not advertise vision in /api/showSupported Ollama endpoints:
| Endpoint | Method | Description |
|---|---|---|
http://localhost:11434/api/version |
GET | Ollama version probe |
http://localhost:11434/api/tags |
GET | Model list |
http://localhost:11434/api/show |
POST | Model metadata |
http://localhost:11434/api/chat |
POST | Chat completions with streaming NDJSON |
http://localhost:11434/v1/* |
Any | OpenAI /v1 passthrough |
For Docker deployments that need host access to 11434:
- Set
ollama.enabled: trueandollama.host: 0.0.0.0in the Dashboard ordata/local.yaml. - Uncomment the
127.0.0.1:${OLLAMA_BRIDGE_PORT:-11434}:11434port mapping indocker-compose.yml. - Keep the host binding on
127.0.0.1unless you intentionally want to expose an unauthenticated Ollama API.
Browser CORS access is limited to loopback origins such as localhost, 127.x.x.x, and ::1; non-local web origins are not allowed to read bridge responses. The bridge injects the configured Codex Proxy API key for /v1/* passthrough requests, so exposing it beyond localhost effectively grants unauthenticated access to the main proxy API.
| Variable | Overrides |
|---|---|
PORT |
server.port |
CODEX_PLATFORM |
client.platform |
CODEX_ARCH |
client.arch |
HTTPS_PROXY |
tls.proxy_url |
OLLAMA_BRIDGE_ENABLED |
ollama.enabled |
OLLAMA_BRIDGE_HOST |
ollama.host |
OLLAMA_BRIDGE_PORT |
ollama.port |
OLLAMA_BRIDGE_VERSION |
ollama.version |
OLLAMA_BRIDGE_DISABLE_VISION |
ollama.disable_vision |
Click to expand full endpoint list
Protocol Endpoints
| Endpoint | Method | Description |
|---|---|---|
/v1/chat/completions |
POST | OpenAI format chat completions |
/v1/responses |
POST | Codex Responses API passthrough |
/v1/messages |
POST | Anthropic format chat completions |
/v1/models |
GET | List available models |
:11434/api/chat |
POST | Ollama-compatible chat completions (requires Ollama Bridge) |
Auth & Accounts
| Endpoint | Method | Description |
|---|---|---|
/auth/login |
GET | OAuth login entry |
/auth/accounts |
GET | Account list (?quota=true / ?quota=fresh) |
/auth/accounts |
POST | Add single account (token or refreshToken) |
/auth/accounts/import |
POST | Bulk import accounts |
/auth/accounts/export |
GET | Export accounts (?format=minimal for compact) |
/auth/accounts/relay |
POST | Add relay account |
/auth/accounts/batch-delete |
POST | Batch delete accounts |
/auth/accounts/batch-status |
POST | Batch update account status |
Account Import/Export Examples
# Export all accounts (full format with tokens)
curl -s http://localhost:8080/auth/accounts/export \
-H "Authorization: Bearer your-api-key" > backup.json
# Export minimal format (refreshToken + label only, safe to share)
curl -s "http://localhost:8080/auth/accounts/export?format=minimal" \
-H "Authorization: Bearer your-api-key" > backup-minimal.json
# Bulk import (token, refreshToken, or both)
curl -X POST http://localhost:8080/auth/accounts/import \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-d '{
"accounts": [
{ "token": "eyJhbGciOi..." },
{ "refreshToken": "v1.abc..." },
{ "refreshToken": "v1.def...", "label": "Backup" }
]
}'
# Returns: { "added": 2, "updated": 1, "failed": 0, "errors": [] }
# One-step backup restore (export file → import to another instance)
curl -X POST http://localhost:8080/auth/accounts/import \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-d @backup.jsonAdmin
| Endpoint | Method | Description |
|---|---|---|
/admin/rotation-settings |
GET/POST | Rotation strategy config |
/admin/quota-settings |
GET/POST | Quota refresh & warning config |
/admin/ollama-settings |
GET/POST | Ollama Bridge config |
/admin/ollama-status |
GET | Ollama Bridge runtime status |
/admin/refresh-models |
POST | Trigger manual model list refresh |
/admin/usage-stats/summary |
GET | Usage stats summary |
/admin/usage-stats/history |
GET | Usage time series |
/health |
GET | Health check |
Proxy Pool
| Endpoint | Method | Description |
|---|---|---|
/api/proxies |
GET/POST | List / add proxies |
/api/proxies/:id |
PUT/DELETE | Update / remove proxy |
/api/proxies/:id/check |
POST | Health check single proxy |
/api/proxies/check-all |
POST | Health check all proxies |
/api/proxies/assign |
POST | Assign proxy to account |
- Node.js 18+ (20+ recommended)
- Rust — required for source builds (compiles TLS native addon); Docker / desktop app ship pre-built
- ChatGPT account — free account is sufficient
- Docker (optional)
- Codex API is stream-only.
stream: falsecauses the proxy to stream internally and return assembled JSON. - This project relies on Codex Desktop's public API. Upstream updates are auto-detected and fingerprints auto-synced.
- Windows source builds need Rust toolchain for the TLS native addon. Docker deployment has it pre-built.
Non-Commercial license:
- Allowed: Personal learning, research, self-hosted deployment
- Prohibited: Any commercial use including selling, reselling, paid proxy services, or commercial product integration
Not affiliated with OpenAI. Users assume all risks and must comply with OpenAI's Terms of Service.


