docs: add 11 new pages + expand 4 existing pages (26 → 37 total)
New pages (sourced from actual codebase):
- Security: command approval, DM pairing, container isolation, production checklist
- Session Management: resume, export, prune, search, per-platform tracking
- Context Files: AGENTS.md project context, discovery, size limits, security
- Personality: SOUL.md, 14 built-in personalities, custom definitions
- Browser Automation: Browserbase setup, 10 browser tools, stealth mode
- Image Generation: FLUX 2 Pro via FAL, aspect ratios, auto-upscaling
- Provider Routing: OpenRouter sort/only/ignore/order config
- Honcho: AI-native memory integration, setup, peer config
- Home Assistant: HASS setup, 4 HA tools, WebSocket gateway
- Batch Processing: trajectory generation, dataset format, checkpointing
- RL Training: Atropos/Tinker integration, environments, workflow
Expanded pages:
- code-execution: 51 → 195 lines (examples, limits, security, comparison table)
- delegation: 60 → 216 lines (context tips, batch mode, model override)
- cron: 88 → 273 lines (real-world examples, delivery options, expression cheat sheet)
- memory: 98 → 249 lines (best practices, capacity management, examples)
2026-03-05 07:28:41 -08:00
---
sidebar_position: 8
title: "Security"
description: "Security model, dangerous command approval, user authorization, container isolation, and production deployment best practices"
---
# Security
Hermes Agent is designed with a defense-in-depth security model. This page covers every security boundary — from command approval to container isolation to user authorization on messaging platforms.
## Overview
The security model has five layers:
1. **User authorization ** — who can talk to the agent (allowlists, DM pairing)
2. **Dangerous command approval ** — human-in-the-loop for destructive operations
3. **Container isolation ** — Docker/Singularity/Modal sandboxing with hardened settings
4. **MCP credential filtering ** — environment variable isolation for MCP subprocesses
5. **Context file scanning ** — prompt injection detection in project files
## Dangerous Command Approval
Before executing any command, Hermes checks it against a curated list of dangerous patterns. If a match is found, the user must explicitly approve it.
### What Triggers Approval
The following patterns trigger approval prompts (defined in `tools/approval.py` ):
| Pattern | Description |
|---------|-------------|
| `rm -r` / `rm --recursive` | Recursive delete |
| `rm ... /` | Delete in root path |
| `chmod 777` | World-writable permissions |
| `mkfs` | Format filesystem |
| `dd if=` | Disk copy |
| `DROP TABLE/DATABASE` | SQL DROP |
| `DELETE FROM` (without WHERE) | SQL DELETE without WHERE |
| `TRUNCATE TABLE` | SQL TRUNCATE |
| `> /etc/` | Overwrite system config |
| `systemctl stop/disable/mask` | Stop/disable system services |
| `kill -9 -1` | Kill all processes |
| `curl ... \| sh` | Pipe remote content to shell |
| `bash -c` , `python -e` | Shell/script execution via flags |
| `find -exec rm` , `find -delete` | Find with destructive actions |
| Fork bomb patterns | Fork bombs |
:::info
2026-03-06 03:37:05 -08:00
**Container bypass**: When running in `docker` , `singularity` , `modal` , or `daytona` backends, dangerous command checks are **skipped ** because the container itself is the security boundary. Destructive commands inside a container can't harm the host.
docs: add 11 new pages + expand 4 existing pages (26 → 37 total)
New pages (sourced from actual codebase):
- Security: command approval, DM pairing, container isolation, production checklist
- Session Management: resume, export, prune, search, per-platform tracking
- Context Files: AGENTS.md project context, discovery, size limits, security
- Personality: SOUL.md, 14 built-in personalities, custom definitions
- Browser Automation: Browserbase setup, 10 browser tools, stealth mode
- Image Generation: FLUX 2 Pro via FAL, aspect ratios, auto-upscaling
- Provider Routing: OpenRouter sort/only/ignore/order config
- Honcho: AI-native memory integration, setup, peer config
- Home Assistant: HASS setup, 4 HA tools, WebSocket gateway
- Batch Processing: trajectory generation, dataset format, checkpointing
- RL Training: Atropos/Tinker integration, environments, workflow
Expanded pages:
- code-execution: 51 → 195 lines (examples, limits, security, comparison table)
- delegation: 60 → 216 lines (context tips, batch mode, model override)
- cron: 88 → 273 lines (real-world examples, delivery options, expression cheat sheet)
- memory: 98 → 249 lines (best practices, capacity management, examples)
2026-03-05 07:28:41 -08:00
:::
### Approval Flow (CLI)
In the interactive CLI, dangerous commands show an inline approval prompt:
```
⚠️ DANGEROUS COMMAND: recursive delete
rm -rf /tmp/old-project
[o]nce | [s]ession | [a]lways | [d]eny
Choice [o/s/a/D]:
```
The four options:
- **once** — allow this single execution
- **session** — allow this pattern for the rest of the session
- **always** — add to permanent allowlist (saved to `config.yaml` )
- **deny** (default) — block the command
### Approval Flow (Gateway/Messaging)
On messaging platforms, the agent sends the dangerous command details to the chat and waits for the user to reply:
- Reply **yes ** , **y ** , **approve ** , **ok ** , or **go ** to approve
- Reply **no ** , **n ** , **deny ** , or **cancel ** to deny
The `HERMES_EXEC_ASK=1` environment variable is automatically set when running the gateway.
### Permanent Allowlist
Commands approved with "always" are saved to `~/.hermes/config.yaml` :
```yaml
# Permanently allowed dangerous command patterns
command_allowlist:
- rm
- systemctl
```
These patterns are loaded at startup and silently approved in all future sessions.
:::tip
Use `hermes config edit` to review or remove patterns from your permanent allowlist.
:::
## User Authorization (Gateway)
When running the messaging gateway, Hermes controls who can interact with the bot through a layered authorization system.
### Authorization Check Order
The `_is_user_authorized()` method checks in this order:
1. **Per-platform allow-all flag ** (e.g., `DISCORD_ALLOW_ALL_USERS=true` )
2. **DM pairing approved list ** (users approved via pairing codes)
3. **Platform-specific allowlists ** (e.g., `TELEGRAM_ALLOWED_USERS=12345,67890` )
4. **Global allowlist ** (`GATEWAY_ALLOWED_USERS=12345,67890` )
5. **Global allow-all ** (`GATEWAY_ALLOW_ALL_USERS=true` )
6. **Default: deny **
### Platform Allowlists
Set allowed user IDs as comma-separated values in `~/.hermes/.env` :
```bash
# Platform-specific allowlists
TELEGRAM_ALLOWED_USERS=123456789,987654321
DISCORD_ALLOWED_USERS=111222333444555666
WHATSAPP_ALLOWED_USERS=15551234567
SLACK_ALLOWED_USERS=U01ABC123
# Cross-platform allowlist (checked for all platforms)
GATEWAY_ALLOWED_USERS=123456789
# Per-platform allow-all (use with caution)
DISCORD_ALLOW_ALL_USERS=true
# Global allow-all (use with extreme caution)
GATEWAY_ALLOW_ALL_USERS=true
```
:::warning
If **no allowlists are configured ** and `GATEWAY_ALLOW_ALL_USERS` is not set, **all users are denied ** . The gateway logs a warning at startup:
```
No user allowlists configured. All unauthorized users will be denied.
Set GATEWAY_ALLOW_ALL_USERS=true in ~/.hermes/.env to allow open access,
or configure platform allowlists (e.g., TELEGRAM_ALLOWED_USERS=your_id).
```
:::
### DM Pairing System
For more flexible authorization, Hermes includes a code-based pairing system. Instead of requiring user IDs upfront, unknown users receive a one-time pairing code that the bot owner approves via the CLI.
**How it works:**
1. An unknown user sends a DM to the bot
2. The bot replies with an 8-character pairing code
3. The bot owner runs `hermes pairing approve <platform> <code>` on the CLI
4. The user is permanently approved for that platform
**Security features** (based on OWASP + NIST SP 800-63-4 guidance):
| Feature | Details |
|---------|---------|
| Code format | 8-char from 32-char unambiguous alphabet (no 0/O/1/I) |
| Randomness | Cryptographic (`secrets.choice()` ) |
| Code TTL | 1 hour expiry |
| Rate limiting | 1 request per user per 10 minutes |
| Pending limit | Max 3 pending codes per platform |
| Lockout | 5 failed approval attempts → 1-hour lockout |
| File security | `chmod 0600` on all pairing data files |
| Logging | Codes are never logged to stdout |
**Pairing CLI commands:**
```bash
# List pending and approved users
hermes pairing list
# Approve a pairing code
hermes pairing approve telegram ABC12DEF
# Revoke a user's access
hermes pairing revoke telegram 123456789
# Clear all pending codes
hermes pairing clear-pending
```
**Storage:** Pairing data is stored in `~/.hermes/pairing/` with per-platform JSON files:
- `{platform}-pending.json` — pending pairing requests
- `{platform}-approved.json` — approved users
- `_rate_limits.json` — rate limit and lockout tracking
## Container Isolation
When using the `docker` terminal backend, Hermes applies strict security hardening to every container.
### Docker Security Flags
Every container runs with these flags (defined in `tools/environments/docker.py` ):
```python
_SECURITY_ARGS = [
"--cap-drop", "ALL", # Drop ALL Linux capabilities
"--security-opt", "no-new-privileges", # Block privilege escalation
"--pids-limit", "256", # Limit process count
"--tmpfs", "/tmp:rw,nosuid,size=512m", # Size-limited /tmp
"--tmpfs", "/var/tmp:rw,noexec,nosuid,size=256m", # No-exec /var/tmp
"--tmpfs", "/run:rw,noexec,nosuid,size=64m", # No-exec /run
]
```
### Resource Limits
Container resources are configurable in `~/.hermes/config.yaml` :
```yaml
terminal:
backend: docker
docker_image: "nikolaik/python-nodejs:python3.11-nodejs20"
container_cpu: 1 # CPU cores
container_memory: 5120 # MB (default 5GB)
container_disk: 51200 # MB (default 50GB, requires overlay2 on XFS)
container_persistent: true # Persist filesystem across sessions
```
### Filesystem Persistence
- **Persistent mode** (`container_persistent: true` ): Bind-mounts `/workspace` and `/root` from `~/.hermes/sandboxes/docker/<task_id>/`
- **Ephemeral mode** (`container_persistent: false` ): Uses tmpfs for workspace — everything is lost on cleanup
:::tip
2026-03-06 03:37:05 -08:00
For production gateway deployments, use `docker` , `modal` , or `daytona` backend to isolate agent commands from your host system. This eliminates the need for dangerous command approval entirely.
docs: add 11 new pages + expand 4 existing pages (26 → 37 total)
New pages (sourced from actual codebase):
- Security: command approval, DM pairing, container isolation, production checklist
- Session Management: resume, export, prune, search, per-platform tracking
- Context Files: AGENTS.md project context, discovery, size limits, security
- Personality: SOUL.md, 14 built-in personalities, custom definitions
- Browser Automation: Browserbase setup, 10 browser tools, stealth mode
- Image Generation: FLUX 2 Pro via FAL, aspect ratios, auto-upscaling
- Provider Routing: OpenRouter sort/only/ignore/order config
- Honcho: AI-native memory integration, setup, peer config
- Home Assistant: HASS setup, 4 HA tools, WebSocket gateway
- Batch Processing: trajectory generation, dataset format, checkpointing
- RL Training: Atropos/Tinker integration, environments, workflow
Expanded pages:
- code-execution: 51 → 195 lines (examples, limits, security, comparison table)
- delegation: 60 → 216 lines (context tips, batch mode, model override)
- cron: 88 → 273 lines (real-world examples, delivery options, expression cheat sheet)
- memory: 98 → 249 lines (best practices, capacity management, examples)
2026-03-05 07:28:41 -08:00
:::
## Terminal Backend Security Comparison
| Backend | Isolation | Dangerous Cmd Check | Best For |
|---------|-----------|-------------------|----------|
| **local ** | None — runs on host | ✅ Yes | Development, trusted users |
| **ssh ** | Remote machine | ✅ Yes | Running on a separate server |
| **docker ** | Container | ❌ Skipped (container is boundary) | Production gateway |
| **singularity ** | Container | ❌ Skipped | HPC environments |
| **modal ** | Cloud sandbox | ❌ Skipped | Scalable cloud isolation |
2026-03-06 03:37:05 -08:00
| **daytona ** | Cloud sandbox | ❌ Skipped | Persistent cloud workspaces |
docs: add 11 new pages + expand 4 existing pages (26 → 37 total)
New pages (sourced from actual codebase):
- Security: command approval, DM pairing, container isolation, production checklist
- Session Management: resume, export, prune, search, per-platform tracking
- Context Files: AGENTS.md project context, discovery, size limits, security
- Personality: SOUL.md, 14 built-in personalities, custom definitions
- Browser Automation: Browserbase setup, 10 browser tools, stealth mode
- Image Generation: FLUX 2 Pro via FAL, aspect ratios, auto-upscaling
- Provider Routing: OpenRouter sort/only/ignore/order config
- Honcho: AI-native memory integration, setup, peer config
- Home Assistant: HASS setup, 4 HA tools, WebSocket gateway
- Batch Processing: trajectory generation, dataset format, checkpointing
- RL Training: Atropos/Tinker integration, environments, workflow
Expanded pages:
- code-execution: 51 → 195 lines (examples, limits, security, comparison table)
- delegation: 60 → 216 lines (context tips, batch mode, model override)
- cron: 88 → 273 lines (real-world examples, delivery options, expression cheat sheet)
- memory: 98 → 249 lines (best practices, capacity management, examples)
2026-03-05 07:28:41 -08:00
## MCP Credential Handling
MCP (Model Context Protocol) server subprocesses receive a **filtered environment ** to prevent accidental credential leakage.
### Safe Environment Variables
Only these variables are passed through from the host to MCP stdio subprocesses:
```
PATH, HOME, USER, LANG, LC_ALL, TERM, SHELL, TMPDIR
```
Plus any `XDG_*` variables. All other environment variables (API keys, tokens, secrets) are **stripped ** .
Variables explicitly defined in the MCP server's `env` config are passed through:
```yaml
mcp_servers:
github:
command: "npx"
args: ["-y", "@modelcontextprotocol/server -github"]
env:
GITHUB_PERSONAL_ACCESS_TOKEN: "ghp_..." # Only this is passed
```
### Credential Redaction
Error messages from MCP tools are sanitized before being returned to the LLM. The following patterns are replaced with `[REDACTED]` :
- GitHub PATs (`ghp_...` )
- OpenAI-style keys (`sk-...` )
- Bearer tokens
- `token=` , `key=` , `API_KEY=` , `password=` , `secret=` parameters
### Context File Injection Protection
Context files (AGENTS.md, .cursorrules, SOUL.md) are scanned for prompt injection before being included in the system prompt. The scanner checks for:
- Instructions to ignore/disregard prior instructions
- Hidden HTML comments with suspicious keywords
- Attempts to read secrets (`.env` , `credentials` , `.netrc` )
- Credential exfiltration via `curl`
- Invisible Unicode characters (zero-width spaces, bidirectional overrides)
Blocked files show a warning:
```
[BLOCKED: AGENTS.md contained potential prompt injection (prompt_injection). Content not loaded.]
```
## Best Practices for Production Deployment
### Gateway Deployment Checklist
1. **Set explicit allowlists ** — never use `GATEWAY_ALLOW_ALL_USERS=true` in production
2. **Use container backend ** — set `terminal.backend: docker` in config.yaml
3. **Restrict resource limits ** — set appropriate CPU, memory, and disk limits
4. **Store secrets securely ** — keep API keys in `~/.hermes/.env` with proper file permissions
5. **Enable DM pairing ** — use pairing codes instead of hardcoding user IDs when possible
6. **Review command allowlist ** — periodically audit `command_allowlist` in config.yaml
7. **Set `MESSAGING_CWD` ** — don't let the agent operate from sensitive directories
8. **Run as non-root ** — never run the gateway as root
9. **Monitor logs ** — check `~/.hermes/logs/` for unauthorized access attempts
10. **Keep updated ** — run `hermes update` regularly for security patches
### Securing API Keys
```bash
# Set proper permissions on the .env file
chmod 600 ~/.hermes/.env
# Keep separate keys for different services
# Never commit .env files to version control
```
### Network Isolation
For maximum security, run the gateway on a separate machine or VM:
```yaml
terminal:
backend: ssh
ssh_host: "agent-worker.local"
ssh_user: "hermes"
ssh_key: "~/.ssh/hermes_agent_key"
```
This keeps the gateway's messaging connections separate from the agent's command execution.