Runtime configuration is resolved from one canonical settings file, an optional env-pointed override file, legacy compatibility files, and per-setting environment overrides. This guide covers what a user needs day to day; the complete field-by-field inventory (including every internal env name) lives in development/CONFIG_FIELDS.md, and the maintainer-level resolution walkthrough lives in development/CONFIG_FLOW.md.
pluginConfig (the persisted name for runtime settings) resolves in this order:
CODEX_MULTI_AUTH_CONFIG_PATH set AND the file exists?
│ yes → load that file as the config source (also the save target)
│ no
▼
<multi-auth root>/settings.json → pluginConfig section valid?
│ yes → load it
│ no / absent
▼
legacy config ladder (config.json, codex-multi-auth-config.json,
openai-codex-auth-config.json under the Codex home roots)
│ none found
▼
DEFAULT_PLUGIN_CONFIG (hardcoded defaults)
After a source is selected, each field is schema-validated, merged over DEFAULT_PLUGIN_CONFIG, and then environment variables apply per-setting overrides — env wins over the file, the file wins over the default. Numeric values are clamped to their documented bounds.
Two behaviors worth knowing:
- A
CODEX_MULTI_AUTH_CONFIG_PATHthat is set but does not exist yet is skipped on load; the next config save still creates/writes that path while the variable remains set. CODEX_MULTI_AUTH_DIRre-homes every multi-auth-owned file. WhenCODEX_HOMEpoints at a non-default directory, the multi-auth root resolves strictly to$CODEX_HOME/multi-auth— no other roots are scanned for an existing account pool.
Dashboard display settings (dashboardDisplaySettings in the same settings.json) are resolved separately: persisted values first, then normalized defaults.
| Layer | Path | Purpose |
|---|---|---|
| Multi-auth root | ~/.codex/multi-auth/ (override with CODEX_MULTI_AUTH_DIR) |
Accounts, settings, cache, logs, governance state |
| Unified settings | <multi-auth root>/settings.json |
pluginConfig + dashboardDisplaySettings; a .bak sibling is kept for recovery |
| Optional config file | CODEX_MULTI_AUTH_CONFIG_PATH=<path> |
Standalone config source and save target |
| Global account pool | <multi-auth root>/openai-codex-accounts.json |
Managed OAuth accounts |
| Per-project pools | <multi-auth root>/projects/<project-key>/ |
Project-scoped accounts when perProjectAccounts is on and CLI sync is off |
| Official Codex state | ~/.codex/auth.json, ~/.codex/accounts.json, ~/.codex/config.toml |
Synced by CODEX_MULTI_AUTH_SYNC_CODEX_CLI; paths overridable via CODEX_CLI_*_PATH |
Full path reference: reference/storage-paths.md.
{
"version": 1,
"dashboardDisplaySettings": {
"menuAutoFetchLimits": true,
"menuSortEnabled": true,
"menuSortMode": "ready-first",
"menuShowQuotaSummary": true,
"menuShowQuotaCooldown": true,
"menuLayoutMode": "compact-details"
},
"pluginConfig": {
"codexMode": true,
"codexRuntimeRotationProxy": true,
"liveAccountSync": true,
"sessionAffinity": true,
"proactiveRefreshGuardian": true,
"preemptiveQuotaEnabled": true,
"fetchTimeoutMs": 60000,
"streamStallTimeoutMs": 45000
}
}Boolean env overrides accept 1/0, true/false, yes/no (case-insensitive). Unparseable env values are ignored with a one-time warning rather than disabling the setting.
These are safe for most operators and cover the common day-to-day adjustments.
| Variable | Effect |
|---|---|
CODEX_MULTI_AUTH_DIR |
Re-home the multi-auth root (settings/accounts/cache/logs) |
CODEX_MULTI_AUTH_CONFIG_PATH |
Load config from an alternate file; becomes the save target while set |
CODEX_MODE=0/1 |
Toggle Codex mode |
CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY=0/1 |
Opt out/in of routing forwarded Codex traffic through the localhost account-rotation proxy |
CODEX_MULTI_AUTH_FORCE_ACCOUNT=<index|email|id> |
Force one account for a single forwarded codex-multi-auth-codex run (equivalent to --account, which wins when both are set). Ephemeral and fail-hard; requires the runtime rotation proxy. See Force an account for one invocation |
CODEX_MULTI_AUTH_APP_ROTATION_IDLE_MS=<ms> |
Idle shutdown for the wrapper-launched Codex app helper (default 12h) |
CODEX_MULTI_AUTH_APP_ROTATION_MAX_LIFETIME_MS=<ms> |
Absolute ceiling on a helper's life regardless of activity (default 24h; 0 disables) |
CODEX_MULTI_AUTH_APP_ROTATION_DETACHED_IDLE_MS=<ms> |
Idle window once a helper's launcher is gone, nothing is connected, and the helper has never served a request (default 15m; 0 restores the full idle timeout) |
CODEX_MULTI_AUTH_APP_BIND=0/1 |
Opt out/in of the first-run packaged Codex app bind (checked before CODEX_MULTI_AUTH_APP_BIND_INSTALL) |
CODEX_MULTI_AUTH_APP_BIND_INSTALL=0/1 |
Opt out/in of packaged Codex app bind self-heal on first durable CLI run or rotation enable |
CODEX_MULTI_AUTH_APP_LAUNCHER_INSTALL=0/1 |
Opt out/in of user-level launcher routing on first durable CLI run or rotation enable |
CODEX_TUI_V2=0/1 |
Toggle TUI v2 |
CODEX_TUI_COLOR_PROFILE=truecolor|ansi256|ansi16 |
TUI color profile |
CODEX_TUI_COLOR_MODE=auto|dark|light |
TUI background mode; auto detects light backgrounds from COLORFGBG |
CODEX_TUI_GLYPHS=ascii|unicode|auto |
TUI glyph mode (auto detects from WT_SESSION/TERM_PROGRAM/TERM) |
CODEX_AUTH_FETCH_TIMEOUT_MS=<ms> |
HTTP request timeout (default 60000, min 1000) |
CODEX_AUTH_STREAM_STALL_TIMEOUT_MS=<ms> |
Stream stall timeout (default 45000, min 1000) |
CODEX_AUTH_SCHEDULING_STRATEGY=hybrid|sequential |
Account scheduling strategy (default hybrid); see Sequential / drain-first scheduling |
CODEX_AUTH_MIN_ROTATION_INTERVAL_MS=<ms> |
Minimum time between global account switches (default 60000; 0 disables the last-served bias) |
CODEX_AUTH_TOKEN_INVALIDATION_COOLDOWN_MS=<ms> |
Cooldown after an explicit upstream token revocation (default 300000) |
CODEX_AUTH_PID_OFFSET_ENABLED=0/1 |
Per-process account-selection bias for parallel agents (default on) |
CODEX_AUTH_ROUTING_MUTEX=legacy|enabled |
Serialize account selection within a single process (default legacy) |
CODEX_AUTH_BACKGROUND_RESPONSES=0/1 |
Stateful background: true Responses compatibility (default off) |
CODEX_AUTH_NO_BROWSER=1 |
Suppress browser launch for headless login |
Use these only for debugging, controlled benchmarking, sandboxed tests, or maintainer workflows. The complete pluginConfig ↔ env matrix plus every wrapper/proxy/internal env name is in development/CONFIG_FIELDS.md.
| Variable | Effect |
|---|---|
CODEX_MULTI_AUTH_SYNC_CODEX_CLI |
Force/disable active-account sync into official Codex CLI files (default on; legacy CODEX_AUTH_SYNC_CODEX_CLI still read with a warning) |
CODEX_MULTI_AUTH_REAL_CODEX_BIN |
Override official Codex binary discovery (absolute path required) |
CODEX_MULTI_AUTH_BYPASS=1 |
Skip multi-auth intercept; forward everything to official Codex |
CODEX_MULTI_AUTH_FORCE_ACCOUNT_INDEX |
Internal 0-based pin published by the wrapper after --account / CODEX_MULTI_AUTH_FORCE_ACCOUNT resolution — not meant to be set by hand |
CODEX_MULTI_AUTH_STATUSLINE=0/1 |
Disable/enable the forwarded-session status line |
CODEX_MULTI_AUTH_AUTO_SYNC_ON_STARTUP=0 |
Skip best-effort active-account sync around forwarded launches |
CODEX_MULTI_AUTH_FORCE_FILE_AUTH_STORE=0 |
Skip the wrapper-injected -c cli_auth_credentials_store="file" and the startup config.toml reconcile |
CODEX_MULTI_AUTH_ENFORCE_CLI_FILE_AUTH_STORE=0 |
Opt out of every persisted cli_auth_credentials_store = "file" rewrite in ~/.codex/config.toml |
CODEX_MULTI_AUTH_DEBUG=1 |
Verbose wrapper/debug notices |
CODEX_MULTI_AUTH_RUNTIME_PROXY_UPSTREAM_BASE_URL |
Pin the proxy upstream to an explicit-port http://127.0.0.1:<port> URL (numeric loopback only; fails closed when set but the command does not route). Wrapper sessions only; for the packaged-app router use codex-multi-auth rotation bind-app --upstream <url> |
CODEX_CLI_AUTH_PATH / CODEX_CLI_ACCOUNTS_PATH / CODEX_CLI_CONFIG_PATH |
Override official Codex auth.json / accounts.json / config.toml paths (sandboxes and tests) |
CODEX_AUTH_* per-field names |
Every pluginConfig field has an env accessor — see the full matrix |
CODEX_AUTH_REFRESH_LEASE* |
Cross-process refresh coordination knobs |
MCODEX_MONITOR_INTERVAL / MCODEX_TMUX_SESSION / MCODEX_TMUX_HISTORY_LIMIT |
mcodex convenience launcher knobs |
CODEX_MULTI_AUTH_TEST_* |
Test-only fault injectors — never set in normal use |
codex-multi-auth config explain # every field: value, default, source (env|unified|file|default)
codex-multi-auth config explain --json # machine-readable form
codex-multi-auth config template # print a starter config (modern|legacy|minimal)
codex-multi-auth status # pool + quota + runtime markers
codex-multi-auth rotation status # runtime proxy stateconfig explain mirrors the real load precedence, so the file and source it reports are exactly what the wrapper resolves — including the CODEX_MULTI_AUTH_CONFIG_PATH env-path override. Use it before and after setting an env override to confirm the override landed.
Human-facing CLI output follows the system locale by default. The first
non-empty value among LC_ALL, LC_MESSAGES, and LANG selects Chinese for
zh_* locales; all other values select English. JSON output remains stable and
is not translated.
Override one invocation with --locale en or --locale zh, or persist the
choice:
codex-multi-auth config locale auto
codex-multi-auth config locale en
codex-multi-auth config locale zhCODEX_MULTI_AUTH_LOCALE=auto|en|zh has precedence for a process. status
prints a compact summary with quota progress bars by default; add --details
for the diagnostic view. The Chinese detailed view localizes runtime,
subscription, forecast, workspace, and API/ZDR labels as well as the quota
bars. JSON output remains stable and machine-readable.
The local dashboard is available with codex-multi-auth web (or codex-ma web). It uses the same account and quota state as status --json, updates
through a server-sent event stream, opens a browser by default, and listens on
127.0.0.1:4317. Use --no-open for headless machines. A non-loopback bind
must include an access token.
codexRuntimeRotationProxy is enabled by default. When enabled through defaults, settings, codex-multi-auth rotation enable, or CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY=1, the codex-multi-auth-codex wrapper starts a localhost-only Responses proxy for forwarded official Codex sessions, including CLI request commands, codex app-server, and codex app launches through the wrapper. For non-interactive request commands and codex app, the wrapper writes a temporary shadow CODEX_HOME/config.toml that selects a custom provider named codex-multi-auth-runtime-proxy, launches the official Codex surface against that provider, and removes the shadow home after the owning process exits. Interactive TUI sessions (with or without the optional initial prompt), resume/fork, and codex app-server instead stay on the canonical CODEX_HOME and receive the same provider through -c overrides, which avoids reindexing session history on every launch and leaves the real config.toml untouched. Set codexRuntimeRotationProxy=false, run codex-multi-auth rotation disable, or set CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY=0 to bypass the proxy.
For mcodex resume <id> targeting a rollout recorded under native openai,
the wrapper preserves the openai provider name for Codex's session lookup and
temporarily points the built-in provider at the local rotation proxy through
Codex's top-level openai_base_url override. It does not create a
[model_providers.openai] table, because recent Codex versions reserve that
built-in provider ID. The resumed request still uses the managed account pool,
while the canonical session files and SQLite state remain shared. resume --last applies the same provider-aware selection.
A single forwarded run can be pinned to one account with codex-multi-auth-codex --account <selector> (or CODEX_MULTI_AUTH_FORCE_ACCOUNT). The pin is applied per-invocation by that run's own proxy instance, so it never touches the persisted switch pin and cannot leak across concurrent sessions. Because the proxy is required for the pin to take effect, --account fails hard when the proxy is disabled rather than silently using a rotated account. See Force an account for one invocation.
The proxy preserves request bodies and streaming responses, replaces outbound auth headers with the selected managed account, and rotates to another account before response bytes are streamed when it sees rate limits, server errors, network failures, or refresh failures. It removes hop-by-hop headers, private account metadata headers, and stale decoded content-encoding from client responses. If every account is unavailable, the proxy returns a structured pool-exhaustion error that points to codex-multi-auth rotation status.
Anti-abuse protection. Rapidly switching OAuth tokens from the same IP can trigger OpenAI's anti-abuse detection and cause accounts to be invalidated in sequence. The proxy includes two mitigations:
- Token-invalidation detection: when the upstream or the token-refresh endpoint returns an explicit OAuth revocation message, the proxy returns the error directly to the client instead of rotating to the next account. The affected account receives a 5-minute cooldown (
tokenInvalidationCooldownMs, default300000) instead of the generic 30-second auth-failure cooldown. Configure viaCODEX_AUTH_TOKEN_INVALIDATION_COOLDOWN_MS. - Rotation-rate throttle: the proxy biases account selection toward the last-served account for a configurable window (default 60 seconds,
minRotationIntervalMs). Accounts that are rate-limited or cooling down are still rotated around. Configure viaCODEX_AUTH_MIN_ROTATION_INTERVAL_MSor set to0to disable.
schedulingStrategy controls how the proxy picks an account for each request:
hybrid(default) spreads load across all available accounts using a weighted health/token/freshness score. Both accounts tend to consume quota at a similar pace.sequential(drain-first) uses accounts one at a time. Runtime policy assigns every account priority1by default, so a priority map is normally present even without user configuration. When all priorities are1, the active cursor stays on its healthy account until it is unavailable, then scans forward and wraps the pool. Quota reset timing does not override that cursor.
Setting at least one account priority to a value other than 1 enables saved list order within the lowest eligible priority tier in sequential mode. Quota reset timing and active-account preference do not reorder that tier. An earlier configured account regains preference when it becomes eligible again. For example, priorities 0, 0, 1 use the first eligible priority-0 account in saved order before falling back to priority 1.
Sequential routing still respects model eligibility, disabled workspaces/accounts, policy blocks, rate limits, cooldowns, circuit breakers, exhausted quota, and the subscription reserve. With customized priorities, it falls back through saved order and subsequent priority tiers when an account is unavailable.
In sequential mode a manual pin (codex-multi-auth switch <index>) still takes precedence and is never overridden. Sequential mode intentionally ignores per-session affinity: every conversation follows the configured order or active cursor, regardless of which account originally handled it. Enable it with schedulingStrategy: "sequential" in settings or CODEX_AUTH_SCHEDULING_STRATEGY=sequential for a per-process trial.
When you drive many agents in parallel (for example a swarm of deep agents), each is usually a separate codex-multi-auth-codex process with its own in-process rotation state. Concentrated load on a small pool causes cascading 429s. The relevant knobs:
pidOffsetEnabled(defaulttrue, envCODEX_AUTH_PID_OFFSET_ENABLED): gives each process a small deterministic account-selection bias so separate processes prefer different accounts instead of all selecting the same one. This is the primary lever for the multi-process swarm case and is on by default; it is a no-op for single-account pools, and a manual pin plus health/quota scoring still take precedence over the small offset. Setfalse(orCODEX_AUTH_PID_OFFSET_ENABLED=0) to force every process to score accounts identically.retryAllAccountsRateLimited(defaultfalse), withretryAllAccountsMaxRetries(default0) andretryAllAccountsMaxWaitMs(default0): when every account is momentarily rate-limited, wait for the soonest quota window and retry instead of returning pool-exhaustion immediately. Keep the retry/wait budgets bounded so a blocking wait does not exceed the host client's own request timeout.routingMutex(defaultlegacy, envCODEX_AUTH_ROUTING_MUTEX): set toenabledto serialize account selection within a single process. It has no effect across separate agent processes.
The structural fix is more accounts: with N accounts and M ≫ N concurrent agents, roughly M/N agents share each account, so rate-limit pressure only drops as N grows. See High parallelism / swarms of agents for the full playbook, including the host-client-side Provider response headers timed out after 10000ms timeout (which this plugin cannot change).
Microsoft/Outlook SSO accounts may be more sensitive to proxy-mediated token use. If an Outlook-linked account is invalidated on every first request through the proxy but works normally on ChatGPT web, the root cause is likely IP or device binding on the Microsoft side. Raising CODEX_AUTH_TOKEN_INVALIDATION_COOLDOWN_MS and re-logging in the affected account typically resolves the cascade. If the problem persists, consider excluding the Microsoft account from the rotation pool via codex-multi-auth switch.
For codex app launches that go through the wrapper, the wrapper automatically starts a small internal helper so rotation can keep working if the desktop app launcher detaches. The helper stores only local runtime status, uses the same per-session proxy client key as the CLI path, and exits after an idle timeout.
codex-multi-auth rotation enable also binds the packaged desktop app to a persistent localhost router. This backs up the real Codex config.toml, writes the codex-multi-auth-runtime-proxy provider into the real Codex home, starts the router immediately, and installs a user login startup entry: a Startup .cmd on Windows or a LaunchAgent on macOS. The persistent provider is marked as not requiring OpenAI auth and uses a local app-bind client token, so the desktop runtime does not display the selected multi-auth account while codex-multi-auth status and quota views still read the router's last-account telemetry. codex-multi-auth rotation disable and codex-multi-auth rotation unbind-app stop that router, remove the startup entry, and restore the backed-up Codex config. The official app files are not patched.
Package install scripts stay side-effect-free (postinstall prints a short notice only). First-run self-heal of desktop defaults runs once on a durable global install when you invoke codex-multi-auth ..., and again as needed from codex-multi-auth rotation enable:
- The official CLI credential store is pinned to
cli_auth_credentials_store = "file"in~/.codex/config.toml, so front-ends that exec the official binary directly stop triggering macOS login-keychain prompts. SetCODEX_MULTI_AUTH_ENFORCE_CLI_FILE_AUTH_STORE=0to skip. - Packaged Codex app bind is repaired when a Codex desktop app is detected. Set
CODEX_MULTI_AUTH_APP_BIND=0orCODEX_MULTI_AUTH_APP_BIND_INSTALL=0to skip, orCODEX_MULTI_AUTH_APP_BIND_INSTALL=1to force it. - Supported user-level launcher routing is installed for global installs. Set
CODEX_MULTI_AUTH_APP_LAUNCHER_INSTALL=0to skip shortcut routing, or runcodex-multi-auth-app-launcher --removeto restore backed-up Windows shortcuts or remove the managed macOS wrapper later. - The one-time claim is recorded at
~/.codex/multi-auth/first-run-setup.json.npxand project-local installs skip first-run setup so they do not consume the marker. An existing marker from before the auth-store step is migrated in place, replaying only that step — app bind and launcher install are not rerun. - Installed wrappers may perform a best-effort daily npm version check during normal forwarded Codex startup. When npm has a newer release, the wrapper only prints a manual notice:
npm install -g codex-multi-auth@latest. It never runs npm install or update commands for you. Notices are shown only on a TTY or whenCODEX_MULTI_AUTH_DEBUG=1.
Some Windows installs expose Codex only as a packaged shell:AppsFolder app entry. Those entries cannot be retargeted like .lnk files, so the persistent app bind is the supported path for making the pinned packaged app use rotation automatically.
Keep these enabled for most environments:
menuAutoFetchLimitsmenuSortEnabledliveAccountSyncsessionAffinityproactiveRefreshGuardianpreemptiveQuotaEnabledpidOffsetEnabled
The shipped config templates expose first-class current OpenAI model aliases:
- both templates lead with GPT-6.1 Sol (
gpt-6.1-sol, the upstream catalog's default and priority-1 model), followed by GPT-6.1 Terra (gpt-6.1-terra, 500K Codex context) and GPT-6 Astra (gpt-6-astra). Baregpt-6.1resolves togpt-6.1-sol; any 6.1 id with aterratoken resolves togpt-6.1-terra; baregpt-6andastraresolve to the flagship Astra. The full current ladder — 6.1 Sol, 6.1 Terra, Astra, Sol and Luna — takeslowthroughultra(Luna stops atmax) and rejectsnone/minimal, which are coerced up tolow - both templates also list GPT-6 Sol (
gpt-6-sol, the everyday workhorse) and GPT-6 Luna (gpt-6-luna, the small, cheap tier), added to the upstream Codex catalog on 2026-09-22. Sol takeslowthroughultra; Luna stops atmax, the same split as the 5.6 tiers. There is no GPT-6 Terra: an id such asgpt-6-terraresolves to Sol, which is where upstream Codex migratesgpt-5.6-terrausers. Baresolandlunaare not aliases for the GPT-6 tiers - any GPT-6 id the alias table does not name resolves to Astra rather than falling back to the previous generation, unless it carries a
solorlunatier token, which picks that tier (gpt-6-sol-2026-09-22stays on Sol), or a6.Nminor token other than6.1(gpt-6.2stays on Astra). That covers thegpt-6-astra-proplan tier, dated snapshots such asgpt-6-astra-2026-09-03, and tiers added after this release. An id carryingaeonresolves togpt-6-astra— the leakedgpt-6-astra-aeonslug was never a durable catalog model and is retired onto the flagship gpt-5.5andgpt-5.5-pro(including dated snapshot ids) resolve togpt-6-solandgpt-6-astrarespectively — upstream's migration targets ahead of the 2026-10-14 OAuth retirement — rather than being sent to the backend verbatim