Skip to content

fix(auth): resolve base_url_env_var via get_env_value everywhere (closes #18757) - #18948

Closed
Sanjays2402 wants to merge 1 commit into
NousResearch:mainfrom
Sanjays2402:fix/18757-base-url-env-var-dotenv
Closed

fix(auth): resolve base_url_env_var via get_env_value everywhere (closes #18757)#18948
Sanjays2402 wants to merge 1 commit into
NousResearch:mainfrom
Sanjays2402:fix/18757-base-url-env-var-dotenv

Conversation

@Sanjays2402

Copy link
Copy Markdown
Contributor

Closes #18757

resolve_api_key_provider_credentials() and friends read base_url_env_var via os.getenv(), which never consults ~/.hermes/.env. A user who sets, e.g., XIAOMI_BASE_URL=https://token-plan-cn.xiaomimimo.com/v1 only in the dotenv file silently falls back to the registry default and gets 401s on auxiliary tasks. API keys are read correctly via get_env_value() — base URLs are not. This is the same bug class fixed for API keys in #16101 and for TTS/STT in #17434.

Why this PR rather than #17246

#17246 (diff) targets the same issue but only patches 2 of 6 buggy spots, and despite the PR description claiming runtime_provider.py was "aligned", the diff doesn't touch that file at all.

File Function #17246 This PR
hermes_cli/auth.py get_api_key_provider_status
hermes_cli/auth.py resolve_api_key_provider_credentials
hermes_cli/auth.py get_external_process_provider_status (Copilot ACP)
hermes_cli/auth.py resolve_external_process_provider_credentials
hermes_cli/runtime_provider.py resolve_runtime_provider (api_key branch) ❌ (claimed but absent)
hermes_cli/model_switch.py _refresh_curated_models builtin endpoint dedup

#17246 also bundles 5 unrelated fixes (cron, env precedence, moonshot detection, gateway lock, etc.) — this PR is scoped solely to #18757 so it can land without dragging the rest along.

Behavior

get_env_value() already preserves shell-export precedence — os.environ wins when the variable is exported, dotenv is the fallback. Existing deployments are unaffected; users who relied on the dotenv file finally get the right endpoint.

Tests

New tests/hermes_cli/test_base_url_dotenv_resolution.py (7 tests):

  • resolve_api_key_provider_credentials / get_api_key_provider_status read base URL from ~/.hermes/.env (Xiaomi)
  • resolve_external_process_provider_credentials / *_status read base URL from ~/.hermes/.env (Copilot ACP)
  • resolve_runtime_provider honours dotenv on the api_key branch
  • model_switch dedup helper is wired through get_env_value
  • Regression guard: shell exports still beat dotenv values
522 passed, 522 warnings in 6.24s

Run on the full intersection of test files touching the changed modules — full green, no regressions.

Files changed

  • hermes_cli/auth.py — top-level get_env_value import + 4 call sites
  • hermes_cli/runtime_provider.py — 1 call site
  • hermes_cli/model_switch.py — 1 call site (with os.environ fallback for safety)
  • tests/hermes_cli/test_base_url_dotenv_resolution.py — new

Closes #18757.

Closes NousResearch#18757.

resolve_api_key_provider_credentials() and friends used os.getenv() to
read `base_url_env_var`, which never consults ~/.hermes/.env. Result:
providers configured solely via the dotenv file (e.g. Xiaomi with a
custom `token-plan-cn.xiaomimimo.com` endpoint) silently fell back to
the registry default and returned 401s on auxiliary tasks.

The same os.getenv() vs get_env_value() pattern was fixed previously
in NousResearch#16101 and NousResearch#17434; this is the third pass for base_url_env_var.

PR NousResearch#17246 attempted this fix but only patched 2 of the 6 affected
spots and notably missed runtime_provider.py despite claiming to fix
it. This commit closes the remaining four:

  * hermes_cli/auth.py
      - get_api_key_provider_status (api_key path)              [PR NousResearch#17246 hit]
      - get_external_process_provider_status (Copilot ACP)      [missed]
      - resolve_api_key_provider_credentials                    [PR NousResearch#17246 hit]
      - resolve_external_process_provider_credentials           [missed]
  * hermes_cli/runtime_provider.py
      - resolve_runtime_provider api_key branch                 [missed]
  * hermes_cli/model_switch.py
      - _refresh_curated_models built-in endpoint dedup         [missed]

get_env_value() preserves shell-export precedence: os.environ wins
when the variable is exported, so existing deployments are unaffected.
The dotenv file is consulted only as a fallback, matching how API keys
have always been resolved.

Adds tests/hermes_cli/test_base_url_dotenv_resolution.py covering all
four fixed paths plus a guard test that shell exports still take
priority.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists area/auth Authentication, OAuth, credential pools area/config Config system, migrations, profiles comp/cli CLI entry point, hermes_cli/, setup wizard labels May 2, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #18797 and #18908 (same base_url dotenv bug). This PR claims more complete coverage (6/6 spots vs 2/6 in #17246). Supersedes #18797 if the coverage claim holds.

1 similar comment
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #18797 and #18908 (same base_url dotenv bug). This PR claims more complete coverage (6/6 spots vs 2/6 in #17246). Supersedes #18797 if the coverage claim holds.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tracing the dotenv/base-URL inconsistency across the original resolver paths. The bug remains on current main: hermes_cli/auth.py:6204, :6236, :6391, and :6449, plus hermes_cli/model_switch.py:1560, still bypass dotenv lookup.

Problems

  • The runtime_provider.py hunk needs a semantic update before salvage. Current main uses profile-scoped _getenv() at hermes_cli/runtime_provider.py:1464; get_env_value() directly reads os.environ (hermes_cli/config.py:7735) and would bypass the fail-closed profile secret scope documented in agent/secret_scope.py:130-155.
  • A current sibling is omitted: hermes_cli/models.py:2571-2573 fingerprints base_url_env_var only from os.environ, despite that fingerprint representing values the provider reads.
  • Current API-key resolution deliberately prefers dotenv via get_env_value_prefer_dotenv() (hermes_cli/auth.py:586-592), so the proposed shell-wins regression expectation needs an explicit policy decision.

Suggested changes

  • Apply the dotenv-aware, scope-safe lookup to the current resolver locations and the model-cache fingerprint; update the tests to use invalidate_env_cache() rather than the stale private cache name.

Automated hermes-sweeper review.

# custom base URLs stored only in the dotenv file (e.g. Xiaomi)
# are honoured here too. See issue #18757.
from hermes_cli.config import get_env_value
env_url = (get_env_value(pconfig.base_url_env_var) or "").strip().rstrip("/")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current main now uses profile-scoped _getenv() for this read. When salvaging, preserve agent.secret_scope isolation rather than replacing it with get_env_value(), which directly reads os.environ and can bypass the fail-closed multiplexed-profile boundary.

@teknium1 teknium1 added 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 sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 12, 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 area/config Config system, migrations, profiles comp/cli CLI entry point, hermes_cli/, setup wizard 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.

[Bug]: resolve_api_key_provider_credentials() uses os.getenv for base_url_env_var — misses ~/.hermes/.env values

3 participants