Skip to content

Soju06/codex-lb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

763 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

codex-lb

English | ็ฎ€ไฝ“ไธญๆ–‡

Load balancer for ChatGPT accounts. Pool multiple accounts, track usage, manage API keys, view everything in a dashboard.

dashboard accounts
More screenshots
Settings Login
settings login
Dashboard (dark) Accounts (dark) Settings (dark)
dashboard-dark accounts-dark settings-dark

Features

Account Pooling
Load balance across multiple ChatGPT accounts
Usage Tracking
Per-account tokens, cost, 28-day trends
API Keys
Per-key rate limits by token, cost, window, model
Dashboard Auth
Password + optional TOTP
OpenAI-compatible
Codex CLI, OpenCode, any OpenAI client
Auto Model Sync
Available models fetched from upstream

Quick Start

# Docker (recommended)
docker volume create codex-lb-data
docker run -d --name codex-lb \
  -p 2455:2455 -p 1455:1455 \
  -v codex-lb-data:/var/lib/codex-lb \
  ghcr.io/soju06/codex-lb:latest

# or uvx
uvx codex-lb

Open localhost:2455 โ†’ Add account โ†’ Done.

Remote Setup

When accessing the dashboard remotely for the first time, a bootstrap token is required to set the initial password.

Auto-generated (default): On first startup (no password configured), the server generates a one-time token and prints it to logs:

docker logs codex-lb
# ============================================
#   Dashboard bootstrap token (first-run):
#   <token>
# ============================================

Open the dashboard โ†’ enter the token + new password โ†’ done. The token is shared across replicas and remains valid until a password is set. In multi-replica setups, replicas must share the same encryption key (the Helm chart default) for restart recovery to work.

Manual token: To use a fixed token instead, set the env var before starting:

docker run -d --name codex-lb \
  -e CODEX_LB_DASHBOARD_BOOTSTRAP_TOKEN=your-secret-token \
  -p 2455:2455 -p 1455:1455 \
  -v codex-lb-data:/var/lib/codex-lb \
  ghcr.io/soju06/codex-lb:latest

Local access (localhost) bypasses bootstrap entirely โ€” no token needed.

Client Setup

Point any OpenAI-compatible client at codex-lb. If API key auth is enabled, pass a key from the dashboard as a Bearer token.

Model availability is discovered from the upstream Codex model catalog and can vary by account plan, workspace, rollout, and upstream deprecation state. Prefer the live GET /v1/models or GET /backend-api/codex/models response over a copied static table when configuring clients or API-key model allowlists.

Logo Client Endpoint Config
OpenAI Codex CLI http://127.0.0.1:2455/backend-api/codex ~/.codex/config.toml
OpenCode OpenCode http://127.0.0.1:2455/v1 ~/.config/opencode/opencode.json
OpenClaw OpenClaw http://127.0.0.1:2455/v1 ~/.openclaw/openclaw.json
Python OpenAI Python SDK http://127.0.0.1:2455/v1 Code
OpenAIโ€‚Codex CLI / IDE Extension

~/.codex/config.toml:

model = "gpt-5.3-codex"
model_reasoning_effort = "xhigh"
model_provider = "codex-lb"

[model_providers.codex-lb]
name = "openai"  # required โ€” enables remote /responses/compact. Lowercase since Codex 2026-05-23; older "OpenAI" stops resolving gpt-5.5
base_url = "http://127.0.0.1:2455/backend-api/codex"
wire_api = "responses"
supports_websockets = true
requires_openai_auth = true # required for codex app

Optional: enable native upstream WebSockets for Codex streaming while keeping codex-lb pooling:

export CODEX_LB_UPSTREAM_STREAM_TRANSPORT=websocket

auto is the default and uses native WebSockets for native Codex headers or models that prefer them. You can also switch this in the dashboard under Settings -> Routing -> Upstream stream transport.

Note: Codex itself does not currently expose a stable documented wire_api = "websocket" provider mode. If you want to experiment on the Codex side, the current CLI exposes under-development feature flags:

[features]
responses_websockets = true
# or
responses_websockets_v2 = true

These flags are experimental and do not replace wire_api = "responses".

Upstream websocket handshakes automatically honor standard proxy environment variables when they are present. wss:// handshakes check wss_proxy, socks_proxy, https_proxy, and all_proxy; plain ws:// handshakes also check ws_proxy and http_proxy. Set CODEX_LB_UPSTREAM_WEBSOCKET_TRUST_ENV=false only when websocket handshakes must bypass those environment proxies and connect directly.

With API key auth:

[model_providers.codex-lb]
name = "openai"
base_url = "http://127.0.0.1:2455/backend-api/codex"
wire_api = "responses"
env_key = "CODEX_LB_API_KEY"
supports_websockets = true
requires_openai_auth = true # required for codex app
export CODEX_LB_API_KEY="sk-clb-..."   # key from dashboard
codex

Verify WebSocket transport

Use a one-off debug run:

RUST_LOG=debug codex exec "Reply with OK only."

Healthy websocket signals:

  • CLI logs contain connecting to websocket and successfully connected to websocket
  • codex-lb logs show WebSocket /backend-api/codex/responses
  • codex-lb logs do not show fallback POST /backend-api/codex/responses for the same run

If you run codex-lb behind a reverse proxy, make sure it forwards WebSocket upgrades.

Migrating from direct OpenAI โ€” codex resume filters by model_provider; old sessions won't appear until you re-tag them. Use the built-in retag command instead of editing Codex files by hand; see Codex session retagging for backups, Docker, WSL, and rollback details.

# Preview what will change first.
codex-lb codex-sessions retag --from openai --to codex-lb --dry-run

# Then close Codex/Codex CLI and apply the retag.
codex-lb codex-sessions retag --from openai --to codex-lb --yes
OpenCodeโ€‚OpenCode

Important: Use the built-in openai provider with baseURL override โ€” not a custom provider with @ai-sdk/openai-compatible. Custom providers use the Chat Completions API which drops reasoning/thinking content. The built-in openai provider uses the Responses API, which properly preserves encrypted_content and multi-turn reasoning state.

Before starting, please ensure that all existing OpenAI credentials is cleared in ~/.local/share/opencode/auth.json You can clean the config by using this one-liner jq 'del(.openai)' ~/.local/share/opencode/auth.json > auth.json.tmp && mv auth.json.tmp ~/.local/share/opencode/auth.json

~/.config/opencode/opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "openai": {
      "options": {
        "baseURL": "http://127.0.0.1:2455/v1",
        "apiKey": "{env:CODEX_LB_API_KEY}"
      },
      "models": {
        "gpt-5.4": {
          "name": "GPT-5.4",
          "reasoning": true,
          "options": { "reasoningEffort": "high", "reasoningSummary": "detailed" },
          "limit": { "context": 1050000, "output": 128000 }
        },
        "gpt-5.3-codex": {
          "name": "GPT-5.3 Codex",
          "reasoning": true,
          "options": { "reasoningEffort": "high", "reasoningSummary": "detailed" },
          "limit": { "context": 272000, "output": 65536 }
        },
        "gpt-5.1-codex-mini": {
          "name": "GPT-5.1 Codex Mini",
          "reasoning": true,
          "options": { "reasoningEffort": "high", "reasoningSummary": "detailed" },
          "limit": { "context": 272000, "output": 65536 }
        },
        "gpt-5.3-codex-spark": {
          "name": "GPT-5.3 Codex Spark",
          "reasoning": true,
          "options": { "reasoningEffort": "xhigh", "reasoningSummary": "detailed" },
          "limit": { "context": 128000, "output": 65536 }
        }
      }
    }
  },
  "model": "openai/gpt-5.3-codex"
}

This overrides the built-in openai provider's endpoint to point at codex-lb while keeping the Responses API code path that handles reasoning properly.

export CODEX_LB_API_KEY="sk-clb-..."   # key from dashboard
opencode
OpenClawโ€‚OpenClaw

~/.openclaw/openclaw.json:

