Skip to content

Latest commit

 

History

886 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

oc-codex-multi-auth: ChatGPT OAuth and multi-account Codex routing for OpenCode

npm version npm downloads CI MIT license

oc-codex-multi-auth is an OpenCode plugin for ChatGPT Plus/Pro OAuth, Codex and GPT-5/GPT-6 model routing (including GPT-6 Astra/Sol/Luna, the Daybreak cyber tiers, and GPT-5.6 Sol/Terra/Luna), multi-account rotation, account switching, health checks, quota visibility, diagnostics, and recovery tools. It installs the OpenCode provider/TUI configuration, registers a 24-tool codex-* command toolkit, and routes OpenCode OpenAI SDK requests through the ChatGPT-backed Codex flow with local account state.

Use it when you want OpenCode to run Codex-style coding workflows from your own ChatGPT subscription while keeping accounts visible, switchable, health-checked, and recoverable from the terminal.

oc-codex-multi-auth OpenCode plugin dashboard for ChatGPT OAuth, Codex routing, and multi-account health

Note

This package is the supported OpenCode plugin line. Older package names and config entries should be replaced with oc-codex-multi-auth.

What You Get

  • OpenCode plugin support for ChatGPT Plus/Pro OAuth and Codex/GPT-5 coding workflows
  • GPT-6 Astra, GPT-6 Sol/Luna, GPT-5.6 Sol/Terra/Luna, and the Daybreak Blue/Red cyber tiers on the responses-lite request path, plus GPT-5.5, GPT-5.5 Fast, GPT-5.4 Nano, and GPT-5.1 templates
  • Routing for the Daybreak-gated cyber tiers (gpt-daybreak-blue-latest, gpt-daybreak-red-latest, gpt-5.6-cyber), deliberately kept out of the shipped templates since they need program approval
  • Compact modern OpenCode config with 10 base families and 53 variant presets; explicit legacy selector IDs when needed
  • Stateless Codex-compatible request handling with store: false and reasoning.encrypted_content
  • Multi-account rotation with hybrid health scoring, cooldowns, automatic token refresh, and failover
  • Explicit saved-account listing, account switching, labeling, tagging, notes, health checks, and diagnostics
  • Per-project account storage under ~/.opencode/projects/<project-key>/...
  • Guided setup, doctor, next-action, dashboard, export/import, keychain, and troubleshooting tools
  • Optional OS-native keychain backend for stored account pools
  • TUI prompt quota status and quota detail views for OpenCode sessions
  • Request logging, runtime metrics, routing visibility, and redacted diagnostic snapshots for debugging
  • Stable docs for install, configuration, troubleshooting, privacy, architecture, testing, and release history

Why Developers Use It

oc-codex-multi-auth makes OpenCode's ChatGPT OAuth state understandable and operable. Instead of treating auth as one opaque provider file, you get a local account pool, deterministic account switching, health-aware request selection, visible quota status, JSON-friendly diagnostics, and safe repair commands for stale or damaged state. The plugin is designed for personal development workflows: credentials stay local, OpenCode keeps owning the host runtime, and the plugin only handles the OAuth-backed Codex routing layer it is installed for.


Current Architecture At A Glance

oc-codex-multi-auth ships four user-visible surfaces:

Surface Purpose
oc-codex-multi-auth npm CLI; explicit install modes manage OpenCode provider/TUI config, while update only clears the managed package cache. Also runs standalone commands: doctor, status, list, limits, dashboard, health, diag, warm
OpenCode plugin entry (index.ts) auth loader, OAuth login modes, provider fetch pipeline, account rotation, retry/failover, and codex-* tool registry
OpenCode TUI plugin (tui.ts) prompt quota status, quota details, shared quota cache, and active-account-aware display
24 codex-* tools setup, help, status, list, switch, warm, limits, health, metrics, doctor, dashboard, pool, backup, keychain, diagnostics, and repair actions

The plugin does not replace OpenCode. OpenCode remains the host; this package installs provider/TUI config and supplies the OAuth-backed Codex request pipeline that OpenCode calls.


Terms and Usage Notice

[!CAUTION] This project is for personal development use with your own ChatGPT Plus/Pro subscription.

By using this plugin, you acknowledge:

  • This is an independent open-source project, not an official OpenAI product
  • It is not intended for commercial resale, shared multi-user access, or production services
  • You are responsible for your own usage and policy compliance
  • For production/commercial workloads, use the OpenAI Platform API

