Skip to content

Configuration

Stephen Cross edited this page Jun 7, 2026 · 5 revisions

Configuration

Config Location

Directory (preferred):

~/.hermes/custom-dangerous-patterns/

All *.yaml files in the directory are loaded and merged alphabetically. Most CLI write commands (enable, disable, add without --target) write delta entries to 99-custom.yaml — user-created files are never modified.

Two exceptions:

  • remove deletes entries directly from source YAML files
  • add --target <filename> writes to the specified file

Single file (fallback):

~/.hermes/custom-dangerous-patterns.yaml

Combined mode: When both the directory and the sibling .yaml file exist, both are loaded and merged (directory files take precedence via dedup).

Override with env var:

export HERMES_CUSTOM_PATTERNS_PATH=/path/to/config.yaml

# Or point to a directory:
export HERMES_CUSTOM_PATTERNS_PATH=/path/to/config-directory/

CLI Management

The hermes custom-dangerous-patterns CLI allows managing patterns without editing YAML directly:

  • list — List all patterns with filtering by type, group, search, status
  • test <cmd> — Test a command against all pattern types without running it
  • init — Bootstrap a config directory (--with-examples for demo patterns)
  • enable / disable — Toggle patterns by index, description, or group (auto-interactive when no target or group)
  • add — Add a pattern interactively or via CLI flags (supports --glob for glob-to-regex, --target for direct file writes)
  • remove — Remove a pattern (truly deletes from source YAML; use --force to skip confirmation)
  • validate — Check config syntax and regex validity (supports --quiet for CI)
  • info — Dashboard: pattern counts, integrity status, group breakdown
  • logs — Extract plugin log entries from Hermes logs

See CLI-Reference for full command details and glob syntax reference.

Block Patterns

These commands trigger the approval prompt:

patterns:
  - pattern: '\bvultr\b'
    description: 'Vultr CLI command'
    enabled: true
    group: cloud
    protected: false
    examples:
      - 'vultr account info'
      - 'vultr instance list'
Field Required Description
pattern Yes* Python regex (matched with re.IGNORECASE | re.DOTALL). Can be omitted if glob is provided — the regex is auto-generated from the glob on config load.
description Yes Human-readable label shown in the approval prompt
examples No Documentation-only list of example commands
enabled No Boolean (default true). Set false to temporarily disable
group No Optional string tag for categorization (e.g., cloud, database)
protected No Boolean (default false). If true, integrity-checked across sessions
glob No Original glob pattern. If pattern is absent, the glob is auto-converted to regex on config load.

* pattern or glob required. At least one must be present. If both are provided, pattern is used as-is. If only glob is present, the regex is auto-generated during config validation.

Allow Patterns

Exempt specific commands from approval, even if they match a block pattern:

allow_patterns:
  - pattern: '\bvultr\s+(account\s+info|instance\s+list)\b'
    description: 'Read-only Vultr commands'
    enabled: true
    group: cloud
Field Required Description
pattern Yes* Python regex (same flags as block patterns). Can be omitted if glob is provided — auto-generated from glob.
description No Documentation-only label
enabled No Boolean (default true). Set false to temporarily disable
group No Optional string tag for categorization
protected No Boolean (default false). If true, integrity-checked across sessions
glob No Original glob pattern. If pattern is absent, auto-converted to regex on load.

* pattern or glob required. At least one must be present. If both are provided, pattern is used as-is.

Deny Patterns

Commands matching deny patterns are blocked immediately without an approval prompt. They are checked before allow and block patterns — deny wraps the original guard function and intercepts first. Useful for commands that should never be run without manual config changes.

deny_patterns:
  - pattern: '\bgit\s+push\s+--force\b'
    description: 'Force git push'
    enabled: true
    group: git
    examples:
      - 'git push --force origin main'
Field Required Description
pattern Yes* Python regex (same flags as block patterns). Can be omitted if glob is provided — auto-generated from glob.
description Yes Human-readable label shown in the block message
examples No Documentation-only list of example commands
enabled No Boolean (default true). Set false to temporarily disable
group No Optional string tag for categorization
protected No Boolean (default false). If true, integrity-checked across sessions
glob No Original glob pattern. If pattern is absent, auto-converted to regex on load.

* pattern or glob required. At least one must be present. If both are provided, pattern is used as-is.

Known limitation: Deny patterns intercept before --yolo/mode=off checks inside the original guard function, so --yolo does not bypass deny patterns. This will be addressed in a future Hermes core integration (v0.5.0).

Additional Pattern Fields

All pattern types (block, allow, deny) support optional enabled, group, protected, and glob fields.

  • enabled (bool, default true) — Set false to temporarily disable a pattern without removing it
  • group (string) — Optional tag for categorization (e.g., cloud, database, testing)
  • protected (bool, default false) — If true, the pattern regex is integrity-checked across sessions
  • glob (string) — Original glob pattern, saved automatically when the pattern was created via --glob. If pattern is not present, the glob is converted to regex on config load.

See each pattern type's field table above for full details. See CLI-Reference#Glob-Syntax for glob-to-regex conversion rules.

