Provider Configuration Architecture
- Explain the four-level priority hierarchy that governs how Hermes resolves configuration values
- Use hermes config set to route settings to the correct file without manual editing
- Apply environment variable substitution to keep sensitive values out of config.yaml while referencing them dynamically
Two Files, One Clear Rule
Hermes separates configuration into two files for a clear reason: security. One file holds secrets, the other holds everything else, and the two files have different visibility and sharing properties.
~/.hermes/.env holds API keys and other secrets. This file should never be committed to version control, never shared with teammates, and never made visible to tools that inspect your configuration. It is a flat list of environment variable assignments -- the same format as any .env file you have used in web development.
~/.hermes/config.yaml holds everything else: model selection, provider routing, auxiliary model configuration, timeout settings, and all other preferences. This file can be safely shared, version-controlled, and copied between machines. It contains no secrets.
The rule is absolute: API keys go in .env. Everything else goes in config.yaml. Mixing them -- putting keys in config.yaml or putting non-secret settings in .env -- creates configuration that is harder to manage and easier to accidentally leak.
The Four-Level Priority Hierarchy
When Hermes needs to resolve a configuration value, it checks four sources in priority order. The first source that has the value wins:
- CLI arguments. Flags passed directly to
hermes chat,batch_runner.py, or other commands. These override everything else for the duration of that run. Use this to temporarily switch models or change a setting without modifying your configuration files. ~/.hermes/config.yaml. Your persistent settings. These apply to every Hermes session unless overridden by a CLI argument.~/.hermes/.env. Environment variables, primarily API keys. These are available to all configuration but not overridable by config.yaml values.- Built-in defaults. Hermes's hardcoded defaults for every setting. These apply only when nothing else provides a value.
Understanding this hierarchy explains why you can pass --model anthropic/claude-opus-4 on the command line to temporarily switch models for one run without changing your config.yaml default. The CLI argument takes priority over the file.
Using hermes config set
You could edit config.yaml and .env directly, but hermes config set is cleaner. It knows which file each setting belongs in and routes accordingly:
hermes config set OPENROUTER_API_KEY sk-or-v1-abc123 -- this is a secret, so it goes to .env
hermes config set model anthropic/claude-opus-4 -- this is a preference, so it goes to config.yaml
The command handles the routing automatically. You do not need to know which file a setting belongs in -- the tool figures it out. For settings you configure once and rarely change (your default model, your API keys), this is the recommended workflow.
For complex nested configuration like provider routing or auxiliary model settings, direct editing of config.yaml is often clearer than chaining multiple config set commands. Use config set for simple key-value settings and direct editing for structured configuration blocks.
Environment Variable Substitution
Sometimes you want to reference an environment variable inside config.yaml without hardcoding the value. Hermes supports ${VAR_NAME} substitution syntax throughout the config file:
auxiliary:
vision:
api_key: ${GOOGLE_API_KEY}
base_url: ${CUSTOM_VISION_URL}
At runtime, Hermes replaces ${GOOGLE_API_KEY} with the actual value of that environment variable from your shell or .env file. If the variable is not set, the placeholder remains literally in the config -- which causes an obvious error rather than a silent misconfiguration.
You can use multiple substitutions in a single value: url: "${HOST}:${PORT}" works as expected. This is useful for configurations that need to vary between environments -- development, staging, production -- without maintaining separate config files.
The substitution syntax is straightforward, but it requires that the referenced variables actually exist in the environment. A common mistake is referencing a variable that is defined in .env but not exported to the shell environment. Hermes reads .env automatically, so variables there are available for substitution.
Where API Keys Actually Live
API keys entered via hermes config set go to ~/.hermes/.env and stay there persistently. They do not need to be set in every terminal session. Hermes reads .env at startup and makes all the variables available for the session.
This is different from how some tools handle environment variables. You do not need to export OPENROUTER_API_KEY=... in your .bashrc or .zshrc. The hermes config set command stores it once and Hermes finds it automatically thereafter.
Hermes also automatically redacts secrets in its log files at ~/.hermes/logs/. If you are debugging a Hermes issue and share a log file, API keys will not appear in it. This is a sensible default but worth being aware of -- if you are trying to debug an authentication problem, the log will show [REDACTED] rather than the actual key.
Docker and Credential Forwarding
When Hermes runs agent sessions inside Docker containers -- either for batch processing or for isolated tool execution -- credentials do not automatically flow into the container environment. You need to explicitly forward them.
The docker_forward_env setting in config.yaml takes a list of environment variable names to inject into the container:
docker_forward_env: - OPENROUTER_API_KEY - ANTHROPIC_API_KEY - CUSTOM_TOOL_KEY
Only the variables you explicitly list are forwarded. This is intentional -- it prevents accidentally leaking environment variables to containers that do not need them. List only the keys that the agent actually needs inside the container for the tools it will use.
Practical Configuration Hygiene
A few habits make configuration easier to manage over time. Keep your config.yaml in version control alongside your project files -- it is safe to share and makes it easy to track what changed when behavior changes. Never version-control .env; add it to your .gitignore and keep it only on the machines that need it.
When you add a new API key, use hermes config set rather than directly editing .env. The command validates the format and routes the key correctly. Direct editing of .env is fine but requires more care to avoid syntax errors.
If you are setting up Hermes on a new machine and want to replicate your configuration, copy config.yaml freely and re-enter API keys fresh via hermes config set. Never copy .env over a network connection without encryption -- treat it like a password file.
- Hermes uses two configuration files: .env for secrets (API keys) and config.yaml for all other settings -- they must never be mixed.
- The four-level priority hierarchy (CLI args, config.yaml, .env, defaults) lets you temporarily override settings per-run without changing persistent configuration files.
- hermes config set routes each value to the correct file automatically, making it the safest way to configure simple key-value settings.
- Environment variable substitution (${VAR_NAME}) in config.yaml lets structured settings reference secrets without hardcoding them, enabling environment-specific configs.
- docker_forward_env in config.yaml must explicitly list which environment variables are injected into Docker containers -- nothing is forwarded automatically.