Installation

For Humans

Option A: Standard install (preserve provider config)

Default mode registers the OpenCode and TUI plugin entries without changing provider.openai.

npx -y oc-codex-multi-auth@latest

Installer flags:

Flag Effect
(default) / --plugin-only Register the plugin and TUI integration without changing provider.openai
--modern Install compact modern catalog: 10 bases, 53 variants
--full Compact bases plus 53 explicit selector IDs
--legacy Explicit-only catalog for older OpenCode
--dry-run Show changed config paths without values or writes
--no-cache-clear Skip clearing the OpenCode plugin cache

Option B: Compact modern model catalog

npx -y oc-codex-multi-auth@latest --modern

Use this when OpenCode does not already provide the OAuth model definitions or you want the shipped variant presets.

Option C: Full explicit model catalog

Use this when you want direct selector IDs such as openai/gpt-5.5-medium in addition to OpenCode variants.

npx -y oc-codex-multi-auth@latest --full

Updating without config changes

npx -y oc-codex-multi-auth@latest update

update clears only the OpenCode-managed package cache. It does not read or write opencode.json or tui.json; restart OpenCode afterward to install the current package.

Option D: Verify wiring

opencode --version
opencode debug config
opencode auth login

The default installer only registers the plugin entry in ~/.config/opencode/opencode.json, enables the TUI status plugin in ~/.config/opencode/tui.json, and clears the cached plugin copy. Catalog modes also merge their selected provider.openai definitions. Changed config files are backed up before writing.

Running from a local checkout

You can point OpenCode at a clone of this repository instead of the published package, which is how the project is developed:

{ "plugin": ["file:///path/to/oc-codex-multi-auth"] }

The installer leaves that entry exactly as written. It identifies an entry by the package it resolves to rather than by how the path is spelled, so a clone is recognized under any directory name, whether it is referenced as a path, a file:// URL, or its build output. oc-codex-multi-auth is appended only when no entry in the config resolves to this plugin, so the installer never replaces a checkout with the published package or registers both at once.

Stale references the installer itself produced are still retired: the bare package name repeated, version-pinned entries, the former oc-chatgpt-multi-auth name, and paths into node_modules or the OpenCode package cache.

Standalone CLI (no agent / no token cost)

oc-codex-multi-auth status
oc-codex-multi-auth list
oc-codex-multi-auth warm
oc-codex-multi-auth doctor
oc-codex-multi-auth health
oc-codex-multi-auth limits
oc-codex-multi-auth dashboard
oc-codex-multi-auth diag
# or: npx -y oc-codex-multi-auth@latest warm --json
For LLM Agents

Step-by-step

  1. Register the plugin without changing provider.openai:
    • npx -y oc-codex-multi-auth@latest
    • Use --modern only when the shipped compact model catalog is required.
  2. Run first login flow:
    • opencode auth login
  3. Validate config:
    • opencode debug config
  4. Run a smoke request (after OpenCode or --modern supplies the selector):
    • opencode run "Explain this repository" --model=openai/gpt-5.5 --variant=medium
  5. Inspect plugin state with the OpenCode tool surface:
    • codex-status
    • codex-doctor
    • codex-list

Verification

opencode debug config
opencode auth login
opencode run "ping" --model=openai/gpt-5.5 --variant=medium

Quick Start

Install and sign in:

npx -y oc-codex-multi-auth@latest
opencode auth login

Run a prompt with compact modern selectors:

opencode run "Summarize the failing test and suggest a fix" --model=openai/gpt-5.5 --variant=medium
opencode run "Summarize the failing test and suggest a fix" --model=openai/gpt-5.5-fast --variant=medium
opencode run "Plan the refactor" --model=openai/gpt-6-astra --variant=high

Use Codex-focused routing:

opencode run "Refactor the retry logic and update the tests" --model=openai/gpt-6-sol --variant=high

If browser launch is blocked, use the alternate login paths in docs/getting-started.md.


Command Toolkit

Start here

Tool What it answers
codex-setup How do I finish first-run setup safely?
codex-help Which plugin commands exist and what do they do?
codex-doctor What is wrong with auth, config, storage, or routing?
codex-next What should I do next to get unstuck?

Daily use

