Runtime architecture for the oc-codex-multi-auth OpenCode plugin, installer, ChatGPT Plus/Pro OAuth flow, Codex/GPT-5 request bridge (including GPT-5.6 responses-lite), multi-account rotation, codex-* tool registry, TUI quota status plugin, and local storage model.
Reflects the codebase as of the current
mainbranch. This file is the maintainer architecture source of truth;docs/architecture.mdis the shorter public-facing overview.
- Make OpenCode ChatGPT OAuth setup short and repeatable (
npx -y oc-codex-multi-auth@latest). - Keep OpenCode as the host runtime while the plugin owns only the OAuth-backed Codex routing layer.
- Preserve Codex backend invariants:
stream: true,store: false, andreasoning.encrypted_content. - Make multi-account state visible through account switching, health checks, diagnostics, quota status, and recovery commands.
- Keep account storage local by default, with explicit export/import and optional OS keychain migration.
- Keep the broad OpenCode tool surface modular: every registered
codex-*tool is its own file underlib/tools/. - Keep public docs search-friendly without overstating support, affiliation, or production/commercial use.
Install / refresh / standalone CLI
|
| npx -y oc-codex-multi-auth@latest
| install: default plugin-only | --modern | --full | --legacy
| [--dry-run] [--no-cache-clear]
| update: managed package cache only
| [--dry-run]
| standalone: doctor | status | list | limits | dashboard | health | diag | warm
v
scripts/install-oc-codex-multi-auth.js
|- delegates to scripts/install-oc-codex-multi-auth-core.js
|- install writes changed ~/.config/opencode/opencode.json and tui.json
|- update never reads or writes OpenCode config
|- merges config/opencode-modern.json and/or config/opencode-legacy.json
|- normalizes old package/plugin entries
|- clears OpenCode plugin cache (unless --no-cache-clear)
OpenCode runtime
|
| loads plugin package
v
index.ts
|- auth loader: browser callback, device code, manual URL paste
|- account manager + V3 storage + optional keychain
|- custom provider fetch pipeline
|- runtime metrics, retry budgets, circuit breaker, recovery hooks
|- ToolContext construction
v
lib/tools/index.ts
|- registers 24 OpenCode tools
|- each tool delegates to lib/tools/codex-*.ts
Request path
|
| OpenCode OpenAI SDK request
v
lib/request/fetch-helpers.ts + lib/request/request-transformer.ts
|- rewrite URL to Codex/ChatGPT backend
|- native mode: preserve host payload shape
|- legacy mode: apply compatibility rewrites
|- force store:false and include reasoning.encrypted_content
|- GPT-5.6: responses-lite reshape + opencode client identity
|- other models: codex_cli_rs client identity (default)
|- resolve modelAccountPools preferred accounts
|- select/refresh account (hybrid health scoring)
|- attach OAuth headers
v
ChatGPT-backed Codex endpoint
|
v
lib/request/response-handler.ts
|- SSE parsing
|- error mapping
|- quota/rate-limit/header extraction
|- empty-response retries
OpenCode TUI runtime
|
v
tui.ts
|- reads account/quota snapshots
|- refreshes compact usage state when possible
|- renders prompt quota status and details
| Subsystem | Key files | Responsibility |
|---|---|---|
| Installer CLI | scripts/install-oc-codex-multi-auth.js, scripts/install-oc-codex-multi-auth-core.js |
npm bin; config merge; cache cleanup; modern/full/legacy catalog selection; standalone doctor/status/list/limits/dashboard/health/diag/warm; TUI plugin enablement |
| OpenCode plugin entry | index.ts |
auth loader, runtime wiring, custom fetch pipeline, account manager lifecycle, ToolContext, OpenCode plugin export |
| TUI plugin entry | tui.ts, lib/tui-status.ts, lib/tui-quota-cache.ts, lib/codex-usage.ts |
prompt quota status, account-aware quota snapshots, usage refresh, details rendering |
| Auth flow | lib/auth/auth.ts, lib/auth/server.ts, lib/auth/browser.ts, lib/auth/device-code.ts, lib/auth/login-runner.ts, lib/auth/scopes.ts |
PKCE OAuth, callback server, device/manual login, workspace/account selection, scope validation |
| Account manager | lib/accounts.ts, lib/accounts/ |
account state facade, persistence, rotation, recovery, rate-limit tracking, workspace identity preservation, warm |
| Storage | lib/storage.ts, lib/storage/ |
V3 JSON storage, atomic writes, migrations, per-project paths, backups, import/export, keychain opt-in, flagged accounts |
| Request bridge | lib/request/fetch-helpers.ts, lib/request/request-transformer.ts, lib/request/response-handler.ts, lib/request/retry-budget.ts, lib/request/rate-limit-backoff.ts, lib/request/helpers/ |
URL/body/header shaping, Codex invariants, responses-lite, client identity, SSE conversion, retry budgets, backoff, error mapping |
| Model/prompt mapping | lib/prompts/codex.ts, lib/prompts/opencode-codex.ts, lib/prompts/codex-opencode-bridge.ts, lib/request/helpers/model-map.ts |
model-family detection, Codex instructions cache, OpenCode prompt adaptation, fallback aliases |
| Tool registry | lib/tools/index.ts, lib/tools/codex-*.ts |
24 OpenCode tools for setup, account switching, status, health, quota resets, diagnostics, backup, keychain, and recovery |
| Runtime support | lib/runtime.ts, lib/circuit-breaker.ts, lib/proactive-refresh.ts, lib/parallel-probe.ts, lib/recovery/, lib/rotation.ts, lib/shutdown.ts |
pure runtime helpers, failure isolation, refresh scheduling, health probing, hybrid selection scoring, session recovery, cleanup |
| UI helpers | lib/ui/ |
terminal formatting, auth menu, select/confirm prompts, theme/color handling, beginner checklist |
| Config templates | config/opencode-modern.json, config/opencode-legacy.json, config/minimal-opencode.json, config/README.md |
copy-paste OpenCode provider templates and model catalog guidance |
| Tests | test/ |
Vitest suites for auth, request transforms, storage, rotation, tools, TUI quota, installer, docs parity, and release regressions |
The current docs tree mirrors the codebase boundaries above: user docs cover setup and operations, and maintainer docs cover internal architecture and validation.
docs/
├── index.md # docs landing page
├── README.md # docs portal navigation
├── DOCUMENTATION.md # repository documentation map
├── architecture.md # public architecture overview
├── getting-started.md # install, auth, and first-run guide
├── tools-and-cli.md # codex-* tool catalog and standalone CLI
├── configuration.md # public config reference
├── troubleshooting.md # operational failure modes and fixes
├── faq.md # short common answers
├── privacy.md # local data and upstream request notes
├── OPENCODE_PR_PROPOSAL.md # upstream OpenCode proposal notes
├── _config.yml # docs site config
└── development/ # maintainer architecture and validation docs
├── ARCHITECTURE.md
├── GITHUB_DISCOVERABILITY.md
├── CONFIG_FIELDS.md
├── CONFIG_FLOW.md
├── TESTING.md
└── TUI_PARITY_CHECKLIST.md
High-level provider fetch flow:
- Parse OpenCode request URL and body.
- Resolve plugin config from defaults,
~/.opencode/openai-codex-auth-config.json, and environment overrides (boolean env truthy only for"1"). - Choose request transform mode:
nativekeeps OpenCode payloads unchanged except required Codex invariants.legacyfetches Codex/OpenCode prompts and applies compatibility rewrites.
- Enforce ChatGPT-backed Codex invariants:
stream: truestore: falseinclude: ["reasoning.encrypted_content"]or equivalent inclusion
- Normalize model aliases and fallback candidates (including GPT-5.6 Sol/Terra/Luna tiers).
- For GPT-5.6 models, apply the responses-lite reshape (
lib/request/helpers/responses-lite.ts): tools move intoinputasadditional_tools, instructions become a developer message, top-leveltools/instructionsare cleared for lite shape, imagedetailis stripped, andx-openai-internal-codex-responses-lite: trueis set. - Resolve client identity (
lib/request/helpers/client-identity.ts): GPT-5.6 defaults tooriginator: opencode; other models default tocodex_cli_rs. Override withCODEX_AUTH_CLIENT_IDENTITY. - Resolve accounts and
preferred/strictpolicy frommodelAccountPoolsandmodelAccountPoolModes; only preferred pools fall back to the general pool when unavailable. - Resolve account/workspace selection with the configured
rotationStrategy(defaulthybridhealth scoring), cooldown, token bucket, and explicitCODEX_AUTH_ACCOUNT_IDconstraints. - Refresh tokens through the queued refresh path when needed.
- Attach OAuth/Codex headers and forward the request.
- Parse SSE responses, quota headers, retryable errors, empty responses, and unsupported-model details.
- Update runtime metrics, account health, circuit breaker state, TUI quota cache, and persisted storage.
- On recoverable failures, apply session recovery / auto-resume hooks when enabled.
The ChatGPT-backed Codex path rejects server-side storage for this plugin's request shape, so the runtime keeps requests stateless with store: false.
Context is preserved through:
- full message history supplied by OpenCode
- tool call and tool output history in that message history
reasoning.encrypted_contentreturned by the backend and sent back on later turns
Legacy mode exists for compatibility with older OpenCode/AI SDK payload behavior. It removes unsupported item_reference items and message IDs that cannot be looked up when store: false is active. Native mode is the default and preserves the host payload shape as much as possible.
GPT-5.6 responses-lite is a separate body shape layered on top of the same stateless contract: tool definitions live in the input prefix rather than the top-level tools field.
The plugin exposes 24 OpenCode tools through lib/tools/index.ts. index.ts builds one ToolContext from plugin-closure state and helper functions, then passes it to createToolRegistry(ctx).
Why this shape exists:
- per-tool modules keep
index.tsfrom absorbing every command implementation - mutable refs let tools invalidate or replace account-manager state without global singletons
- shared helpers keep formatting, routing visibility, and beginner diagnostics consistent
- schema helpers stay close to each tool to avoid leaking bundled
zodtype identities across module boundaries
Tool groups:
| Group | Tools |
|---|---|
| Setup and help | codex-setup, codex-help, codex-next |
| Daily account use | codex-list, codex-switch, codex-warm, codex-status, codex-limits, codex-reset, codex-dashboard |
| Account metadata and routing | codex-label, codex-tag, codex-note, codex-pool, codex-remove, codex-refresh |
| Diagnostics | codex-health, codex-metrics, codex-doctor, codex-diag, codex-diff |
| Backup/secrets | codex-export, codex-import, codex-keychain |
Standalone CLI mirrors a subset without loading the agent: doctor, status, list, limits, dashboard, health, diag, warm.
Canonical OpenCode plugin state lives under ~/.opencode, while OpenCode config lives under ~/.config/opencode.
| File | Purpose |
|---|---|
~/.config/opencode/opencode.json |
OpenCode provider/plugin config managed by installer |
~/.config/opencode/tui.json |
OpenCode TUI plugin config managed by installer |
~/.opencode/auth/openai.json |
OpenCode auth token file |
~/.opencode/openai-codex-auth-config.json |
plugin runtime config |
~/.opencode/oc-codex-multi-auth-accounts.json |
global V3 account pool |
~/.opencode/projects/<project-key>/oc-codex-multi-auth-accounts.json |
project-scoped V3 account pool |
~/.opencode/oc-codex-multi-auth-flagged-accounts.json |
flagged/deactivated account metadata |
~/.opencode/backups/ |
account backup/export target |
~/.opencode/logs/codex-plugin/ |
request/debug logs when enabled |
Storage invariants:
- V1/V2 account files migrate into V3 on load/save paths (V2 surfaces a typed recovery error rather than silent discard).
- Per-project storage is enabled by default and keyed by detected project identity.
- JSON files are written atomically where supported.
- Optional keychain storage is opt-in via
CODEX_KEYCHAIN=1. - Import supports dry-run preview and creates pre-import backups when existing accounts are present.
When sessionRecovery is true (default), lib/recovery/ may rewrite OpenCode host storage:
| Path | Purpose |
|---|---|
$XDG_DATA_HOME/opencode/storage (or %APPDATA%/opencode/storage on Windows; else ~/.local/share/opencode/storage) |
Root |
…/message/{sessionID}/… |
Session messages |
…/part/{messageID}/*.json |
Message parts (thinking inject/strip, synthetic tool results) |
Recovered classes: tool_result_missing, thinking_block_order, thinking_disabled_violation. Optional autoResume re-prompts after thinking recovery.
tui.ts is loaded by OpenCode's TUI plugin system after the installer writes ~/.config/opencode/tui.json.
- Resolve the active account fingerprint from stored accounts.
- Read OpenCode KV quota state and the shared quota cache.
- Refresh usage data when enough time has passed and the active account is eligible.
- Render compact prompt status only inside active sessions.
- Expose quota details without leaking account tokens.
The request path also writes quota snapshots from response headers, so the TUI can reflect the account/workspace used by the latest request.
The default installer preserves provider.openai. --modern writes the modern OpenCode template (config/opencode-modern.json):
- 12 base model families in the picker:
gpt-5.6-sol,gpt-5.6-terra,gpt-5.6-lunagpt-5.5,gpt-5.5-fastgpt-5.4-mini,gpt-5.4-nanogpt-5.1-codex-max,gpt-5.1-codex,gpt-5.1-codex-mini,gpt-5.1,gpt-5-codex
- 53 effective variants through OpenCode's variant selector
store: falsereasoning.encrypted_content- large context/output metadata for supported model families
--full adds 53 explicit selector IDs for scripts. --legacy writes the explicit-only template (53 entries) for older OpenCode versions.
Unsupported-model behavior is strict by default. Default auto-fallbacks still cover common entitlement gates for GPT-5.6 tiers → gpt-5.5, and for gpt-5.5 / gpt-5-codex through the GPT-5.4 family. Full generic fallback can be enabled through config or environment variables.
rotationStrategydefaulthybrid: stick while healthy, otherwise score-select (health + tokens + freshness). Alternatives:sticky,round-robin.lib/rotation.tsowns hybrid health scoring;lib/accounts/rotation.tswires it into account manager state.- Circuit breaker isolates repeated failures per account/path.
- Empty-response retries use
emptyResponseMaxRetries/emptyResponseRetryDelayMs. - Optional
parallelProbingcan probe account health concurrently (default off).
- OAuth callback port remains
1455. - Dist output is generated; source of truth is
index.ts,tui.ts,lib/,scripts/,config/, anddocs/. - The canonical package and plugin entry is
oc-codex-multi-auth(exports"."and"./tui"). - The installer should normalize stale
oc-chatgpt-multi-authentries rather than preserve duplicates. - ChatGPT-backed Codex requests use
store: false. reasoning.encrypted_contentmust stay available for multi-turn continuity.- Account emails and tokens must not be exposed in diagnostic payloads or response headers.
- Keychain failures must not silently delete JSON credentials.
- Account pool limits stay at
ACCOUNT_LIMITS(max 20, 30s auth cooldown, remove after 3 consecutive auth failures). - Codex CLI hydrate from
~/.codexstays on unlessCODEX_AUTH_SYNC_CODEX_CLI=0. - Startup prewarm runs only for legacy request transform when not disabled via
CODEX_AUTH_PREWARM=0. - Installer help/post-install strings must match the live catalog (12 modern bases / 53 variants; 53 legacy explicit).
- Tool additions require a per-file factory, registry wiring, and focused test/docs updates.
- Boolean environment overrides are truthy only for the literal string
"1". - Docs, package metadata, GitHub About text, and plugin metadata should lead with OpenCode, ChatGPT OAuth, Codex/GPT-5 routing, multi-account rotation, account switching, health checks, diagnostics, and recovery tools.
Recommended local validation for architecture/docs/metadata changes:
npm test -- test/doc-parity.test.ts
npm run typecheck
npm run lint
npm run build
git diff --checkUse npm test when source behavior changes or when documentation edits touch tested runtime contracts.