Directory Config Loading

Instead of a single file, you can use a directory. The plugin loads all *.yaml files in alphabetical order and merges them:

  • Lists (patterns, allow_patterns, deny_patterns) are extended (appended)
  • Scalars override previous values

This is useful for splitting configs by tool or team:

~/.hermes/custom-dangerous-patterns/
├── 10-cloud.yaml       # cloud CLI patterns
├── 20-database.yaml     # database patterns
└── 30-deployment.yaml   # deploy tool patterns

Use hermes custom-dangerous-patterns init to create a config directory with starter files, or set the directory path via $HERMES_CUSTOM_PATTERNS_PATH.

CLI Write Behavior in Directory Mode

When the config path is a directory, CLI write commands behave differently:

  • enable / disable and add (without --target) write only the changed entries (delta) to 99-custom.yaml, which sorts last in the merge order (highest precedence). User-created files (10-cloud.yaml, 20-database.yaml) are never touched.
  • add --target <filename> writes directly to the specified file (skips the delta system). The file must have a .yaml extension and no path separators.
  • remove deletes entries directly from source YAML files using ruamel.yaml round-trip editing. Lines are removed — no disabled remnant is left behind. Protected patterns cannot be removed via CLI.
  • To reset CLI-managed patterns, delete 99-custom.yaml and restart Hermes.

Config Integrity

The plugin tracks configuration integrity across sessions:

  • A SHA-256 hash of the full config YAML is stored in ~/.hermes/.custom-patterns-hash. If the config changes between sessions, a WARNING is logged with old and new pattern counts.
  • Protected patterns (protected: true) have their individual regex hashed and tracked. If a protected pattern is modified or removed, a CRITICAL warning is logged at startup.
  • Allow shadowing detection warns if an allow pattern could bypass a built-in dangerous pattern without a corresponding custom block pattern.

These checks are detective, not preventive — they log changes but do not prevent them.

A Note on \b (Word Boundaries)

Patterns use \b to match whole words only. This prevents false positives:

Pattern Matches Doesn't match
\bvultr\b vultr instance list echo vultr_test, my-vultr-server
\baws\s+ec2\b aws ec2 describe-instances aws-ec2-tool, paws ec2
\bterraform destroy\b terraform destroy -auto-approve echo "terraform destroy" in a script

Without \b, \bvultr would match any string containing "vultr" — including hostnames, variable names, or unrelated commands.

Tip: Use single-quoted YAML strings for patterns — backslashes pass through literally ('\bvultr\b'), avoiding the double-escaping needed with double quotes ("\\bvultr\\b").

Evaluation Order

Note: The runtime evaluation order differs because the deny-pattern wrapper runs before the original check_all_command_guards() function that contains the yolo/mode=off check. Deny patterns intercept --yolo and mode=off by design.

Each check is tagged with its source:

  • [Plugin] — this plugin's custom checks
  • [Hermes] — Hermes Agent's built-in checks
 1. [Plugin]  Deny patterns (custom)        → BLOCKED immediately, no prompt
               (wraps original check_all_command_guards)
 2. [Hermes]  Hardline check                → blocked unconditionally
 3. [Hermes]  Sudo stdin guard              → blocked unconditionally
 4. [Hermes]  Yolo / mode=off               → bypasses steps 5-7
 5. [Plugin]  Allow patterns (custom)       → command runs, no prompt (allow wins over block)
 6. detect_dangerous_command():             — same approval prompt for both —
    a. [Plugin]  Block patterns (custom)    → [o]nce/[s]ession/[a]lways/[d]eny
    b. [Hermes]  Built-in patterns          → [o]nce/[s]ession/[a]lways/[d]eny
 7. [Hermes]  Tirith security scan          → approval prompt if findings

Key rules:

  • Deny wins over allow. Deny patterns are checked first (step 1). If a command matches a deny pattern, it is blocked before allow patterns are even evaluated.
  • Allow wins over block. If a command matches both an allow pattern (step 5) and a block pattern (step 6a), allow wins and no prompt is shown.
  • Deny is immediate-block; block is approval-prompt. Block patterns go through the detect_dangerous_command() approval flow. Deny patterns skip it entirely.
  • Deny bypasses yolo. Deny patterns are evaluated outside the original guard function, so --yolo does not bypass them.

Full Example

# ~/.hermes/custom-dangerous-patterns.yaml
#
# TIP: Use single-quoted strings for patterns — backslashes pass through
# literally:  '\bvultr\b'  not  "\\bvultr\\b"

