This page is my own reference. I run one agent against seven providers. When a request fails, I need to know which layer moved. This page answers that.
Last verified: 2026-09-20.
Where Each Setting Lives
Hermes resolves settings in this order. The first match wins:
- CLI arguments (
hermes chat --model ...) config.yaml— all non-secret settings.env— secrets, and a fallback for env vars- Built-in defaults
Rule: secrets go in .env. Everything else goes in config.yaml.
The Two Provider Slots
The main model and the fallback chain are different settings.
Main model — model in config.yaml:
model:
default: deepseek-v4.1-flash
provider: opencode-go
base_url: https://opencode.ai/zen/go/v1
api_mode: chat_completions
extra_body: {"cache_prompt": true}
Fallback chain — top-level fallback_providers list:
fallback_providers:
- provider: ollama-cloud
model: deepseek-v4.1-flash
- provider: agnes
model: agnes-3.0-flash
Each entry needs both provider and model. An entry with one field is ignored.
Run hermes fallback to edit the chain interactively. It reuses the hermes model picker.
How Fallback Behaves
- The chain is tried entry by entry. Activation is one-shot per session.
- The swap keeps your conversation. You do not lose context.
- Failover is per turn. The primary provider is restored on the next turn.
- Triggers: rate limits, server errors, auth failures, connection drops.
- There are no environment variables for this chain. You configure it in
config.yamlonly. This is deliberate, so a stale shell export cannot override your choice.
fallback_model (singular) is the legacy key. Hermes still reads it. fallback_providers (plural) wins when both are set.
Where Fallback Works
| Context | Supported |
|---|---|
| CLI sessions, interactive and one-shot | Yes |
| Messaging gateway (Telegram, Discord) | Yes |
| Desktop app and TUI | Yes |
| Subagent delegation | Yes, via delegation.fallback_providers |
| Cron jobs | Yes, cron inherits the chain |
Auxiliary tasks on provider: auto |
Yes |
A chain added while a chat is open applies from the next turn.
Auxiliary Task Routing
Side tasks use their own resolution. Each one tries the main provider first:
Main provider + main model → auxiliary.<task>.fallback_chain →
fallback_providers → skip the task and warn
The built-in text discovery chain, used when the main route is unavailable:
OpenRouter → Nous Portal → Custom endpoint → Codex OAuth →
z.ai / Kimi / MiniMax / Xiaomi MiMo / Hugging Face / Anthropic → give up
Tasks with independent resolution: vision, compression, skills_hub, mcp, approval, title_generation, review, triage_specifier.
A billing or quota failure quarantines only the failed custom endpoint. Other endpoints stay eligible.
Named Custom Providers
Define extra endpoints under providers: in config.yaml:
providers:
agnes:
api: https://api.agnes-ai.cn/v1
key_env: HERMES_CUSTOM_AGNES_API_KEY
default_model: agnes-2.5-flash
sensenova:
api: https://token.sensenova.cn/v1
key_env: HERMES_CUSTOM_SENSENOVA_API_KEY
default_model: glm-5.2
Accepted fields: api, name, key_env, inline api_key, key_cmd, transport, default_model, models, context_length, discover_models, extra_body, extra_headers, session_affinity_header, ssl_ca_cert, ssl_verify, catalog_provider, enabled.
Use key_env and not a literal key. The key then stays out of config.yaml.
Set transport explicitly. The values are chat_completions, anthropic_messages, and codex_responses. Auto-detection runs only as a fallback.
OpenRouter Routing
Control how OpenRouter picks a backend:
provider_routing:
sort: "throughput" # price (default), throughput, latency
data_collection: "deny"
Shortcuts: append :nitro to a model name for throughput sorting. Append :floor for price sorting.
Diagnostic: A 401 From Every Custom Provider
I hit this. Three custom providers returned 401 at the same time. The endpoints were correct.
Cause: key_env names an environment variable. The variable was not in the process environment. Hermes keeps those keys in its own secret store.
Check it this way:
# Does the variable exist in the shell?
echo "${HERMES_CUSTOM_AGNES_API_KEY:-NOT SET}"
# Does the endpoint answer at all?
curl -s -o /dev/null -w "%{http_code}\n" https://api.agnes-ai.cn/v1/models
A 401 proves the endpoint exists and asks for a key. A 000 means the network blocks it.
Fix: let Hermes resolve the key from its store. Do not export the value in a shell profile.
Check the Chain
hermes fallback list # show the chain
hermes doctor # config and dependency check
hermes config get model # resolved main model
hermes status --all # component status
hermes fallback list reads config.yaml. It does not prove that the keys work. Run one request per provider to prove that.
OpenCode Go Note
Hermes pins each OpenCode provider to its own relay. opencode-zen uses /zen/v1. opencode-go uses /zen/go/v1.
The model you pick decides the relay. Switching from a Zen model to a Go-only model never sends the request to Zen. The same rule applies when you resume a session.
A -vision marker in a model id marks the model as vision-capable. Example: deepseek-v4-flash-vision-exp.