Skip to content
Back to skills

Hermes

ASecurity

Building and operating Hermes agents — the SOUL.md / config.yaml / skills profile shape, model and turn-budget config, installing a profile into a Hermes home, and the Telegram gateway with its DM policy. Read when scaffolding with `--framework hermes`, when a Hermes bot needs to answer on Telegram, when deciding what belongs in SOUL.md versus config.yaml, or when a profile isn't loading.

  • 2 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added October 2, 2026
ai-agentsdebuggingbackend

Works with

  • claude code
  • cli

Security analysis

A100/100

Scanned October 2, 2026

npx -y skills add Folken2/nuvel --skill hermes --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Hermes?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Hermes
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/folken2-hermes/badge)](https://www.skillsdirectory.com/skills/folken2-hermes)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: hermes
description: Building and operating Hermes agents — the SOUL.md / config.yaml / skills profile shape, model and turn-budget config, installing a profile into a Hermes home, and the Telegram gateway with its DM policy. Read when scaffolding with `--framework hermes`, when a Hermes bot needs to answer on Telegram, when deciding what belongs in SOUL.md versus config.yaml, or when a profile isn't loading.
---

# Hermes agents

A Hermes agent is not a program you run. It is a **profile directory** the
Hermes runtime loads:

```
<hermes_home>/profiles/<name>/
├── SOUL.md        identity — loaded every turn
├── config.yaml    model, max_turns, platforms, enabled skills
├── .env.example   the secrets the profile needs (copy to .env / export)
└── skills/<slug>/SKILL.md
```

`nuvel new <name> --framework hermes` stamps exactly that. Installing it means
moving the stamped package directory into `<hermes_home>/profiles/<name>/`
(default home `~/.hermes`, overridable with `HERMES_HOME`) — the same layout
`nuvel bots` manages through the `hermes` CLI.

There is no server, no model loop, and no requirements.txt in a Hermes agent.
If you find yourself writing one, you're building an ADK or Buzz agent and
should switch frameworks rather than fight this one.

## SOUL.md vs config.yaml

The split is the thing most worth internalizing, because getting it wrong is
what makes a profile impossible to tune later:

| | SOUL.md | config.yaml |
|---|---|---|
| Holds | who the agent is, what it's for, how it speaks | which model, how many turns, which platforms |
| Changing it | changes behaviour | changes cost, reach, or latency |
| Audience | the model, every single turn | the runtime, at load |

`model.default` never belongs in SOUL.md, and "be concise in Telegram" never
belongs in config.yaml. SOUL.md is loaded on every turn — keep it short. A
600-line SOUL.md is a per-turn tax on every conversation the agent ever has.

## Configuration

```yaml
model:
  default: deepseek/deepseek-v4-flash   # bare id — no LiteLLM provider prefix
  provider: openrouter
agent:
  max_turns: 60
platforms:
  telegram:
    enabled: false
skills:
  enabled: []
```

Two things that trip people up:

- **The model id is bare.** `provider` selects the endpoint, so
  `openrouter/deepseek/deepseek-v4-flash` is wrong here even though that's the
  form nuvel's ADK backend uses for LiteLLM routing. Strip the prefix.
- **`skills.enabled: []` does not mean "no skills."** It's a force-load list.
  Empty is the normal state: every skill under `skills/` stays discoverable by
  description, and bodies load on demand. Name a slug there only when the
  skill must be in context from the first token — each one is a permanent
  prompt cost.

`max_turns` bounds model↔tool round trips per turn. If you see it hit in logs,
the agent is stuck in a loop, not working hard.

Anything set here has a `hermes config set <key> <value>` equivalent
(`model.default`, …). The file is the checked-in starting point; the CLI is
how you change one knob on a running install without editing YAML.

## Skills

Anthropic format: `skills/<slug>/SKILL.md`, YAML frontmatter with `name` and
`description`, then the body. Only frontmatter is loaded up front — that's
what makes fifty skills affordable.

Write the `description` for a reader deciding *whether to open the file*: name
the situation ("Read when an alert fires"), not the topic ("about incidents").
It's the only signal available at selection time.

Skills are shared across the nuvel ecosystem — `nuvel bots skills list` browses
the hub and `nuvel bots create --skills <refs>` installs them straight into
`<hermes_home>/profiles/<bot>/skills/`. A skill written for a Hermes profile
drops into Claude Code or an ADK agent unchanged, as long as it doesn't hard-
code a harness-specific tool name.

## Telegram

`nuvel new <name> --framework hermes --with-telegram` swaps in a config.yaml
with the platform on:

```yaml
platforms:
  telegram:
    enabled: true
    bot_token: ${TELEGRAM_BOT_TOKEN}
    dm_policy: allowlist
    allowed_users: []
    mention_only: true
```

- **`bot_token` stays an env reference.** The token *is* the bot — anyone
  holding it can post as the agent. It belongs in `.env` or a secret manager,
  never in a committed config.
- **`dm_policy: allowlist` is the safe default.** `open` means anyone who
  finds the bot can spend your model budget. Start restricted; widen once you
  know what traffic looks like.
- **`mention_only: true` in groups.** A bot answering every message in a busy
  group is expensive and socially exhausting — the same lesson the Buzz
  backend encodes as `BUZZ_REPLY_POLICY=mention`.

Slack and Teams are ADK-only. `--with-slack` / `--with-teams` are rejected here
rather than silently ignored, as are `--persona` (a Hermes profile already has
a SOUL.md), `--with-composio`, `--workflow`, `--with-eval`, and `--with-acp`
(Hermes owns the runtime, so there's no process to hand an ACP adapter to —
use `--framework buzz` if ACP is what you want).

## Debugging

| Symptom | Look at |
|---|---|
| Profile doesn't appear in `hermes profile list` | it's not under `<hermes_home>/profiles/<name>/`; check `HERMES_HOME` |
| Model calls fail with an auth error | the key env for `model.provider` isn't exported into Hermes' environment |
| "unknown model" from the provider | a provider prefix left on `model.default` |
| Telegram bot silent in a group | `mention_only: true` and nobody @-mentioned it |
| Telegram bot silent in DMs | `dm_policy: allowlist` and the sender's numeric id isn't in `allowed_users` |
| Agent ignores a skill it should have used | the `description` names a topic, not a situation — rewrite it for the selection moment |

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.