fix(credentials): prefer ~/.hermes/.env over stale os.environ on key rotation (#20591) - #55528
Merged
teknium1 merged 1 commit intoJun 30, 2026
Conversation
Duplicate of #20602 — identical fix for the credential-rotation 401 (#20591): adds |
…rotation `_resolve_api_key_provider_secret` resolved API keys via `get_env_value`, which returns the `os.environ` value first and only falls back to `~/.hermes/.env`. After a user rotates a key in `.env`, a stale value still exported in the parent shell (Codex CLI, test runner, login profile) shadows the fresh key on every request, producing persistent 401s. The credential-pool seeding path was already fixed to prefer `.env` (NousResearch#18254/NousResearch#18755), but the live request-time resolution path was not — so the pool re-seeded with the fresh key while `_resolve_api_key_provider_secret` kept returning the stale shell export. This closes that remaining path. - config: add `get_env_value_prefer_dotenv()` — checks `~/.hermes/.env` first, then `os.environ`. Distinct from `get_env_value()` (unchanged, os.environ-first) so only Hermes-managed credential resolution flips precedence; the generic helper's many callers are unaffected. - auth: `_resolve_api_key_provider_secret` resolves through the new helper. - tests: regression coverage for both the pool-seeding path and the auth resolution path (a rotated `.env` key must beat a stale shell export). Closes NousResearch#20591.
kshitijk4poor
force-pushed
the
salvage/20591-prefer-dotenv-on-resolve
branch
from
June 30, 2026 09:45
de37eda to
1c78354
Compare
teknium1
enabled auto-merge (squash)
June 30, 2026 09:47
13 tasks
7 tasks
7 tasks
dtera
pushed a commit
to dtera/hermes-agent
that referenced
this pull request
Jul 1, 2026
…rotation (NousResearch#55528) `_resolve_api_key_provider_secret` resolved API keys via `get_env_value`, which returns the `os.environ` value first and only falls back to `~/.hermes/.env`. After a user rotates a key in `.env`, a stale value still exported in the parent shell (Codex CLI, test runner, login profile) shadows the fresh key on every request, producing persistent 401s. The credential-pool seeding path was already fixed to prefer `.env` (NousResearch#18254/NousResearch#18755), but the live request-time resolution path was not — so the pool re-seeded with the fresh key while `_resolve_api_key_provider_secret` kept returning the stale shell export. This closes that remaining path. - config: add `get_env_value_prefer_dotenv()` — checks `~/.hermes/.env` first, then `os.environ`. Distinct from `get_env_value()` (unchanged, os.environ-first) so only Hermes-managed credential resolution flips precedence; the generic helper's many callers are unaffected. - auth: `_resolve_api_key_provider_secret` resolves through the new helper. - tests: regression coverage for both the pool-seeding path and the auth resolution path (a rotated `.env` key must beat a stale shell export). Closes NousResearch#20591. Co-authored-by: 0xDevNinja <manmit0x@gmail.com>
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
…rotation (NousResearch#55528) `_resolve_api_key_provider_secret` resolved API keys via `get_env_value`, which returns the `os.environ` value first and only falls back to `~/.hermes/.env`. After a user rotates a key in `.env`, a stale value still exported in the parent shell (Codex CLI, test runner, login profile) shadows the fresh key on every request, producing persistent 401s. The credential-pool seeding path was already fixed to prefer `.env` (NousResearch#18254/NousResearch#18755), but the live request-time resolution path was not — so the pool re-seeded with the fresh key while `_resolve_api_key_provider_secret` kept returning the stale shell export. This closes that remaining path. - config: add `get_env_value_prefer_dotenv()` — checks `~/.hermes/.env` first, then `os.environ`. Distinct from `get_env_value()` (unchanged, os.environ-first) so only Hermes-managed credential resolution flips precedence; the generic helper's many callers are unaffected. - auth: `_resolve_api_key_provider_secret` resolves through the new helper. - tests: regression coverage for both the pool-seeding path and the auth resolution path (a rotated `.env` key must beat a stale shell export). Closes NousResearch#20591. Co-authored-by: 0xDevNinja <manmit0x@gmail.com>
Jasper6439
pushed a commit
to Jasper6439/hermes-agent
that referenced
this pull request
Jul 5, 2026
…rotation (NousResearch#55528) `_resolve_api_key_provider_secret` resolved API keys via `get_env_value`, which returns the `os.environ` value first and only falls back to `~/.hermes/.env`. After a user rotates a key in `.env`, a stale value still exported in the parent shell (Codex CLI, test runner, login profile) shadows the fresh key on every request, producing persistent 401s. The credential-pool seeding path was already fixed to prefer `.env` (NousResearch#18254/NousResearch#18755), but the live request-time resolution path was not — so the pool re-seeded with the fresh key while `_resolve_api_key_provider_secret` kept returning the stale shell export. This closes that remaining path. - config: add `get_env_value_prefer_dotenv()` — checks `~/.hermes/.env` first, then `os.environ`. Distinct from `get_env_value()` (unchanged, os.environ-first) so only Hermes-managed credential resolution flips precedence; the generic helper's many callers are unaffected. - auth: `_resolve_api_key_provider_secret` resolves through the new helper. - tests: regression coverage for both the pool-seeding path and the auth resolution path (a rotated `.env` key must beat a stale shell export). Closes NousResearch#20591. Co-authored-by: 0xDevNinja <manmit0x@gmail.com>
habarmc1223-sudo
pushed a commit
to habarmc1223-sudo/hermes-agent-fluxmem
that referenced
this pull request
Jul 8, 2026
…rotation (NousResearch#55528) `_resolve_api_key_provider_secret` resolved API keys via `get_env_value`, which returns the `os.environ` value first and only falls back to `~/.hermes/.env`. After a user rotates a key in `.env`, a stale value still exported in the parent shell (Codex CLI, test runner, login profile) shadows the fresh key on every request, producing persistent 401s. The credential-pool seeding path was already fixed to prefer `.env` (NousResearch#18254/NousResearch#18755), but the live request-time resolution path was not — so the pool re-seeded with the fresh key while `_resolve_api_key_provider_secret` kept returning the stale shell export. This closes that remaining path. - config: add `get_env_value_prefer_dotenv()` — checks `~/.hermes/.env` first, then `os.environ`. Distinct from `get_env_value()` (unchanged, os.environ-first) so only Hermes-managed credential resolution flips precedence; the generic helper's many callers are unaffected. - auth: `_resolve_api_key_provider_secret` resolves through the new helper. - tests: regression coverage for both the pool-seeding path and the auth resolution path (a rotated `.env` key must beat a stale shell export). Closes NousResearch#20591. Co-authored-by: 0xDevNinja <manmit0x@gmail.com>
santhreal
pushed a commit
to santhreal/hermes-agent
that referenced
this pull request
Jul 13, 2026
…rotation (NousResearch#55528) `_resolve_api_key_provider_secret` resolved API keys via `get_env_value`, which returns the `os.environ` value first and only falls back to `~/.hermes/.env`. After a user rotates a key in `.env`, a stale value still exported in the parent shell (Codex CLI, test runner, login profile) shadows the fresh key on every request, producing persistent 401s. The credential-pool seeding path was already fixed to prefer `.env` (NousResearch#18254/NousResearch#18755), but the live request-time resolution path was not — so the pool re-seeded with the fresh key while `_resolve_api_key_provider_secret` kept returning the stale shell export. This closes that remaining path. - config: add `get_env_value_prefer_dotenv()` — checks `~/.hermes/.env` first, then `os.environ`. Distinct from `get_env_value()` (unchanged, os.environ-first) so only Hermes-managed credential resolution flips precedence; the generic helper's many callers are unaffected. - auth: `_resolve_api_key_provider_secret` resolves through the new helper. - tests: regression coverage for both the pool-seeding path and the auth resolution path (a rotated `.env` key must beat a stale shell export). Closes NousResearch#20591. Co-authored-by: 0xDevNinja <manmit0x@gmail.com>
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
…rotation (NousResearch#55528) `_resolve_api_key_provider_secret` resolved API keys via `get_env_value`, which returns the `os.environ` value first and only falls back to `~/.hermes/.env`. After a user rotates a key in `.env`, a stale value still exported in the parent shell (Codex CLI, test runner, login profile) shadows the fresh key on every request, producing persistent 401s. The credential-pool seeding path was already fixed to prefer `.env` (NousResearch#18254/NousResearch#18755), but the live request-time resolution path was not — so the pool re-seeded with the fresh key while `_resolve_api_key_provider_secret` kept returning the stale shell export. This closes that remaining path. - config: add `get_env_value_prefer_dotenv()` — checks `~/.hermes/.env` first, then `os.environ`. Distinct from `get_env_value()` (unchanged, os.environ-first) so only Hermes-managed credential resolution flips precedence; the generic helper's many callers are unaffected. - auth: `_resolve_api_key_provider_secret` resolves through the new helper. - tests: regression coverage for both the pool-seeding path and the auth resolution path (a rotated `.env` key must beat a stale shell export). Closes NousResearch#20591. Co-authored-by: 0xDevNinja <manmit0x@gmail.com>
This was referenced Jul 30, 2026
leewenjie
pushed a commit
to leewenjie/hermes-agent
that referenced
this pull request
Aug 7, 2026
…rotation (NousResearch#55528) `_resolve_api_key_provider_secret` resolved API keys via `get_env_value`, which returns the `os.environ` value first and only falls back to `~/.hermes/.env`. After a user rotates a key in `.env`, a stale value still exported in the parent shell (Codex CLI, test runner, login profile) shadows the fresh key on every request, producing persistent 401s. The credential-pool seeding path was already fixed to prefer `.env` (NousResearch#18254/NousResearch#18755), but the live request-time resolution path was not — so the pool re-seeded with the fresh key while `_resolve_api_key_provider_secret` kept returning the stale shell export. This closes that remaining path. - config: add `get_env_value_prefer_dotenv()` — checks `~/.hermes/.env` first, then `os.environ`. Distinct from `get_env_value()` (unchanged, os.environ-first) so only Hermes-managed credential resolution flips precedence; the generic helper's many callers are unaffected. - auth: `_resolve_api_key_provider_secret` resolves through the new helper. - tests: regression coverage for both the pool-seeding path and the auth resolution path (a rotated `.env` key must beat a stale shell export). Closes NousResearch#20591. Co-authored-by: 0xDevNinja <manmit0x@gmail.com>
melon-xf
added a commit
to melon-xf/hermes-agent
that referenced
this pull request
Sep 3, 2026
…rotation (NousResearch#55528) `_resolve_api_key_provider_secret` resolved API keys via `get_env_value`, which returns the `os.environ` value first and only falls back to `~/.hermes/.env`. After a user rotates a key in `.env`, a stale value still exported in the parent shell (Codex CLI, test runner, login profile) shadows the fresh key on every request, producing persistent 401s. The credential-pool seeding path was already fixed to prefer `.env` (NousResearch#18254/NousResearch#18755), but the live request-time resolution path was not — so the pool re-seeded with the fresh key while `_resolve_api_key_provider_secret` kept returning the stale shell export. This closes that remaining path. - config: add `get_env_value_prefer_dotenv()` — checks `~/.hermes/.env` first, then `os.environ`. Distinct from `get_env_value()` (unchanged, os.environ-first) so only Hermes-managed credential resolution flips precedence; the generic helper's many callers are unaffected. - auth: `_resolve_api_key_provider_secret` resolves through the new helper. - tests: regression coverage for both the pool-seeding path and the auth resolution path (a rotated `.env` key must beat a stale shell export). Closes NousResearch#20591. Co-authored-by: 0xDevNinja <manmit0x@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A rotated API key in
~/.hermes/.envnow wins over a stale value still exported in the parent shell, closing the remaining path that produced persistent 401s after key rotation.Background
#20591 was closed as fixed, but only the credential-pool seeding path was corrected (#18254/#18755). The live request-time resolution path was still broken:
_resolve_api_key_provider_secret(hermes_cli/auth.py) resolved keys viaget_env_value(), which returns theos.environvalue first. So after a.envrotation, the pool re-seeded with the fresh key while the resolution path kept returning the stale shell export → 401s on every request.Verified live on current
mainbefore this fix:Changes
hermes_cli/config.py: addget_env_value_prefer_dotenv()— checks~/.hermes/.envfirst, thenos.environ. Distinct fromget_env_value()(unchanged, os.environ-first) so only Hermes-managed credential resolution flips precedence; the generic helper's many other callers are unaffected.hermes_cli/auth.py:_resolve_api_key_provider_secretresolves through the new helper.tests/: regression coverage for both the pool-seeding path and the auth-resolution path (a rotated.envkey must beat a stale shell export).Validation
_resolve_api_key_provider_secret(rotated .env, stale shell).envkeyget_env_value()(generic helper)tests/tools/test_credential_pool_env_fallback.py+tests/agent/test_credential_pool.py(89 prior + 2 new regressions); ruff clean.HERMES_HOME,.envvsos.environ): the rotated key now wins, with a negative control confirmingget_env_value()is unchanged (no blast-radius regression).Credit
Salvage of #20602 by @0xDevNinja, who located the exact still-broken path (
_resolve_api_key_provider_secret→get_env_value) that the earlier pool-only fix didn't cover. Cherry-picked to preserve authorship; rebased onto currentmain(the original was ~5.5k commits behind). The PR'scredential_pool.pychange was dropped — that path already prefers.envon currentmain(viasecret_scope), so the substantive fix isconfig.py+auth.pyonly.Closes #20591.