Skip to content

Latest commit

 

History

History
757 lines (599 loc) · 37 KB

File metadata and controls

757 lines (599 loc) · 37 KB

Codex Proxy

Your Local Codex Coding Assistant Gateway

Expose Codex Desktop's capabilities as standard OpenAI / Anthropic / Gemini APIs, seamlessly connecting any AI client.

Node.js TypeScript Hono Docker Desktop License

Quick StartFeaturesModelsClient SetupConfigurationAcknowledgements

简体中文 | English


X Issues Donate


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 key provider) and this proxy — your own personal AI coding assistant gateway, running locally.

🚀 Quick Start

Desktop App (Easiest)

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.

Docker

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 in

Data persists in data/. Cross-container access: use host LAN IP (e.g. 192.168.x.x:8080), not localhost. Uncomment Watchtower in docker-compose.yml for auto-updates. To enable the Ollama-compatible bridge in Docker, see Ollama Bridge configuration.

From Source

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 mode

Requires 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.

Verify

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.

🌟 Features

1. 🔌 Full Protocol Compatibility

  • 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 Outputsresponse_format (json_object / json_schema) and Gemini responseMimeType
  • Function Calling — native function_call / tool_calls across all protocols
  • Third-party API keys — supports OpenAI / Anthropic / Gemini / OpenRouter / custom OpenAI-compatible providers, routed by model.

2. 🔐 Account Management & Smart Rotation

  • OAuth PKCE login — one-click browser auth
  • Multi-account rotationleast_used, round_robin, and sticky strategies
  • 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
  • Passive quota collection — updates account quota from upstream response headers and WebSocket rate-limit events; quota.refresh_interval_minutes only controls local usage snapshots, and 0 disables that timer.
  • Ban detection — upstream 403 auto-marks banned; 401 token invalidation auto-expires and switches account
  • API key provider pool — manage third-party API keys, model lists, import/export, and enable/disable state from the dashboard.
  • Web dashboard — account management, usage stats, batch operations; dashboard login gate for remote access

3. 🌐 Proxy Pool

  • 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

4. 🛡️ Anti-Detection & Protocol Impersonation

  • Rust Native TLS — built-in reqwest + rustls native addon, TLS fingerprint matches real Codex Desktop exactly (pinned dependency versions)
  • Desktop header replicationoriginator, User-Agent, x-openai-internal-codex-residency, x-codex-turn-state, x-client-request-id headers sent per real client behavior
  • Cookie persistence — automatic Cloudflare cookie capture and replay
  • Fingerprint auto-update — polls Codex Desktop update feed, auto-syncs app_version and build_number

🏗️ Architecture

                                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/API │  │ Rust (rustls) │  │ Static + Dynamic │  │
│  │ API Keys │  │  Headers/UA   │  │  Plan Routing    │  │
│  └──────────┘  └───────────────┘  └──────────────────┘  │
│                                                          │
└──────────────────────────────────────────────────────────┘
                          │
              Rust Native Addon (napi-rs)
            reqwest 0.12.28 + rustls 0.23.36
           (TLS fingerprint = real Codex Desktop)
                          │
                   ┌──────┴──────┐
                   ▼             ▼
             chatgpt.com   3rd-party providers
         /backend-api/codex  (3rd-party API)

📦 Available Models

Model ID Reasoning Current context Max context Max output Output Description
gpt-5.5 low / medium / high / xhigh 272,000 272,000 128,000 text Frontier model for complex coding, research, and real-world work
gpt-5.4 low / medium / high / xhigh 272,000 1,000,000 128,000 text Strong model for everyday coding (default)
gpt-5.4-mini low / medium / high / xhigh 400,000 128,000 text GPT-5.4 lightweight model
gpt-5.3-codex low / medium / high / xhigh 400,000 128,000 text GPT-5.3 coding-optimized model
gpt-5.2 low / medium / high / xhigh 400,000 128,000 text Professional work & long-running agents
gpt-5-codex low / medium / high 400,000 128,000 text GPT-5 coding-optimized model
gpt-5-codex-mini medium / high text Lightweight Codex / CLI coding model
gpt-oss-120b low / medium / high 131,072 text Open-source 120B model
gpt-oss-20b low / medium / high 131,072 text Open-source 20B model
gpt-image-2 image Image-generation tool backend, invoked via image_generation

Suffixes: Append -fast to any chat model for Fast mode, -high/-low for 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 the models returned for that account by the Codex backend. Do not treat old Plus-only notes as fixed model access rules. Models are dynamically fetched and auto-synced; if a model appears in the Dashboard or /v1/models/catalog, it can be used as the request model.

