Introduction
The first decision every agent makes before answering anything is who is speaking. With Hermes, that decision is not buried in code or scattered across config flags. It lives in one file you control: ~/.hermes/SOUL.md. Whatever you put in this file becomes slot one of the system prompt, and every response after that flows downstream from it.
Key Concepts
- SOUL.md: A plain Markdown file at
~/.hermes/SOUL.md(or$HERMES_HOME/SOUL.md) that holds the agent's identity. - Slot #1: The first position of the system prompt, traditionally reserved for the agent's persona. Hermes replaces its built-in default identity with whatever SOUL.md contains.
- HERMES_HOME: The environment variable that overrides the default location. If unset, Hermes uses
~/.hermes/. - Identity layer: A separate concern from project context (AGENTS.md), tools, memory, or skills.
Real World Context
A team using Hermes wants the assistant to default to direct, no-hype responses across every repo and every session. They could ask each developer to paste a long system prompt every session, or to repeat the same paragraph in every AGENTS.md. Both are friction. With SOUL.md, they write the identity once at ~/.hermes/SOUL.md and the voice carries everywhere. No copy-pasting, no per-project drift.
Deep Dive
Hermes loads SOUL.md from a single fixed location: HERMES_HOME (defaulting to ~/.hermes). It does not search the current working directory, and it does not fall back to environment variables for content. The lookup is intentionally narrow because identity is global: you do not want one repo's SOUL.md silently overriding another's.
The file itself is plain Markdown. Hermes does not parse it for special structure. There is no YAML frontmatter, no required headers, no schema. You can write prose, bullet lists, sub-sections, or even a one-liner. The content is injected verbatim into the system prompt.
A minimal but useful SOUL.md might look like this:
markdown# Personality You are a direct, pragmatic engineering assistant. ## Style - Prefer short answers over long ones. - Admit uncertainty rather than fabricate. - No hype, no sycophancy, no emoji.
When SOUL.md is empty, whitespace-only, or unreadable, Hermes falls back to the built-in default identity. There is no error and no crash. This is the gentle path: a misconfigured SOUL.md still leaves you with a working agent.
Two related guards run at load time. First, prompt-injection scanning checks the file for known injection patterns before placing the content in the prompt. Second, size truncation trims the content if the file is unreasonably large, rather than letting it eat your context window. These guards exist because SOUL.md is a high-privilege surface: anything you write goes directly into the agent's identity slot. Treat the file the same way you treat your shell rc files. Edit deliberately.
Common Pitfalls
- Mistaking SOUL.md for project context: SOUL.md is global identity. Project-specific instructions (commands, repo conventions, file paths) belong in
AGENTS.mdinside the repo, not in your global SOUL.md. - Editing SOUL.md mid-session expecting instant effect: The file is read at session start. Changes apply on the next launch.
Best Practices
- Keep SOUL.md small and stable: Identity does not need to change every week. Twenty sharp lines beat two pages of vague description.
- Version-control SOUL.md alongside your dotfiles: Like
~/.zshrcor~/.gitconfig, SOUL.md is personal config. A dotfiles repo gives you backup, history, and easy sync between machines.
Summary
- SOUL.md is the agent's identity file at
~/.hermes/SOUL.md(or$HERMES_HOME/SOUL.md). - It is plain Markdown, loaded once at session start into slot #1 of the system prompt.
- Empty or missing files fall back to Hermes's built-in default identity; oversized files are truncated.
- It is global, not project-scoped. Repo-specific guidance goes in AGENTS.md.
Code Examples
# Locate (or create) your SOUL.md
ls -la ~/.hermes/SOUL.md 2>/dev/null || touch ~/.hermes/SOUL.md
# Override the location with HERMES_HOME if you keep a custom dotfiles layout
export HERMES_HOME="$HOME/dotfiles/hermes"
# Now Hermes loads $HOME/dotfiles/hermes/SOUL.md instead
# A misconfigured file is fine: empty SOUL.md just falls back to the default identity
echo "" > ~/.hermes/SOUL.md && hermes