6.4 KiB
Hermes Configuration Discipline
Do not modify Hermes config.yaml by adding keys you guessed or assumed existed. Always verify before applying.
The Rule
You must verify every config key exists in official Hermes documentation before adding it to config.yaml.
Hermes config keys are not arbitrary YAML — they are read by specific code paths in the Hermes runtime. A made-up key is silently ignored, not validated, and leaves you thinking a feature was configured when it wasn't.
Verification Sources (in priority order)
- The
hermes configCLI —hermes config set KEY VALvalidates the key path before applying. Ifhermes config setaccepts it, it's a real key. - The official docs — https://hermes-agent.nousresearch.com/docs/user-guide/configuration
- The
hermes-agentskill — loaded withskill_view(name='hermes-agent'), it documents known config sections. - The source code —
hermes_cli/config.pycontains DEFAULT_CONFIG which enumerates every recognized top-level key. - The internal help —
hermes config --help,hermes config check,hermes config show
What NOT to do
- Do not assume patterns from other tools carry over. Just because some other tool uses
fallback_providersdoesn't mean Hermes reads them. - Do not write a key + value into config.yaml that you haven't seen in the docs, the skill, or the source. Writing and testing is not the right order — verify first, then write.
- Do not patch config.yaml by hand with arbitrary YAML when
hermes config setexists. The CLI is the safe path. Use it.
What config changes look like for Hermes
If the user wants automatic fallback to a local model, verify the installed Hermes schema first. Current Hermes uses model.fallbacks for the main agent and delegation.fallback for child agents. Do not substitute guessed names such as fallback_providers, fallback_chain, or fallback_order.
Example shape (verify against the installed version before applying):
model:
fallbacks: '[{"model":"llama3.2:3b","provider":"ollama-local"}]'
delegation:
fallback: '[{"model":"llama3.2:3b","provider":"ollama-local"}]'
providers:
ollama-local:
base_url: http://localhost:11434/v1
Verify a custom OpenAI-compatible provider
A provider is active only if it is represented in current config.yaml under providers or the active model block. A stale auth.json record containing only a label, URL, status, or secret fingerprint is metadata -- it does not prove the credential still exists or that Hermes can use it.
- Parse
config.yaml; inspectmodel,providers,delegation, andauxiliarywithout printing secrets. - Check
.envfor the expected named variable; report only presence and length. - Treat
auth.jsonfingerprints as clues, not usable credentials. - Call
<base_url>/modelswith the actual credential and verify the requested model ID appears. - Send a minimal chat completion using that exact model.
- Only after both calls succeed report the provider and model as functional.
Verify Ollama as a fallback
- Confirm the binary, service, and listener. Keep it local-only unless remote access was explicitly requested.
- Pull the exact configured model.
- Verify both
/api/tagsand OpenAI-compatible/v1/modelslist it. - Send a deterministic
/v1/chat/completionsprobe and verify returned content. - Confirm
model.fallbacksand, when required,delegation.fallbackreference the same provider/model.
Installing a model is not complete until inference succeeds. A configured provider with an empty model list is not operational.
Config change workflow
User request → check docs/skill/source for real key →
if EXISTS → use `hermes config set` or edit via `hermes config edit`
if NOT FOUND → tell user it doesn't exist, suggest alternatives
When the user corrects you
If the user calls out a made-up config key:
- Acknowledge immediately — don't deflect
- Remove the invalid key from config.yaml via the proper CLI or a direct edit
- Document the lesson
Known missing features (as of 2026-07-05)
| User wants | Hermes has | Workaround |
|---|---|---|
| Automatic provider failover | fallback_providers does NOT exist |
Manual provider switch via CLI, or local ollama for maintenance tasks |
| Multiple fallback providers | Not supported | N/A |
| Provider priority list | Not supported | N/A |
Web tools use_gateway pitfall
Hermes has two config knobs for web tools:
web:
backend: firecrawl # Which web backend to use
use_gateway: false # true = route through Nous Portal gateway
The trap: When web.use_gateway: true (the default or a previously-set value), Hermes routes Firecrawl requests through Nous Portal's gateway — it ignores your FIRECRAWL_API_KEY from .env entirely. If you're not logged into Nous Portal (no OAuth session), web_search and web_extract fail with:
"Web tools are not configured. Set FIRECRAWL_API_KEY for cloud Firecrawl..."
Even though the key IS set and valid. The fix is:
hermes config set web.use_gateway false
Then a new session (/reset) picks up the change. Note: /reset itself does NOT fix this — the problem is the config value, not the session state. Multiple resets without changing the config value will all fail the same way.
Verification: Test the key directly against Firecrawl's API before and after:
source ~/.hermes/.env 2>/dev/null
curl -s -X POST "https://api.firecrawl.dev/v1/search" \
-H "Authorization: Bearer $FIRECRAWL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query":"test","limit":1}'
# Expected: HTTP 200, {"success": true, "data": [...]}
Key learning: web.use_gateway: true is NOT a fallback or override layer — it replaces the local key. If you're self-hosting the Firecrawl key in .env, this must be false. Setting it back to true after switching to Nous Portal OAuth would be correct for that auth mode.
Debugging flow for "web tools not configured"
1. Check .env: grep FIRECRAWL_API_KEY ~/.hermes/.env
→ If missing, add key. If present, proceed.
2. Check config: grep -A2 '^web:' ~/.hermes/config.yaml
→ If use_gateway: true, flip to false via:
hermes config set web.use_gateway false
→ Then /reset or start new session.
3. Test key: direct curl to api.firecrawl.dev (see above)
→ 200 = key valid. Non-200 = key expired/wrong.
4. Verify tools: try web_search or web_extract