Configuration
Hermes is configured by three files under /root/.hermes/:
| File | What it is |
|---|---|
config.yaml | LLM provider + model picker |
.env | Channel adapter credentials |
SOUL.md | Free-form persona / system prompt |
skills/ | Drop-in skill folders (loaded at gateway start) |
intern-server writes the first two from config.json when it switches a device to Hermes. SOUL.md is a free-form persona file you can edit by hand — intern-server only touches a single pinned block at the top.
All files are owned by root and mode 600 (config.yaml, .env) or 644 (SOUL.md).
config.yaml
The format intern-server writes:
model:
default: 'claude-opus-4-6' # picked by user at setup
provider: autonomous
providers:
autonomous:
name: Autonomous
base_url: 'https://<your-llm-base-url>'
api_key: '<scoped-llm-key>'
transport: anthropic_messages
discover_models: false # do NOT let Hermes auto-discover — the proxy
# doesn't expose an OpenAI-style /models route
models:
claude-opus-4-6:
context_length: 500000
claude-haiku-4-5:
context_length: 200000
minimax/minimax-m2.7:
context_length: 200000
z-ai/glm-5.1:
context_length: 200000
qwen/qwen3.6-plus:
context_length: 200000
A few things to know about this file:
model.defaultis what/modelin the Hermes Telegram chat picks first. It must be a key in themodels:map.providers.autonomous.transport: anthropic_messagesis required — the Autonomous LLM proxy speaks the Anthropic Messages API shape, not OpenAI's.discover_models: falseis critical. Without it, Hermes hits the provider's/modelsendpoint at startup; since the proxy doesn't expose that route, the model picker would end up empty.models:map — the keys are model IDs (any YAML-safe id: alnum,-,/,.). Hermes usescontext_lengthto size token budgets per entry.- All string values are single-quoted in the generated file so colons / hashes in API keys don't accidentally start a YAML map.
Live model list
The list above is the built-in fallback. At setup time, intern-server fetches the live model roster from the Autonomous proxy and writes one entry per live model. A periodic loop reconciles the models: map every 30 minutes:
- New model ids are inserted with
context_length= upstreamcontextWindow(or200000when omitted). - Existing entries'
context_lengthis refreshed when it differs. - Existing keys are never removed — entries you added by hand stick around.
hermes-gatewayis restarted only when the file actually changed.
If you want to force a re-sync without waiting, restart intern-server.
Editing by hand
Editing config.yaml directly is fine for keys outside providers.autonomous.models — top-level model.*, agent.*, skills.* etc. are preserved by the YAML roundtrip the sync loop uses.
sudo nano /root/.hermes/config.yaml
sudo systemctl restart hermes-gateway
If you edit a models.<key>.context_length, the next sync tick may overwrite it back to the upstream value. To pin a custom value, edit it on the upstream side (i.e., the Autonomous proxy roster), not here.
.env
Channel credentials, one platform per group of vars. Adapters whose required vars are missing are simply not registered at gateway start — so a Telegram-only device only writes the Telegram block.
# Telegram — bot token + allowed user(s) + default outbound destination
TELEGRAM_BOT_TOKEN=123456:abc...
TELEGRAM_ALLOWED_USERS=12345678
TELEGRAM_HOME_CHANNEL=12345678
# Slack — bot token + app token (Socket Mode) + allowed user
SLACK_BOT_TOKEN=xoxb-...
SLACK_APP_TOKEN=xapp-...
SLACK_ALLOWED_USERS=U01ABCDEFG
# Discord — bot token + allowed user(s)
DISCORD_BOT_TOKEN=...
DISCORD_ALLOWED_USERS=123456789012345678
A few details on what intern-server writes here:
TELEGRAM_ALLOWED_USERSis the auth filter;TELEGRAM_HOME_CHANNELis the default destination for outbound messages. For single-user setups they're set to the same value.- Slack requires both
SLACK_BOT_TOKENandSLACK_APP_TOKEN— without the app token, Socket Mode won't open. - Discord uses
discord_user_idfromconfig.jsonas the allowed-users list.discord_guild_idis not written here (the reference upstream.envdoesn't include it). - WhatsApp is intentionally not written. Hermes upstream doesn't ship a WhatsApp adapter;
/api/device/setuprejects WhatsApp creds whenactive_agent=hermes.
Updating channels without a full swap
If a Hermes-active device gets /api/device/channel (or /api/device/setup with the same active_agent), intern-server only:
- Re-writes
~/.hermes/.envfrom the new payload. - Restarts
hermes-gateway.
Skipping the install check, the model-list refresh, the SOUL.md edit, and the skill download — the only difference is the adapter env vars.
SOUL.md
This is Hermes's persona / system prompt file — the equivalent of OpenClaw's AGENTS.md. It's a free-form Markdown document; Hermes loads the whole thing at session start.
intern-server only manages a single block at the top:
<!-- DO NOT REMOVE -->
**MANDATORY:** Before ANY reply, check `skills/autonomous-intern-onboarding/` — read its SKILL.md and follow instructions.
---
<your free-form persona content below>
Everything below the --- separator is yours. The <!-- DO NOT REMOVE --> marker is how intern-server detects the pinned block on re-runs:
- If the exact current block is already present, the file isn't touched.
- If an older pinned block is present (different skill name etc.), it's replaced.
- If no marker is found, the new block is prepended.
hermes-gateway is restarted whenever the block is touched, so the next conversation picks up the new persona.
Skills
Skills live under /root/.hermes/skills/<skill-name>/. Each is a folder with a SKILL.md and any supporting files (Python modules, prompts, examples). The autonomous-intern-onboarding skill is downloaded from autonomous-ecm/intern-skills every time intern-server switches the device to Hermes.
To add your own:
sudo mkdir -p /root/.hermes/skills/my-skill
sudo nano /root/.hermes/skills/my-skill/SKILL.md
sudo systemctl restart hermes-gateway
There's no registry to update — Hermes scans the directory on each gateway start.
Switching back to OpenClaw
Symmetric to the install steps, but in reverse:
ssh system@172.168.20.145
# 1. Stop hermes-gateway
sudo systemctl stop hermes-gateway
sudo systemctl disable hermes-gateway
systemctl is-active hermes-gateway # → inactive
# 2. Start OpenClaw (the unit is already installed and its config is intact)
sudo systemctl enable --now openclaw
systemctl is-active openclaw # → active
# 3. Flip active_agent
sudo jq '.active_agent = "openclaw"' /root/config/config.json | \
sudo tee /root/config/config.json.tmp > /dev/null && \
sudo mv /root/config/config.json.tmp /root/config/config.json
~/.hermes/ is left intact — files stay on disk so a later swap back to Hermes is fast. If you want to wipe it for a from-scratch reinstall, sudo rm -rf /root/.hermes && sudo apt-get remove --purge hermes (then re-run Manual Install).
File-level checklist
A healthy Hermes device should pass all of these:
ls -l /root/.hermes/config.yaml /root/.hermes/.env /root/.hermes/SOUL.md
# config.yaml + .env: -rw------- root root (mode 600)
# SOUL.md: -rw-r--r-- root root (mode 644)
sudo head -1 /root/.hermes/SOUL.md
# <!-- DO NOT REMOVE -->
sudo grep -c "context_length" /root/.hermes/config.yaml
# ≥ 2 (one per model entry)
sudo grep -E "^TELEGRAM_BOT_TOKEN=|^SLACK_BOT_TOKEN=|^DISCORD_BOT_TOKEN=" /root/.hermes/.env
# At least one match
If any of these fail, the corresponding section above is where to look.