Skip to content

fix(aux): resolve provider keys via .env-preferring lookup so rotation isn't shadowed by a stale shell export - #55972

Open
Frowtek wants to merge 1 commit into
NousResearch:mainfrom
Frowtek:fix/aux-credentials-prefer-dotenv-on-rotation
Open

Frowtek wants to merge 1 commit into
NousResearch:mainfrom
Frowtek:fix/aux-credentials-prefer-dotenv-on-rotation

Conversation

@Frowtek

@Frowtek Frowtek commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Extends the #55528 credential-rotation fix to the auxiliary client. #55528
established that Hermes-managed credential resolution must prefer
~/.hermes/.env over os.environ: Hermes loads .env without overriding
an existing process-env var, so a value exported in the parent shell (login
profile, Codex CLI, test runner) shadows a freshly-rotated .env key and
produces persistent 401s. That fix switched the main request path
(_resolve_api_key_provider_secret), get_anthropic_key, and the
Azure-Foundry status read to get_env_value_prefer_dotenv.

The auxiliary client — which authenticates every side-LLM task (title
generation, curator skill review, vision analysis, web-search summarization,
session search) — still resolved provider keys via os.getenv(...). So after a
key rotation the main agent used the fresh key while auxiliary tasks kept
authenticating with the stale shell export and 401'd — a confusing split
failure ("my chat works but titles/vision are broken"). It's reachable on the
common single-key setup (no credential pool → the env-var path is taken
directly) and whenever the pool is exhausted.

Related Issue

Sibling of #55528 / #20591 — extends the same .env-preferring resolution to
the auxiliary client. Searched existing PRs/issues; none covers the auxiliary
path.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • agent/auxiliary_client.py — add _prefer_dotenv_env(key) (wraps
    get_env_value_prefer_dotenv) and route the auxiliary credential reads
    through it, so a rotated .env key wins over a stale shell export:
    • _try_openrouter (OpenRouter key)
    • _resolve_custom_runtime and resolve_provider_client (OpenAI key)
    • _describe_openrouter_unavailable (the "not set" diagnostic, for consistency)
  • tests/agent/test_auxiliary_client.py — regression test asserting the
    auxiliary OpenRouter path resolves a rotated .env key over a stale
    os.environ value.

How to Test

Reproduction: export OPENROUTER_API_KEY=old in the shell, then rotate it to a
new value in ~/.hermes/.env.

Tests run and results:

pytest tests/agent/test_auxiliary_client.py -k try_openrouter -v
  test_try_openrouter_pool_exhausted_falls_back_to_env ................... PASSED
  test_try_openrouter_pool_exhausted_no_env_marks_unhealthy ............. PASSED
  test_try_openrouter_prefers_rotated_dotenv_over_stale_shell_export (new) PASSED
  -> 3 passed

pytest tests/agent/test_auxiliary_client.py -q
  -> 275 passed

ruff check and scripts/check-windows-footguns.py on the changed files: both
clean.

Checklist

  • Read the Contributing Guide; commit follows Conventional Commits (fix(aux): …)
  • Searched existing PRs/issues to avoid a duplicate
  • PR contains only changes related to this fix
  • Ran the tests for the affected area — pass (results above)
  • Added tests for the change
  • Documentation / config / tool-schema — N/A (internal credential-resolution fix; no API/config/schema change)

…n isn't shadowed by a stale shell export

NousResearch#55528 established that Hermes-managed credential resolution must prefer
`~/.hermes/.env` over `os.environ`: Hermes loads `.env` without overriding an
existing process-env var, so a value exported in the parent shell (login
profile, Codex CLI, test runner) shadows a freshly-rotated `.env` key and
produces persistent 401s. That fix switched the main request path
(`_resolve_api_key_provider_secret`), `get_anthropic_key`, and the
Azure-Foundry status read to `get_env_value_prefer_dotenv`.

The auxiliary client — which authenticates every side-LLM task (title
generation, curator skill review, vision analysis, web-search summarization,
session search) — still resolved provider keys via `os.getenv(...)`, so after a
key rotation the main agent used the fresh key while auxiliary tasks kept
authenticating with the stale shell export and 401'd. This is reachable on the
common single-key setup (no credential pool → the env-var path is taken
directly) and whenever the pool is exhausted.

Route the auxiliary OpenRouter / OpenAI key resolution (and the OpenRouter
"unavailable" diagnostic) through a small `_prefer_dotenv_env` helper that wraps
`get_env_value_prefer_dotenv`, so a rotated `.env` key wins over a stale export
here too. Adds a regression test asserting the auxiliary OpenRouter path
resolves the rotated `.env` key over a stale `os.environ` value.
@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint area/auth Authentication, OAuth, credential pools sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data P2 Medium — degraded but workaround exists labels Jul 1, 2026
@teknium1

Copy link
Copy Markdown
Collaborator

Thanks for extending the key-rotation fix to the auxiliary OpenRouter path. The premise is confirmed on current main: agent/auxiliary_client.py:1956 still reads OPENROUTER_API_KEY with os.getenv(), whereas hermes_cli/config.py:7759 provides the dotenv-preferred resolver introduced by #55528.

Problems

  • The same stale-export behavior remains in auxiliary configured fallback credentials at agent/auxiliary_client.py:4001 and named custom-provider credentials at agent/auxiliary_client.py:4735; both read configured key_env values with os.getenv().
  • The added test covers only OpenRouter. It does not protect those remaining configured credential paths.

Suggested changes

  • Apply the shared dotenv-preferred lookup to the remaining auxiliary key_env / api_key_env credential reads.
  • Add a stale-shell-export versus fresh .env regression test for a configured auxiliary credential route.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants