Local dashboard, account manager, and Anthropic-compatible proxy for routing Claude Code through one of multiple OpenAI Codex OAuth accounts.
The project is built for a very specific workflow: keep several Codex accounts on one machine, isolate each account's auth state, switch the active route without touching Claude manually, and expose a local bridge that Claude Code can talk to as if it were an Anthropic-compatible backend.
- Runs a local dashboard on
http://127.0.0.1:9000 - Launches the real
codex loginbrowser OAuth flow per account - Stores each account in its own isolated
CODEX_HOME - Lets you choose an active account and a default account
- Persists model selection per account
- Supports global fast mode and per-account fast mode overrides
- Shows live account state, recent events, and real-time rate-limit telemetry
- Generates and optionally writes Claude Code settings for the local bridge
- Exposes Anthropic-compatible
/v1/models,/v1/messages, and/v1/messages/count_tokens - Supports foreground runs, background service control, and optional
systemd --userinstall
Codex OAuth is tied to local CLI state, which makes multi-account usage awkward. This app makes that state explicit and manageable:
- each account gets its own auth directory
- switching accounts becomes a UI action instead of shell surgery
- Claude Code integration is generated from the currently active account
- live limits are surfaced in the dashboard so you can see when a route is nearly exhausted
| Area | Details |
|---|---|
| Account management | Add, rename, enable, disable, delete, activate, and mark default accounts |
| OAuth flow | Uses the installed codex CLI instead of reimplementing login |
| Model routing | Persists preferred model per account and exposes available models to the dashboard |
| Fast mode | Global default plus per-account override |
| Limits | Displays account limit summaries and app-server rate-limit windows, including reset times and stale-data warnings |
| Claude integration | Builds ANTHROPIC_* and related environment variables and can write them to Claude settings |
| Proxy | Anthropic-compatible message endpoints backed by the active Codex account |
| Operations | Health endpoint, logs, service commands, optional systemd user unit |
Claude Code
-> local Anthropic-compatible bridge (/anthropic/v1/*)
-> active Codex account selected in app state
-> Codex provider adapter
-> local codex CLI / app-server / OAuth files
Dashboard UI
-> Fastify API (/api/*)
-> state store + event log + login session manager + Claude settings service
Main areas:
src/client/ React dashboard
src/server/ Fastify backend and bridge entrypoints
src/server/providers/codex/ Codex provider adapter and Anthropic protocol bridge
src/server/services/ Login session management and Claude settings integration
src/server/store/ State and event persistence
src/shared/ Shared types and validation
scripts/ Setup, smoke test, and systemd helper scripts
.local-data/ Runtime state, account homes, logs, PID files
- Node.js
20+ npmcodexCLI available onPATHclaudeCLI is optional for running the bridge itself, but expected if you want to wire Claude Code to it
- Clone the repository.
- Copy the example environment file.
- Install dependencies.
- Build the app.
- Start the service.
- Open the dashboard and connect accounts.
cp .env.example .env
npm install
npm run build
npm run service:startOpen http://127.0.0.1:9000.
If you just want the app built and started with one command:
npm run setup:startThat script installs dependencies if needed, builds the production bundle, starts the background service, and prints current status.
- Open the dashboard.
- Add an account from the
Accountssection. - Click
Connectto start the real Codex OAuth browser flow. - Finish authentication in the browser window opened by
codex login. - Wait for the login session to report success in the UI.
- Choose the active account.
- Pick a model for that account.
- Decide whether fast mode should be global or account-specific.
- Review the generated Claude integration block.
- Apply the generated settings to Claude if desired.
The dashboard exposes two kinds of limit information:
- Account-level limit metadata derived from the auth payload when available
- Real-time rate-limit windows fetched from the Codex app-server
The rate-limit panel is designed to show the full remaining capacity for the current account, including:
- 5-hour windows
- weekly windows
- per-limit buckets such as model- or tier-specific quotas
- reset timestamps
- warnings when the app-server data may be stale
If the live meter is marked stale or unavailable, the account can still exist and be connected; it only means the separate rate-limit snapshot could not be refreshed at that moment.
Run the client and server in development mode:
npm run devDefault dev ports:
- Vite frontend:
127.0.0.1:5173 - Fastify backend:
127.0.0.1:9000
Build the project:
npm run buildRun in the foreground:
npm startRun as the managed background service:
npm run service:start
npm run service:status
npm run service:restart
npm run service:stopService logs are written to:
.local-data/runtime/service.log
After building, you can install the bundled CLI globally using either symlink or package install.
Symlinked install:
npm install
npm run build
npm linkGlobal package install:
npm install
npm run build
npm install -g .This installs:
ccbridgeclaude-codex-bridge
Examples:
ccbridge start
ccbridge status
ccbridge health
ccbridge logs
ccbridge restart
ccbridge stopNotes:
- The global CLI uses the installed package directory as the app root.
- The default global data directory is
~/.local/share/claude-codex-oauth-bridge. - You can override persistence with
DATA_DIR=/custom/path. npm install -gby itself is not enough; usenpm install -g .from this repository.
| Command | Purpose |
|---|---|
npm run dev |
Start Vite and the Fastify server in development mode |
npm run build |
Clean dist/, compile the server, and build the client |
npm start |
Run the built production server |
npm test |
Run the smoke suite |
npm run test:smoke |
Execute the end-to-end smoke checks |
npm run ci |
Run the build and smoke suite together |
npm run setup:start |
Install if needed, build, and start the managed service |
npm run service:start |
Start the background service |
npm run service:status |
Print service status |
npm run service:restart |
Restart the background service |
npm run service:stop |
Stop the background service |
npm run systemd:install |
Install a systemd --user unit |
npm run systemd:uninstall |
Remove the systemd --user unit |
npm run global:install |
Install the package globally |
npm run global:link |
Link the package globally for local development |
The server exposes two public groups of routes: dashboard API routes and Anthropic-compatible bridge routes.
| Method | Route | Purpose |
|---|---|---|
GET |
/health |
Lightweight runtime health summary |
| Method | Route | Purpose |
|---|---|---|
GET |
/api/state |
Full dashboard state, including service info, accounts, models, integration preview, and recent events |
GET |
/api/logs?limit=120 |
Recent event log entries |
POST |
/api/accounts |
Create an account |
PATCH |
/api/accounts/:accountId |
Rename, enable/disable, change model, or update fast mode override |
DELETE |
/api/accounts/:accountId |
Delete an account and its isolated auth directory |
POST |
/api/accounts/:accountId/activate |
Set the active account |
POST |
/api/accounts/:accountId/default |
Set the default account |
POST |
/api/accounts/:accountId/connect |
Start a Codex OAuth browser-login session |
POST |
/api/accounts/:accountId/test |
Test upstream connectivity for one account |
GET |
/api/login-sessions/:sessionId |
Poll a login session |
DELETE |
/api/login-sessions/:sessionId |
Cancel a login session |
PATCH |
/api/settings |
Update global fast mode, port fallback, and CORS settings |
PATCH |
/api/integration |
Update Claude settings path and auto-apply behavior |
POST |
/api/integration/apply |
Write generated environment variables into Claude settings |
POST |
/api/integration/test |
Test the active account through the integration layer |
These routes require the locally generated auth token from the integration preview. The token can be sent as either:
Authorization: Bearer <token>x-api-key: <token>
Routes:
| Method | Route | Purpose |
|---|---|---|
GET |
/anthropic/v1/models |
List models known to the active Codex account |
POST |
/anthropic/v1/messages/count_tokens |
Return an input token estimate for a request body |
POST |
/anthropic/v1/messages |
Execute a completion or stream events using Anthropic-style message semantics |
Base URL for Claude Code:
http://127.0.0.1:9000/anthropic
The dashboard generates and can write the following environment variables into your Claude settings file:
ANTHROPIC_BASE_URLANTHROPIC_AUTH_TOKENANTHROPIC_MODELANTHROPIC_DEFAULT_OPUS_MODELANTHROPIC_DEFAULT_SONNET_MODELANTHROPIC_DEFAULT_HAIKU_MODELCLAUDE_CODE_SUBAGENT_MODELENABLE_EXPERIMENTAL_MCP_CLI
Default Claude settings path:
~/.claude/settings.json
You can override it with CLAUDE_SETTINGS_PATH in .env or from the dashboard.
Example .env:
PORT=9000
HOST=127.0.0.1
ALLOW_PORT_FALLBACK=false
DATA_DIR=.local-data
CLAUDE_SETTINGS_PATH=
AUTO_APPLY_CLAUDE_SETTINGS=true
CORS_ORIGIN=
SERVICE_LOG_LEVEL=info| Variable | Default | Meaning |
|---|---|---|
PORT |
9000 |
Server port |
HOST |
127.0.0.1 |
Bind address |
ALLOW_PORT_FALLBACK |
false |
Fail if the configured port is busy instead of silently switching |
DATA_DIR |
.local-data |
Persistent app state, event logs, PID files, and account homes |
CLAUDE_SETTINGS_PATH |
empty | Override the Claude settings file path |
AUTO_APPLY_CLAUDE_SETTINGS |
true |
Auto-write integration settings when state changes |
CORS_ORIGIN |
empty | Optional explicit local origin for browser access |
SERVICE_LOG_LEVEL |
info |
Service logging verbosity |
Behavior notes:
ALLOW_PORT_FALLBACK=trueallows the service to move to the next free port on startup.CORS_ORIGINis intentionally blank by default for local-only usage.- Changes to startup behavior apply on the next process start, not retroactively to a running service.
State that survives restarts:
- account registry
- active/default account selection
- per-account model preference
- fast mode settings
- Claude integration settings
- recent runtime metadata and events
Per-account Codex auth state is isolated under:
.local-data/accounts/<account-id>/codex-home/
That separation matters because the Codex CLI keeps auth and refresh state on disk. Without isolated homes, one account can overwrite another.
Run the production build and smoke suite:
npm run build
npm run test:smokeThe smoke test covers:
- account persistence and active/default behavior
- integration preview and Claude settings application
- event log read/write flow
- auth status detection from isolated Codex account homes
- request shaping for the Codex bridge
- non-retryable upstream error mapping
- client API request header behavior
- browser-auth flow wiring
For the same sequence used in CI:
npm run ciThe package also builds cleanly for distribution because dist/ is cleared before each build and prepack rebuilds automatically.
Install a managed systemd --user service:
npm run systemd:installRemove it:
npm run systemd:uninstallNotes:
- The installer builds the app before writing the unit file.
- The service is configured to restart on failure.
- The unit binds to
127.0.0.1by default. - For boot-time startup before interactive login, your Linux machine may need:
sudo loginctl enable-linger "$USER"Basic health check:
curl http://127.0.0.1:9000/healthThe dashboard also shows:
- current active account and model
- integration preview state
- effective fast mode
- auth freshness
- recent proxy, OAuth, integration, and system events
- live limits and stale-limit warnings
- Leave
ALLOW_PORT_FALLBACK=falseif you want startup to fail loudly. - Set
ALLOW_PORT_FALLBACK=trueif you prefer the service to choose the next open port. - If the port changes, regenerate or reapply Claude settings so the
ANTHROPIC_BASE_URLmatches.
- Make sure the
codexCLI is installed and callable from the same environment that launched the server. - Poll the login session until it reaches a terminal state.
- Review the recent event log for
oauthandsystementries. - Check the account's isolated
CODEX_HOMEunder.local-data/accounts/<account-id>/codex-home/.
- The account can still be connected even when the rate-limit snapshot is stale.
- A stale warning usually means the auxiliary app-server rate-limit request did not return fresh data.
- Refresh the dashboard or retry the account status/test path after a short delay.
- Reapply the integration settings from the dashboard.
- Confirm that
ANTHROPIC_BASE_URLpoints to this bridge and not another local proxy. - Confirm that the auth token in Claude settings matches the current integration preview.
- The service is local-only by default and binds to
127.0.0.1. - The Anthropic-compatible bridge is protected by a locally generated bearer token.
- Codex OAuth state is file-based because the upstream CLI expects local auth files.
- Account auth/config files are written with restricted permissions where possible.
- There is no built-in TLS termination.
- This should not be exposed directly to remote networks without a reverse proxy, TLS, and stronger auth.
- CORS is disabled by default and should only be enabled intentionally for a known local origin.
- Backend: Fastify
- Frontend: React + Vite
- Validation: Zod
- Persistence: JSON and JSONL with atomic writes
- OAuth: delegated to the real
codex loginbrowser flow - Token refresh and rate-limit queries: delegated to Codex CLI tooling against the account-specific
CODEX_HOME - Extensibility: provider-specific logic lives under
src/server/providers/
- Contribution guide: CONTRIBUTING.md
- Security policy: SECURITY.md
- Code of conduct: CODE_OF_CONDUCT.md
This repository is currently UNLICENSED. No reuse rights are granted unless the owner adds a different license later.