Tool What it answers
codex-list Which accounts are saved and which one is active?
codex-switch How do I move to a different saved account?
codex-warm How do I start every account's usage window now (stagger quota cooldowns)?
codex-status Which account, model family, and routing state are active?
codex-limits What quota or rate-limit state is visible now?
codex-reset Do I have a banked rate-limit reset credit, and how do I redeem it?
codex-dashboard What does a read-only snapshot of account eligibility, retry budgets, and refresh queue health show?
codex-pool Which accounts are preferred for each model, and how do I change them?

Most of these also run as a direct CLI with no agent or model involvement, so there is no token cost. Examples are oc-codex-multi-auth warm, oc-codex-multi-auth status, or npx -y oc-codex-multi-auth@latest warm. Use oc-codex-multi-auth warm to open every enabled account's usage window at the start of a session and stagger the rolling quota cooldowns. Add --json for scriptable output.

Account management

Tool What it answers
codex-label How do I name an account?
codex-tag How do I group accounts with tags?
codex-note How do I attach a private note to an account?
codex-remove How do I remove a saved account safely?
codex-refresh How do I refresh the OAuth tokens of every saved account to verify they are still valid?

Diagnostics and backup

Tool What it answers
codex-health Which accounts look healthy, limited, or disabled?
codex-metrics What runtime counters and request metrics are visible?
codex-diag Can I export a redacted diagnostic snapshot?
codex-diff What changed between account/config snapshots?
codex-export How do I back up account storage?
codex-import How do I restore accounts with a dry-run first?
codex-keychain Which credential backend is active and can I migrate it?

Reliability behavior

  • stateless request handling forces store: false
  • reasoning.encrypted_content is preserved for multi-turn continuity
  • GPT-6 Astra/Sol/Luna, the Daybreak tiers and the GPT-5.6 tiers use the responses-lite request shape and default client identity opencode; other models default to codex_cli_rs
  • account rotation is health-aware (rotationStrategy default hybrid) and avoids repeatedly selecting cooling accounts
  • The quota guard checks each enabled account at a bounded interval (30 minutes by default). When it finds a fully spent 5-hour or weekly subscription quota, rotation skips that account until its reported reset instead of drawing from paid Credits. codex-limits applies the same guard immediately when run manually. After running standalone limits, restart an already-running OpenCode instance or wait for its next quota poll to reload the updated account state.
  • same-host OpenCode processes sharing an account file serialize refresh-token exchange and commit so one current single-use token is exchanged once
  • 5xx bursts, network failures, and quota responses penalize account health
  • token refresh is queued to avoid refresh races
  • unsupported-model handling is strict by default, with opt-in fallback controls
  • TUI quota status follows the account/workspace used by the latest request
  • Business workspace memberships and Personal accounts keep separate usage and quota windows. Business members sharing one workspace are distinguished by their member/seat identity, so their usage is not collapsed into one row.
  • An account identifies itself by its own ChatGPT email and the last 6 characters of its account id, with the email masked when maskEmail is on. An account id names a ChatGPT workspace and every member of a Business workspace shares it, so a record that also carries a member/seat id prints a short excerpt of that as seat:. The excerpt is a 6-character tail where that is enough to tell the listed accounts apart. Where it is not, it widens, moves to where those ids first differ, or joins two short excerpts with .. - real member ids are long, share a leading prefix, and differ in more than one place, so a tail alone often cannot separate them. Where no excerpt that short can separate them, seat: is instead an opaque hash prefix such as 719f78b5: it identifies the seat and stays stable, but it is not part of the member id and cannot be matched against anything ChatGPT shows you. Whichever form it takes, two distinct seats never render the same seat: and a seat: is never longer than 32 characters. A record with no member id renders exactly as before. The OAuth id_token also lists the API-platform organizations the login belongs to; those are not ChatGPT workspaces and are never used to name an account, so logging in clears a label left behind by one. A label you set with codex-label is always kept.
  • The ChatGPT plan (Free, Plus, Pro, Business, Business Premium, Enterprise) is read from the access token, refreshed on every token refresh, and shown by codex-list and codex-status. codex-limits and the TUI read the plan live from the usage endpoint and name it the same way. An unrecognized plan is reported verbatim rather than renamed.

Storage Paths

