feat: api_key_env for custom_providers - #4674
Conversation
… env vars Adds an `api_key_env` field to custom_providers entries in config.yaml that names an environment variable to read at runtime, instead of storing the literal API key in the config file. When both `api_key` and `api_key_env` are present, `api_key_env` wins so operators can migrate incrementally. The credential pool, runtime provider resolution, and model setup flows all use the new resolver. Write-back paths are guarded so resolved secrets are never leaked back into config.yaml. Example config: ```yaml custom_providers: - name: Api.x.ai base_url: https://api.x.ai/v1 api_key_env: XAI_API_KEY ``` Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds support for resolving API keys for custom_providers entries from an environment variable (api_key_env) at runtime, to avoid storing secrets in config.yaml/auth.json and to support incremental migration from literal api_key.
Changes:
- Introduces
resolve_custom_api_key()and uses it when resolving named custom providers at runtime. - Updates CLI custom-provider selection/model flow to avoid writing env-resolved secrets back into
config.yaml. - Updates custom credential pool seeding to use the same resolver for
custom_providersentries.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
hermes_cli/runtime_provider.py |
Adds resolve_custom_api_key() and uses it when loading named custom provider config for runtime resolution. |
hermes_cli/main.py |
Resolves custom provider API key via helper for menu flows and guards config write-back to prevent secret leakage. |
agent/credential_pool.py |
Seeds custom provider pools using the resolver (supports api_key_env as well as api_key). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| saved_model = entry.get("model", "") | ||
| model_hint = f" — {saved_model}" if saved_model else "" | ||
| providers.append((key, f"{name} ({short_url}){model_hint}")) | ||
| from hermes_cli.runtime_provider import resolve_custom_api_key | ||
| _custom_provider_map[key] = { | ||
| "name": name, | ||
| "base_url": base_url, | ||
| "api_key": entry.get("api_key", ""), | ||
| "api_key": resolve_custom_api_key(entry), | ||
| "api_key_env": entry.get("api_key_env", ""), # preserve source flag | ||
| "model": saved_model, |
There was a problem hiding this comment.
Importing resolve_custom_api_key inside the for-loop causes a redundant import statement on each iteration and makes the loop harder to read. Prefer moving the import to the top of select_provider_and_model() (or at least above the loop) since this isn’t a conditional dependency.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
Closing this — the underlying capability (env-var-resolved API keys for custom providers) now exists upstream via No need to carry this forward — |
Summary
api_key_envfield tocustom_providersentries in config.yaml — names an environment variable to resolve at runtime instead of storing the literal API key in the config fileapi_keyandapi_key_envare present,api_key_envwins (incremental migration)Motivation
Custom providers currently require hardcoding API keys in
config.yamlandauth.json. This is a security concern — config files end up in backups, dotfile repos, and are readable by any process. Environment variables are the standard way to manage secrets, and every other provider type in Hermes already supports env var resolution.Example
Changes
hermes_cli/runtime_provider.pyresolve_custom_api_key()helper; patched_get_named_custom_providerand_resolve_named_custom_runtimeagent/credential_pool.py_seed_custom_pool()uses resolver instead of literal readhermes_cli/main.py_model_flow_named_customguards write-back to prevent secret leakageTest plan
api_key_env, verifyhermesresolves and authenticateshermes modelflow, verify config.yaml is not polluted with resolved secret🤖 Generated with Claude Code