You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'm the author of the recently opened auth-v2 PRs from a new GitHub account (my prior account lost access). One of the PRs was closed as potential spam — totally fair given the account/volume — so I'm starting an issue to discuss direction first.
The full RFC is in specs/provider-auth-v2.md (included in the PR branches).
Problem
Today's auth path is effectively single-credential and makes it hard to support:
multiple OAuth subscription accounts per provider
deterministic rotation/cooldowns on 429 within the same user request
refresh-on-expired (401/403) where supported
a single composable integration point across providers
Proposal (Auth v2)
Encrypted-at-rest credential vault (AES-256-GCM) with atomic writes + lockfile coordination.
Multi-record credential store (providerId + namespace + label), enabling multiple accounts per provider.
Provider auth registry/adapters to unify OAuth flows + apply auth headers for Anthropic/OpenAI/Google/Copilot/Qwen/Cursor.
Fetch-level rotation middleware:
rotate on 429 (Retry-After-aware) and retry within the same request
refresh on 401/403 when supported
persist pool ordering + cooldowns
Migrate legacy auth files into the new store; keep behavior compatible.
Totally understand if this isn't a direction you want in core — consider this a proposal with a concrete implementation you can cherry-pick from (or close entirely).
Why I think it's valuable:
Security: credentials are encrypted at rest (vault + atomic writes/locking) instead of living as plaintext JSON.
Reliability: same-request rotation on 429 (Retry-After-aware) and refresh-on-expired where supported reduces user-visible failures.
UX: supports multiple connected subscription accounts per provider/namespace; makes it possible to pool/rotate accounts intentionally.
Architecture: keeps auth concerns behind provider adapters and a single fetch integration point, so it's provider-agnostic.
No pressure to merge — happy to iterate to match your preferences (split PRs, smaller surface area, rename/restructure) or you can close it and I'll keep it as a fork/local patch.
Thanks — re: credential storage, I agree Bun.secrets / system keyring is probably the right long-term direction (and I see #4318 is already tracking that).
My current branch uses encrypted record files on disk (AES-256-GCM) + a vault key loaded from env or generated locally, mainly for portability/headless environments + easy backup/restore.
If you’d prefer aligning with Bun.secrets, I can change the implementation to match #4318 in one of two ways:
A) Store each credential secret JSON directly in Bun.secrets (service opencode, name based on record id/provider/namespace) and keep only non-secret metadata/pool/cooldowns on disk.
B) Keep encrypted record files, but store the vault key in Bun.secrets (with file/env fallback for CI/server).
Given someone is already working on #4318, I’d rather not duplicate effort — happy to either (1) refactor this to use a pluggable backend so it can swap to Bun.secrets, or (2) rebase/cherry-pick the rotation/multi-account parts on top of the eventual keyring PR.
The additional piece this proposal tries to bring is the same-request rotation/refresh engine (Retry-After-aware 429 rotation + refresh-on-expired where supported) behind a single fetch integration point, plus the provider adapter registry so auth differences live in one place.
If you’d rather land this as smaller pieces that align with those issues, I’m happy to restructure.
@rekram1-node totally agree re: using Bun.secrets / system keyring for how secrets are stored (and +1 to aligning with #4318).
The main value I’m aiming for here is multi-account OAuth subscription failover (not API-key billing):
Allow multiple OAuth “subscription” accounts per provider/namespace.
On 429 (Retry-After aware) rotate to the next account and retry within the same user request.
On expired auth (401/403) refresh where supported; otherwise rotate.
Persist pool ordering + cooldowns so failover is stable across runs.
This is implemented as a fetch-level wrapper so most requests are transparent to the user; the known limitation is mid-stream rotation once tokens have started emitting — that still surfaces an error and the user retries (called out in the RFC).
Happy to rework the storage backend to use Bun.secrets while keeping the multi-account + rotation engine intact.
I created a new focused PR that aligns with the Bun.secrets/keychain direction and scopes to the core value (multi-account OAuth subscription failover):
Only the OAuth secrets go into Bun.secrets (service opencode); API-key / wellknown creds stay in auth.json for now
Robust refresh persistence: when providers/plugins persist refreshed tokens via /auth/:providerID, Auth.set() now updates the correct OAuth record by matching the refresh token (no API shape changes)
Rotation behavior is covered by focused tests (429 w/ Retry-After rotation + 401 refresh/rotate)
No pressure to merge — if this isn’t the direction you want in core, totally fine to close; feel free to cherry-pick the rotation bits if they’re useful.
✅ Multi-record credential store (multiple OAuth accounts per provider)
✅ Fetch-level rotation on 429 (Retry-After-aware) with retry within same request
✅ Refresh on 401/403 before failover
✅ Pool ordering + cooldowns persisted
✅ Works for all OAuth providers (core + plugins) without modification
What'''s not included (intentionally simplified):
❌ Encrypted vault (uses file-based storage with file locking instead)
❌ TUI credential manager
❌ Model discovery via /models
❌ Vault key CLI
The simplified approach avoids complexity and potential conflicts with #4318'''s broader keyring work, while still delivering the core value of multi-account OAuth failover.
Activity
jroth1111 commentedon Dec 18, 2025
Totally understand if this isn't a direction you want in core — consider this a proposal with a concrete implementation you can cherry-pick from (or close entirely).
Why I think it's valuable:
429(Retry-After-aware) and refresh-on-expired where supported reduces user-visible failures.fetchintegration point, so it's provider-agnostic.No pressure to merge — happy to iterate to match your preferences (split PRs, smaller surface area, rename/restructure) or you can close it and I'll keep it as a fork/local patch.
github-actions commentedon Dec 18, 2025
This issue might be a duplicate of existing issues. Please check:
Feel free to ignore if your proposal addresses aspects beyond these existing issues.
rekram1-node commentedon Dec 18, 2025
regarding a part of this:
how credentials are stored
Bun added support for native secrets api, we should probably just use that and I think we have an issue w someone working on that support
jroth1111 commentedon Dec 18, 2025
Thanks — re: credential storage, I agree Bun.secrets / system keyring is probably the right long-term direction (and I see #4318 is already tracking that).
My current branch uses encrypted record files on disk (AES-256-GCM) + a vault key loaded from env or generated locally, mainly for portability/headless environments + easy backup/restore.
If you’d prefer aligning with Bun.secrets, I can change the implementation to match #4318 in one of two ways:
Bun.secrets(serviceopencode, name based on record id/provider/namespace) and keep only non-secret metadata/pool/cooldowns on disk.Bun.secrets(with file/env fallback for CI/server).Given someone is already working on #4318, I’d rather not duplicate effort — happy to either (1) refactor this to use a pluggable backend so it can swap to Bun.secrets, or (2) rebase/cherry-pick the rotation/multi-account parts on top of the eventual keyring PR.
Which approach would you prefer?
jroth1111 commentedon Dec 18, 2025
Re the possible-duplicate bot note: totally fair — this overlaps with a few threads.
The additional piece this proposal tries to bring is the same-request rotation/refresh engine (Retry-After-aware
429rotation + refresh-on-expired where supported) behind a single fetch integration point, plus the provider adapter registry so auth differences live in one place.If you’d rather land this as smaller pieces that align with those issues, I’m happy to restructure.
jroth1111 commentedon Dec 18, 2025
@rekram1-node totally agree re: using Bun.secrets / system keyring for how secrets are stored (and +1 to aligning with #4318).
The main value I’m aiming for here is multi-account OAuth subscription failover (not API-key billing):
429(Retry-After aware) rotate to the next account and retry within the same user request.401/403) refresh where supported; otherwise rotate.This is implemented as a fetch-level wrapper so most requests are transparent to the user; the known limitation is mid-stream rotation once tokens have started emitting — that still surfaces an error and the user retries (called out in the RFC).
Happy to rework the storage backend to use Bun.secrets while keeping the multi-account + rotation engine intact.
jroth1111 commentedon Dec 18, 2025
I created a new focused PR that aligns with the Bun.secrets/keychain direction and scopes to the core value (multi-account OAuth subscription failover):
Highlights:
429(Retry-After aware)401/403(force refresh once, else rotate)Bun.secrets(serviceopencode), disk stores only metadata/pool stateThis avoids the larger auth-v2/UI/model-discovery/vault CLI scope.
jroth1111 commentedon Dec 18, 2025
@rekram1-node quick update — I took your feedback and narrowed this down to the core value, while avoiding overlap with the keyring work in #4318.
Bun.secrets(serviceopencode); API-key / wellknown creds stay inauth.jsonfor now/auth/:providerID,Auth.set()now updates the correct OAuth record by matching therefreshtoken (no API shape changes)429w/ Retry-After rotation +401refresh/rotate)No pressure to merge — if this isn’t the direction you want in core, totally fine to close; feel free to cherry-pick the rotation bits if they’re useful.
jroth1111 commentedon Jan 15, 2026
Closing in favor of #8591 (OAuth Marathon).
This proposal has been implemented with a simpler approach:
What'''s included in #8591 / PR #8590:
What'''s not included (intentionally simplified):
The simplified approach avoids complexity and potential conflicts with #4318'''s broader keyring work, while still delivering the core value of multi-account OAuth failover.