{
  "agents": {
    "defaults": {
      "model": { "primary": "codex-lb/gpt-5.4" },
      "models": {
        "codex-lb/gpt-5.4": { "params": { "cacheRetention": "short" } }
        "codex-lb/gpt-5.4-mini": { "params": { "cacheRetention": "short" } }
        "codex-lb/gpt-5.3-codex": { "params": { "cacheRetention": "short" } }
      }
    }
  },
  "models": {
    "mode": "merge",
    "providers": {
      "codex-lb": {
        "baseUrl": "http://127.0.0.1:2455/v1",
        "apiKey": "${CODEX_LB_API_KEY}",   // or "dummy" if API key auth is disabled
        "api": "openai-responses",
        "models": [
          {
            "id": "gpt-5.4",
            "name": "gpt-5.4 (codex-lb)",
            "contextWindow": 1050000,
            "contextTokens": 272000,
            "maxTokens": 4096,
            "input": ["text"],
            "reasoning": false
          },
          {
            "id": "gpt-5.4-mini",
            "name": "gpt-5.4-mini (codex-lb)",
            "contextWindow": 400000,
            "contextTokens": 272000,
            "maxTokens": 4096,
            "input": ["text"],
            "reasoning": false
          },
          {
            "id": "gpt-5.3-codex",
            "name": "gpt-5.3-codex (codex-lb)",
            "contextWindow": 400000,
            "contextTokens": 272000,
            "maxTokens": 4096,
            "input": ["text"],
            "reasoning": false
          }
        ]
      }
    }
  }
}

Set the env var or replace ${CODEX_LB_API_KEY} with a key from the dashboard. If API key auth is disabled, local requests can omit the key, but non-local requests are still rejected until proxy authentication is configured.

The /v1 route is the simplest OpenAI-compatible setup. If your OpenClaw build uses a Codex-native provider path such as openai-codex-responses and needs Codex-style usage/accounting behavior, point that provider at http://127.0.0.1:2455/backend-api/codex instead. For third-party Codex-compatible backends, the client must allow opaque bearer-token passthrough and should only send chatgpt-account-id when it actually decoded one from an official ChatGPT/Codex token.

Pythonโ€‚OpenAI Python SDK
from openai import OpenAI

client = OpenAI(
    base_url="http://127.0.0.1:2455/v1",
    api_key="sk-clb-...",  # from dashboard, or any non-empty string if auth is disabled
)

response = client.chat.completions.create(
    model="gpt-5.3-codex",
    messages=[{"role": "user", "content": "Hello!"}],
)
print(response.choices[0].message.content)

API Key Authentication

API key auth is disabled by default. In that mode, only local requests to the protected proxy routes can proceed without a key; non-local requests are rejected until proxy authentication is configured. Enable it in Settings โ†’ API Key Auth on the dashboard when clients connect remotely or through Docker, VM, or container networking that appears non-local to the service.

When enabled, clients must pass a valid API key as a Bearer token:

Authorization: Bearer sk-clb-...

