Plugins are opt-in (with a few exceptions)
General plugins and user-installed backends are disabled by default - discovery finds them (so they show up in hermes plugins and /plugins ), but nothing with hooks or tools loads until you add the plugin's name to plugins.enabled in ~/.hermes/config.yaml . This stops third-party code from running without your explicit consent. plugins.enabled governs plugins only Gateway event hooks under ~/.hermes/hooks/<name>/ are not plugins and are not gated by plugins.enabled or plugins.disabled . That directory is trusted by placement: any subdirectory holding a valid HOOK.yaml + handler.py is imported by the gateway at startup, and placing the files there is the opt-in. See the gateway hook trust model . Three ways to flip state: After hermes plugins install owner/repo , you're asked Enable 'name' now? [y/N] - defaults to no. Skip the prompt for scripted installs with --enable or --no-enable . For a reproducible install, pin a full immutable commit (tags, branches, and abbreviated SHAs are not accepted): Hermes checks out the commit detached, verifies that HEAD exactly matches the requested SHA, and records the canonical source, installed revision, and pin status in the current profile. her…
From the official Hermes Agent documentation.
plugins:
enabled:
- my-tool-plugin
- disk-cleanup
disabled: # optional deny-list — always wins if a name appears in both
- noisy-plugin
# Optional: deadline (seconds) for each Git clone, fetch or checkout
# during plugin installation, including automatic memory-provider migration.
# Default 300; values above 3600 are clamped. A subdirectory install
# (owner/repo/path/to/plugin) downloads only that folder's files.
clone_timeout_seconds: 300
# Optional: wall-clock cap (seconds) for timeout-bounded in-process Python
# plugin hook callbacks (hot-path observers + pre_tool_call). Default 30;
# set 0 to disable; values above 600 are clamped. Timed-out pre_tool_call
# callbacks fail closed (block the tool). Caller-thread hooks such as
# subagent_stop are never moved onto a timeout worker.
# Shell hooks keep their own per-entry timeout under the top-level hooks: key.
hook_callback_timeout: 30
# Optional: deadline (seconds) for one plugin's import + register() at load.
# A plugin that overruns it is skipped with the reason "load timed out after
# Ns" (reported like any other load failure: the startup warning and the
# in-session `/plugins` listing) and the remaining plugins keep loading; the
# stuck thread is abandoned. Default 10; set 0 to disable; values above 600
# are clamped.
load_timeout_seconds: 10