Skip to main content

Configuration

Hermes is configured by three files under /root/.hermes/:

FileWhat it is
config.yamlLLM provider + model picker
.envChannel adapter credentials
SOUL.mdFree-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.default is what /model in the Hermes Telegram chat picks first. It must be a key in the models: map.
  • providers.autonomous.transport: anthropic_messages is required — the Autonomous LLM proxy speaks the Anthropic Messages API shape, not OpenAI's.
  • discover_models: false is critical. Without it, Hermes hits the provider's /models endpoint 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 uses context_length to 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 = upstream contextWindow (or 200000 when omitted).
  • Existing entries' context_length is refreshed when it differs.
  • Existing keys are never removed — entries you added by hand stick around.
  • hermes-gateway is 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_USERS is the auth filter; TELEGRAM_HOME_CHANNEL is the default destination for outbound messages. For single-user setups they're set to the same value.
  • Slack requires both SLACK_BOT_TOKEN and SLACK_APP_TOKEN — without the app token, Socket Mode won't open.
  • Discord uses discord_user_id from config.json as the allowed-users list. discord_guild_id is not written here (the reference upstream .env doesn't include it).
  • WhatsApp is intentionally not written. Hermes upstream doesn't ship a WhatsApp adapter; /api/device/setup rejects WhatsApp creds when active_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:

  1. Re-writes ~/.hermes/.env from the new payload.
  2. 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.