The previous approach used a /usr/bin/hermes shim to proxy commands from root
to the hermes user, and a hand-crafted system-level systemd unit for the
gateway. This worked for the default profile but broke down for named profiles:
- hermes profile create <name> generates an alias script in
~/.local/bin/<name> that calls hermes with -p <name>. These aliases live
in the hermes user's PATH, not root's, so root could not invoke them.
- Maintaining parity would require per-profile shims, a watcher daemon to
create/remove them, and system-unit mirrors for each profile gateway — all
of which would need to stay in sync with hermes internals across updates.
New approach — work with hermes, not around it:
- loginctl enable-linger hermes: ensures the hermes user's systemd session
starts at boot and persists without login. All user-unit gateways (default
and per-profile) now survive reboots automatically.
- Gateway service management delegated entirely to hermes: 'hermes gateway
install' / 'hermes setup' create and enable the user unit natively.
The install script no longer pre-installs the gateway; hermes prompts the
user to do so at the end of 'hermes setup'.
- hermes-dashboard.service remains a system unit (no native install command
exists for it). Its After= no longer references hermes-gateway.service
since there is no system-unit gateway to depend on.
- /usr/bin/hermes shim removed. Root is guided to 'su - hermes' via a two-
line /etc/profile.d/hermes-hint.sh message on login, with a one-liner to
make the switch automatic. Once logged in as hermes, all hermes commands,
profile aliases, and gateway management work natively.
- update_script simplified: only hermes-dashboard (our unit) is stopped and
restarted. hermes update --yes handles gateway service lifecycle itself.
- default_credentials: username null/null (no SSH login for hermes
service account; access is via root like all PVE Helper Scripts)
- Update setup note to reference root instead of hermes user
- Update dashboard tunnel note to use root and -fNL flags
The hermes service account has no password or SSH keys and cannot
be used to log in. Access is via root (standard PVE Helper Scripts
pattern). Also add -fN flags to the dashboard tunnel command so it
runs in the background without opening a shell session.
The response_store.db and session JSON files under ~/.hermes/ are stored
in plaintext and readable by any process with filesystem access. Set
~/.hermes to 0700 (owner-only) and ~/home/hermes to 0750 to restrict
access to conversation history, credentials, and session data.
Ref: https://github.com/NousResearch/hermes-agent/issues/7486
Hermes strips sensitive env vars from tool subprocesses, but child
processes can recover them by reading /proc/<parent_pid>/environ. Add
ProtectProc=invisible and ProcSubset=pid to both systemd services to
hide other processes' /proc entries and limit /proc to the service's
own PID namespace.
Ref: https://github.com/NousResearch/hermes-agent/issues/4427
The Anthropic OAuth helper writes credential files with the process
default umask, resulting in 0644 permissions on sensitive files. Set
UMask=0077 on both hermes-gateway and hermes-dashboard services so all
files created at runtime are owner-only (0600/0700).
Ref: https://github.com/NousResearch/hermes-agent/issues/11003
HERMES_REDACT_SECRETS is off by default, exposing API keys in chat output
and session JSON files. Add HERMES_REDACT_SECRETS=true to the .env file
created by the installer.
Ref: https://github.com/NousResearch/hermes-agent/issues/17691
Adds container scripts for Hermes Agent (Nous Research), a self-improving
AI agent with LLM provider integration, terminal execution, web browsing,
and multi-platform messaging support.
Files:
- ct/hermes-agent.sh
- install/hermes-agent-install.sh
- json/hermes-agent.json
- ct/headers/hermes-agent
Deviations from standard patterns (justified):
1. Uses upstream installer (curl-pipe) instead of fetch_and_deploy_gh_release:
Hermes is a uv-managed Python application with complex dependency
resolution, virtualenv management, and binary placement—not a single
binary or tarball from GitHub Releases.
2. Dedicated 'hermes' service user (not running as root):
The agent executes arbitrary terminal commands on behalf of the user.
Running as root would give the AI unrestricted system access. This
follows the protonmail-bridge service-user pattern for isolation.
3. Dashboard (port 9119) bound to localhost only, requiring SSH tunnel:
The web UI provides admin access to an AI that can execute commands.
SSH tunnel provides an authentication/authorization boundary.
4. /usr/bin/hermes shim script:
The hermes CLI validates cwd permissions; running 'hermes' as root
from /root fails. The shim cd's to /home/hermes and exec's as the
hermes user via runuser.
5. setsid --wait wrapping of upstream installer:
The upstream installer probes /dev/tty for interactive prompts even
with --skip-setup; setsid detaches the controlling terminal.
- drop redundant curl of default.yml (file is part of release tarball)
- stop creating /opt/authentik-data dirs in install (mounted later as separate volume)
- enable services without --now in install (started after data volume is attached)
Introduce GitLab release helpers and use them to conditionally update Storyteller.
- misc/tools.func: add get_latest_gitlab_release and check_for_gl_release functions (GitLab API calls, token support, pinning, error handling, requires jq).
- ct/storyteller.sh: wrap update sequence with check_for_gl_release so rebuild/deploy only runs when a new GitLab release is detected; update author metadata.
- install/storyteller-install.sh: remove python3 from apt dependencies and drop NODE_OPTIONS export; minor whitespace cleanup.
These changes prevent unnecessary rebuilds by detecting upstream GitLab releases and improve robustness of release detection and error reporting.
Download authentik's lib/default.yml for the specified AUTHENTIK_VERSION before running go mod/download and build (added curl to ct/authentik.sh and install/authentik-install.sh to write to /opt/authentik/authentik/lib/default.yml). Also normalize Bitfocus Companion app name to "Bitfocus-Companion" in ct/bitfocus-companion.sh.