Hermes
Hermes connector stages default-profile hooks and exact approvals for the Hermes agent runtime, with bounded inventory and an explicit reload boundary.
The Hermes connector wires DefenseClaw into the Hermes agent runtime through
the effective default-profile HERMES_HOME/config.yaml
(%LOCALAPPDATA%\hermes\config.yaml on native Windows when HERMES_HOME is
unset) and exact approvals in shell-hooks-allowlist.json.
Platform support
| Platform | Status | Connector path |
|---|---|---|
| macOS and Linux | Supported | Default-profile configuration and the portable Hermes command hook. Reload every affected Hermes host after lifecycle changes. |
| Native Windows x64 | Supported | Direct, absolute defenseclaw-hook.exe argv launched with shell=False; no DefenseClaw shell bridge. Running-client state remains pending reload, validation metadata remains empty, and live=false. |
Hermes terminal commands still use Git Bash on Windows
Hermes itself officially runs its terminal tool through installer-managed
PortableGit/Git Bash. DefenseClaw does not install, find, configure, invoke, or
test that dependency. Its Windows hook is a separately registered, directly
quoted absolute defenseclaw-hook.exe argv launched by Hermes with
shell=False; there is no DefenseClaw PowerShell, Bash, WSL, Docker, VM, MSYS,
or Cygwin bridge.
Setup
defenseclaw setup hermesDefenseClaw stages the default-profile hook configuration and exact allowlist entries. Reload every affected Hermes process after setup.
defenseclaw setup hermesDefenseClaw registers one directly quoted absolute defenseclaw-hook.exe argv.
Hermes parses it with shlex.split and launches it with
subprocess.run(..., shell=False), so the command must not contain PowerShell,
&, a .ps1, Bash, or WSL. Hermes's own terminal tool separately uses its
installer-managed PortableGit/Git Bash; DefenseClaw does not install, locate,
invoke, or test that upstream dependency. Reload every affected Hermes host
after setup.
setup hermes is shorthand for setup guardrail --connector hermes: it stages
hooks in the resolved default-profile config.yaml, adds only the exact 23
DefenseClaw (event, command) approval entries, and inventories the bounded
default-profile sources. It preserves the operator's hooks_auto_accept value
and third-party approval entries. There is no proxy-enforcement path for
Hermes — blocking happens hook-side only when a valid synchronous
pre_tool_call stdout response requests block. Hermes has no documented native
human-approval or system-message response shape, so confirm verdicts remain
audit/alert-only with raw_action preserved for audit. In the standalone
enterprise profile, a confirm verdict on a pre_tool_call is blocked instead,
and the message says the organization's rule needs a confirmation that Hermes
cannot ask for.
Discovery keeps installation and contract compatibility distinct. A healthy
older client such as v0.17.0 is reported as
detected-but-unsupported-version, never as absent. If action mode is
requested, Setup prints the exact installed version and the
hermes-hooks-v1 >=0.19.0,<0.21.0 or hermes-hooks-v2 >=0.21.0,<0.22.0 requirement, then saves Hermes in observe mode with
upgrade guidance. The latest official release rechecked on 2026-09-14 is
v0.21.3 (v2026.9.14); its source keeps the same 23 claimed shell-hook
events, but that source review is not packaged or real-client
certification.
Reload Hermes before treating setup or disable as live
Hermes registers shell callbacks in each running process. DefenseClaw has no
vendor-proven command that reloads all Hermes CLI, gateway, desktop, and service
hosts, so Setup and Doctor report pending_reload and live: false. Reload or
restart every affected Hermes host after Setup and after Disable. Restarting
defenseclaw-gateway does not reload Hermes. On Windows, Disable first writes
an exact direct-native disabled tombstone so stale cached callbacks safely
no-op while revocation is pending.
One default profile only
The connector rejects named-profile homes, a non-default active_profile, and
multiplex gateways because one HERMES_HOME cannot cover them. Project plugins
and other launch-directory-conditional sources remain unverified. Enterprise,
managed, Team, ProgramData, cloud-dashboard, and MDM surfaces are outside this
connector contract.
Standalone enterprise profile: other hooks in config.yaml
Hermes runs every hook registered for an event, in order, and a
pre_tool_call hook can rewrite the tool input, so a hook added after
DefenseClaw's could change a command DefenseClaw already checked. Hermes has
no way to lock or order hooks. On Linux and macOS hosts with the standalone
enterprise profile, DefenseClaw's Hermes hook runs the
foreign-hook guard before each tool
call: while config.yaml, or the managed-scope config.yaml in the folder
HERMES_MANAGED_DIR names, has a hook entry that is not DefenseClaw's and
that your organization has not approved, tool calls are blocked with a
message that names the file and the digest to approve, and the guardian
removes the entry (with a backup). A Hermes process that started with the
entry keeps running it, so it stays blocked until Hermes restarts. The
output_spill and outbound settings under hooks are not hooks; the
guard leaves them alone.
What setup hermes actually does
The table highlights convenience options rather than the complete alias
surface. The alias also accepts --mode, --rule-pack, --fail-mode,
approval, block-message, add/replace, workspace, and rule-pack-directory
options. See the quick-alias reference; use
full guardrail setup for scanner, detection-strategy,
and judge-provider configuration.
| Flag | Default | What it does |
|---|---|---|
--yes / -y | off | Skip the confirmation prompt. |
--restart / --no-restart | --restart | Bounce defenseclaw-gateway after applying changes. This does not reload Hermes; reload each Hermes host separately. |
--with-local-stack / --no-local-stack | --no-local-stack | Also run setup local-observability up; follow the command's printed gateway-restart step after it writes the export destination. |
The alias defaults Hermes to observe mode and can join an existing hook-connector roster when you choose Add. To tune Hermes after install, keep using defenseclaw setup guardrail --connector hermes — see the variations below.
Common variations — pick the recipe that fits your phase
defenseclaw setup hermesConfirms once, stages the hooks block and exact allowlist approvals, and restarts
the DefenseClaw gateway. Reload each Hermes host before expecting the callbacks
to be live. Findings flow to mandatory SQLite event history and the TUI;
configured v8 destinations receive only the buckets/signals their routes
select. No traffic is intercepted and no requests are blocked. Pass --yes to
skip the confirmation in CI.
defenseclaw setup hermes --yes --with-local-stackSame as standard but also runs setup local-observability up so Prom/Loki/Tempo/Grafana come up locally for ad-hoc dashboards. That command writes the export destination after the alias has already restarted the gateway, so run its printed defenseclaw-gateway restart step before expecting exports. See Local observability.
export DEFENSECLAW_LLM_KEY='replace-with-your-key'
defenseclaw setup hermes # base alias first
defenseclaw setup guardrail \
--connector hermes \
--rule-pack strict \
--scanner-mode local \
--detection-strategy regex_judge \
--judge-model anthropic/claude-sonnet-4-20250514 \
--judge-api-key-env DEFENSECLAW_LLM_KEY \
--judge-hook-connectors hermes \
--restartThe alias selects Hermes; the follow-up setup guardrail --connector hermes swaps in the strict rule pack, keeps scanning local, and turns the LLM judge on as a second-pass adjudicator on regex-flagged events. Configure the remote scanner separately through full guardrail setup, which validates its endpoint and API-key environment variable.
Hermes has no proxy enforcement, but its pre-tool hook can enforce directly:
connector_hooks:
hermes:
enabled: true
mode: action # observe (default) | action
fail_mode: open # Hermes is always fail-open at the host boundaryThen defenseclaw setup guardrail --restart to re-wire. With mode: action,
Hermes's pre-tool-call hook blocks only when DefenseClaw returns valid
synchronous Hermes block JSON. A confirm result is recorded and alerted but
returns no hook output because Hermes has no native ask/approve or general
message response; the TUI can review the audit event but cannot resume the
call. The standalone enterprise profile blocks it instead (see above). A requested global/per-connector
fail_mode: closed, strict availability, timeout, nonzero exit, malformed
output, auth failure, or gateway outage cannot create Hermes enforcement and
remains fail-open.
Decision aids — should I turn this on?
Human-in-the-loop (HITL)
Per-connector ask matrix. Hermes has no native ask/approve response; confirm is audit/alert-only and returns no hook output.
Full setup guardrail flag reference
All ~20 flags you can pass via `setup guardrail --connector hermes` when tuning Hermes.
Defaults & rule packs
What permissive / default / strict actually ship, and which one matches your risk tolerance.
Interactive wizard
Animated terminal demo of the prompt-by-prompt setup flow — the safest path the first time.
Not sure what to pick? Run defenseclaw setup guardrail (no flags) — the interactive wizard walks you through every choice with safe defaults pre-selected and inline help. The Prompt → flag mapping table gives you the CI-shaped command for the same configuration.
Files DefenseClaw will modify
Inventory is read-only and bounded to the resolved default profile: configured
MCP, local skills and existing skills.external_dirs, SOUL.md, built-in
memories/MEMORY.md and memories/USER.md, memory.provider provenance, and
bundled/Nix, user, and pip hermes_agent.plugins plugins with activation
provenance. Project plugins depend on launch cwd/environment and are reported
unverified; named-profile conditional sources are unsupported.
Hermes stores installer-synced and operator-authored skills in the same tree.
DefenseClaw marks a skill vendor-bundled only while its manifest hash, installed
copy, and matching hermes-agent/skills source all agree. Those unchanged
vendor skills remain visible but are discovery-only: they are never scanned,
blocked, disabled, or quarantined. Modified, untracked, linked, or unverifiable
copies remain ordinary scanable skills.
Hook capabilities
Block events
- pre_tool_call
Native ask events
None — confirm verdicts are downgraded with the raw action preserved.
Hermes can block only pre_tool_call through valid synchronous JSON, inject
context at pre_llm_call, and continue its bounded verification loop at
pre_verify. DefenseClaw registers all 23 Hermes v0.19 shell-hook-valid events,
but transform, API, gateway, approval, Kanban, and ordinary lifecycle events
remain attributed audit unless the official shell response parser documents a
compatible effect. Hermes blocks a pre_tool_call only on a valid block
answer and, from Hermes 0.21, on exit code 2. Other exit codes, a timeout,
malformed output and auth or network failures let the call run in the
releases DefenseClaw supports (DefenseClaw's entries do not set Hermes'
fail_closed), and Hermes has no DefenseClaw ask surface.
Confirm verdicts are audit/alert-only, with no synthesized hook output, except
in the standalone enterprise profile, which blocks a confirmed pre_tool_call.
Registration is not claimed live until Hermes reload evidence exists; Doctor
reports the staged state as unhealthy/pending reload.
The official-source record and full cross-surface matrix are in
docs/research/HERMES-NATIVE-WINDOWS.md.
OpenShell sandbox
DefenseClaw can build a Hermes OpenShell overlay image. The files below exist only in the image; nothing on your host changes.
-
Hooks in the managed layer, user tier. Hermes 0.19 reads an administrator layer from
/etc/hermes/config.yamland merges it over~/.hermes/config.yamlkey by key, with the managed value winning. The image registers the DefenseClaw hook for all 23 Hermes events in that root-owned file. It also pinshooks_auto_accept: true(the hooks register without the first-use prompt),plugins.enabled: [],terminal.backend: local(approved commands run inside the sandbox),agent.disabled_toolsets: [code_execution],security.tirith_enabled: false,security.allow_lazy_installs: falseandmodel_catalog.enabled: false. A user setting can add hooks for other events but cannot drop or replace DefenseClaw's.Hermes still reads settings and imports code from its home directory, which the agent can write. It loads
~/.hermes/.envover the process environment, whereHERMES_SAFE_MODEswitches every hook off andHERMES_MANAGED_DIRmoves the managed layer. It also imports model-provider plugins from~/.hermes/plugins/model-providerswhateverplugins.enabledsays. The hooks therefore depend on the launcher's checks, not only on file ownership, so the tier is user. -
Launcher checks. The root-owned launcher runs before every Hermes start. It refuses to start while any of these is true:
- an argument is
--safe-modeor a prefix of it that Hermes accepts (--saand longer). DefenseClaw's own--safe, which keeps the harness's prompts, goes before the harness name. - a
.envor.op.envin the Hermes home or one of its profiles names a variable that switches the hooks off, moves the managed layer or the Hermes home, or loads other code (HERMES_SAFE_MODE,HERMES_MANAGED_DIR,HERMES_HOME,HERMES_ENABLE_PROJECT_PLUGINS,HERMES_PYTHON*,PYTHON*,LD_*and a few more). - the home or a profile holds Python code under
plugins/. - a user
config.yamlhas asecretssection. Its secret sources set environment variables before the managed settings apply.
The launcher also pins
HOMEandHERMES_HOME, and drops everyPYTHON*variable and the variables that move Hermes' code before the harness starts./etc/hermes/.env, which Hermes applies last, pins safe mode and project plugins off. The refusal names the file; remove it and start Hermes again. - an argument is
-
Fail closed. Hermes ignores a hook's exit status and treats a timeout as no opinion, so the sandbox hook prints a Hermes block directive on every failure: missing token, ingress down, or a bad reply. It does so well inside the 30-second hook timeout.
-
Blocks on screen. Hermes 0.19 shows nothing for a tool call a hook blocked: the reason goes to the model as the tool's error, and the terminal shows only
preparing terminal…. A root-owned module in the image prints the reason under that line, the way Hermes prints its own tool lines, for example┊ ✗ terminal blocked by DefenseClaw rule <ID>: <title>. …. It prints only to a terminal, and what the model gets is unchanged. -
Known gaps. An agent can still start a second Hermes from a tool call, for example by running the pinned binary directly. That start skips the launcher's checks. DefenseClaw judges that tool call first, and hook-silence detection is the backstop. Command rules judge the terminal tool's commands, and the text the process tool sends to a background process (judged as shell input). The image turns off
execute_code, which runs model-written Python that command rules cannot read. A command that a script runs, when the agent writes the script to a file and runs it, is judged by the command that starts the script. -
Install pin. The image installs Hermes Agent 0.19.0 from PyPI as a root-owned
uv toolon a private CPython 3.12.13, with dependency resolution cut off at the review date. The build refuses a Hermes version outside the reviewed hook contracts (>=0.19.0,<0.22.0). -
Model access. Use one of these provider profiles. Each key is an OpenShell placeholder that resolves only at the provider endpoint, and only for the harness's own interpreter.
defenseclaw sandbox run hermespicks the first set ofOPENAI_API_KEY,ANTHROPIC_API_KEYandAWS_BEARER_TOKEN_BEDROCK(--llmpicks one):defenseclaw-openaior the regionaldefenseclaw-bedrock-mantle-openai-<region>, through the image's manageddefenseclawprovider (--provider defenseclaw -m <model>)defenseclaw-anthropic, through Hermes' built-inanthropicprovider
-
Launch flags. Skip-permissions mode adds
--yolo. A run with--safedrops--yoloand every prefix Hermes accepts for it. A headless run ishermes chat -q <prompt> -Q; pass-- chat -q "TEXT"tosandbox runfor a detached run. -
Ctrl-Z. Hermes suspends itself by signalling its process group, which the OpenShell sandbox refuses. Hermes prints its own "has been suspended. Run
fgto bring Hermes Agent back." line first, which DefenseClaw cannot keep off the screen; the image then answers the call withdefenseclaw: Ctrl-Z cannot suspend a harness in an OpenShell sandbox (the sandbox refuses the signal): Hermes Agent keeps running, and there is nothing to bring back with fg., and Hermes keeps running. -
Ctrl-C. One Ctrl-C at an empty Hermes prompt ends Hermes at once (
Shutting down… (finalizing session)), and DefenseClaw goes on to the end of the session. That is Hermes' own binding: with a draft, Ctrl-C clears it, and while the agent runs it interrupts the agent. The sandbox is kept;defenseclaw sandbox connect <name>starts Hermes in it again. -
Other traffic. Apart from the model, Hermes reaches only
models.devat start (its model metadata, which has no setting to turn off). The image marks the install the way Hermes' own image does (an.install_methodofdockernext to the code), so Hermes neither checkspypi.orgfor updates nor prints its "pip installs are no longer an officially supported platform" notice;hermes updatethen says it does not apply in a container (rebuild the image instead). The managed layer pinsmodel_catalog.enabled: false, which stops the fetch of Hermes' curated OpenRouter and Nous Portal model lists (hermes-agent.nousresearch.com, redirected tonousresearch.github.io). It also turns off the Tirith scanner download and runtime installs, which reachedgithub.comand fetched about 20 MB fromrelease-assets.githubusercontent.comandfiles.pythonhosted.orgbefore. Remaining traffic goes through the DefenseClaw egress proxy, which logs it and applies the blocklist. -
Status. Verified end to end. A run through the DefenseClaw daemon in an OpenShell sandbox, on a mock model behind a credential binding, showed:
- hooks reaching the sandbox ingress, with OpenShell substituting the model key;
- a command that a DefenseClaw rule blocks refused by the hook, with the rule's reason passed back to the model;
- egress through the proxy, including a blocklisted host that a sandbox-scoped unblock then let through.
The image also passes the hook-fire probe. In that probe a hostile user
config.yamland a launch environment with a plantedsitecustomizemodule change nothing, and the launcher refuses to start with a planted.env, model-provider plugin or profilesecretssection. The curated provider profiles have not carried a real model inside a sandbox; see the harness's verification note for what was run.
Enterprise deployments
In an enterprise deployment the guardian writes the
DefenseClaw hooks into each enrolled user's default Hermes config.yaml and
repairs them when the user changes them. The enterprise profile writes no
machine-wide Hermes configuration, so the hooks live in a file the user
owns, and Hermes coverage is advisory against a user who sets out to avoid
it:
- Safe mode and another Hermes home.
hermes --safe-modeskips the user'sconfig.yaml, andHERMES_HOMEpointing at a copy of it without thehooksblock does the same. No DefenseClaw hook runs, nothing is logged, the guardian never repairs that session, and status and verify keep reporting the user's Hermes ready.--ignore-user-configon its own keeps the hooks. - A killed hook lets the call run.
hermes-hook.shruns as the user. Hermes blocks a tool call only on a block answer (and, from Hermes 0.21, exit code 2), so when the user kills their own hook process the call runs with no DefenseClaw decision; Hermes' laterpost_tool_callhook can still record it after it ran. In the Hermes releases DefenseClaw supports, a hook timeout also lets the call run. Some builds block the call when a stalled hook times out, but Hermes does not document that.
What reduces it:
- Where you need enforcement, prefer agents that DefenseClaw governs through machine policy (Claude Code, Codex, Cursor, GitHub Copilot and OpenCode).
- Use application control (fapolicyd on Linux, Santa or similar on macOS,
WDAC or AppLocker on Windows) so users can start Hermes only the way you
allow, for example only from an administrator-owned launcher that resets
HERMES_HOMEandHERMES_MANAGED_DIRand refuses--safe-modeand--ignore-user-config. - Use EDR process protection so users cannot stop or kill the hook process.
See R1, R5 and R18 in the enterprise threat model.
Disable
defenseclaw guardrail disable --connector hermes --yesDisabling removes only DefenseClaw's entries: its hooks from config.yaml,
which keeps every byte outside the hooks mapping (comments included), and
its approvals from shell-hooks-allowlist.json.
Then reload or restart every affected Hermes host. Until that happens, the
Windows native tombstone keeps any cached exact DefenseClaw callback disabled;
DefenseClaw does not manage Hermes's PortableGit terminal behavior. On Linux
and macOS, ~/.defenseclaw/hooks/hermes-hook.sh stays as a stub that exits
without doing anything, for the same reason: a running Hermes process keeps
calling it until it restarts.
Cursor
Cursor connector wires user hooks for observe-only inspection or event-native action denial.
OpenCode
The OpenCode connector wires DefenseClaw into opencode via a dependency-free JavaScript bridge plugin auto-loaded from ~/.config/opencode/plugins/, whose tool.execute.before hook blocks risky tool calls by throwing.