Skip to content

Fix/1389 credential store - #2012

Open
Tapraj-Pathak wants to merge 7 commits into
1jehuang:masterfrom
Tapraj-Pathak:fix/1389-credential-store
Open

Tapraj-Pathak wants to merge 7 commits into
1jehuang:masterfrom
Tapraj-Pathak:fix/1389-credential-store

Conversation

@Tapraj-Pathak

Copy link
Copy Markdown
Contributor

Credentials: one store, read at use — delete set_var, captured auth, and the auto allowlist (#1389)

Summary

Makes the env file the single credential store. The process environment is a read-only override that is sampled at use time. Secret lookups happen per request, never cached at startup, so re-keying or editing the env file takes effect immediately without a restart.

  • One store: credentials live in the env file (~/.config/jcode/*.env); nothing writes or removes process env vars as a side effect of login/logout/refresh.
  • Read at use: runtime auth headers are resolved from the store per request. Renaming an API key mid-session is picked up on the next request.
  • Names, not values: ProviderAuth now stores key names (key_name, env_file) and resolves the actual token only at request time. AuthResolver closures are deleted.

Included

  1. Creds facade + env-file store migration (jcode-provider-env)

    • New Creds API (get/set/clear/has/require) over env-then-file.
    • Migrated raw secret readers: cursor, codex, OpenRouter, Anthropic, dispatch, active-method dual-credential probe, ZAI alias, memory-recall bench.
    • save_env_value_to_env_file no longer touches process env — no more secret set_var / remove_var outside tests.
    • Foreign/legacy reads (Claude Code OAuth, AWS SDK chain, Gmail, Grok deployment key, etc.) annotated ponytail: carve-outs with justification.
  2. ProviderAuth: names resolved at request time (jcode-provider-openrouter-runtime)

    • AuthorizationBearer { key_name, env_file, label } / HeaderValue { header_name, key_name, ... } resolve live via resolve_key() → load_runtime_api_key (env → env file → Muse subscription store).
    • Static config/test keys use InlineBearer / InlineHeader (resolved at construction).
    • AuthResolver deleted; resolve_auth() → resolve_auth_names(); eager require_all() keeps fail-fast at /serve boot.
    • Rebased against upstream's new Muse credential store; the OpenAI-compatible profile runtime keeps its Muse gate and error messages.
  3. Catalog-driven --provider auto (src/cli/provider_init.rs)

    • Removed the hardcoded 7-name allowlist; autodetect now scans the openai-compatible profile catalog for configured credentials.
  4. Guardrail

    • New scripts/check_secret_env_reads.py ratchet (baseline: 7 legacy reads / 5 files) wired into CI and scripts/check_guardrails.sh — new direct env::var reads of *KEY*/*TOKEN*/*SECRET* break the build.

Regression tests added

  • auth_names_resolve_live_between_requests — re-key between requests, process env never set, clear → "Run jcode login" error.
  • catalog_request_headers_follow_provider_auth_rules
  • auto_provider_detects_non_allowlisted_compat_catalog_credentials — only DEEPSEEK_API_KEY in env → auto must bootstrap the DeepSeek profile.
  • Env-file tests (env stays unset, live re-key) in the openrouter runtime suite.

Verification

  • cargo check --workspace: 0 errors.
  • secret env read ratchet ok (7 legacy reads).
  • openrouter-runtime suite: 185 passed; failures match pristine origin/master exactly (pre-existing Windows env-dir test issues) — zero new failures.
  • jcode-provider-env: 9/9 pass.
  • Formatting clean; code-size/panic/swallowed-error ratchets unaffected.

Notes / caveats

  • Kept the OpenAI on_auth_changed → reload_credentials_now() hook (documented deviation — deleting it would regress save_named_api_key writes secrets into the process environment, shadowing the env file until restart #1386 for the OpenAI tree).
  • Carve-outs annotated where foreign code legitimately reads env (claude.rs, grok_build.rs, gmail.rs, provider-bedrock AWS chain, cursor.rs CURSOR_ACCESS_TOKEN, etc.).
  • The 6 pre-existing openrouter-runtime failures on Windows are unrelated (tests redirect XDG_CONFIG_HOME/HOME/APPDATA, which dirs::config_dir() ignores on Windows); follow-up: switch those tests to JCODE_HOME.

Commits: a74d80f74 · 5a1f98e2c · 47828f722 (28 files, +650/−277, plus reconciliation)

…t use time (1jehuang#1389)

The process environment and per-provider env files stored credentials in
two places and copied between them, causing three bug classes: the
in-memory copy wins and goes stale, the in-memory copy leaks into child
processes, and startup-time resolution sees nothing. Make the env file
the truth and the process env a read-only override sampled only at use
time.

- jcode-provider-env: add a Creds facade (get/set/clear/has/require) over
  the env-file store; save_env_value_to_env_file no longer writes or
  removes the process env.
- Migrate raw env readers of app-written secrets to the facade /
  load_api_key_from_env_or_config (cursor, codex, provider_fetch,
  anthropic, openrouter dispatch, zai alias, memory recall bench, anthropic
  runtime, dual-credential active-method probe).
- Annotate intentional foreign/legacy env reads and process-env writes
  (Claude Code's own env var, Grok deployment key, Composio, AWS SDK
  chain, lifecycle legacy file->env sync for foreign readers) as
  ponytail carve-outs.
- openrouter runtime: ProviderAuth now stores key *names* (+ env file),
  resolving the value per request via apply(); AuthResolver closures are
  gone; constructors fail fast with require_all() but never capture the
  token. Inline/config keys use InlineBearer/InlineHeader resolved at
  construction.
- Add a ratchet script banning new raw env::var reads of secrets
  (scripts/check_secret_env_reads.py + baseline), wired into CI and
  scripts/check_guardrails.sh.
- --provider auto: catalog-driven compat-profile detection already scans
  openai_compatible_profiles(); add a regression test proving a
  non-allowlisted profile (DeepSeek) with direct credentials bootstraps
  instead of an empty deferred MultiProvider.

Test results on Windows: 6 pre-existing openrouter-runtime failures and
the jcode-base environment-dependent failures (`sqlite3` CLI, browser
tool, Windows path-escape JSON fixtures, Windows socket WouldBlock) are
identical at pristine HEAD; no new failures from this change. New
regression tests pass: auth_names_resolve_live_between_requests,
creds_set_and_clear_are_file_only_and_read_live,
auto_provider_detects_non_allowlisted_compat_catalog_credentials.
…ebase (1jehuang#1389)

Upstream (646 commits ahead) moved new_openai_compatible_profile_runtime
into openai_compatible_runtime.rs and added a Jcode-managed Muse
subscription credential store. Adapt the names-based ProviderAuth refactor
to the new layout:

- Keep the constructor in openai_compatible_runtime.rs; gate on
  load_openai_compatible_profile_api_key so the native Muse subscription
  route is only enabled by its stored credentials (a plain Meta API key is
  never accepted) and still store key names, resolved live at request time.
- ProviderAuth::resolve_key now uses load_runtime_api_key, which reads
  env-then-file and falls back to the Muse subscription store.
- Drop the duplicate constructor from lib.rs and the unused autodetect
  timing variable; fix the loopback-catalog test fixture to InlineBearer.
@greptile-apps

greptile-apps Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[High impact] Safe to merge based on the reviewed changes. No outstanding blocking findings remain.

Summary

The PR moves credential reads behind shared helpers and resolves request keys when they are used.

  • Jcode reads provider keys from the current environment or env file when they are needed.
  • OpenAI-compatible requests use the current key for each request.
  • --provider auto finds configured OpenAI-compatible profiles from the catalog.
  • CI blocks new direct reads of secret-looking environment variables.

No outstanding blocking findings remain. The reviewed changes are safe to merge.

Reviews (4) · Last reviewed commit: "fix(auth): reconcile env readers/writers..." · Reviewed by Greptile

Comment thread crates/jcode-provider-env/src/lib.rs Resolved
Comment thread crates/jcode-provider-env/src/lib.rs Resolved
Comment thread crates/jcode-base/src/auth/cursor.rs Resolved
…entries, isolate credential test

Three review issues addressed:

- provider-env: Creds::set now updates Jcode-managed process env copies when
  saving a replacement key, so readers that prefer the process env
  (load_env_value_from_env_or_config / load_api_key_from_env_or_config) see
  the new key immediately instead of continuing to use the stale copy.
  Adds regression test replacement_key_reconciles_jcode_managed_process_copy.

- provider-env: Both file-reading loops now trim each line before prefix
  matching (line.trim().strip_prefix), restoring the behaviour of the old
  per-provider readers. Files with indented entries such as
  '  CURSOR_API_KEY=abc' were silently broken after migration to the shared
  reader. Adds regression test loads_api_key_from_indented_env_file_line.

- openrouter-runtime: optional_bearer_resolves_live_per_request now acquires
  ENV_LOCK before creating its EnvVarGuards. Without the lock the test's
  JCODE_HOME change could race with lock-holding credential tests and corrupt
  their file operations.
Comment thread crates/jcode-provider-env/src/lib.rs Resolved

This branch has not been deployed

No deployments
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.

1 participant