patterns:
  # ── Cloud CLI tools (destructive) ────────────────────────────────
  - pattern: '\bvultr\s+(instance\s+create|instance\s+delete|snapshot\s+create|snapshot\s+delete)\b'
    description: 'Vultr destructive instance/snapshot command'
    examples:
      - 'vultr instance delete --instance-id cb670a12-e4f5-6d78-ab90-1234567890ab'
      - 'vultr snapshot delete --snapshot-id 5a3b2c1d'

  - pattern: '\bterraform\s+(destroy|apply)\b'
    description: 'Terraform destroy/apply (infrastructure mutation)'
    examples:
      - 'terraform destroy -auto-approve'
      - 'terraform apply -auto-approve'

  - pattern: '\baws\s+(ec2|s3|rds|iam|lambda|cloudformation)\b'
    description: 'AWS CLI mutating service command'
    examples:
      - 'aws ec2 terminate-instances --instance-ids i-12345'
      - 'aws s3 rb s://my-bucket --force'
      - 'aws rds delete-db-instance --db-instance-identifier mydb'

  - pattern: '\bgcloud\s+(compute\s+instances\s+delete|projects\s+delete)\b'
    description: 'GCP destructive command'
    examples:
      - 'gcloud compute instances delete my-vm --zone=us-central1-a'

  - pattern: '\boci\s+(compute\s+instance\s+terminate|database\s+db\s+system\s+delete|network\s+vcn\s+delete)\b'
    description: 'Oracle Cloud destructive command'
    examples:
      - 'oci compute instance terminate --instance-id ocid1.instance.oc1..aaaaaaaa'
      - 'oci database db-system delete --db-system-id ocid1.dbsystem.oc1..aaaaaaaa'

  - pattern: '\bdoctl\s+(compute\s+droplet\s+delete|kubernetes\s+cluster\s+delete|databases\s+delete)\b'
    description: 'DigitalOcean destructive command'
    examples:
      - 'doctl compute droplet delete 12345678'
      - 'doctl kubernetes cluster delete my-cluster'

  - pattern: '\bkubectl\s+delete\s+namespace\b'
    description: 'Kubernetes namespace deletion'
    examples:
      - 'kubectl delete namespace staging'

  # ── Deployment tools ─────────────────────────────────────────────
  - pattern: '\bcap\s+\w+\s+deploy\b'
    description: 'Capistrano production deploy'
    examples:
      - 'cap production deploy'

  - pattern: '\bfab\s+\w*\s*deploy\b'
    description: 'Fabric deploy'
    examples:
      - 'fab deploy production'

  # ── Database operations ──────────────────────────────────────────
  - pattern: '\bDROP\s+(TABLE|DATABASE)\b'
    description: 'SQL DROP statement'
    examples:
      - 'DROP TABLE users'
      - 'DROP DATABASE production'

  - pattern: '\bmongodump\b.*--drop\b'
    description: 'MongoDB dump with --drop (overwrites existing data)'
    examples:
      - 'mongodump --drop --db production'

# ── Allow patterns ────────────────────────────────────────────────
# Commands matching these are EXEMPT from approval, even if they
# also match a blocked pattern. Evaluated BEFORE block patterns.
# Allow wins over block.

allow_patterns:
  # ── Read-only cloud commands (safe) ─────────────────────────────
  - pattern: '\bvultr\s+(account\s+info|instance\s+list|dns\s+list|plan\s+list)\b'
    description: 'Read-only Vultr commands'

  - pattern: '\baws\s+(ec2\s+describe|s3\s+ls|s3\s+cp.*--dry-run|iam\s+list)\b'
    description: 'AWS read-only commands'

  - pattern: '\bterraform\s+(plan|state\s+list|output)\b'
    description: 'Terraform read-only commands'

  - pattern: '\boci\s+(compute\s+instance\s+list|network\s+vcn\s+list|database\s+db\s+system\s+list)\b'
    description: 'Oracle Cloud read-only commands'

  - pattern: '\bdoctl\s+(compute\s+droplet\s+list|kubernetes\s+cluster\s+list|databases\s+list)\b'
    description: 'DigitalOcean read-only commands'

  # ── Help and utility (safe) ─────────────────────────────────────
  - pattern: '\b(vultr|gcloud|aws|terraform|kubectl|oci|doctl)\s+(-h|--help|help)\b'
    description: 'Help flags are safe'

  - pattern: '\b(vultr|gcloud|aws|terraform|oci|doctl)\s+completion\b'
    description: 'Shell completion scripts are safe'

Edge Cases

Scenario Behavior
Config file missing Plugin loads silently, no patterns injected
Config file invalid YAML Log WARNING, plugin loads with empty pattern list
Invalid regex in pattern Log WARNING for that pattern, skip it, load valid ones
Block pattern matches but allow also matches Allow wins — no prompt (allow wins over block)
Deny pattern matches (and allow also matches) Deny wins — blocked immediately (deny checked first)
Glob with no pattern Auto-converted to regex on config load
Glob and pattern disagree Warning emitted by validate command
Config changed since last session WARNING logged with old/new pattern counts
Protected pattern missing/modified CRITICAL warning logged
Allow pattern shadows built-in pattern WARNING logged with details
--yolo mode Block patterns (custom + built-in) bypassed. Deny patterns still block — checked outside the original guard function.
approvals.mode: off Block patterns bypassed. Deny patterns still block — checked outside the original guard function.
approvals.mode: smart Custom patterns assessed by auxiliary LLM
Cron session + cron_mode: deny Custom patterns blocked in cron
Container backend (docker, etc.) All approval checks skipped (sandboxed)
command_allowlist "always" choice Persisted to config.yaml — survives restarts

Clone this wiki locally