Skip to content

fix(rate-limits): read OpenCode Go usage with the Go API key - #22514

Closed
kespineira wants to merge 4 commits into
stablyai:mainfrom
kespineira:opencode-go-api-key-usage
Closed

kespineira wants to merge 4 commits into
stablyai:mainfrom
kespineira:opencode-go-api-key-usage

Conversation

@kespineira

@kespineira kespineira commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

ELI5

Orca used to read your OpenCode Go usage by pretending to be your browser, using a cookie you copied from opencode.ai. OpenCode changed its website and that trick stopped working for most accounts. Now Orca asks OpenCode directly with your OpenCode Go API key, the same key OpenCode itself uses. You usually don't have to paste anything, because Orca finds the key OpenCode saved when you ran /connect.

What Changed

Before: the OpenCode Go bar needed a pasted opencode.ai Cookie header and a workspace lookup against the legacy console. Since 2026-09-22 that console sends accounts without a Black subscription to the login page, so the bar stays empty.

After: the bar shows 5h, weekly and 30-day usage from the API key. Orca picks the key in this order:

  1. A new optional API key field in Settings > Accounts > OpenCode Go. Main stores the key in its own encrypted file, the same store the MiniMax API key uses, and the renderer never reads it back.
  2. The OPENCODE_API_KEY environment variable.
  3. The key OpenCode saved with /connect ($XDG_DATA_HOME/opencode/auth.json or ~/.local/share/opencode/auth.json, entry opencode-go). Orca reads the file read-only, with a 1 MB cap, on each poll.
  4. The existing cookie path, unchanged, as a fallback for Black and legacy accounts.

Mechanism:

  • opencode-go-api-key.ts resolves the key.
  • opencode-go-api-usage-fetcher.ts calls GET https://opencode.ai/zen/go/v1/usage with Authorization: Bearer. It uses the existing isolated Electron session, so Orca, environment and system proxies still apply. Cookies are omitted and redirects are refused.
  • The parser maps rolling, weekly and monthly percentages onto the existing session, weekly and monthly windows.
  • A 401, a 403 or a transport error each map to a fixed message that never contains the key.
  • If the key fails and a cookie is configured, Orca tries the cookie and keeps the key error if that fails too.
  • Main publishes an optional opencodeGoApiKeyConfigured flag each cycle, the same way MiniMax and Grok do. The renderer shows the bar when only the key exists, and the key itself stays in main.
  • The Settings field writes through opencodeGoCredentials:saveApiKey / clearApiKey IPC and reads back only { apiKeyConfigured }. After each save or clear, and after a cookie or workspace change, the service invalidates OpenCode Go and refreshes, so a result fetched with the old credential is dropped.
  • OPENCODE_API_KEY is shared with OpenCode Zen. When an environment key gets a 403, Orca tries the auth.json opencode-go key once. With no such key, the 403 means "not configured" and no bar is shown. A 403 for a Settings or auth.json key stays a visible error.
  • A saved key that cannot be decrypted is skipped and the other sources still run. The decrypt error shows only when nothing else produces usage.
  • The config hash is now SHA-256 over the credential source, key, cookie and workspace. Changing any of them resets stale usage. It also stops holding the plaintext cookie in memory.

Why

The API key endpoint is what OpenCode's own server exposes for Go usage (packages/console/app/src/routes/zen/go/v1/usage.ts in anomalyco/opencode). It needs no workspace ID, it doesn't depend on console internals like the server-function hash, and CodexBar already uses it.

Alternatives I rejected:

  • Fixing the cookie flow for the new Console means tracking a private, undocumented surface that just broke once. It would also keep the paste-a-cookie setup.
  • Dropping cookies entirely would break Black accounts that still use the legacy console, so the cookie stays as the fallback.

Linked Issue

Fixes #22512

Visual Proof

Before: on v1.4.209 the OpenCode Go bar shows no usage for an account without Black.

After, with a fresh profile and nothing configured in Settings (key auto-detected from auth.json):

Settings > Accounts > OpenCode Go with the new API key field and the cookie relabeled as the legacy fallback:

Testing

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

On macOS I ran pnpm dev with ORCA_BACKGROUND_LAUNCH=1 and an empty user-data profile, then drove it over CDP. The status bar and popover showed OpenCode Go 5h, weekly and 30-day windows matching a direct curl of /zen/go/v1/usage with the same key. The weekly reset matched the API's resetsAt.

New tests:

  • opencode-go-api-key.test.ts: source precedence; missing, malformed and oversized auth.json; XDG_DATA_HOME.
  • opencode-go-api-usage-fetcher.test.ts: 200/401/403/5xx, transport errors, proxy settings, no key in errors.
  • opencode-go-usage-parsing.test.ts: clamping, rate-limited windows, optional monthly.
  • service-opencode-go-api-key.test.ts: key over cookie, cookie fallback, config-hash reset per source.

Existing persistence, status-bar and settings tests were extended. The shared RateLimitService test harness now mocks the key resolver, so no suite reads a developer's real OPENCODE_API_KEY or auth.json. The harness asserts that with a sentinel env key set.

src/main/proxy-guarded-fetch-call-site-audit.test.ts now lists the new fetcher. It uses the same isolated session and proxy path as the cookie fetcher.

Local checks on macOS:

  • pnpm lint and pnpm typecheck pass.
  • pnpm test: every suite this PR touches passes. Seven unrelated files also fail on a clean main at the same base commit (dac82f61bc): grok-replay-guard, claude-structured-real-cli, claude-tui-resume-real-binary.integration, pty-runtime-hidden-at-spawn-mark, opencode/hook-plugin-module-contract, e2e-worker-env-isolation.unit and relay-region-correction.unit. So does worktree-base-divergence-real-git, which times out. I checked each one on main.
  • pnpm build: every desktop, web-client and skills step passes. The mobile web bundle step can't run here because mobile/ dependencies aren't installed. This PR doesn't touch mobile/.

Not tested: Windows and Linux were not run by hand. Path resolution follows OpenCode's xdg-basedir usage, which is the same on every OS.

AI Disclosure

I coordinated the work with Orca orchestration. Codex (gpt-6-astra, xhigh) wrote the implementation, Claude (claude-fable-5-1) reviewed it, and Codex applied the review fixes. Claude Code (Opus) did the investigation, the endpoint verification and the manual testing. I reviewed the diff and re-ran the checks.

Review

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • Security: the key is encrypted at rest with safeStorage in a main-owned file (src/main/credentials/encrypted-api-key-file-store.ts, shared with MiniMax without changing its format). It is not part of GlobalSettings, so settings:get, settings:changed and remote settings.get never carry it, and it is never logged or put in error strings.
  • SSH/remote: usage is account-scoped and fetched by the desktop main process. auth.json detection only reads the local machine and adds no remote reads.
  • Mixed versions: opencodeGoApiKeyConfigured is an optional field, and a missing flag from an older host reads as not configured. The credential IPC is desktop-only, and the web client fails closed.
  • Known gap: Windows users who ran /connect inside WSL don't get auto-detection yet. Kimi's WSL home resolver is async and provider-specific, so reusing it needs polling changes. They can paste the key or set OPENCODE_API_KEY meanwhile. I'll follow up separately.
  • Author: @kevimux

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred). The failures that remain also fail on main, and the mobile bundle step needs mobile/ deps. See Testing.

OpenCode's new Console sends accounts without a Black subscription away
from the legacy console, so the cookie-based workspace lookup no longer
returns usage. Read GET /zen/go/v1/usage with the OpenCode Go API key.
The key comes from a new protected setting, then OPENCODE_API_KEY, then
the key OpenCode saved with /connect in auth.json. The cookie path stays
as the fallback for Black and legacy accounts.

Fixes stablyai#22512
@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 4262e4c5-f0ae-44f1-9b0c-e1b6b22a219e

📥 Commits

Reviewing files that changed from the base of the PR and between 518a01d and 73aaf6a.