File Default path
OpenCode config ~/.config/opencode/opencode.json
OpenCode TUI config ~/.config/opencode/tui.json
OpenCode auth tokens ~/.opencode/auth/openai.json
Plugin config ~/.opencode/openai-codex-auth-config.json
Global account storage ~/.opencode/oc-codex-multi-auth-accounts.json
Per-project accounts ~/.opencode/projects/<project-key>/oc-codex-multi-auth-accounts.json
Flagged accounts oc-codex-multi-auth-flagged-accounts.json, written beside the active accounts file (per-project path when perProjectAccounts is on)
Backups ~/.opencode/backups/ or ~/.opencode/projects/<project-key>/backups/
Logs ~/.opencode/logs/codex-plugin/
TUI quota cache OpenCode state dir plus oc-codex-multi-auth-tui-quota.json, else $OPENCODE_STATE_DIR/oc-codex-multi-auth-tui-quota.json or ~/.local/state/opencode/oc-codex-multi-auth-tui-quota.json
TUI pool quota cache oc-codex-multi-auth-tui-quota-overview.json, in the same directory, written only when quotaStatus.mode is overview

Per-project storage is enabled by default. The plugin walks up from the current directory to find a project root, then stores account pools under the project-specific key. If no project root is found, it falls back to global storage.


Configuration

Primary config files:

  • ~/.config/opencode/opencode.json
  • ~/.config/opencode/tui.json
  • ~/.opencode/openai-codex-auth-config.json

Quota percentage display

Every quota percentage a person reads is worded as the headroom still left, which is how Codex itself reports a quota:

5h limit: 88% left      # codex-limits, quota details dialog
5h 88%                  # TUI prompt status line

Set quotaDisplay to "used" to report consumption instead:

{
  "quotaDisplay": "used"
}
5h limit: 12% used
5h 12%

Add it to ~/.opencode/openai-codex-auth-config.json, or set CODEX_AUTH_QUOTA_DISPLAY=used, then quit and restart OpenCode. The setting covers the TUI prompt status line and quota details dialog, codex-limits, the standalone limits CLI, the interactive account check, and the macOS quota notifications below.

It changes wording only. Quota exhaustion, rotation blocks, notification thresholds, and the status line's warning/danger colouring all stay keyed on the percentage remaining, so a nearly spent account still colours red while reading 95%. The usedPercent and leftPercent fields in --json / format="json" output are unaffected.

Pool-wide quota status

The prompt status line describes the account that served the last request. On a pool of several accounts that account changes as rotation moves, so the line changes identity under you and no single glance shows where the pool stands.

Set quotaStatus.mode to "overview" to describe the whole pool on one constant line instead, which only changes when a quota does:

{
  "quotaStatus": {
    "mode": "overview",
    "layout": "accounts",
    "accountNames": "number",
    "order": "number",
    "multipliers": false,
    "allotment": false,
    "resetTimes": "low",
    "resetCredits": false,
    "recovery": false,
    "rows": 1,
    "showFor": "always"
  }
}
24%: #1 13%, #2 0% 3d, #3 12%               # defaults
24%: 3 accounts                             # "layout": "count"
24%: #1 5x 13%, #2 20x 0% 3d, #3 1x 12%     # "multipliers": true
24%: #1 5x 13%, #2 20x 0% 3d 1r, #3 1x 12%  # + "resetCredits": true
24%: 3 accounts, +12% in 3d                 # "layout": "count", "recovery": true
24% of 26x: #1 13%, #2 0% 3d, #3 12%        # "allotment": true
24%: #1 13% 2d, #2 0% 3d, #3 12% 5d         # "resetTimes": "always"
24%: 13%, 0% 3d, 12%                        # "accountNames": "none"
24%: damian 13%, work 0% 3d, spare 12%      # "accountNames": "label"
24%: #2 0% 3d, #1 13%, #3 12%               # "order": "most-used"
24%: 13% 2d, 0% 3d 4d 5d                    # "layout": "aggregate"

Each switch is independent, so any combination works. #N is the account number codex-list and codex-switch use. An account is shown by whichever of its windows has the least headroom, since that is the one that stops a request; a reset time (3d) is added for an account at or below 25% by default, for every account under "resetTimes": "always", and for none under "never". 1r counts banked rate-limit resets that account can redeem now.

order takes number, most-used, least-used, renewing-earliest, or renewing-latest. layout: "aggregate" prints a shared percentage once and keeps only what differs after it, which matters most on a pool where several accounts are spent.

