# How the Dangerous Command Approval System Works in Hermes Agent

> Discover how Hermes Agent's dangerous command approval system safeguards your terminal. Learn about regex pattern matching, allow-lists, and user approval before command execution.

- Repository: [Nous Research/hermes-agent](https://github.com/NousResearch/hermes-agent)
- Tags: deep-dive
- Published: 2026-03-09

---

**Hermes Agent’s dangerous command approval system intercepts destructive shell commands in the terminal tool using regex pattern matching, maintains thread-safe session and permanent allow-lists, and prompts users for interactive or asynchronous approval before execution.**

The Hermes Agent repository implements a robust safety layer to prevent accidental data loss when executing shell commands through its terminal tool. This dangerous command approval system analyzes every command against predefined destructive patterns, manages user consent through multiple approval pathways, and persists safety preferences across sessions. Understanding this mechanism is essential for developers integrating Hermes Agent into automated workflows or extending its tool registry.

## Architecture of the Dangerous Command Approval System

The system centers on two primary modules: [`tools/approval.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/approval.py) serves as the single source of truth for dangerous command detection and state management, while [`tools/terminal_tool.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/terminal_tool.py) acts as the execution gateway that invokes these safety checks.

### Core Components and Data Structures

The [`tools/approval.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/approval.py) module defines **`DANGEROUS_PATTERNS`**, a list of tuples containing compiled regex patterns and human-readable descriptions. These patterns detect operations like recursive deletion, filesystem formatting, or permission changes on sensitive paths.

State management relies on three thread-safe data structures:

- **`_session_approved`**: Tracks patterns approved for the current process lifetime
- **`_permanent_approved`**: Maintains patterns approved indefinitely 
- **`_pending`**: Queues approval requests for asynchronous gateway processing

The [`tools/terminal_tool.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/terminal_tool.py) module implements **`_check_dangerous_command()`**, a thin wrapper that calls `approval.check_dangerous_command()` before executing any shell command.

## Command Detection Flow in terminal_tool.py

When a command arrives at the terminal tool, the system executes a multi-stage validation pipeline to determine if user intervention is required.

### Pattern Matching Against Dangerous Commands

The **`detect_dangerous_command()`** function in [`tools/approval.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/approval.py) iterates through `DANGEROUS_PATTERNS`, testing each regex against the normalized command string. When a match occurs, the function returns a tuple containing a boolean flag, the pattern key, and the description.

```python
from tools.approval import detect_dangerous_command

is_dangerous, pattern_key, description = detect_dangerous_command("rm -rf /home/user/data")

# Returns: (True, "rm", "recursive deletion")

```

### Environment-Based Bypasses for Containerized Execution

Container-based backends receive automatic approval because they execute in isolated environments. The [`tools/terminal_tool.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/terminal_tool.py) module checks the `env_type` parameter and bypasses dangerous command detection for `docker`, `singularity`, `modal`, and `daytona` environments.

```python
if env_type in ("docker", "singularity", "modal", "daytona"):
    return {"approved": True, "message": None}

```

### Session State Verification

For local execution environments, the system checks the **`HERMES_SESSION_KEY`** against `_session_approved`. If the user previously approved this pattern during the current session, execution proceeds without prompting.

```python
if is_approved(session_key, pattern_key):
    return {"approved": True, "message": None}

```

## Approval Pathways and User Interaction

When a dangerous command is detected and not pre-approved, Hermes Agent supports three distinct pathways for obtaining user consent.

### Interactive CLI Prompting

In command-line interface mode, **`prompt_dangerous_approval()`** renders an ASCII warning displaying the command, the risk description, and four options: **[o]nce**, **[s]ession**, **[a]lways**, or **[d]eny**. The function uses `prompt_toolkit` for robust input handling.

Selecting **session** adds the pattern to `_session_approved`, while **always** triggers `approve_permanent()` and persists the choice to `~/.hermes/config.yaml` under the `command_allowlist` key.

### Asynchronous Gateway Approval

For messenger or gateway integrations (Telegram, Discord, etc.), the system uses environment variables **`HERMES_GATEWAY_SESSION`** or **`HERMES_EXEC_ASK`** to detect async contexts. Instead of blocking for input, `check_dangerous_command()` calls **`submit_pending()`** to queue the request and returns a payload with `"status": "approval_required"`.

The gateway retrieves the pending request using **`pop_pending()`** when the user responds, allowing the command to proceed or abort based on the remote approval decision.

### Force Execution Mode

After obtaining user consent through any pathway, subsequent calls to `terminal_tool()` can pass **`force=True`** to bypass the approval check entirely. This flag indicates that the user has already explicitly approved this specific execution context.

## Permanent Allow-List Persistence

The dangerous command approval system maintains continuity across application restarts through a persistent configuration store.

### Configuring command_allowlist in config.yaml

When a user selects the **always** option during approval, the system invokes **`approve_permanent(pattern_key)`**, which adds the pattern to an in-memory set and triggers **`save_permanent_allowlist()`**. This function writes to `~/.hermes/config.yaml`, appending the pattern key to the `command_allowlist` list.

```python
from tools.approval import approve_permanent, save_permanent_allowlist, load_permanent_allowlist

# Permanently approve the 'rm' pattern

approve_permanent('rm')
save_permanent_allowlist(load_permanent_allowlist() | {'rm'})

```

### State Hydration on Startup

During initialization, [`tools/approval.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/approval.py) calls **`load_permanent_allowlist()`**, which reads `~/.hermes/config.yaml` and populates `_permanent_approved`. This ensures that previously approved dangerous command patterns remain authorized across system restarts without requiring re-prompting.

## Practical Implementation Examples

### Manually Invoking the Approval Check

For testing or custom tool development, you can directly call the approval detection logic:

```python
from tools.approval import check_dangerous_command

info = check_dangerous_command(
    command="chmod 777 -R /",
    env_type="local",
)

print(info)

# Output: {'approved': False, 'message': '...', 'status': 'approval_required', ...}

```

### Bypassing Approval After Explicit Consent

When building automation that has already obtained user approval, use the force flag to skip redundant checks:

```python

# First call - triggers approval prompt

res1 = terminal_tool(command="rm -rf /tmp/old_data")

# After user approves "once", second call with force=True

res2 = terminal_tool(command="rm -rf /tmp/old_data", force=True)

```

### Adding Patterns to Permanent Allow-List Programmatically

For enterprise deployments or automated setup scripts:

```python
from tools.approval import approve_permanent, save_permanent_allowlist, load_permanent_allowlist

# Approve specific pattern permanently

approve_permanent('chmod_recursive')
save_permanent_allowlist(load_permanent_allowlist() | {'chmod_recursive'})

```

## Key Source Files

| File | Description | Key Functions |
|------|-------------|---------------|
| [`tools/approval.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/approval.py) | Central authority for dangerous command detection and approval state management | `detect_dangerous_command()`, `check_dangerous_command()`, `approve_permanent()`, `save_permanent_allowlist()`, `load_permanent_allowlist()`, `submit_pending()`, `pop_pending()` |
| [`tools/terminal_tool.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/terminal_tool.py) | Terminal tool implementation that invokes approval checks before execution | `_check_dangerous_command()`, `terminal_tool()` |
| [`hermes_cli/config.py`](https://github.com/NousResearch/hermes-agent/blob/main/hermes_cli/config.py) | Configuration management for persistent allow-lists | Handles `command_allowlist` in `~/.hermes/config.yaml` |
| [`tools/registry.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/registry.py) | Tool schema registration (exposes terminal tool to LLM) | Terminal tool registration |

## Summary

- **Centralized Detection**: The [`tools/approval.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/approval.py) module maintains `DANGEROUS_PATTERNS` and provides `detect_dangerous_command()` to identify destructive operations before execution.
- **Multi-Layered Approval**: The system supports interactive CLI prompts, asynchronous gateway approvals for messaging platforms, and automatic bypasses for containerized environments.
- **Persistent State Management**: User preferences for "session" or "always" approvals are tracked in thread-safe sets and persisted to `~/.hermes/config.yaml` via `save_permanent_allowlist()`.
- **Integration Point**: [`tools/terminal_tool.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/terminal_tool.py) calls `_check_dangerous_command()` before executing any shell command, ensuring the dangerous command approval system gates all local execution.

## Frequently Asked Questions

### How does Hermes Agent detect dangerous commands in the terminal tool?

Hermes Agent uses regex pattern matching defined in [`tools/approval.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/approval.py). The `detect_dangerous_command()` function iterates through `DANGEROUS_PATTERNS`, testing each compiled regex against the command string. When a match occurs, the system identifies the specific risk (such as recursive deletion or permission changes) and triggers the approval workflow before allowing [`tools/terminal_tool.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/terminal_tool.py) to execute the command.

### What happens when a dangerous command is detected in a Docker or container environment?

Containerized environments including `docker`, `singularity`, `modal`, and `daytona` bypass the dangerous command approval system entirely. In [`tools/terminal_tool.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/terminal_tool.py), the `_check_dangerous_command()` function checks the `env_type` parameter and returns immediate approval for these isolated environments, as the container boundary provides sufficient protection against system-wide damage.

### How does the permanent allow-list work across application restarts?

When a user selects the "always" option during approval, the system calls `approve_permanent()` in [`tools/approval.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/approval.py), which adds the pattern key to an in-memory set and triggers `save_permanent_allowlist()`. This function writes the pattern to the `command_allowlist` key in `~/.hermes/config.yaml`. On startup, `load_permanent_allowlist()` reads this configuration file and rehydrates the `_permanent_approved` set, ensuring approved patterns persist across restarts without requiring re-prompting.

### Can the dangerous command approval system be bypassed programmatically?

Yes, after obtaining explicit user consent, developers can pass `force=True` to the `terminal_tool()` function in [`tools/terminal_tool.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/terminal_tool.py) to skip the approval check entirely. Additionally, the `check_dangerous_command()` function in [`tools/approval.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/approval.py) can be imported and called directly for custom validation logic, or specific patterns can be added to the permanent allow-list programmatically using `approve_permanent()` and `save_permanent_allowlist()`.