hermes gateway install silently resets HERMES_HOME on macOS LaunchAgent
Summary
On macOS, running hermes gateway install rewrites ~/Library/LaunchAgents/ai.hermes.gateway.plist from a hard-coded template that ignores the current HERMES_HOME of the running CLI environment. The previous HERMES_HOME value (and the HERMES_HOME-derived StandardOutPath / StandardErrorPath) are silently overwritten with defaults. There is no flag to pin them.
This bit us in production: a Slack-ingesting gateway pinned to ~/.hermes-bake-off/ was reinstalled via hermes gateway install to apply an unrelated config change. The install reset HERMES_HOME to ~/.hermes/ (default), the agent restarted reading the wrong tree, and Slack ingest broke for ~5 minutes until we noticed and manually re-pinned the plist.
Environment
- macOS (Apple Silicon; expect Intel behaves the same)
- Hermes CLI: latest as of 2026-05-26
- LaunchAgent label:
ai.hermes.gateway
Reproduction
# Set up a non-default HERMES_HOME tree
mkdir -p ~/.hermes-custom
HERMES_HOME=~/.hermes-custom hermes gateway install
# Verify the plist points at the custom tree
/usr/libexec/PlistBuddy -c 'Print :EnvironmentVariables:HERMES_HOME' \
~/Library/LaunchAgents/ai.hermes.gateway.plist
# → /Users/<you>/.hermes-custom ✓
# Time passes. Now run install again (no env override this time)
hermes gateway install
# Plist is silently rewritten:
/usr/libexec/PlistBuddy -c 'Print :EnvironmentVariables:HERMES_HOME' \
~/Library/LaunchAgents/ai.hermes.gateway.plist
# → /Users/<you>/.hermes ✗ (silently reset; no warning)
Expected behavior
One of:
- Read current plist's
HERMES_HOME first and preserve it across reinstall.
- Accept a
--hermes-home <path> flag so callers can pin explicitly. (Current help shows only --force, --system, --run-as-user.)
- Honor
$HERMES_HOME from the calling shell when generating the plist (current behavior appears to use a hard-coded default regardless of env).
- At minimum, emit a warning to stderr when the new plist's
HERMES_HOME differs from the previous one.
Affected fields
The install templates all four fields off the default ~/.hermes root:
| Field |
Default after install |
Custom value silently lost |
EnvironmentVariables.HERMES_HOME |
~/.hermes |
~/.hermes-custom |
StandardOutPath |
~/.hermes/logs/gateway.log |
~/.hermes-custom/logs/gateway.log |
StandardErrorPath |
~/.hermes/logs/gateway.error.log |
~/.hermes-custom/logs/gateway.error.log |
EnvironmentVariables.PATH |
trims trailing entries beyond a default set |
custom PATH tail (e.g., claude-plugin caches) silently stripped |
Our workaround
Shipped a local wrapper that wraps hermes gateway install:
- Captures the four fields above via
PlistBuddy before install.
- Backs up the plist with a timestamp suffix.
- Runs
hermes gateway install "$@" (passes through --force etc.).
- Re-patches the four fields via
PlistBuddy.
- Validates via
plutil -lint.
launchctl bootout + bootstrap to apply.
- Verifies the agent reaches
state = running within 10s; restores backup on any failure.
Source: ~/.claude/helpers/hermes-gateway-install-safe.sh (private; happy to share if useful).
Why this matters
Anyone running a non-default Hermes home (parallel gateways, bake-off setups, per-org isolation, staging vs prod on one Mac) currently can't safely reinstall the gateway without manual plist forensics. The failure mode is silent — the agent comes back up reading the wrong tree, and depending on what's in ~/.hermes/ vs ~/.hermes-custom/ you may not notice until something visible breaks (in our case Slack ingest).
A one-line preservation in the macOS install path would close this.
Happy to send a PR if the maintainers can confirm which of the four behaviors above is preferred.
hermes gateway installsilently resets HERMES_HOME on macOS LaunchAgentSummary
On macOS, running
hermes gateway installrewrites~/Library/LaunchAgents/ai.hermes.gateway.plistfrom a hard-coded template that ignores the currentHERMES_HOMEof the running CLI environment. The previousHERMES_HOMEvalue (and theHERMES_HOME-derivedStandardOutPath/StandardErrorPath) are silently overwritten with defaults. There is no flag to pin them.This bit us in production: a Slack-ingesting gateway pinned to
~/.hermes-bake-off/was reinstalled viahermes gateway installto apply an unrelated config change. The install resetHERMES_HOMEto~/.hermes/(default), the agent restarted reading the wrong tree, and Slack ingest broke for ~5 minutes until we noticed and manually re-pinned the plist.Environment
ai.hermes.gatewayReproduction
Expected behavior
One of:
HERMES_HOMEfirst and preserve it across reinstall.--hermes-home <path>flag so callers can pin explicitly. (Current help shows only--force,--system,--run-as-user.)$HERMES_HOMEfrom the calling shell when generating the plist (current behavior appears to use a hard-coded default regardless of env).HERMES_HOMEdiffers from the previous one.Affected fields
The install templates all four fields off the default
~/.hermesroot:EnvironmentVariables.HERMES_HOME~/.hermes~/.hermes-customStandardOutPath~/.hermes/logs/gateway.log~/.hermes-custom/logs/gateway.logStandardErrorPath~/.hermes/logs/gateway.error.log~/.hermes-custom/logs/gateway.error.logEnvironmentVariables.PATHOur workaround
Shipped a local wrapper that wraps
hermes gateway install:PlistBuddybefore install.hermes gateway install "$@"(passes through--forceetc.).PlistBuddy.plutil -lint.launchctl bootout+bootstrapto apply.state = runningwithin 10s; restores backup on any failure.Source:
~/.claude/helpers/hermes-gateway-install-safe.sh(private; happy to share if useful).Why this matters
Anyone running a non-default Hermes home (parallel gateways, bake-off setups, per-org isolation, staging vs prod on one Mac) currently can't safely reinstall the gateway without manual plist forensics. The failure mode is silent — the agent comes back up reading the wrong tree, and depending on what's in
~/.hermes/vs~/.hermes-custom/you may not notice until something visible breaks (in our case Slack ingest).A one-line preservation in the macOS install path would close this.
Happy to send a PR if the maintainers can confirm which of the four behaviors above is preferred.