Auth: paste Service Account JSON(s) as the API key — keychain-backed markers + key pool rotation (re: #6821)
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 22/100
- Issue type
- Feature
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- google-cloud, typescript
- Domain
- api, authentication, cloud
Research direction
Start with src/lib/gcp-adc.ts for the existing service_account and authorized_user exchange, and api-key-resolve.ts, the sync read gate. Also read key-failover.ts for apiKeyPool rotation and storeProviderKeyInKeychain for the keychain pattern. Done means a pasted SA JSON is stored in the keychain, only a gcp-sa marker is persisted, and pool failover works with markers; the design needs maintainer input before implementation.
Written by the indexing model from the issue text.
Description
Area
Authentication and account pool
What are you trying to accomplish?
I want to set up the built-in google-vertex provider entirely from the provider config, by pasting a GCP Service Account JSON directly into the API-key slot — the same way JSON-based auth flows typically work:
{"type": "service_account", "private_key": "...", "client_email": "..."}
and ideally paste several credential JSONs (comma-separated) into the key pool so they rotate under 429 quota exhaustion, mixed with regular API keys:
{"type": "service_account", ...}, {"type": "service_account", ...}, sk-regular-key
What prevents this today?
Two limitations:
- No in-band credential setup. A Service Account JSON can only be provided through ambient ADC — the
GOOGLE_APPLICATION_CREDENTIALSenv var or gcloud user ADC files. The path lives outside opencodex, so swapping or rotating the key requires touching the environment again, and the provider config alone is not portable (machine migration, sharing a config minus secrets). - No JSON equivalent of the key pool. opencodex already has multi-key 429 failover (
apiKeyPool+ per-key cooldown inkey-failover.ts), but credentials that are only available as JSON cannot participate — each pool entry is a single string and only literal keys /${ENV}/keychain:references are recognized. - Related friction:
google-vertexalso shows an empty model list on a fresh config (no static preset list and no live discovery for vertex mode), which compounds the setup experience.
What should OpenCodex do?
When I paste a JSON object with type: "service_account" (or authorized_user) into the API-key field:
- The GUI should accept and validate it immediately (bad JSON rejected at save time, with specific guidance for the common mistakes: a file path instead of the JSON body, an unrecognized
typefield, truncated JSON). - The secret material (private key / client secret / refresh token) should be stored in the OS keychain — the same store
keychain:references already use — and only a marker (e.g.gcp-sa:<account>) should be persisted toconfig.json. No key material in config or its backups. - At request time, the existing ADC machinery in
src/lib/gcp-adc.tsshould treat the marker as its highest-priority source: it already implements the full RS256 JWT exchange forservice_accountand refresh-token exchange forauthorized_user, plus the token cache / refresh skew / in-flight dedup — none of that needs to change. - Each
apiKeyPoolentry should be able to hold a marker (or a mixed set of markers and literal keys), so the existing 429 failover rotates credentials — including SA JSONs — with per-key cooldown, no failover logic changes. - vertex's
x-goog-api-keyfast path should skip markers (they are not API-key material) so they reach the ADC branch instead.
Example usage or interface
# paste a Service Account JSON as the key (GUI or API):
{"type": "service_account", "client_email": "[email protected]", "private_key": "-----BEGIN PRIVATE KEY-----\n..."}
# config.json afterwards holds only the marker:
"apiKey": "gcp-sa:p/a1b2c3d4"
# pool with mixed key types, rotating on 429 as usual:
"apiKeyPool": [
{ "id": "a1b2c3d4", "key": "gcp-sa:p/a1b2c3d4" },
{ "id": "e5f6a7b8", "key": "gcp-sa:p/e5f6a7b8" },
{ "id": "c9d0e1f2", "key": "sk-regular-api-key" }
]
Expected request flow with a marker: the vertex branch uses project/location + Authorization: Bearer <token from the RS256 JWT exchange> — the ADC path that already exists.
Alternatives or workarounds
GOOGLE_APPLICATION_CREDENTIALSenv var + gcloud user ADC — works today, but the credential lives outside opencodex: not portable, and key rotation means touching the environment.- Store the JSON file path in the provider config — keeps the config portable-ish, but the path still points outside, and the secret is not covered by opencodex's keychain layer.
- Make
resolveProviderApiKeyasync and parse JSON at request time — would let JSON live directly in the pool entries, but it breaks the sync read gate that routing/catalog/quota callers all rely on, so it is a much bigger change. - The marker approach in this proposal keeps everything synchronous and reuses the existing keychain layer; I have a working prototype on #6841 if that direction is acceptable.
Additional context
src/lib/gcp-adc.tsalready documents explicit source priority (env file → gcloud ADC → metadata server) with per-source cache keys; a keychain-backed marker source slots in at the top without touching the rest.resolveProviderApiKey(api-key-resolve.ts) is the single sync read gate; a marker string keeps it sync — no signature changes.keychain:references already prove the OS keychain layer (including the read-back verification pattern instoreProviderKeyInKeychain); markers would use the same service name and entry factory.- The empty model list for
google-vertexis a separate but related friction —buildModelsRequestskips live discovery for vertex mode and the registry entry ships no staticmodels, so a fresh provider shows zero models.
Checks
- I searched existing issues and documentation.
- This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
- I removed secrets and personal data.
- Dominant language
- TypeScript
- Stars
- 16.9k
- Forks
- 1.3k
- Avg merge
- 4h 58m
- Merged PRs (30d)
- 616
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from lidge-jun/opencodex
-
account-pool enhancement proxy
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
Maintainers usually reply within 1 day
-
account-pool enhancement gui proxy
Difficulty 5/5 Over a week Newbie friendliness 8/100
Maintainers usually reply within 1 day
-
bug platform service
Difficulty 4/5 3-5 days Newbie friendliness 22/100
Maintainers usually reply within 1 day
-
catalog enhancement
Difficulty 4/5 3-5 days Newbie friendliness 35/100
lidge-jun/opencodex#6784 · 2 comments ·
Maintainers usually reply within 1 day
-
bug
Difficulty 4/5 3-5 days Newbie friendliness 18/100
lidge-jun/opencodex#6764 · 1 comment ·
Maintainers usually reply within 1 day
All issues in lidge-jun/opencodex
Similar issues
-
[Docs] README: FAQ setup command, IDA in the intro, Node badgePossibly taken @akram1089 claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
morluto/rea#1353 · 1 comment ·
Maintainers usually reply within 1 day
-
[Feature]: [P3] engine-rs: the package source hash should ignore line endings and untracked filesOpen
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
maniator/verticopolis#880 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
siyuan-note/siyuan#20353 ·
Maintainers usually reply within 1 day
-
afk-ok area:data-quality importer size:S
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
enorm-labs/event-junkie#3027 ·
Maintainers usually reply within 1 day