Dashboard model picker ≠ config file: Changing the model in the Dashboard only affects the UI display and API examples — it does not modify model.default in config/default.yaml or data/local.yaml. The actual model used is determined by the model field in each client request (Cursor, Claude Code, etc.). The model.default config is only a fallback when the client omits the model field.

Max token note: the table follows the current config/models.yaml and Codex runtime /v1/models/catalog metadata. means the current catalog does not return that field, not that the model is unavailable. Runtime data fetched from the Codex backend overrides static values and preserves contextWindow, maxContextWindow, maxOutputTokens, and truncationPolicyLimit. Request fields such as context_window, max_context_window, truncation_policy, and max_output_tokens are not usable switches; forwarding them to the native Codex API returns 400 Unsupported parameter.

🖼️ Image Generation

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.

The /v1/chat/completions compatibility path accepts the image_generation tool so OpenAI clients do not fail schema validation, but image payloads are only exposed reliably through /v1/responses as image_generation_call.result. Use /v1/responses when you need the image bytes.

🔗 Client Setup

Get your API Key from the dashboard (http://localhost:8080). Use a concrete model ID (default gpt-5.4) or any model ID as the model name.

Claude Code (CLI)

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 ...
claude

Copy 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 CLI

~/.codex/config.toml:

[model_providers.proxy_codex]
name = "Codex Proxy"
base_url = "http://localhost:8080/v1"
wire_api = "responses"

# Inline the API Key (recommended for local single-user setups)
[model_providers.proxy_codex.http_headers]
Authorization = "Bearer your-api-key"

[profiles.default]
model = "gpt-5.4"
model_provider = "proxy_codex"

💡 To keep the key out of the config file (shared machine / open-source repo), drop the http_headers block and use env_key = "PROXY_API_KEY" instead, then export PROXY_API_KEY=your-api-key && codex.

Claude Desktop

  1. Enable Developer Mode: Click menu HelpTroubleshootingEnable Developer Mode.
  2. Configure Third-Party Inference: Click the new Developer menu → Configure Third-Party Inference....
  3. Fill in details:
    • Endpoint: http://127.0.0.1:8080
    • API Key: your-api-key
    • Model: claude-opus-4-7 / claude-sonnet-4-6 / claude-haiku-4-5

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:

{
  "disableDeploymentModeChooser": true,
  "inferenceProvider": "gateway",
  "inferenceGatewayBaseUrl": "http://127.0.0.1:8080",
  "inferenceGatewayApiKey": "your-api-key",
  "inferenceGatewayAuthScheme": "bearer",
  "inferenceModels": [
    "claude-opus-4-7",
    "claude-sonnet-4-6",
    "claude-haiku-4-5"
  ]
}

Built-in Claude-shaped model names map to Codex models. Put custom mappings in data/local.yaml; do not edit config/models.yaml:

model:
  aliases:
    claude-opus-4-7: gpt-5.5
    claude-sonnet-4-6: gpt-5.4
    claude-haiku-4-5: gpt-5.3-codex
    my-openai: openai:gpt-4o
    my-deepseek: deepseek-chat

The left side is the model name used by the client; the right side is the real upstream model. The target can be a Codex model ID, a provider-prefixed model such as openai:gpt-4o / anthropic:claude-sonnet-4-5 / gemini:gemini-2.5-pro, or a model already bound to a custom provider via model_routing such as deepseek-chat. Aliases appear in /v1/models; direct provider requests rewrite the outgoing model to the mapped target.

💡 Troubleshooting (Windows): If Claude Desktop shows ERR_CONNECTION_REFUSED when using 127.0.0.1 (and must use https when using localhost), it means Node.js is only binding to IPv6 by default. Go to the Codex Proxy dashboard settings, change Host to 127.0.0.1, or add server: { host: "127.0.0.1" } to data/local.yaml and restart the proxy.

💡 LAN Usage Tip: Claude Desktop strictly validates the endpoint and only allows https:// or exactly http://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:

  1. SSH Tunnel (Easiest): Run ssh -L 8080:127.0.0.1:8080 user@192.168.x.x on your client machine, then use http://127.0.0.1:8080 in Claude.
  2. Reverse Proxy: Setup Caddy or Nginx with a valid HTTPS certificate for your LAN IP.

Codex Desktop (Official App)

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"

[model_providers.proxy_codex.http_headers]
Authorization = "Bearer your-api-key"

[profiles.default]
model = "gpt-5.4"
model_provider = "proxy_codex"

💡 Why not env_key? macOS/Windows GUI apps do not inherit env vars from your shell rc files — export PROXY_API_KEY=... in your terminal is invisible to the GUI process and Codex Desktop will fail with Missing environment variable. Inlining Authorization via http_headers avoids launchctl setenv / LaunchAgent gymnastics. Switch back to env_key = "PROXY_API_KEY" only when you need the key out of the config file.

⚠️ When logged in via "ChatGPT account", existing sessions might bypass this config and hit the official upstream directly. New sessions started after [model_providers.proxy_codex] is wired up + profiles.default.model_provider = "proxy_codex" will route through the proxy.

Claude for VSCode / JetBrains

Open Claude extension settings → API Configuration:

  • API Provider: Anthropic
  • Base URL: http://localhost:8080
  • API Key: your API key

Cursor

  1. Settings → Models → OpenAI API
  2. Base URL: http://localhost:8080/v1
  3. API Key: your API key
  4. Add model gpt-5.4

Windsurf

  1. Settings → AI Provider → OpenAI Compatible
  2. API Base URL: http://localhost:8080/v1
  3. API Key: your API key
  4. Model: gpt-5.4

Cline (VSCode Extension)

  1. Cline sidebar → gear icon
  2. API Provider: OpenAI Compatible
  3. Base URL: http://localhost:8080/v1
  4. API Key: your API key
  5. Model ID: gpt-5.4

Continue (VSCode Extension)

~/.continue/config.json:

{
  "models": [{
    "title": "Codex",
    "provider": "openai",
    "model": "gpt-5.4",
    "apiBase": "http://localhost:8080/v1",
    "apiKey": "your-api-key"
  }]
}

aider

aider --openai-api-base http://localhost:8080/v1 \
      --openai-api-key your-api-key \
      --model openai/gpt-5.4

Cherry Studio

  1. Settings → Model Services → Add
  2. Type: OpenAI
  3. API URL: http://localhost:8080/v1
  4. API Key: your API key
  5. Add model gpt-5.4

Ollama-Compatible Clients

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.1 by default; do not expose it to the public internet or untrusted LANs.

Any OpenAI-Compatible Client

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 || "");
}

