CLI Providers, Accounts, and Models
These commands configure upstream providers, authenticate accounts, manage credential pools, and control the model catalog exposed to Codex.
Providers
Section titled “Providers”ocx provider <subcommand>
Section titled “ocx provider <subcommand>”Non-interactive provider management. Registry entries are seeded by name; a custom name requires
both --adapter and --base-url.
| Subcommand | Supported flags | Action |
|---|---|---|
list |
--json, --jsonl |
List configured providers and the remaining registry entries; --jsonl emits one configured provider object per line. |
add <name> |
--adapter <adapter>, --base-url <url>, --api-key <key>, --default-model <model>, --set-default, --force, --json, --sync |
Add a registry/custom provider. --force overwrites; --sync refreshes a running proxy in human-output mode. |
edit <name> |
provider field flags, --headers <json>, --json |
Edit validated live provider fields without replacing key pools. --headers merges custom request headers; pass {} or - to clear them. |
test <name> |
--json |
Probe the real upstream model endpoint. |
show <name> |
--json |
Show config with API keys masked. |
remove <name> |
--json |
Remove a non-default provider; the last provider cannot be removed. |
set-default <name> |
--json |
Select an existing provider as the default. |
selected <name> |
--set <ids>, --clear, --json |
Read or update the provider model allowlist. |
quota |
--refresh, --json |
Read provider quota reports. |
resets |
--limit <n>, --json |
List recently detected quota-window resets. |
presets |
--json |
List dashboard provider presets. |
account-mode |
pool, direct, --json |
Select pooled or direct Codex account routing. |
ocx provider list --jsonocx provider list --jsonl # one configured provider object per lineocx provider test arkocx provider add anthropic --api-key sk-ant-... --set-default --syncocx provider add local-dev --adapter openai-chat --base-url http://localhost:11434/v1ocx provider show anthropic --jsonocx models --provider anthropic --jsonocx models live --provider ark --json--jsonl writes only configured providers, one JSON object per line, and omits the
registryCount summary from --json. Each object has the same fields as an item in the configured array.
Use it for scripts that process one configured provider object per line.
--json and --jsonl cannot be combined.
Authentication
Section titled “Authentication”Diagnosing missing main-account quota
Section titled “Diagnosing missing main-account quota”ocx account list openai --quota --refresh --json includes a quotaRefresh object on
the main-account row when that operation attempts a WHAM usage read. The existing
GET /api/codex-auth/accounts?refresh=1 response exposes the same diagnostic.
Its status is ok, not_reported (no parseable quota in a successful response),
http_error, timeout, network_error, invalid_response, or internal_error.
Only http_error includes a numeric httpStatus. No raw response, error message,
credential, or account identifier is included in this object. Cache-only reads,
credential deferrals, and invalidated account snapshots omit it; older servers
also omit it. Absence is not proof of success. A non-success HTTP status remains
http_error even if its error body cannot be read; timeout and network_error
describe failures before headers or while reading a successful response.
A valid login does not guarantee that this separate usage request succeeds. These categories do not change authentication, account selection, or quota freshness rules, and do not turn unknown quota into zero usage. This diagnostic currently covers the native main account, not pool-account refreshes. When reporting missing quota, share the category and HTTP status rather than credential files or a raw network capture.
ocx login <provider>
Section titled “ocx login <provider>”Start the provider’s registered login flow. OAuth-style account providers open a browser and store
credentials under ~/.opencodex/ (refreshable tokens rotate automatically; durable key grants such
as OrcaRouter are reused until the provider revokes them); API-key login providers open their key dashboard, prompt for the
key, validate it when possible, and save the resulting provider config. The command prints the
currently accepted OAuth and API-key provider ids when the name is missing or unknown.
Use the same command to reauthenticate after ocx status / ocx doctor reports
reauthentication required or a terminal refresh failure (or use Reauthenticate in the dashboard).
Codex pool accounts are not one of those OAuth or API-key providers, but ocx login codex reaches
them anyway: it routes to the account-pool login, so ocx login codex --reauth is the same thing as
ocx account reauth codex. The dashboard Codex account pool (Reauthenticate) does it too. That route
runs inside the proxy, so it needs a running one.
ocx login xaiocx login anthropicocx login orcarouter-oauth # browser consent + S256 PKCEocx login orcarouter # paste an existing API keyOAuth reauthentication preserves operator settings such as model selections, pricing overrides, and account failover preferences. Login-owned transport/authentication fields and registry-owned catalog metadata are refreshed. A live-discovery provider keeps its selected default model; a static provider can replace a default that no longer exists in its refreshed catalog.
For Antigravity, an upstream 401 can refresh the rejected account’s OAuth credential and
retry the request once. The retry uses that credential’s Cloud Code Assist project. If refresh
fails or no usable project is available, the request returns an authentication error; use the
reauthentication flow above. A second 401 does not start another refresh/retry cycle.
A proxy that is already running picks up the new credential without a restart: the CLI asks it to reload that one provider from disk, and the request carries no credential of its own. If the running proxy cannot accept that request — most often because it started from a build that predates attested reload — the login still succeeds and the credential is still written to disk, but the live process keeps serving the previous one. The CLI says so and asks you to restart:
⚠️ A proxy is running but could not reload this provider (unattested-target). The credential is saved to disk; the running proxy keeps using the previous one. Restart it to pick this up: ocx restartocx logout <provider>
Section titled “ocx logout <provider>”Remove the stored OAuth credential for a provider.
Accounts and key pools
Section titled “Accounts and key pools”Main-account 99% protection
Section titled “Main-account 99% protection”In Codex settings → Multi-auth → Advanced settings, Block main account at 99% is an independent opt-in beside Ultra Fast. Enabling it first shows the consequences; cancelling does not change the setting. The main-account card shows monitoring, unknown usage, or a current policy block even when Advanced settings is closed.
The policy uses the 5h window when present, otherwise the weekly window. Monthly-only accounts use their monthly window. It does not take the highest percentage across windows. A fresh 0% observation automatically releases the block while the switch stays on; the next 99% observation blocks again. Unknown usage does not fabricate a zero, and a missing reading does not erase an already measured blocking tuple. A predicted reset time alone does not unlock it. While blocked, the existing once-per-minute background cycle checks fresh owned usage; failed or invalid readings retain the block. Other pause, reauthentication, and upstream limits remain independent.
The persisted option is "codexMainAccountHardLock": true in OpenCodex’s config.json; it is off
by default. This protects new requests using the identified main account, not the last 1% itself:
already-running requests, unmatched caller-owned keyring credentials, and traffic outside the
proxy can still spend quota. Added accounts and other providers remain available.
With protection enabled, an owned startup restores the main credential’s in-memory identity binding after native-profile recovery and cleanup, so a persisted 99% block survives a restart. Caller-owned Direct, exact-main, main-fallback, and main-pin requests can briefly receive 503 while that binding is pending; healthy stored Pool accounts stay eligible throughout. No credential is read from a foreign or unconfirmed service home for this initialization.
While this policy blocks main, Luna Reserve on that account is blocked too. Staying below ordinary quota exhaustion may prevent Reserve activation. Disabling the switch restores normal local handling, not additional upstream entitlement. Use the account quota refresh action to obtain a fresh observation; no reset credit is consumed automatically.
Luna Reserve alongside routed models
Section titled “Luna Reserve alongside routed models”The optional authless Desktop mode keeps Desktop’s native Reserve-only picker gate inactive. It also disables Desktop’s automatic Reserve handling: Reserve is an explicit model choice, not an automatic fallback.
Keep the built-in OpenAI provider enabled in ChatGPT-forward mode, enable the account model picker,
and configure a public selector for the stored main account. With effective loopback authless mode
enabled, ocx sync includes <main-selector>/gpt-reserve alongside routed provider models. A bare
gpt-reserve, an added-account selector, and API-key model discovery are not added to the catalog.
The authless setting is ignored for remote-client routing or a listener that needs an admission header.
When public and local listeners run together, Reserve compatibility applies only to requests admitted
by the local listener. An authenticated public request stays on the normal path even if it originates
from the same machine; request headers cannot select the local policy.
Enable authless Desktop mode with ocx system settings --desktop-authless on, run ocx sync,
then fully quit and reopen Codex Desktop so it reloads the rewritten configuration and catalog.
Follow the canonical authless Desktop workflow.
Each compatibility request checks a credential-bound server authorization, cached for at most 60 seconds. OpenCodex sends the Reserve capability header on an owned main-account usage read and requires ordinary usage to be disallowed, the Luna Reserve banner, and exactly one allowed Reserve bucket. Missing, denied, stale or mismatched evidence refuses the request; it does not switch accounts or silently use ordinary Luna. Passive usage can revoke authorization but cannot create it. Global cooldown, pause, reauthentication and the 99% hard lock still apply. Disable the hard lock if you want to use Reserve on an exhausted main account; doing so does not grant server entitlement. This compatibility path supports conversation requests and compaction, not Reserve as a vision or web-search helper or a standalone search-relay model. Choose another model for those helpers.
The picker prefers actual Reserve metadata. When none has been observed, it uses an explicitly marked Luna metadata adaptation, following Desktop’s Reserve-or-Luna preset mapping. A visible entry is not proof of availability. Desktop source and fixture-backed paths were checked; a live Reserve-active account was not used to validate this compatibility path.
ocx account <subcommand>
Section titled “ocx account <subcommand>”List and switch provider accounts and API-key pools through the running proxy. The shipped help surface is:
Usage: ocx account <list|current|use|refresh|auto-switch|priority|login|reauth|code|cancel|remove|add-key|reset-credits|grok-reset-coupons> ...
list [provider] Codex account pool, OAuth accounts and API keys (identifiers shown masked as the API returns them).current <provider> Show the active account or key.use <provider> <id> Switch the active credential; 'main' selects the Codex App login.refresh <provider> Force-refresh Codex or provider quota reports.auto-switch <provider> <on|off|status|threshold N> Control the Codex pool threshold.priority <provider> <id|main> [first|earlier|normal|later|last|-100..100|reset] Selection order; omit the value to read it.remove <provider> <id> --yes Remove a stored account or key after an existence check.add-key <provider> [--label <label>] Add a key read only from piped stdin.login/reauth/code/cancel Run browser or manual-code auth from a headless shell.reset-credits <id|main> [--consume --yes] Inspect or consume Codex reset credits.grok-reset-coupons [<id>] [--consume --yes] [--token-id <token-id>] [--operation-id <uuid>] Inspect or redeem Grok reset coupons.Switching the active account takes effect immediately; running threads move on their next request, and in-flight requests keep the account they captured.A selection-order change applies from the next unbound request and never moves a bound thread.All subcommands require the proxy to be running; the CLI auto-resolves its recorded runtime port.
Successful operations exit 0. Invalid usage, an unknown provider or account/key id, an unreachable
proxy, or an API failure exits 1. Credential fields are displayed exactly as the management API
returns them (including its masking); raw API keys and OAuth tokens are never returned. Display
conveniences are synthesized client-side, same as the dashboard: main is the CLI alias for the
Codex App login in the openai account pool, OAuth accounts without an email appear as Account N,
and the plan/label column falls back across plan, masked email, label, and masked key.
--json account rows use this common shape (optional fields are omitted when unavailable):
{ "provider": "openai", "type": "codex | oauth | api-key", "id": "__main__", "label": "plus", "email": "m***@example.com", "plan": "plus", "masked": "sk-ab****wxyz", "priority": 0, "active": true, "needsReauth": false, "quota": null}ocx account list [provider] [--json] [--all] [--quota [--refresh]]
Section titled “ocx account list [provider] [--json] [--all] [--quota [--refresh]]”Without a provider, lists the Codex pool, OAuth accounts, and configured API-key pools. Empty
providers are skipped unless --all is present. With a provider, lists only that credential family.
Human output uses PROVIDER TYPE ID PLAN/LABEL PRIORITY STATUS; a manually chosen Codex row is marked
selected. PRIORITY is the signed Codex selection order (0 when unset) and shows - for rows
where ordering does not apply, such as OAuth accounts and API keys. By default, with two or more eligible stored Kiro accounts, a 429 rotates automatically to
another account and prefers the one with the most known remaining allowance; rotation is
presence-driven and cannot be turned off — oauthAccountFailover.enabled: false declines the
pre-dispatch account preference, not 429 recovery; ocx account login kiro
adds accounts to the pool one at a time. An empty result is still success. --json
returns:
{ accounts: AccountRow[], notes: string[] }--quota adds a QUOTA column with each account’s own usage, for providers that support a
per-account probe (Anthropic, Kiro, and Google Antigravity today). It is opt-in because the proxy probes the upstream
once per stored credential; the default listing stays a local read. --refresh bypasses the
cached result. An account with no per-account quota shows -, and one whose probe failed shows
unavailable — blank would read as “no usage” rather than “not measured”. --json carries the
full breakdown per account, not just the summarized windows:
Google Antigravity rows carry the same Gem / Cla windows as the provider-level quota, computed
from that account’s own credential and Cloud Code Assist project id. The per-account probe always
talks to Google’s Cloud Code Assist host through the pinned outbound transport, regardless of a
configured baseUrl: a custom base URL is a routing choice for requests, not a second source of
Google’s accounting for a stored credential. An account without a project id, or one whose probe
is redirected or fails, shows unavailable.
$ ocx account list anthropic --quotaPROVIDER TYPE ID PLAN/LABEL PRIORITY STATUS QUOTAanthropic oauth 1278f8da a***r@examp***.com - 5h 7% wk 62%anthropic oauth e112f28b k***1@examp***.net - active 5h 9% wk 45%Kiro bills a monthly allowance and reports no shorter window, so its accounts render a mo
figure instead:
$ ocx account list kiro --quotaPROVIDER TYPE ID PLAN/LABEL PRIORITY STATUS QUOTAkiro oauth 3f0a91c2 a***r@examp***.com - active mo 15%kiro oauth 8b24de70 k***1@examp***.net - mo 88%With two or more Kiro accounts logged in, a 429 rotates to another account automatically and
prefers the one with the most remaining allowance. Accounts are added one at a time —
ocx account login kiro hands off to the Kiro CLI and appends the new account to the pool.
ocx account current <provider> [--json]
Section titled “ocx account current <provider> [--json]”Shows the active account or key. A Codex pool with no manual pin reports the priority-aware
automatic selection: the highest-priority eligible tier is chosen, and the lowest-usage account
within that tier is selected under quota routing; another family with no active credential reports
that state and still exits 0. --json returns:
{ provider, type, activeId: string | null, autoSwitchThreshold?: number, account: AccountRow | null }ocx account use <provider> <account-or-key-id|main> [--json]
Section titled “ocx account use <provider> <account-or-key-id|main> [--json]”Selects an existing Codex account, OAuth account, or API key. For openai, main selects the Codex
App login. A Codex Pool selection clears process-local affinity and applies to the next request,
including one from an existing visible task; proxy restart or affinity eviction can also leave a task
unbound, while in-flight requests keep their captured account. This controls Pool routing only;
Direct mode keeps using the caller-owned/native main credential. Usage-based proactive switching,
401/403 reauthentication, 429/retry-after cooldowns, exclusion, and pre-output 429/402 failure
recovery may later select another eligible Pool account. Those recovery paths remain active when
usage-based switching is off. OpenCodex replays the conversation after an account change, but the
provider-side prompt cache may be cold. Unknown providers or ids exit 1.
On a 401/403, App login clears that account’s process-local affinity and requires reauthentication.
On a 429, opencodex honors Retry-After, starts the account cooldown, clears affinity, and may
rotate the request to another eligible Pool account. These failure transitions remain active with
autoSwitchThreshold: 0; that setting disables only usage-based proactive switching.
--json returns:
{ ok: true, provider, type, activeId }ocx account refresh <provider> [--json]
Section titled “ocx account refresh <provider> [--json]”For the Codex pool, use ocx account refresh openai [--json]. It force-refreshes account quotas and
prints available weekly/monthly percentages and reset times; missing quota data is reported as
unknown, not 0%. Its JSON envelope is { accounts: AccountRow[] }, with quota on each Codex row.
For OAuth and API-key providers, this force-refreshes the provider quota-report endpoint; it is not a
token re-login or a plain account-list re-read. --json returns
{ provider, report: ProviderQuotaReport | null }. A provider with no supported quota report prints
no quota report available for <provider> and exits 0. Unknown providers and management-API
failures exit 1; an upstream quota probe that fails or times out degrades to a null or stale report
instead (exit 0), matching the dashboard’s quota bars.
ocx account auto-switch <provider> <on|off|status|threshold <0-100>> [--json]
Section titled “ocx account auto-switch <provider> <on|off|status|threshold <0-100>> [--json]”Controls the openai Codex pool threshold, or stores a threshold for a generic OAuth pool. on stores 80%, off stores 0%, and threshold <n> accepts 0–100. A generic pool threshold steers selection only while pool.kernel is on with strategy: "fill-first"; with the flag off, saving one does not enable threshold-based switching. It never changes the provider enablement override or disables reactive 429 rotation. status and mutation output for generic pools use the confirmed server response. For generic pools, poolEnabled is the stored provider override (null means unspecified), not inherited effective state; inert: true means the threshold is stored but not applied, inert: false means the pool is applying it, and an absent inert is an unknown capability, which never reports enabled: true. API-key providers, Anthropic and invalid values are rejected.
openai: { provider, autoSwitchThreshold: number, enabled: boolean }generic OAuth: { provider, autoSwitchThreshold: number | null, enabled: boolean, poolEnabled: boolean | null, inert: boolean | null }ocx account priority <provider> <account-id|main> [<-100..100|first|earlier|normal|later|last|reset>] [--json]
Section titled “ocx account priority <provider> <account-id|main> [<-100..100|first|earlier|normal|later|last|reset>] [--json]”Reads or sets one Codex pool account’s selection order: higher is used earlier, the default is
0, and the range is -100 through 100. Only the openai Codex pool is ordered, so other
providers exit 1. main targets the Codex Desktop login, which is ordered like any other pool
account — ocx account priority openai main last is how you keep it as the reserve.
Preset words stand in for small integers: first is +2, earlier is +1, normal is 0,
later is -1, and last is -2. reset returns the account to the default and drops its stored
entry. Omitting the value reads the current order instead of writing one.
Ordering picks which accounts are considered first, not which are usable: selection still runs among
eligible accounts, taking the highest order tier that still has quota headroom and leaving
accountPoolStrategy to choose inside it. Pause, cooldown, and reauthentication are unaffected.
Changes apply from the next unbound request, not only from newly started sessions: preemption moves
an unbound request up as soon as a higher order regains headroom. Threads already bound to an account
normally keep it until that account is drained; a reauthentication failure or a quota cooldown still
releases the binding immediately. A transient-failure streak (5xx and other non-quota failures
reaching upstreamFailoverThreshold, default 3) no longer deletes a live binding: the request is
served by another account while the binding is kept, and the task returns to its own account as soon
as that account serves again. If the account is still failing after 10 minutes the binding is
released normally. This hold is independent of pool.cacheAffinity. Any accepted write also releases a manual
“use this account now” pin, on whichever account held it, including a write that stores the
order an account already had — this is the only way to clear a pin while keeping the account
that is currently selected. (Clearing the active account through the management API releases a
pin too, but it drops that selection along with it.) An unreachable proxy, an
unknown account id, or a value outside the accepted set exits 1. --json returns:
{ ok: true, provider, id, priority: number, preset: string | null }ocx account login|reauth|code|cancel ...
Section titled “ocx account login|reauth|code|cancel ...”Run browser-based or manual-code account authentication from a headless shell. Use
ocx account --help for the provider-specific command shape. If a Codex account login is saved but
its model-catalog refresh remains pending, human output still exits successfully and prints fixed
ocx sync recovery guidance on stderr. --json keeps stdout parseable and carries
catalogRefreshPending: true in the completed login state without the human warning.
ocx account login openai --device runs OpenAI’s device-code login instead of the browser
callback. Use it when the proxy host has no browser, or when nothing can reach its
localhost:1455 — a container, a VPS, or any hub reached over SSH:
ocx account login openai --device --no-wait --json# { "flow": "...", "url": "https://auth.openai.com/codex/device", "deviceCode": "ABCD-EFGH" }Open that URL on any other machine, enter the short code, and the login completes. Without
--no-wait the command polls until you finish; the device grant lives 15 minutes, and the
command waits that long rather than the 5 minutes a browser login allows, because the point
is that you walk away to another device. kimi, nous, and github-copilot accept the flag
as a no-op because their only login is already a device flow; a provider with no device grant
rejects it.
In the dashboard, the same login is selected by the “Don’t open a browser on the proxy machine” checkbox on the add-account modal. That setting already means the operator is not sitting at the proxy host, which is exactly when a callback URL is useless — so ticking it switches the Codex login to the device flow and shows a copyable code instead.
ocx account remove <provider> <id|main> --yes [--json]
Section titled “ocx account remove <provider> <id|main> --yes [--json]”This guarded, non-interactive deletion requires --yes. Before deleting, it verifies that the id
exists; a missing id exits 1 without sending DELETE. The main Codex App login cannot be removed, so
remove openai main --yes is refused. After deletion, the family is read again: removing the pinned
Codex account clears the pin and returns to automatic selection; OAuth promotes the first remaining
account or reports none; API-key pools promote the first remaining key or report none. --json
success and failure shapes are:
{ ok: true, provider, id, removedActive: boolean, promotedActiveId: string | null, catalogRefreshPending?: boolean }{ error: string } // stderr, exit 1catalogRefreshPending is present on Codex removals only. When it is true, the account deletion is
already saved; human output prints generic ocx sync recovery guidance on stderr and still exits 0.
OAuth-account and API-key removal envelopes do not gain this field.
ocx account add-key <provider> [--label <label>] [--json]
Section titled “ocx account add-key <provider> [--label <label>] [--json]”Adds and activates a key for an API-key provider. The key is read only from non-TTY piped/redirected stdin; interactive TTY input, empty input, OAuth/Codex providers, and API failures exit 1. The key is never echoed, including when it appears inside a label. Prefer a secret manager or a here-string:
ocx account add-key openrouter --label personal <<< "$OPENROUTER_API_KEY"security find-generic-password -w openrouter | ocx account add-key openrouter --json--json returns { ok: true, id: string | null, label?: string } and never includes the key.
ocx account reset-credits <id|main> [--consume --yes]
Section titled “ocx account reset-credits <id|main> [--consume --yes]”Inspect Codex reset credits for an account. Consuming a credit is destructive and requires both
--consume and --yes.
After a confirmed reset, fresh usage can recover the same account’s eligible existing
shared reset-derived cooldown. Paused accounts, accounts needing reauthentication and
cooldowns owned by an in-flight probe remain excluded from this recovery. A failed or busy
usage refresh after confirmed consumption does not require another credit: check usage
again instead of repeating --consume. Consume success does not guarantee routability;
see the management API recovery contract
for reset/replay, freshness and scope limits.
ocx account grok-reset-coupons [<account-id>] [--consume --yes [--token-id <id>] [--operation-id <uuid>]] [--json]
Section titled “ocx account grok-reset-coupons [<account-id>] [--consume --yes [--token-id <id>] [--operation-id <uuid>]] [--json]”Inspects remaining reset coupons or redeems one for an xAI / Grok account.
When invoked without --consume, returns the available coupon tokens and validity windows:
ocx account grok-reset-couponsocx account grok-reset-coupons acc_xai_01 --jsonRedeeming a reset coupon mutates billing state and permanently exhausts one coupon token. --consume strictly requires --yes:
ocx account grok-reset-coupons --consume --yesocx account grok-reset-coupons --consume --yes --token-id <token-id>Pass --operation-id <uuid> (must be a valid UUIDv4) to guarantee idempotent settlement. If the network drops or the command is retried, identical operation IDs replay the durably recorded outcome instead of consuming a second coupon.
ocx account main <subcommand>
Section titled “ocx account main <subcommand>”Manage named native Codex main-login profiles without changing OpenCodex account-pool routing:
ocx account main doctor [--json]ocx account main list [--json]ocx account main register <label> [--json]ocx account main add <label>ocx account main reauth --device [--no-wait] [--json]ocx account main reauth status --flow <id> [--json]ocx account main reauth cancel --flow <id> [--json]ocx account main switch <profile-id-or-label> --yes [--json]ocx account main recover [--rollback --yes] [--json]ocx account main reauth --device --no-wait --json writes one JSON object to stdout on success, without the human-readable follow up: line. Use its flowId with ocx account main reauth status --flow <id> --json to check progress.
Each mutating command reports the canonical effective CODEX_HOME returned by the running proxy.
This path can differ from the caller’s CODEX_HOME; commands that support JSON expose the same
value as effectiveCodexHome.
Version 1 supports file-based Codex authentication, encrypts stored profiles with AES-256-GCM, and
keeps the encryption key in the operating-system credential store. add stages the official Codex
login flow before importing the resulting credential. Close Codex before switching profiles; a
successful switch preserves local tasks and history, then requires Codex to be restarted. Use
doctor to inspect profile state and recover to finish or roll back an interrupted transition.
switch accepts either the profile ID or its label.
reauth re-authenticates the existing native main identity with an OpenAI device code (#3898) instead of enrolling a new profile. It is the headless-hub recovery path: no local Codex App, no codex binary, and no OS keyring are required. The device login must complete for the same ChatGPT account that already holds the native main slot; the credential write is fenced by the exclusive claim and a path/hash/inode snapshot, and the command output carries only the flow id, the verification URL, the device code, and status. The pool login route stays pool-only and keeps rejecting __main__; the equivalent dashboard surface is the Codex Auth main card’s Re-login with device code control.
The v1 recovery matrix covers an OpenCodex process exiting after a transaction file has been
published by rename. It does not claim durability across an OS or kernel crash or sudden power
loss: atomicWriteFileAsync() does not fsync either the file or its parent directory.
The encrypted vault, switch journal, recovery marker, and journal quarantine live in the canonical
<real CODEX_HOME>/.opencodex-native-main-profiles directory, so every OpenCodex instance sharing
that Codex home observes one owner and one recovery state. Plaintext login staging remains isolated
under each <OPENCODEX_HOME>/native-main-profile-staging directory.
Before native-main traffic or journal recovery is admitted, the lifetime owner takes the exclusive
credential claim and removes only exact auth.json.ocx.<pid>.<sequence>.tmp crash residues. Each
candidate must remain a single-linked regular file under the unchanged canonical CODEX_HOME; it is
truncated, flushed, and then unlinked. Link/reparse substitutions, identity changes, and other
ambiguity keep native-main traffic closed, while near-miss names are never removed automatically.
This protects against cooperative OpenCodex crashes, not a malicious process already running as the
same OS user. That user and the filesystem containing CODEX_HOME remain trusted, and truncation
does not promise physical erasure from copy-on-write storage, snapshots, or SSD remanence.
Preview builds used <OPENCODEX_HOME>/native-main-profiles. That layout is never imported silently.
If doctor reports legacy profile state, stop every OpenCodex proxy sharing the same CODEX_HOME.
Then either back up and move the matching *.vault.json, *.journal.json, recovery marker, and any
referenced journal-quarantine file together into the canonical directory while preserving owner-only
permissions, or remove the old preview set and run ocx account main register again. Do not choose
between multiple old roots or run both layouts while any sharing proxy is active.
On Windows, preview state keyed by the former case-folded home identity must be reset rather than
moved because its encrypted AAD and operating-system keyring identity are intentionally not reused.
Models
Section titled “Models”ocx models [subcommand] · ocx model <subcommand>
Section titled “ocx models [subcommand] · ocx model <subcommand>”ocx model is an alias of ocx models. With no subcommand, list the models statically seeded in
configured providers. --provider filters one configured provider and --json returns model
metadata. live reads the running catalog; add, edit, remove, and list-custom manage manual
catalog entries; enable, disable, and provider control visibility; selected controls a
provider allowlist; context controls provider context caps; and shadow manages background
shadow-call interception.
Every per-model operation the dashboard offers is available here, so a headless install never needs
the GUI to manage a catalog. add, remove, and list-custom work against the config file and apply
to a running proxy through a catalog sync; the rest talk to the live management API and require the
proxy to be running (ocx start, or an installed service).
| Subcommand | Supported flags | Action |
|---|---|---|
list (default) |
--provider <name>, --json |
List models seeded in configured providers. |
live |
--provider <name>, --json |
Read the running catalog, including models discovered at runtime. Rows are flagged native/routed, custom, and enabled/disabled. |
price <provider/model> |
--json |
Read the model’s saved manual price override; no override means automatic pricing. |
set-price <provider/model> |
--input <rate>, --output <rate>, --cache-read <rate>, --cache-write <rate>, --auto, --json |
Set display prices in USD per 1M tokens. Input/output are required when setting; omitted cache rates become zero. --auto removes only this model’s override. |
add <provider> <modelId> |
--display-name <name>, --context-window <tokens>, --modalities <text,image,audio> |
Register a model the provider catalog does not advertise. |
edit <custom-id> |
--model-id <id>, --display-name <name|->, --context-window <tokens|0>, --modalities <text,image,audio|->, --json |
Edit a custom model. - clears a field; 0 clears the context window. |
remove <custom-id|provider/modelId> |
--yes |
Delete a custom model. Requires --yes when stdin is not an interactive terminal. |
list-custom |
--json |
Show all custom models with the custom-id the other subcommands take. |
enable <provider/model|native-model> |
--native, --json |
Make one model visible to Codex. |
disable <provider/model|native-model> |
--native, --json |
Hide one model from Codex. |
provider <name> <on|off> |
--json |
Enable or disable every model of one provider in a single write. |
selected <provider> |
--set <id,id...>, --clear, --json |
Read or replace the provider model allowlist. --clear removes the allowlist so every model is offered. |
context <status|value <tokens> [--set-all]|provider <name> on [--value <tokens>]|provider <name> off|all <on|off>> |
--json |
Read or set the context-window cap, globally or per provider. value <tokens> --set-all also re-points every routed provider (like the dashboard toggle); without it the value only becomes the default. provider ... on --value <tokens> sets an explicit cap for that provider only (--value is valid with on only). |
shadow <status|set> [model|-] |
--enabled <on|off>, --json |
Read or set the replacement model for Codex’s background helper calls. - clears the model. status also reports sourceModels, the helper slugs the proxy intercepts (default: gpt-5.6-luna; clients through 0.144.x used gpt-5.4-mini, which an explicit sourceModels override can restore). |
ocx models live --json # what Codex can actually see right nowocx models disable anthropic/claude-haiku-4 # hide one routed modelocx models enable gpt-5.6-sol # no slash, so it is treated as nativeocx models provider zenmux off # hide a noisy provider wholesaleocx models selected anthropic --set claude-opus-5,claude-fable-5ocx models selected anthropic --clear # drop the allowlist againocx models add deepseek deepseek-v4 --display-name 'DeepSeek V4' --context-window 128000 --modalities text,imageocx models list-custom --json # read the custom-id for edit/removeocx models remove deepseek/deepseek-v4 --yesA model selector with a slash is routed (anthropic/claude-opus-5); a bare id is treated as a
native OpenAI model, so --native is only needed to force that reading for an id that would
otherwise look routed.
--modalities accepts only text, image, and audio. Codex parses that field as a closed enum
and rejects an entire catalog containing any other value, so add, edit, and the management API
all refuse the bad value rather than storing something the catalog writer would have to strip later
(#759).
Mark one model text-only
Section titled “Mark one model text-only”Use ocx provider add mine --adapter openai-chat --base-url https://example.com/v1 --default-model model-a --text-only when registering a provider, or ocx provider edit mine --model model-a --text-only for an existing provider. Add can use --model or its default model; edit requires --model. The flag updates only that exact model’s modelCapabilities.inputModalities to ["text"], preserving other models and axes.
Cached quota history
Section titled “Cached quota history”ocx account history openai <pool-account-id> [--limit 1-200] [--json] reads stored observations without contacting the provider. The output separates actual observation time, WHAM or response-header source, window family and usage percentage. At most 200 observations per account are retained for 30 days, with global storage bounds.
Ordinary token refresh preserves history. Reauthentication, removal or account replacement retires the old publication. Native main and probes performed before a login is published are not included. Missing history means insufficient observations, not zero usage. This command does not spend quota. Effective estimates, when supported by observations, carry the limitations below.
The history output also includes effective reported-token estimates when same-window observations and attributable usage support them. Each estimate includes a sample count and low confidence. Quota rounding, external usage and assumed log-label continuity limit the inference; it is not your provider’s token allowance. Missing or truncated ledger evidence returns insufficient evidence. --limit controls displayed history, not the bounded estimate input.

