Skip to content

fix(gateway): load $HERMES_HOME/.env from systemd unit so plugins see env vars - #18606

Closed
vvb-1 wants to merge 1 commit into
NousResearch:mainfrom
vvb-1:fix/systemd-load-env-file
Closed

fix(gateway): load $HERMES_HOME/.env from systemd unit so plugins see env vars#18606
vvb-1 wants to merge 1 commit into
NousResearch:mainfrom
vvb-1:fix/systemd-load-env-file

Conversation

@vvb-1

@vvb-1 vvb-1 commented May 2, 2026

Copy link
Copy Markdown

Summary

Both generated hermes-gateway.service unit templates (system-wide and --user) are missing an EnvironmentFile= directive, so ~/.hermes/.env is never loaded when the gateway runs under systemd. The shell-exported env vars that work in interactive hermes sessions never reach the systemd cgroup.

User-visible symptom: plugins that declare requires_env (Hindsight, OpenAI-backed tools, internal HTTP plugins, etc.) silently skip registration and the gateway boots into a degraded toolset, while the same Hermes install run interactively exposes the full toolset.

This is the root cause of #2765. PR #2768 added warnings that surface the symptom but does not fix the underlying unit template.

Repro

On a Linux host with systemd --user:

hermes gateway install --user
hermes gateway start
GW_PID=$(systemctl --user show hermes-gateway -p MainPID --value)
sudo cat /proc/$GW_PID/environ | tr '\0' '\n' \
  | grep -cE '^(HINDSIGHT|OPENAI|TELEGRAM_BOT|DISCORD_BOT)'
# → 0   (no app env vars present in the gateway process)

Even though ~/.hermes/.env contains valid keys, the gateway process never sees them.

Fix

Add an EnvironmentFile=-{hermes_home}/.env line under [Service] in both templates (rendered by generate_systemd_unit() in hermes_cli/gateway.py):

  • The directive is placed before the existing Environment= lines so unit-defined values still take precedence on conflict.
  • The leading - makes the directive optional — a missing .env file does not fail unit start.
  • The path uses the same {hermes_home} interpolation already used for Environment="HERMES_HOME=...", so profile-aware paths (e.g. ~/.hermes-dev/.env) resolve correctly without further changes.

After this change, the same repro yields a non-zero count corresponding to the keys present in ~/.hermes/.env. Plugins that previously skipped registration now register their tools, and the gateway exposes the full toolset to messaging platforms (Telegram, Discord, etc.).

Tests

Two new cases in tests/hermes_cli/test_gateway_service.py::TestGeneratedSystemdUnits:

Test Asserts
test_user_unit_loads_hermes_env_file User unit (default.target) renders EnvironmentFile=-…/.env
test_system_unit_loads_hermes_env_file System unit (multi-user.target) renders EnvironmentFile=-…/.env

Result locally: 5 passed for the full TestGeneratedSystemdUnits class (3 existing + 2 new).

Backwards compatibility

  • The - prefix means hosts without ~/.hermes/.env are unaffected (no new failure mode).
  • Unit-staleness checks (_normalize_service_definition) compare normalized text, so existing installs will be flagged as outdated and refreshed by the next hermes gateway start/restart/install — same path already used for any other unit-template change.
  • No behavior change for non-systemd transports (launchd plist, Windows, foreground hermes gateway run).

Related

… env vars

Closes #2765 (root cause; warnings added in #2768 made this
visible without resolving it).

systemd starts services with a clean environment; ~/.hermes/.env was
never loaded by the generated hermes-gateway.service unit, so plugins
that declare requires_env (Hindsight, OpenAI-backed tools, internal
HTTP plugins, etc.) silently skipped registration and the gateway
booted into a degraded toolset. The user-facing symptom in the field
was a gateway exposing only the default-fallback tools while the same
Hermes install in an interactive shell exposed the full set, because
the shell's exported env never reached the systemd cgroup.

Both unit templates (system-wide and --user) now contain:

    EnvironmentFile=-{hermes_home}/.env

inserted directly under [Service] and before any Environment= lines so
unit-defined values still win on conflict. The leading '-' makes the
directive optional; a missing file does not fail unit start.

The reference resolves through the same {hermes_home} interpolation
already used by Environment="HERMES_HOME=...", so profile-aware paths
(e.g. ~/.hermes-dev/.env) work without further changes.

Tests: two new cases under TestGeneratedSystemdUnits assert the
directive is present and points at a *.env file for both the user
and system unit.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery comp/cli CLI entry point, hermes_cli/, setup wizard labels May 2, 2026
@vvb-1 vvb-1 closed this by deleting the head repository May 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hindsight plugin silently skips tool registration when HINDSIGHT_API_URL is missing/empty

2 participants