The leading figure is the pool total, and it is a weighted mean: a Pro seat spent to 50% has given up twenty times the capacity a Business Standard seat does at 50%, so an unweighted average would describe a pool nobody has. The per-plan ratios are listed in docs/plan-allotments.md, and "allotment": true shows what they add up to.

mode also accepts a list, and the line then alternates between those screens every rotateMs (default 5000). The third screen, resets, appears only once every account is spent and lists the banked reset credits worth redeeming, latest reset first - redeeming one on an account that renews by itself tomorrow throws it away:

{
  "quotaStatus": { "mode": ["overview", "resets"] }
}
Free resets: 6d 1r damian@nowaker.net, 4d 2r work@example.com

"rows" (1-4, default 1) is a ceiling rather than a height: a rendering that fits on one row still takes one, so "rows": 2 costs nothing on a wide terminal and buys the whole line back on a narrow one, where the agent/model label beside it has already wrapped to two rows anyway. "showFor": "codex-models" hides the line unless the session is running a model this plugin routes.

Percentages follow quotaDisplay, so the first line above reads 76%: #1 87%, #2 100% 3d, #3 88% under "used". The whole setting is presentation only: rotation, quota blocks and the line's warning/danger colouring stay keyed on the headroom remaining.

Add the object to ~/.opencode/openai-codex-auth-config.json. It is read from that file only - a display preference belongs to a person, not to a shell - and the status line re-reads it while sessions are open, so an edit takes effect within a couple of seconds without a restart.

Desktop quota notifications

Quota notifications are an optional macOS-only feature. Separately, the quota guard checks enabled accounts every 30 minutes by default and prevents rotation from drawing paid Credits after the backend reports a fully spent subscription window. While notifications are enabled, the same poll also alerts through Notification Center when the best remaining 5-hour or weekly pool quota crosses 25%, 10%, or 0%. Alerts are disabled by default; the quota guard is enabled.

Each line reports the enabled account with the most headroom in that window, together with that same account's reset time, so the pair always describes a quota that one account actually has. When a different account recovers sooner, that reset is appended under its own label rather than folded into the first one. Windows a plan has switched off are skipped rather than counted as full. Account identities are omitted for readability and lock-screen privacy:

5h: 10% | resets 02:00 | another account resets 22:30
Weekly: 72% | resets 22:30 on Aug 30

The percentage follows quotaDisplay, so the same two lines read 90% and 28% under "used". thresholds are always remaining-percent values regardless.

{
  "quotaNotifications": {
    "enabled": true,
    "autoProtectCredits": true,
    "intervalMs": 1800000,
    "notifyEveryCheck": false,
    "thresholds": [25, 10, 0]
  }
}

Add the object above to ~/.opencode/openai-codex-auth-config.json, or set CODEX_AUTH_QUOTA_NOTIFICATIONS=1, then quit and restart OpenCode. The minimum interval is 30 seconds. If macOS blocks the alert, allow notifications for the process shown in System Settings > Notifications. The setting is ignored on Windows and Linux.

Set "notifyEveryCheck": true to show the aggregate quota notification after every successful poll interval instead of only when a configured threshold is crossed. Set "thresholds": [] to turn threshold alerts off entirely; pair it with "notifyEveryCheck": true to keep receiving alerts. The quota guard keeps polling unless "autoProtectCredits": false is set.

Delivery state lives beside the accounts file the alerts are computed from, so OpenCode processes working in the same account scope show only one alert per interval. With the default perProjectAccounts, that scope is one project: two projects have separate account pools and therefore alert independently.

Route models to preferred accounts

Use modelAccountPools to assign one or more preferred ChatGPT accounts or Business seats to a model. Account references use stable account or Business-seat identities, so adding, removing, or reordering accounts does not silently change a model's routing. A Business membership and a Personal account remain separate pool and usage identities even when they belong to the same login.

{
  "modelAccountPools": {
    "gpt-5.6-sol": [
      "org-example-account-id",
      "00000000-0000-0000-0000-000000000000"
    ],
    "gpt-5.6-terra": [
      "org-another-account-id"
    ]
  },
  "modelAccountPoolModes": {
    "gpt-5.6-sol": "strict",
    "gpt-5.6-terra": "preferred"
  }
}

Save this configuration in ~/.opencode/openai-codex-auth-config.json, then restart OpenCode. Model matching is case-insensitive and uses the effective model after request model normalization.

Use codex-pool to manage these mappings with ordinary 1-based account numbers. The tool resolves those numbers and writes stable IDs to disk:

codex-pool
codex-pool action="set" model="gpt-5.6-sol" accounts=[7,8]
codex-pool action="add" model="gpt-5.6-sol" accounts=[9]
codex-pool action="remove" model="gpt-5.6-sol" accounts=[7]
codex-pool action="set-mode" model="gpt-5.6-sol" poolMode="strict"
codex-pool action="clear" model="gpt-5.6-sol"

Add dryRun=true to preview a mutation. Use format="json" for structured output; stable IDs remain redacted unless includeSensitive=true is also set. Restart OpenCode after an applied mutation. The plugin configuration is global while account storage is per-project by default, so a reference unresolved in the current project is reported but never automatically deleted.

Routing behavior:

  • A mapped model defaults to preferred mode and uses healthy, selectable accounts in its pool.
  • Existing rotation strategy, quota, cooldown, and token-health rules still apply within the preferred pool.
  • In preferred mode, an unavailable pool falls back to the healthy general account pool.
  • In strict mode, routing never leaves the configured pool and immediately returns strict_pool_unavailable when no pooled account is selectable.
  • An unmapped model or an empty account list uses the general account pool directly.
  • codex-status, codex-dashboard, and routing diagnostics also report strict and strict-unavailable modes.

Account IDs are local account metadata but should still be treated as private configuration. Do not publish a populated configuration file.

Selected runtime/environment overrides:

Variable Effect
OPENAI_BASE_URL=https://gateway.example/v1 OpenAI-compatible OAuth inference gateway; requires CODEX_AUTH_ALLOW_OPENAI_BASE_URL=1
CODEX_AUTH_ALLOW_OPENAI_BASE_URL=1 Explicitly allow the trusted gateway to receive the ChatGPT OAuth access token; remote gateways require HTTPS, while HTTP is accepted only on literal loopback IPs
CODEX_AUTH_REQUEST_TRANSFORM_MODE=legacy Re-enable legacy Codex request rewriting
CODEX_MODE=0/1 Disable/enable bridge prompt behavior
CODEX_TUI_V2=0/1 Disable/enable codex-style tool output
CODEX_TUI_COLOR_PROFILE=truecolor|ansi256|ansi16 Force terminal color profile
CODEX_TUI_GLYPHS=ascii|unicode|auto Force terminal glyph style
CODEX_TUI_MASK_EMAIL=0/1 Mask account emails across account-display surfaces (list/status/limits/health/dashboard/menus + TUI quota status)
CODEX_TUI_MASK_EMAIL_DETAILS=0/1 Also hide account email in quota details when prompt masking is enabled
CODEX_AUTH_QUOTA_DISPLAY=free|used Word quota percentages as headroom left (default, matching Codex) or as consumption

| CODEX_AUTH_PER_PROJECT_ACCOUNTS=0/1 | Disable/enable per-project account pools | | CODEX_AUTH_CREDENTIAL_SNAPSHOTS=0/1 | Disable/enable pre-write snapshots of the credential store (default on) | | CODEX_AUTH_CREDENTIAL_SNAPSHOTS_MAX_COUNT=<n> | How many credential snapshots to keep (0 keeps all of them) | | CODEX_AUTH_AUTO_UPDATE=0/1 | Disable/enable daily npm update check and cache refresh | | CODEX_AUTH_ROTATION_STRATEGY=hybrid\|sticky\|round-robin | Account selection strategy | | CODEX_AUTH_UNSUPPORTED_MODEL_POLICY=strict\|fallback | Control unsupported-model retry behavior | | CODEX_AUTH_ACCOUNT_ID=<id> | Force a specific workspace/account id | | CODEX_AUTH_FETCH_TIMEOUT_MS=<ms> | Request timeout override | | CODEX_AUTH_STREAM_STALL_TIMEOUT_MS=<ms> | SSE stream stall timeout override | | ENABLE_PLUGIN_REQUEST_LOGGING=1 | Enable request metadata logs | | CODEX_PLUGIN_LOG_BODIES=1 | Include raw request/response bodies in logs; sensitive | | CODEX_KEYCHAIN=1 | Opt in to OS-native keychain account storage | | CODEX_AUTH_QUOTA_NOTIFICATIONS=1 | Enable desktop quota notifications (macOS only) | | CODEX_AUTH_AUTO_PROTECT_CREDITS=0/1 | Disable/enable the quota guard that keeps rotation off paid Credits after a spent subscription window (default on) | | CODEX_AUTH_QUOTA_NOTIFICATIONS_INTERVAL_MS=<ms> | Override the quota poll interval (default 1800000, minimum 30000) |

Boolean env overrides are truthy only for the literal string "1".

Validate config after changes:

opencode debug config
opencode run "test" --model=openai/gpt-5.5 --variant=medium

Modern OpenCode versions use config/opencode-modern.json. Older versions can use config/opencode-legacy.json. See config/README.md for the full model template matrix.


Credential Storage

Default JSON backend

By default, account pools are stored locally as V3 JSON files. File permissions are restricted where the platform supports them.

Use JSON storage when you want predictable, inspectable local files and easy backup/export behavior.

Before the store is changed in a way that matters, the plugin copies the previous version of the file into backups/ as codex-credential-snapshot-*.json, mode 0600 in a 0700 directory. The snapshot holds the state being replaced, not the state replacing it, which is what makes it useful if the file is ever overwritten wholesale. Token refreshes count as significant, which bounds how stale a restore can be. Refresh tokens are single-use, so a snapshot taken just before a refresh holds the consumed token for the one account that refresh rotated - that account needs a fresh opencode auth login - while every other account in the pool comes back with the token that was live at that moment. A snapshot old enough to predate many refreshes restores a pool where most or all accounts can no longer authenticate, which is the failure this bounding exists to avoid. Rotation bookkeeping - lastUsed, rate-limit and cooldown state, quota stamps, and the rotation cursor - never triggers one on its own, so the kept snapshots are not churned away by ordinary traffic. The plugin keeps the 10 most recent and prunes strictly by that filename prefix, so nothing else in backups/ is touched. Set credentialSnapshots: false to turn it off, or credentialSnapshotsMaxCount to keep a different number (0 keeps all of them).

Optional OS keychain backend

Set CODEX_KEYCHAIN=1 to store account pools in the OS keychain instead:

  • macOS: Keychain
  • Windows: Credential Manager
  • Linux: libsecret, with a running secret service such as GNOME Keyring or KWallet

Manage the backend from OpenCode:

codex-keychain command="status"
codex-keychain command="migrate"
codex-keychain command="rollback"

If the keychain is unavailable, the plugin logs a warning and falls back to JSON storage for that operation. Credentials are never silently deleted.


Troubleshooting

60-second recovery
codex-doctor fix=true
codex-next
codex-status format="json"

If still broken:

opencode auth login
Common symptoms
  • Plugin does not load: rerun npx -y oc-codex-multi-auth@latest, then restart OpenCode
  • Config looks wrong: run opencode debug config and confirm "plugin": ["oc-codex-multi-auth"], or the path to your checkout when running one
  • OAuth callback fails: free port 1455, then rerun opencode auth login
  • Browser launch is blocked: use the remote/headless login path from docs/getting-started.md
  • Wrong account is selected: run codex-list, then codex-switch
  • Account pool looks unhealthy: run codex-health format="json" and codex-doctor deep=true format="json"
  • Import/export feels risky: run codex-import path="..." dryRun=true before applying
  • Debugging model fallback: enable ENABLE_PLUGIN_REQUEST_LOGGING=1 and inspect ~/.opencode/logs/codex-plugin/
Diagnostics pack
codex-status format="json"
codex-limits format="json"
codex-health format="json"
codex-next format="json"
codex-list format="json"
codex-dashboard format="json"
codex-metrics format="json"
codex-doctor deep=true format="json"

Documentation


Release Notes

  • Current published version: see the npm badge above, or run npm view oc-codex-multi-auth version
  • Changelog: CHANGELOG.md
  • Releases are automated with release-please

Merging the release-please PR cuts the tagged release and publishes the package through the configured release workflow. Manual npm publish is not required for routine releases.

License

MIT License. See LICENSE.

Legal
  • Not affiliated with OpenAI.
  • "ChatGPT", "GPT-5", "Codex", and "OpenAI" are trademarks of OpenAI.
  • You assume responsibility for your own usage and compliance.

About

OpenCode plugin for ChatGPT Plus/Pro OAuth with Codex/GPT-5 routing, multi-account rotation, account switching, health checks, diagnostics, and recovery tools

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

190 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages