Skip to main content

Command approval

Before executing any terminal command, Hermes checks it against a list of dangerous patterns. When a match is found, execution is paused and you’re asked to approve or deny.

How detection works

The dangerous command detector (tools/approval.py) scans every shell command against a set of regex patterns before it runs. Matched categories include:
Container backends (Docker, Singularity, Modal, Daytona) bypass approval entirely — commands run inside the isolated container without prompting. Use a container backend when you want unrestricted agent execution without approval interruptions.

Approval choices

When a dangerous command is detected, you’ll be presented with:
  • Once — allow this specific command, this time only
  • Session — approve this command pattern for the rest of the current session
  • Always — add the pattern to your permanent allowlist (saved to config.yaml)
  • Deny — block the command; the agent is told it was rejected and must not retry
In messaging platforms (Telegram, Discord, Slack), the approval prompt is sent as a message with inline buttons.

Approval modes

Configure the approval mode in ~/.hermes/config.yaml:
Smart mode is inspired by the OpenAI Codex Smart Approvals guardian subagent. It uses your configured auxiliary model (fast/cheap recommended) to assess whether a flagged command is a false positive before interrupting you.

Command allowlist

Patterns approved with “Always” are saved to the command_allowlist in config.yaml:
Entries use human-readable description strings that match the pattern descriptions in the table above. Approved patterns skip the prompt for all future sessions until you remove them from the list.
Be selective about permanent allowlist entries. Broadly allowing “recursive delete” means any rm -r command will run without a prompt, including paths you may not expect.
To remove an allowlist entry, edit ~/.hermes/config.yaml directly and remove the line from command_allowlist.

Tirith pre-exec scanning

Hermes optionally integrates with tirith for additional pre-execution security scanning. Tirith detects homograph URLs, pipe-to-shell patterns, terminal injection, and environment manipulation. Install tirith:
Configure in config.yaml:
When tirith issues a warn finding, it’s combined with the dangerous command check into a single approval prompt. When tirith issues a block, the command is rejected immediately with no approval option.

API key security

All API keys are stored in ~/.hermes/.env, which is created with 0600 permissions (owner read/write only). The directory ~/.hermes/ itself uses 0700 permissions.
Keys are never written to config.yaml, never committed to version control, and never exposed to the agent in tool outputs. By default, Hermes redacts secrets from terminal output:
Set redact_secrets: false only for debugging authentication issues.

Security for messaging platforms

Allowed users

Every platform supports an allowlist of user IDs. Messages from users not on the list are silently ignored.
To allow all users on all platforms (only for trusted internal deployments):
Leaving GATEWAY_ALLOW_ALL_USERS=true on a public bot exposes your agent — and any tools it has access to — to anyone who finds your bot. Only use this on private or internal deployments.

DM pairing

DM pairing is a code-based authorization mechanism. When enabled, users must send a one-time pairing code (generated out-of-band) before they can interact with the bot. This is useful for giving access to people without knowing their platform user IDs in advance. Pairing state is managed by the PairingStore and persists across gateway restarts.
By default the Telegram bot responds to anyone who messages it unless TELEGRAM_ALLOWED_USERS is set. Always set an allowlist for bots on public Telegram.For group chats, the bot only responds to messages that mention it or are direct commands. Each participant in a group gets their own isolated session by default (group_sessions_per_user: true in config.yaml).
In server channels the bot requires an @mention to respond by default:
DMs do not require a mention.
Slack access is controlled by SLACK_ALLOWED_USERS. The bot is invoked via /hermes <subcommand> or by mentioning it in a channel.Required OAuth scopes constrain what the bot can see: it only reads channels and DMs it is invited to.
The EMAIL_ALLOWED_USERS list is compared to the From: header of incoming emails. Only emails from listed addresses trigger the agent.For Gmail, use an App Password rather than your account password — App Passwords can be revoked independently.

Container isolation

Running Hermes with a container terminal backend (Docker, Singularity, Modal, or Daytona) provides strong isolation: the agent executes all terminal commands inside the container, not on your host machine.
Container backends are the recommended security posture for giving Hermes broad tool access. Dangerous command approval is automatically bypassed inside containers because the container itself provides the isolation boundary.
Configure the terminal backend in ~/.hermes/config.yaml:

What isolation provides

  • Agent cannot read ~/.hermes/.env (API keys stay on the host)
  • Agent cannot modify its own code
  • Destructive commands are contained within the container filesystem
  • Container is destroyed or reset at the end of the session (when container_persistent: false)

SSH remote execution

Alternatively, run terminal commands on a remote server via SSH. The agent code stays on your local machine; only commands are forwarded:
This is a strong isolation model: the agent cannot reach your local filesystem or API keys file.

Serverless backends

Modal and Daytona provide serverless persistence — the environment hibernates when idle and wakes on demand. This costs nearly nothing between sessions while still providing full container isolation.