📒 Files selected for processing (2)
  • src/main/rate-limits/opencode-go-api-key.test.ts
  • src/main/rate-limits/opencode-go-api-key.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

The change adds protected storage and credential controls for the OpenCode Go API key. Rate-limit fetching can resolve keys from settings, the environment, or OpenCode’s local auth file, then request usage through a Bearer-authenticated endpoint or use the cookie fallback. The rate-limit service tracks whether a key is configured and updates status-bar provider visibility. Account settings include API-key save and clear controls, revised cookie and workspace guidance, search entries, and translations.

Merge Risk: ⚪ Minimal · up to 73aaf

No material merge risk remains: a Zen-only key does not make OpenCode Go appear configured when its request is rejected.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is detailed and covers the user impact, implementation, motivation, linked issue, testing, security, compatibility, and AI disclosure. However, the required Visual Proof section contai… Attach the required before-and-after screenshots or videos for the OpenCode Go status bar and Settings UI, then check the corresponding Visual Proof checklist item. If no visual change is intended, replace the section content with exactly `…
Docstring Coverage ⚠️ Warning Docstring coverage is 5.77% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 52 functions across 63 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: reading OpenCode Go usage with the Go API key.
Linked Issues check ✅ Passed [#22512] The PR resolves keys from the Settings override, OPENCODE_API_KEY, and OpenCode auth.json. It calls /zen/go/v1/usage with Bearer authentication and parses rolling, weekly, and monthly u…
Out of Scope Changes check ✅ Passed The changes remain within [#22512]. The encrypted key store, IPC bridge, settings UI, localization, API fetcher, parser, cookie fallback, visibility state, and related tests support OpenCode Go key re…
Full details: Description check

Explanation

The description is detailed and covers the user impact, implementation, motivation, linked issue, testing, security, compatibility, and AI disclosure. However, the required Visual Proof section contains placeholder text instead of attached before-and-after screenshots or videos, and the related checklist item remains unchecked.

Resolution

Attach the required before-and-after screenshots or videos for the OpenCode Go status bar and Settings UI, then check the corresponding Visual Proof checklist item. If no visual change is intended, replace the section content with exactly N/A and explain why.

  • Fix all pre-merge checks with AI

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: bb093996-bde1-4f44-af1e-31f298857063

📥 Commits

Reviewing files that changed from the base of the PR and between 641a7f3 and e01d385.

📒 Files selected for processing (48)
  • src/main/persistence-protected-secret-fail-closed.test.ts
  • src/main/persistence/applying-settings/settings-update.ts
  • src/main/persistence/loading-store/loaded-state-parsing.ts
  • src/main/persistence/loading-store/state-serialization-secret-handling.ts
  • src/main/persistence/loading-store/state-write-round-trip.test.ts
  • src/main/protected-secret-persistence.ts
  • src/main/proxy-guarded-fetch-call-site-audit.test.ts
  • src/main/rate-limits/opencode-go-api-key.test.ts
  • src/main/rate-limits/opencode-go-api-key.ts
  • src/main/rate-limits/opencode-go-api-usage-fetcher.test.ts
  • src/main/rate-limits/opencode-go-api-usage-fetcher.ts
  • src/main/rate-limits/opencode-go-status-parsing.ts
  • src/main/rate-limits/opencode-go-usage-fetcher.ts
  • src/main/rate-limits/opencode-go-usage-parsing.test.ts
  • src/main/rate-limits/rate-limit-service-test-harness.ts
  • src/main/rate-limits/service-account-target-selection.test.ts
  • src/main/rate-limits/service-antigravity-usage.test.ts
  • src/main/rate-limits/service-inactive-account-previews.test.ts
  • src/main/rate-limits/service-live-claude-usage.test.ts
  • src/main/rate-limits/service-minimax-usage.test.ts
  • src/main/rate-limits/service-opencode-go-api-key.test.ts
  • src/main/rate-limits/service-refresh-orchestration.test.ts
  • src/main/rate-limits/service-window-activation.test.ts
  • src/main/rate-limits/service/service-configuration.ts
  • src/main/rate-limits/service/service-full-cycle-preparation.ts
  • src/main/rate-limits/service/service-state.ts
  • src/main/rate-limits/service/service-types.ts
  • src/main/startup/main-process-account-services.ts
  • src/renderer/src/components/settings/AccountsPane.test.tsx
  • src/renderer/src/components/settings/AccountsPane.tsx
  • src/renderer/src/components/settings/accounts-pane-provider-setting-sections.tsx
  • src/renderer/src/components/settings/accounts-pane-types.ts
  • src/renderer/src/components/settings/accounts-search.test.ts
  • src/renderer/src/components/settings/accounts-search.ts
  • src/renderer/src/components/status-bar/status-bar-provider-visibility.test.ts
  • src/renderer/src/components/status-bar/status-bar-provider-visibility.ts
  • src/renderer/src/components/status-bar/use-status-bar-controller.ts
  • src/renderer/src/i18n/en-runtime-required.json
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/fr.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/shared/default-global-settings.ts
  • src/shared/global-settings-types.ts
  • src/shared/rate-limit-state-factory.ts
  • src/shared/rate-limit-types.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread src/shared/global-settings-types.ts Resolved

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Minor suggestions only — the credential plumbing, persistence wiring, and upstream contract all check out.

Reviewed changes

  • New API-key usage path: opencode-go-api-key.ts resolves the Go key from the Settings override, OPENCODE_API_KEY, or OpenCode's local auth.json; opencode-go-api-usage-fetcher.ts reads GET /zen/go/v1/usage with Authorization: Bearer on the isolated Electron session.
  • Parser: opencode-go-status-parsing.ts maps usage.rolling/weekly/monthly onto the session/weekly/monthly windows, clamping percent to 0–100; session and weekly are required, monthly optional.
  • Routing and fallback: opencode-go-usage-fetcher.ts prefers the key, falls back to the existing console cookie path on any error, and the service resets stale usage through a new SHA-256 config hash over source/key/cookie/workspace.
  • Persistence: settings.opencodeGoApiKey is a new protected-secret slot, wired through encrypt/decrypt, removal, and the fail-closed safeStorage paths.
  • Renderer: new Settings field, visibility through a published opencodeGoApiKeyConfigured flag, and the cookie/workspace fields relabeled as legacy fallbacks.

ℹ️ A credential change does not refresh the bar or discard an in-flight result until the next poll

Editing the new key field, or OpenCode rewriting auth.json, is only observed at the start of the next full cycle, and nothing enqueues one. If the change lands while a fetch is in flight, the previous credential's result is still applied, because the generation counter is only bumped inside the cycle that observes the change. MiniMax solves the analogous case with an explicit settings-change invalidation; OpenCode Go has no equivalent.

Technical details
# OpenCode Go credential changes lack immediate invalidation

## Affected sites
- src/main/rate-limits/service/service-full-cycle-preparation.ts:78-99 — key re-resolved and hash compared only at cycle start; the generation bump lives here.
- src/main/startup/main-process-account-services.ts:90-110 — `onSettingsChanged` invalidates MiniMax on endpoint/group/model changes but has no OpenCode Go branch and enqueues no refresh.
- src/main/rate-limits/service/service-full-cycle-application.ts:149,180-184 — an in-flight cycle applies its result whenever the generation still matches, which it does when no newer cycle has started.

## Required outcome
- After the user pastes or clears the key, the bar should refresh without waiting for the next scheduled poll.
- A result fetched under a credential that has since changed should not be applied.

## Suggested approach
- Mirror the MiniMax path: add an `opencodeGoApiKey` branch to the settings-change listener that invalidates the OpenCode Go snapshot and calls `refresh()`.
- This behavior is pre-existing for the cookie, so this is informational rather than blocking.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

…dential changes

Move the OpenCode Go API key out of GlobalSettings into a main-owned
encrypted file store behind write-only IPC, sharing the store with the
MiniMax API key without changing its on-disk format. The renderer only
learns whether a key is configured.

Invalidate and refresh OpenCode Go usage after a key save or clear and
after cookie or workspace changes, so a result fetched with an old
credential is dropped.

Treat an OPENCODE_API_KEY key that gets a 403 as not configured, since
OpenCode Zen declares the same variable.

Refs stablyai#22512
@kespineira

Copy link
Copy Markdown
Contributor Author

@pullfrog the credential-change note from your review is addressed in 30c8514. Saving or clearing the key, or changing the cookie or workspace ID, now runs invalidateOpenCodeGoCredentialState() and then refresh(), mirroring MiniMax. The generation bump drops any result that was in flight with the old credential. Details are in the reply on the main-process-account-services.ts thread.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No critical issues — one minor suggestion inline. The credential-change invalidation from the prior review is addressed cleanly.

Reviewed changes

  • Main-owned key store: the OpenCode Go key moved out of GlobalSettings into an encrypted ~/.orca/opencode-go-api-key.enc, backed by a new generic createEncryptedApiKeyFileStore shared with MiniMax (same envelope format).
  • Write-only IPC: opencodeGoCredentials:getStatus / saveApiKey / clearApiKey expose only { apiKeyConfigured }; the web preload fails closed on save.
  • Credential-change invalidation: saving/clearing the key, or changing the cookie/workspace, now calls invalidateOpenCodeGoCredentialState() (generation bump + snapshot clear) then refresh(), mirroring MiniMax; the prior review's concern.
  • Environment-key 403: a 403 for an OPENCODE_API_KEY-sourced key maps to unavailable (bar hidden), while Settings/auth.json keys stay visible errors.
  • Settings hygiene: unknown opencodeGoApiKey fields are stripped on load and update, and the field is removed from PROTECTED_SECRET_SLOT / GlobalSettings.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No critical issues — one edge case in the credential fallback, inline.

Reviewed changes

  • Key moved out of settings: opencodeGoApiKey is gone from GlobalSettings and the protected-secret slot. The key now lives in a main-owned encrypted file (encrypted-api-key-file-store.ts, shared with MiniMax with an unchanged on-disk format, plus the new opencode-go-api-key-store.ts), and the renderer only reads back { apiKeyConfigured } through write-only IPC.
  • Immediate refresh and in-flight discard: saving or clearing the key, and changing the cookie or workspace, now bump opencodeFetchGeneration and enqueue refresh(), so a result fetched under the previous credential is dropped rather than applied.
  • Env-key 403 scoping: a 403 from an OPENCODE_API_KEY-sourced key reads as unavailable (bar hidden, the prior thread addressed), while a Settings or auth.json key stays a visible error and the cookie is still tried first.
  • Unreadable-key isolation: a stored key that fails to decrypt is confined to the OpenCode Go slot so it cannot abort other providers or leak credential detail, and the SHA-256 config hash no longer holds the plaintext cookie.
  • Renderer, preload, tests: new write-only Settings field with a Saved/Not saved badge, cookie and workspace relabeled as legacy fallback, web preload fails closed, and new store/IPC/service/fetcher tests plus a shared-harness env guard.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

… or unreadable key

Resolve OpenCode Go keys as an ordered candidate list. When an
OPENCODE_API_KEY key gets a 403, try the distinct auth.json opencode-go
key once before treating Go as not configured, so a Zen key in the
environment no longer hides a Go key saved by /connect.

Treat a saved key that cannot be decrypted as absent and keep the
cookie and workspace, so the remaining sources still run. The decrypt
error shows only when nothing else produced usage.

Refs stablyai#22512

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No critical issues — one minor suggestion inline.

Reviewed changes

  • Resolved OpenCode Go keys as an ordered candidate list, so an OPENCODE_API_KEY 403 retries a distinct auth.json opencode-go key once before treating Go as not configured.
  • Split the stored-key resolver from the cookie/workspace resolver, so a key that fails to decrypt is treated as absent and the environment, auth.json and cookie sources still run; the decrypt error is shown only when nothing else produced usage.
  • Extended the SHA-256 config hash to the full candidate list, so a change to either the environment or auth.json key discards stale usage.
  • Added fetcher- and service-level tests for the env-403→auth-file retry (200/403), no retry on 200/401/503, each fallback after a decrypt failure, and the equal-key dedupe.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

  • Auth-file source preserved on an equal key: resolveOpenCodeGoApiKeys now pushes the environment candidate only when it differs from the auth.json opencode-go key, and always pushes the auth-file candidate. An equal key is sent once but classified as auth-file, so a 403 stays a visible error rather than unavailable (bar hidden), and the redundant retry is correctly skipped.
  • Test updated: the dedupe test now asserts [{ source: 'auth-file', key: 'fake-same' }] (keeps the auth-file source once when it matches the environment key), and still asserts the distinct-key order is environment then auth-file.

This addresses the only open finding from the prior incremental review; no other files changed in this delta.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@nwparker

Copy link
Copy Markdown
Contributor

I owe you an apology here: your PR came first and I didn't check for one before opening mine. You filed this at 17:54; I opened #22551 hours later and merged it. That's duplicated your work, and it was avoidable — I should have searched open PRs for #22512 before starting.

Having now read yours side by side, your credential handling is better than what I merged, and I'd like to take it:

  • A dedicated write-only encrypted store (encrypted-api-key-file-store.ts) with opencodeGoCredentials:{getStatus,saveApiKey,clearApiKey} IPC handlers means the key is never readable from the renderer at all. Mine reuses the existing PROTECTED_SECRET_SLOT settings path and keeps a opencodeGoApiKeyConfigured boolean on RateLimitState to drive visibility — same intent, weaker boundary.
  • Generalising it so MiniMax uses the same store is a real cleanup; mine left that duplication in place.

The one thing mine has that yours doesn't: a tier that reads OpenCode's credential SQLite table, not just auth.json. That matters because OpenCode 2 imports auth.json into SQLite once (migration 20260805200742_import_legacy_credentials) and then writes only there — so a fresh OpenCode 2 install has no auth.json entry at all and an auth.json-only resolver finds nothing.

Worth knowing before you rebase, from verifying on two real Windows hosts: the credential table is not an OpenCode 2 marker — a 1.18.16 install creates it too (empty, same columns). And neither host had an auth.json for OpenCode anywhere. So both tiers are needed, and neither can be used to infer the version.

Concretely, I'd suggest: rebase this onto main and keep your store plus IPC handlers, replacing the settings-slot storage I merged, while keeping the credential-table tier and the 403 no-subscription state from #22551. If you'd rather not spend more time on it after I trampled the first attempt, say so and I'll port your store myself and credit you in the commit — your call.

Sorry again for the overlap.

@kespineira

Copy link
Copy Markdown
Contributor Author

No worries at all. #22551 got the fix out, and the credential-table tier is the part mine was missing.

I took you up on the offer and ported the store onto main: #22630. It keeps your resolver, the credential-table tier and the 403 no-subscription state, and replaces the settings slot with the main-owned encrypted store and write-only opencodeGoCredentials IPC, with MiniMax on the same store and its on-disk format unchanged.

One change to the precedence I'd like you to check. #22630 resolves the credential table before auth.json. Since OpenCode 2 imports auth.json once and then only writes SQLite, a key someone re-connects in v2 was being shadowed by the stale auth.json one. On 1.x the table is there but empty, so auth.json still wins, which matches what you saw on your two Windows hosts. I confirmed the row shape on a real v2.0.15 install (integration_id='opencode-go', value {"type":"key",...}).

#22630 also migrates keys already saved under #22551 into the new store. The sealed value stays on disk, orcad included, until the new store holds the key, so a locked keychain at first launch doesn't lose it.

Closing this one in favor of #22630.

@kespineira kespineira closed this Sep 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: OpenCode Go usage stops loading after OpenCode moved to the new Console

2 participants