The protected proxy routes covered by this setting are:

  • /v1/* (except /v1/usage, which always requires a valid key)
  • /backend-api/codex/*
  • /backend-api/transcribe

Creating keys: Dashboard โ†’ API Keys โ†’ Create. The full key is shown only once at creation. Keys support optional expiration, model restrictions, and rate limits (tokens / cost per day / week / month).

Configuration

Environment variables with CODEX_LB_ prefix or .env.local. See .env.example. SQLite is the default database backend; PostgreSQL is optional via CODEX_LB_DATABASE_URL (for example postgresql+asyncpg://...).

The Docker Compose postgres profile uses the Postgres 18 image and mounts the named data volume at /var/lib/postgresql, the parent of the image's versioned PGDATA directory.

Existing Postgres 16 compose volumes must be upgraded before the Postgres 18 container starts:

docker compose --profile postgres stop postgres
docker run --rm -v codex-lb-postgres-data:/var/lib/postgresql -v "$PWD:/backup" alpine \
  tar -C /var/lib/postgresql -czf /backup/codex-lb-postgres-data-before-pg18.tgz .
docker compose --profile postgres-upgrade run --rm postgres-upgrade
docker compose --profile postgres up -d postgres

The postgres-upgrade profile runs pg_upgrade in one-shot mode against the same named volume and exits after the data directory has been upgraded to the Postgres 18 layout. Because that helper mounts and rewrites the operator's database volume, Compose pins the helper image by digest; refresh and review the digest deliberately when changing the helper image tag. Keep the backup until the application has started and codex-lb-db check succeeds against the upgraded database.

The normal postgres service refuses to start when it detects the old root-level PG_VERSION file from a pre-18 Compose volume. If that guard fires, run the postgres-upgrade profile above before starting Postgres again. It also refuses nested /var/lib/postgresql/data directories that still report a pre-18 major version, because those layouts need an explicit pg_upgrade before the Postgres 18 container can safely open them.

Dashboard authentication modes

codex-lb supports three dashboard auth modes via environment variables:

  • CODEX_LB_DASHBOARD_AUTH_MODE=standard โ€” built-in dashboard password with optional TOTP from the Settings page.
  • CODEX_LB_DASHBOARD_AUTH_MODE=trusted_header โ€” trust a reverse-proxy auth header such as Authelia's Remote-User, but only from CODEX_LB_FIREWALL_TRUSTED_PROXY_CIDRS. Built-in password/TOTP remain available as an optional fallback, and password/TOTP management still requires a fallback password session.
  • CODEX_LB_DASHBOARD_AUTH_MODE=disabled โ€” fully bypass dashboard auth. Use only behind network restrictions or external auth. Built-in password/TOTP management is disabled in this mode.

trusted_header mode also requires:

CODEX_LB_FIREWALL_TRUST_PROXY_HEADERS=true
CODEX_LB_FIREWALL_TRUSTED_PROXY_CIDRS=172.18.0.0/16
CODEX_LB_DASHBOARD_AUTH_PROXY_HEADER=Remote-User

If the trusted header is missing and no fallback password is configured, the dashboard fails closed and shows a reverse-proxy-required message instead of loading the UI.

Docker examples

Authelia / trusted header

docker run -d --name codex-lb \
  -p 2455:2455 -p 1455:1455 \
  -e CODEX_LB_DASHBOARD_AUTH_MODE=trusted_header \
  -e CODEX_LB_DASHBOARD_AUTH_PROXY_HEADER=Remote-User \
  -e CODEX_LB_FIREWALL_TRUST_PROXY_HEADERS=true \
  -e CODEX_LB_FIREWALL_TRUSTED_PROXY_CIDRS=172.18.0.0/16 \
  -v codex-lb-data:/var/lib/codex-lb \
  ghcr.io/soju06/codex-lb:latest

Hard override / no app-level dashboard auth

docker run -d --name codex-lb \
  -p 2455:2455 -p 1455:1455 \
  -e CODEX_LB_DASHBOARD_AUTH_MODE=disabled \
  -v codex-lb-data:/var/lib/codex-lb \
  ghcr.io/soju06/codex-lb:latest

For Helm, pass the same values through extraEnv.

Data

Environment Path
Local / uvx ~/.codex-lb/
Docker /var/lib/codex-lb/

Backup this directory to preserve your data.

Troubleshooting

Kubernetes

helm install codex-lb oci://ghcr.io/soju06/charts/codex-lb \
  --set postgresql.auth.password=changeme \
  --set config.databaseMigrateOnStartup=true \
  --set migration.schemaGate.enabled=false
kubectl port-forward svc/codex-lb 2455:2455

Open localhost:2455 โ†’ Add account โ†’ Done.

The Helm chart auto-configures HTTP /responses owner handoff for multi-replica installs using a headless-service DNS name per pod. The default cluster domain is cluster.local; set Helm clusterDomain if your cluster uses a different suffix. Override config.sessionBridgeAdvertiseBaseUrl only if pods must be reached through a different internal address.

For external database, production config, ingress, observability, and more see the Helm chart README.

Fast Mode and service-tier behavior is documented in Responses API compatibility context.

Development

# Docker
docker compose watch

# Local
uv sync && cd frontend && bun install && cd ..
uv run fastapi run app/main.py --reload        # backend :2455
cd frontend && bun run dev                     # frontend :5173

Contributors โœจ

Thanks goes to these wonderful people (emoji key):

Soju06
Soju06

๐Ÿ’ป โš ๏ธ ๐Ÿšง ๐Ÿš‡
Jonas Kamsker
Jonas Kamsker

๐Ÿ’ป ๐Ÿ› ๐Ÿšง
Quack
Quack

๐Ÿ’ป ๐Ÿ› ๐Ÿšง ๐ŸŽจ
Jill Kok, San Mou
Jill Kok, San Mou

๐Ÿ’ป โš ๏ธ ๐Ÿšง ๐Ÿ›
PARK CHANYOUNG
PARK CHANYOUNG

๐Ÿ“– ๐Ÿ’ป โš ๏ธ
Choi138
Choi138

๐Ÿ’ป ๐Ÿ› โš ๏ธ
LYAโššCAPโššOCEAN
LYAโššCAPโššOCEAN

๐Ÿ’ป โš ๏ธ
Diรณgenes Castro
Diรณgenes Castro

๐Ÿ’ป โš ๏ธ
Eugene Korekin
Eugene Korekin

๐Ÿ’ป ๐Ÿ› โš ๏ธ
jordan
jordan

๐Ÿ’ป ๐Ÿ› โš ๏ธ
DOCaCola
DOCaCola

๐Ÿ› โš ๏ธ ๐Ÿ“–
JoeBlack2k
JoeBlack2k

๐Ÿ’ป ๐Ÿ› โš ๏ธ
Peter A.
Peter A.

๐Ÿ“– ๐Ÿ’ป ๐Ÿ›
Hannah Markfort
Hannah Markfort

๐Ÿ’ป โš ๏ธ
mws-weekend-projects
mws-weekend-projects

๐Ÿ’ป โš ๏ธ
Quang Do
Quang Do

๐Ÿ’ป โš ๏ธ
Anand Aiyer
Anand Aiyer

๐Ÿ› ๐Ÿ’ป โš ๏ธ
defin85
defin85

๐Ÿ’ป ๐Ÿ› โš ๏ธ
Jacky Fong
Jacky Fong

๐Ÿ’ป ๐Ÿ› ๐Ÿ’ฌ ๐Ÿšง โš ๏ธ
flokosti96
flokosti96

๐Ÿ’ป โš ๏ธ
Woonggi Min
Woonggi Min

๐Ÿ’ป โš ๏ธ
Yigit Konur
Yigit Konur

๐Ÿ› ๐Ÿ’ป
Ruben
Ruben

๐Ÿ’ป โš ๏ธ ๐Ÿ›
Steve Santacroce
Steve Santacroce

๐Ÿ’ป โš ๏ธ ๐Ÿ›
Hugh Do
Hugh Do

๐Ÿ’ป โš ๏ธ
Hubert Salwin
Hubert Salwin

๐Ÿ’ป โš ๏ธ
Teemu Koskinen
Teemu Koskinen

๐Ÿ“–
Yu Peng Zheng
Yu Peng Zheng

๐Ÿ“– ๐Ÿ’ป
embogomolov
embogomolov

๐Ÿ’ป โš ๏ธ
Renat Sharipov
Renat Sharipov

๐Ÿ’ป โš ๏ธ
Liu Rui
Liu Rui

๐Ÿ“– ๐Ÿ’ป โš ๏ธ ๐Ÿ›
OverHash
OverHash

๐Ÿ’ป โš ๏ธ
Kazet
Kazet

๐Ÿ’ป โš ๏ธ
Bala Kumar
Bala Kumar

๐Ÿ’ป โš ๏ธ ๐Ÿค”
ihazgithub
ihazgithub

๐Ÿ’ป โš ๏ธ
Temirkhan
Temirkhan

๐Ÿ’ป โš ๏ธ ๐Ÿ“– ๐Ÿ›
tobwen
tobwen

๐Ÿ’ป โš ๏ธ ๐Ÿ›
Rio
Rio

๐Ÿ’ป ๐Ÿ› โš ๏ธ
Mika
Mika

๐Ÿ’ป ๐Ÿ“– โš ๏ธ
Darafei Praliaskouski
Darafei Praliaskouski

๐Ÿ’ป ๐Ÿ“– โš ๏ธ ๐Ÿ›
Maxim Feofilov
Maxim Feofilov

๐Ÿ’ป โš ๏ธ
JeffKandt
JeffKandt

โš ๏ธ ๐Ÿ‘€
klaascommerce
klaascommerce

๐Ÿ’ป โš ๏ธ
ozpool
ozpool

๐Ÿค” ๐Ÿ“– ๐Ÿ’ป โš ๏ธ
Manu
Manu

โš ๏ธ ๐Ÿ‘€
Wojtek Majewski
Wojtek Majewski

โš ๏ธ
Andrew Noble
Andrew Noble

๐Ÿ’ป โš ๏ธ
Josu Gorostegui
Josu Gorostegui

๐Ÿ’ป โš ๏ธ
Linus Mixson
Linus Mixson

๐Ÿ’ป โš ๏ธ
Lotfree
Lotfree

๐Ÿ’ป โš ๏ธ ๐Ÿ“– ๐Ÿ›
timefox
timefox

๐Ÿ’ป โš ๏ธ
Nikhil
Nikhil

๐Ÿ’ป โš ๏ธ
Miha Orazem
Miha Orazem

๐Ÿ’ป โš ๏ธ
Steven (Minh) Dang
Steven (Minh) Dang

๐Ÿ“–
onlysdesign-ui
onlysdesign-ui

๐Ÿ’ป โš ๏ธ
Mahir Taha ร–zdin
Mahir Taha ร–zdin

๐Ÿค” ๐Ÿ’ป โš ๏ธ
hikki
hikki

๐Ÿ’ป ๐ŸŽจ
Nataprom
Nataprom

๐Ÿ’ป โš ๏ธ
Iweisc
Iweisc

๐Ÿ’ป โš ๏ธ
ram/haidar
ram/haidar

๐Ÿ’ป
Rudra Tiwari
Rudra Tiwari

๐Ÿ’ป
Wu Chao
Wu Chao

๐Ÿ’ป
zwd0313
zwd0313

๐Ÿ’ป
jhordanjw123
jhordanjw123

๐Ÿ’ป โš ๏ธ
mastertyko
mastertyko

๐Ÿ’ป โš ๏ธ
NeoClaw
NeoClaw

๐Ÿ’ป
abarsegov
abarsegov

๐Ÿ’ป
Akshay Kakatkar
Akshay Kakatkar

๐Ÿ’ป โš ๏ธ
softkleenex
softkleenex

๐Ÿ’ป โš ๏ธ
plastictaste
plastictaste

๐Ÿ’ป โš ๏ธ ๐Ÿ“–
Rubรฉn Pรฉrez Bachiller
Rubรฉn Pรฉrez Bachiller

๐Ÿ’ป โš ๏ธ ๐Ÿ›
n3crosis
n3crosis

๐Ÿ’ป โš ๏ธ
copilot
copilot

๐Ÿ’ป
geoHeil
geoHeil

๐Ÿ’ป โš ๏ธ
Jonรกลก Sivek
Jonรกลก Sivek

๐Ÿ’ป โš ๏ธ
Guanwei Chen
Guanwei Chen

๐Ÿ’ป โš ๏ธ
่ฝๅถ
่ฝๅถ

๐Ÿ’ป ๐Ÿ“– โš ๏ธ ๐ŸŒ

This project follows the all-contributors specification. Contributions of any kind welcome!

About

Codex/ChatGPT multiple account load balancer & proxy with usage tracking, dashboard, and OpenCode-compatible endpoints

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors