Codex Auth Router is an unofficial, local-first account router for people who
use the official Codex CLI with more than one Codex-capable OAuth account. It
ships the codex-multi-auth CLI, a loopback-only router daemon, and reversible
helpers for Codex config and desktop/startup integration.
Refresh tokens stay in local secret storage. Codex clients talk to a local, authenticated router instead of receiving account refresh tokens directly.
This is a community project. It is not affiliated with, endorsed by, or supported by OpenAI.
Codex Auth Router is pre-v1 and experimental. The Rust implementation is usable from source, and CI builds/tests Linux, macOS, and Windows, but packaged releases, broader end-to-end coverage, fault/load testing, and release hardening are still in progress.
Use it locally on a trusted machine. Do not run it as a hosted service or expose the router to a network.
Use this project to select between OpenAI accounts that you own or are authorized to use, for example to keep personal, work, and organization-scoped Codex sessions separate on one local machine.
Do not use it to share accounts, pool account capacity, bypass rate or usage limits, or continue the same workload by automatically moving it to another account after a plan limit is reached.
When a Codex session is started with --account, the local router token is
pinned to that account. Pinned sessions do not automatically fail over to a
different account on upstream retryable statuses such as HTTP 429.
This project targets workflows where the official Codex CLI normally reads one active local config, but you need to keep several eligible accounts available on the same machine. Instead of manually replacing local Codex credentials, you can keep account metadata and refresh tokens in local storage, then route Codex-compatible requests through an eligible account according to deterministic local policy.
Use it when you want to:
- keep several Codex OAuth accounts available on one machine;
- run Codex through a local router without giving clients refresh tokens;
- inspect which account would be selected and why;
- opt into reversible Codex config, desktop launcher, or startup integration.
It is not:
- an OpenAI Platform API key proxy;
- a hosted or multi-user service;
- a patched Codex binary;
- a compatibility layer for the old TypeScript package internals.
- Native Rust CLI:
codex-multi-auth. - OAuth login with PKCE, callback/manual modes, nonce validation, and ID-token signature checks when an ID token is returned.
- Local account inventory, labels, enable/disable, pinned account selection, status, and explain output.
- Loopback-only router daemon with per-client bearer tokens.
- Router support for official Codex Responses, models, Codex API helpers, and Codex backend helper routes used by account usage, apps, and cloud task views.
- Unix control socket on Linux/macOS and current-user ACL named pipe on Windows.
- Secret storage through the OS keyring by default, with an opt-in encrypted-file backend for environments without a usable keyring.
- Reversible
bind codexandunbind codexfor official Codex config. - Reversible platform launcher integration: Linux desktop/autostart entries, macOS app wrapper/LaunchAgent, and Windows Start Menu/startup shortcuts.
- Legacy JSON import preview and metadata export with redacted credentials.
doctor --fixfor repairable stale router state.
Actively supported targets are Linux, macOS, and Windows. CI currently builds and tests on:
- Ubuntu latest
- macOS latest
- Windows latest
Other operating systems are not supported for router control or launcher integration.
The package MSRV is Rust 1.75; CI currently pins Rust 1.75.0.
Default data roots are platform-native:
| Platform | Config root | State root |
|---|---|---|
| Linux | $XDG_CONFIG_HOME/codex-multi-auth or ~/.config/codex-multi-auth |
$XDG_STATE_HOME/codex-multi-auth or ~/.local/state/codex-multi-auth |
| macOS | ~/Library/Application Support/codex-multi-auth |
~/Library/Application Support/codex-multi-auth/state |
| Windows | %APPDATA%\codex-multi-auth |
%LOCALAPPDATA%\codex-multi-auth |
For tests or isolated runs, set CMA_CONFIG_ROOT and CMA_STATE_ROOT.
Packaged releases are not published yet. Install from a checkout:
git clone https://github.com/kazuki-hanai/codex-auth-router.git
cd codex-auth-router
cargo install --path crates/cma-cli --lockedYou also need the official Codex CLI available as codex. If it is installed
somewhere else, either set CODEX_MULTI_AUTH_CODEX or configure an executable
override:
codex-multi-auth config set codex.executable_override /path/to/codex-
Log in to the first account:
codex-multi-auth login --label work
When neither
--browsernor--manualis provided in an interactive terminal, the CLI asks which login flow to use.If browser callback is not convenient, use manual mode:
codex-multi-auth login --manual --label personal
-
Check local state:
codex-multi-auth account list codex-multi-auth account list --interactive codex-multi-auth status
-
Start the local router:
codex-multi-auth router start codex-multi-auth router status
-
Run official Codex through an ephemeral local config:
codex-multi-auth codex -- exec "summarize this repository"
Arguments after
--are passed to the officialcodexexecutable. When at least one local account is available, the ephemeral Codex home also receives local ChatGPT auth metadata so Codex UI commands such as/usagecan route through the local router. The generated ephemeral Codex config starts from your existing Codexconfig.tomland overrides only the active model provider plus the local router provider settings.To pin this Codex process to a specific account:
codex-multi-auth codex --account work -- exec "summarize this repository"
-
Optionally make the integration persistent:
codex-multi-auth bind codex --dry-run codex-multi-auth bind codex
To undo the Codex config change:
codex-multi-auth unbind codex
Add another account:
codex-multi-auth login --browser --label personal
codex-multi-auth account listSelect a default account or model-family-specific account:
codex-multi-auth use work
codex-multi-auth use --interactive
codex-multi-auth use personal --model-family gpt-5Run two Codex sessions with different accounts:
codex-multi-auth codex --account work -- exec "work task"
codex-multi-auth codex --account personal -- exec "personal task"
codex-multi-auth codex --select-account -- exec "pick an account first"List and resume saved Codex sessions:
codex-multi-auth session list
codex-multi-auth session list --all
codex-multi-auth session list --interactive
codex-multi-auth resume --account work --last
codex-multi-auth resume --select-account
codex-multi-auth resume --select-account --all
codex-multi-auth resume --account personal 019f0000-0000-7000-8000-000000000000resume resolves --last and the default interactive session picker locally,
then forwards the selected session id to the official codex resume command
through the local router. This avoids asking Codex to scan the full session
tree before resuming.
Explain routing for the current local state:
codex-multi-auth explainInspect and repair common local state problems:
codex-multi-auth doctor
codex-multi-auth doctor --fixTry the tool without touching your normal local state:
export CMA_CONFIG_ROOT="$(mktemp -d)"
export CMA_STATE_ROOT="$(mktemp -d)"
codex-multi-auth statuscodex-multi-auth login [--label <name>] [--browser|--manual]
codex-multi-auth account list [--interactive]|show|enable|disable|remove|rename
codex-multi-auth use [<account-id|label>] [--interactive] [--model-family <family>]
codex-multi-auth status [--json]
codex-multi-auth explain [--request <fixture.json>]
codex-multi-auth router start [--foreground]|stop|status|restart
codex-multi-auth router run [--account <account-id|label>|--select-account] -- <official-codex-args...>
codex-multi-auth codex [--account <account-id|label>|--select-account] -- <official-codex-args...>
codex-multi-auth session list [--all] [--archived] [--limit <n>] [--cwd <dir>] [--interactive]
codex-multi-auth resume [--account <account-id|label>|--select-account] [--select] [--last|--all|<official-codex-resume-args...>]
codex-multi-auth bind codex [--dry-run|--rotate-token]
codex-multi-auth bind launcher [--kind all|desktop|startup] [--dry-run]
codex-multi-auth unbind codex [--dry-run]
codex-multi-auth unbind launcher [--kind all|desktop|startup] [--dry-run]
codex-multi-auth config get|set|edit|path
codex-multi-auth import legacy-json [--path <path>|--discover] [--dry-run]
codex-multi-auth export metadata <path> [--credentials redacted|none]
codex-multi-auth doctor [--fix] [--json]
On Linux, the default OS keyring backend uses the freedesktop.org Secret Service over DBus. Minimal desktops, servers, WSL sessions, and containers may not have a Secret Service provider installed, running, or unlocked. You can either start/unlock a compatible provider such as GNOME Keyring, KWallet, or KeePassXC Secret Service, or use the opt-in encrypted-file backend:
codex-multi-auth config set secrets.backend encrypted-file
export CMA_SECRETS_PASSPHRASE='choose a long local passphrase'
codex-multi-auth login --browserWhen running from a checkout with cargo run, pass the command after --:
cargo run -p cma-cli --bin codex-multi-auth -- config set secrets.backend encrypted-file
export CMA_SECRETS_PASSPHRASE='choose a long local passphrase'
cargo run -p cma-cli --bin codex-multi-auth -- loginThe same passphrase must be available to later commands that need to decrypt stored refresh tokens.
Make sure codex is on PATH, set CODEX_MULTI_AUTH_CODEX, or configure an
explicit executable path:
codex-multi-auth config set codex.executable_override /path/to/codexCodex Auth Router is designed for a single local user account on a trusted machine.
What is stored locally:
- OAuth refresh tokens are stored through the OS keyring by default.
- The encrypted-file backend is opt-in and requires
CMA_SECRETS_PASSPHRASEevery time secrets need to be read or written. - Plaintext file secrets are compile-time disabled unless the
danger-plaintext-secretsfeature is explicitly enabled. - Persistent
bind codexwrites a sensitive local router bearer token into the Codex config. It does not write account refresh tokens there. - Persistent
bind codexdoes not modify Codexauth.json. Ephemeralcodex-multi-auth codex -- ...creates a temporaryauth.jsoncontaining only the local router token and synthetic account metadata. The temporary Codex home is removed after the process exits. - Ephemeral runs inherit the existing Codex
config.tomlfromcodex.config_path_override,CODEX_HOME/config.toml, or~/.codex/config.tomland then override only the local router provider settings. Existingauth.jsonand providerapi_keyvalues are not copied into the temporary Codex home. - Ephemeral runs expose the existing Codex
sessions/directory to the temporary Codex home so normal Codex session history,session list, andresumecontinue to use the same local session files. - Ephemeral
codex-multi-auth codex --account <account> -- ...pins the local router token to that account for the lifetime of that Codex process. - Codex config backups may contain local router tokens; treat them as sensitive local files.
What is proxied:
- The router binds only to loopback hosts.
/healthcan be unauthenticated by default for local health checks. Setrouter.allow_unauthenticated_healthtofalseif you do not want that exception.- Other router requests require a local bearer token or
x-cma-client-token. - The router proxies official Codex Responses/models routes, Codex API helper
routes such as
/api/codex/images/generationsand/api/codex/alpha/search, and Codex backend helper routes such as/api/codex/usage,/api/codex/tasks/list,/api/codex/tasks/{task_id}, and rate-limit reset endpoints. - Token-bearing files are written with private permissions where the platform supports them.
What is reversible:
- Codex config and launcher changes are explicit, opt-in, backed up, and
reversible with
unbind. unbind codexrestores Codex config, but it does not revoke router clients that were already created. Treat previously written local router tokens as sensitive until you rotate or remove local router state.
Do not expose the router port to a network, share state directories across users, or run this as a hosted service.
For vulnerability reporting, see SECURITY.md.
Repository layout:
crates/
cma-domain/ shared types, errors, config, redaction, routing policy
cma-platform/ OS paths, permissions, process helpers, randomness
cma-store/ config, SQLite state, secret backends, import/export
cma-oauth/ PKCE, callback server, token exchange/refresh
cma-router/ loopback HTTP proxy and routing
cma-control/ router daemon lifecycle and local control API
cma-codex/ Codex discovery, ephemeral run, bind/unbind, launcher ops
cma-cli/ `codex-multi-auth` native CLI
xtask/ schema and fixture helper tasks
Run the local checks:
mise trust
mise install rust
mise run checkThe mise.toml file pins Rust 1.75.0 to match rust-toolchain.toml.
Task auto-install is disabled, so install Rust explicitly before running tasks.
Individual tasks are available as mise run fmt, mise run clippy,
mise run test, and mise run build.
Without mise, run the same checks directly:
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
cargo build -p cma-cli --bin codex-multi-auth --releaseThe GitHub Actions workflow runs the same core checks on Linux, macOS, and Windows.
Before a v1 release, the project still needs packaging and release automation, broader end-to-end coverage, fault/load testing, dependency/audit gates, and published install artifacts.
Issues and pull requests are welcome. For changes that touch authentication, secret storage, router behavior, Codex config mutation, or platform launcher integration, include a short threat-model note in the PR and run the full local check set above.
Please keep changes explicit and reversible: this project should not mutate Codex config, launchers, or startup entries unless the user runs a command that clearly asks for it.
MIT. See LICENSE.