⚙️ Configuration

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, default_service_tier, aliases, custom_models, inject_desktop_context Default model, reasoning config, aliases, and custom catalog entries
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 Usage snapshots, threshold config, exhausted-account skipping
session ttl_minutes, cleanup_interval_minutes Dashboard session management
ollama enabled, host, port, version, disable_vision Ollama-compatible bridge

Model Aliases

model.aliases maps client-facing model names to the real upstream model. This is useful when Claude Desktop / Cursor / Continue only lets you pick certain model IDs, or when you want shorter local names.

You can also manage aliases in Dashboard → Settings → Model Aliases. Saving writes to data/local.yaml and hot-reloads the backend, so you do not need to edit config/default.yaml.

model:
  aliases:
    claude-opus-4-7: gpt-5.5
    sonnet-local: gpt-5.4
    openai-fast: openai:gpt-4o
    deepseek-local: deepseek-chat

providers:
  custom:
    deepseek:
      api_key: "sk-..."
      base_url: "https://api.deepseek.com/v1"
      models: ["deepseek-chat"]
model_routing:
  deepseek-chat: deepseek

Alias resolution runs before model_routing and built-in Claude/Gemini auto-routing. Aliases targeting Codex models still work with Codex suffixes such as -fast / -high; aliases targeting third-party providers rewrite the outgoing direct request model field to the mapped target.

If you need to add fully custom Codex-compatible model IDs to the catalog, configure model.custom_models in data/local.yaml. A string entry uses default text/medium metadata; an object entry can define display name, reasoning efforts, context, and output limits:

model:
  custom_models:
    - local-simple
    - id: local-rich
      display_name: Local Rich
      description: Local rich model
      supported_reasoning_efforts: [low, high]
      default_reasoning_effort: high
      input_modalities: [text, image]
      output_modalities: [text]
      context_window: 12345
      max_context_window: 23456
      max_output_tokens: 3456

Quota Rotation

When quota.skip_exhausted: true, the account pool skips accounts whose cached quota is already exhausted before session affinity / preferredEntryId is applied. A long conversation therefore cannot force routing back to a cached-exhausted account.

The skip condition is currently rate_limit.limit_reached === true, secondary_rate_limit.limit_reached === true, or code_review_rate_limit.limit_reached === true in cached quota. If used_percent is merely near 100, for example 99%, but upstream has not set limit_reached, the proxy may still use that account. Once upstream returns 429, the account is marked rate_limited, enters backoff, and the request is retried with another available account. Secondary and code-review windows are removed from cache after their own reset_at passes, so an account is not skipped forever on stale quota data.

Ollama Bridge Configuration

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/show

Supported 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:

  1. Set ollama.enabled: true and ollama.host: 0.0.0.0 in the Dashboard or data/local.yaml.
  2. Uncomment the 127.0.0.1:${OLLAMA_BRIDGE_PORT:-11434}:11434 port mapping in docker-compose.yml.
  3. Keep the host binding on 127.0.0.1 unless 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.

Listen Address

The source/Docker default config listens on :: (IPv6 unspecified, usually still reachable from localhost). Electron passes 127.0.0.1 at startup unless data/local.yaml explicitly overrides server.host. To force localhost-only binding:

server:
  host: "127.0.0.1"

To allow LAN access, set server.host: "0.0.0.0" in data/local.yaml and use a strong proxy API key.

API Key

server:
  proxy_api_key: "pwd"    # clients use Authorization: Bearer pwd
  # proxy_api_key: null   # no global key; logged-in accounts still have account-level codex-proxy-xxxx keys

On first startup, if data/local.yaml is missing, Codex Proxy creates it with server.proxy_api_key: pwd. The active key is shown in the dashboard API Configuration section.

Environment Variable Overrides

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

📡 API Endpoints

Click to expand main endpoint list

Protocol Endpoints

Endpoint Method Description
/v1/chat/completions POST OpenAI format chat completions
/v1/responses POST Codex Responses API passthrough
/v1/responses/compact POST Codex compact response proxy
/v1/messages POST Anthropic format chat completions
/v1/models GET List available models
/v1/models/catalog GET Full model catalog for the dashboard
/v1/models/:modelId/info GET Reasoning and metadata for one model
/v1beta/models GET Gemini-format model list
/v1beta/models/:modelAction POST Gemini generateContent / streamGenerateContent
: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/batch-delete POST Batch delete accounts
/auth/accounts/batch-status POST Batch update account status
/auth/accounts/health-check POST Batch account health check
/auth/accounts/:id/refresh POST Refresh and probe one account
/auth/accounts/:id/quota GET Actively query one account quota
/auth/accounts/:id/cookies GET/POST/DELETE Manage account Cloudflare cookies
/auth/quota/warnings GET Current quota warning state

Third-Party API Keys

Endpoint Method Description
/auth/api-keys/catalog GET Built-in providers and suggested model catalog
/auth/api-keys GET/POST List / add API keys
/auth/api-keys/models POST Fetch models from a custom OpenAI-compatible provider
/auth/api-keys/export GET Export API key config
/auth/api-keys/import POST Import API key config
/auth/api-keys/batch-delete POST Batch delete API keys
/auth/api-keys/:id DELETE Delete one API key
/auth/api-keys/:id/label PATCH Update API key label
/auth/api-keys/:id/status PATCH Enable or disable an API key

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.json

Admin

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
/admin/logs GET Request log list
/admin/logs/state GET/POST Log capture settings
/admin/update-status GET Self-update status
/admin/check-update POST Check for updates
/admin/apply-update POST Apply self-update
/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
/api/proxies/assignments GET View account proxy assignments
/api/proxies/assign-bulk POST Bulk assign proxies
/api/proxies/assign-rule POST Rule-based proxy assignment
/api/proxies/export GET Export proxy pool YAML
/api/proxies/import POST Import proxy pool YAML

📋 Requirements

  • 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)

⚠️ Notes

  • Codex API is stream-only. stream: false causes 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.

☕ Donate & Community

WeChat Donate
☕ Donate
WeChat Group
💬 WeChat
Telegram Group
💬 Telegram

🙏 Acknowledgements

Codex Proxy is primarily maintained by one person, but it has been improved by a lot of community help. Special thanks to these contributors who submitted code, documentation, fixes, or PRs:

@SsuJojo · @TutuchanXD · @kanweiwei · @et2010 · @d-demand-priv · @hangox · @jarvisluk · @jeasonstudio · @JPClaw12 · @lezi-fun · @lookvincent · @pocper1 · @woai66 · @xsShuang · @yuwei5380

Thanks as well to everyone who opened Issues with bug reproductions, logs, compatibility reports, and feature suggestions. Those reports directly shaped account rotation, proxy compatibility, the Dashboard, Ollama Bridge, model compatibility, and error observability.

📄 License

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.


Built with Hono + TypeScript | Powered by Codex Desktop API