From 0c0a905011a61aa869a750caebd1488e0e83a65a Mon Sep 17 00:00:00 2001 From: William Chen <57119977+OCWC22@users.noreply.github.com> Date: Thu, 21 May 2026 22:33:12 -0700 Subject: [PATCH] docs(gateway): add multi-profile gateways operations guide Covers running multiple Hermes profiles as managed services on one host: - A shell-loop wrapper pattern for start/stop/restart/status across every profile (the per-profile CLI commands stay unchanged). - Per-platform service file locations (LaunchAgent on macOS, systemd user unit on Linux), plus the rules around clashes. - Log paths per profile and how to tail every gateway at once. - Config file layout per profile and the restart-after-edit workflow. - Keeping the host awake: caffeinate flags on macOS, systemd-inhibit + loginctl enable-linger on Linux. - Token-conflict auditing across .env files. - Troubleshooting for the common "Could not find service in domain for user gui: 501" message and stale PIDs after a crash. Tested locally with five profiles on macOS launchd. Co-Authored-By: Claude Opus 4.7 --- .../docs/user-guide/multi-profile-gateways.md | 332 ++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 website/docs/user-guide/multi-profile-gateways.md diff --git a/website/docs/user-guide/multi-profile-gateways.md b/website/docs/user-guide/multi-profile-gateways.md new file mode 100644 index 000000000..6f00c24cf --- /dev/null +++ b/website/docs/user-guide/multi-profile-gateways.md @@ -0,0 +1,332 @@ +--- +sidebar_position: 4 +--- + +# Running Many Gateways at Once + +Operate multiple [profiles](./profiles.md) — each with its own bot tokens, +sessions, and memory — as managed services on a single machine. This page +covers the operational concerns: starting them all together, viewing logs +across profiles, preventing the host from sleeping, and recovering from common +launchd/systemd quirks. + +If you only run one Hermes agent, you don't need this page — see +[Profiles](./profiles.md) for the basics. + +## When to use this + +You want this setup when you have two or more Hermes agents that should all +be online at the same time. Common reasons: + +- A personal assistant on one Telegram bot and a coding agent on another +- One agent per family member or one per Slack workspace +- Sandbox + production instances of the same configuration +- A research agent + a writing agent + a cron-driven bot — each with isolated + memory and skills + +Every profile already gets its own per-platform LaunchAgent +(`ai.hermes.gateway-.plist`) or systemd user service +(`hermes-gateway-.service`). This guide adds the patterns for managing +them collectively. + +## Quick start + +```bash +# Create profiles (once) +hermes profile create coder +hermes profile create personal-bot +hermes profile create research + +# Configure each +coder setup +personal-bot setup +research setup + +# Install each gateway as a managed service +coder gateway install +personal-bot gateway install +research gateway install + +# Start them all +coder gateway start +personal-bot gateway start +research gateway start +``` + +That's it — three independent agents, each on its own process, restarting +automatically on crash and on user login. + +## Start, stop, or restart all gateways at once + +The CLI ships with single-profile lifecycle commands. To act across every +profile, wrap them in a shell loop. Put the snippet below in +`~/.local/bin/hermes-gateways` and `chmod +x` it: + +```sh +#!/bin/sh +set -eu + +# Add or remove profile names here as you create / delete profiles. +profiles="default coder personal-bot research" + +usage() { + echo "Usage: hermes-gateways {start|stop|restart|status|list}" +} + +run_for_profile() { + profile="$1" + action="$2" + if [ "$profile" = "default" ]; then + hermes gateway "$action" + else + hermes -p "$profile" gateway "$action" + fi +} + +action="${1:-}" +case "$action" in + start|stop|restart|status) + for profile in $profiles; do + echo "==> $action $profile" + run_for_profile "$profile" "$action" + done + ;; + list) + hermes gateway list + ;; + *) + usage + exit 2 + ;; +esac +``` + +Then: + +```bash +hermes-gateways start # start every configured profile +hermes-gateways stop # stop every configured profile +hermes-gateways restart # restart all +hermes-gateways status # status across all +hermes-gateways list # delegates to `hermes gateway list` +``` + +:::tip +The `default` profile is targeted with `hermes gateway ` (no `-p`), +not `hermes -p default gateway `. The wrapper above handles both forms. +::: + +## Manage one profile + +The shortcut commands every profile installs: + +```bash +coder gateway run # foreground (Ctrl-C to stop) +coder gateway start # start the managed service +coder gateway stop # stop the managed service +coder gateway restart # restart +coder gateway status # status +coder gateway install # create the LaunchAgent / systemd unit +coder gateway uninstall # remove the service file +``` + +These are equivalent to `hermes -p coder gateway ` — useful if a +profile alias is not on `PATH` or if you target profiles dynamically from a +script. + +## Service files + +Each profile installs its own service with a unique name, so installations +never clash: + +| Platform | Path | +| -------- | ----------------------------------------------------------------- | +| macOS | `~/Library/LaunchAgents/ai.hermes.gateway-.plist` | +| Linux | `~/.config/systemd/user/hermes-gateway-.service` | + +The default profile keeps the historical names: `ai.hermes.gateway.plist` / +`hermes-gateway.service`. + +## Viewing logs + +Each profile writes to its own log files: + +```bash +# Default profile +tail -f ~/.hermes/logs/gateway.log +tail -f ~/.hermes/logs/gateway.error.log + +# Named profile +tail -f ~/.hermes/profiles//logs/gateway.log +tail -f ~/.hermes/profiles//logs/gateway.error.log +``` + +Stream every profile's log simultaneously: + +```bash +tail -f ~/.hermes/logs/gateway.log ~/.hermes/profiles/*/logs/gateway.log +``` + +The CLI also has a structured log viewer: + +```bash +hermes logs --tail # follow default profile +hermes -p coder logs --tail # follow one profile +hermes logs --help # filters, levels, JSON output +``` + +## Identify what's actually running + +```bash +hermes profile list # profiles + model + gateway state +hermes-gateways status # full status across every profile +launchctl list | grep hermes # macOS — PIDs and labels +systemctl --user list-units 'hermes-gateway-*' # Linux — units +``` + +## Editing configuration + +Every profile keeps its config inside its own directory: + +``` +~/.hermes/profiles// +├── .env # API keys, bot tokens (chmod 600) +├── config.yaml # model, provider, toolsets, gateway settings +└── SOUL.md # personality / system prompt +``` + +The default profile uses `~/.hermes/` directly with the same three files. + +Edit them with any editor or via the CLI: + +```bash +hermes config set model.model anthropic/claude-sonnet-4 # default profile +coder config set model.model openai/gpt-5 # named profile +``` + +After editing `.env` or `config.yaml`, restart the affected gateway: + +```bash +coder gateway restart +# or, for everything: +hermes-gateways restart +``` + +## Keeping the host awake + +The gateway process can run all day, but the operating system will still try +to sleep when idle. Two patterns: + +### macOS — `caffeinate` + +`caffeinate` is built into macOS and prevents sleep while it runs. No install. + +```bash +caffeinate -dis # block display, idle, and system sleep +caffeinate -dis -t 28800 # same, auto-exit after 8 hours +caffeinate -i -w $(cat ~/.hermes/gateway.pid) & # awake while default gateway runs + +# Persistent: run in background and forget +nohup caffeinate -dis >/dev/null 2>&1 & +disown + +# Inspect / stop +pmset -g assertions | grep -iE 'caffeinate|prevent|user is active' +pkill caffeinate +``` + +| Flag | Effect | +| ------ | ------------------------------------------------- | +| `-d` | block display sleep | +| `-i` | block idle system sleep (default) | +| `-m` | block disk sleep | +| `-s` | block system sleep (AC-powered Macs only) | +| `-u` | simulate user activity (prevents screen lock) | +| `-t N` | auto-exit after `N` seconds | +| `-w P` | exit when PID `P` exits | + +:::warning Lid-close still sleeps the Mac +`caffeinate` cannot override the hardware-driven lid-close sleep on MacBooks. +For lid-closed operation, change your Energy Saver / Battery preferences or +use a third-party tool. +::: + +### Linux — `systemd-inhibit` or `loginctl` + +```bash +# Inhibit suspend while a command runs +systemd-inhibit --what=idle:sleep --who=hermes --why="gateways running" \ + sleep infinity & + +# Allow user services to keep running after logout (recommended) +sudo loginctl enable-linger "$USER" +``` + +After enabling lingering, your systemd user units (including +`hermes-gateway-.service`) continue running across SSH disconnects +and reboots. + +## Token-conflict safety + +Each profile must use unique bot tokens for each platform. If two profiles +share a Telegram, Discord, Slack, WhatsApp, or Signal token, the second +gateway refuses to start with an error naming the conflicting profile. + +To audit: + +```bash +grep -H 'TELEGRAM_BOT_TOKEN\|DISCORD_BOT_TOKEN' \ + ~/.hermes/.env ~/.hermes/profiles/*/.env +``` + +## Updating the code + +`hermes update` pulls the latest code once and syncs new bundled skills into +every profile: + +```bash +hermes update +hermes-gateways restart +``` + +User-modified skills are never overwritten. + +## Troubleshooting + +### "Could not find service in domain for user gui: 501" + +You ran `hermes gateway start` after a previous `hermes gateway stop`. The +CLI's `stop` does a full `launchctl unload`, which removes the service from +launchd's registry. The CLI catches this specific error on `start` and +automatically re-loads the plist (`↻ launchd job was unloaded; reloading +service definition`). The service starts normally. Nothing to fix. + +### Stale PID after a crash + +If a profile's gateway shows `not running` but a process is still alive: + +```bash +ps -ef | grep "hermes_cli.*-p " +cat ~/.hermes/profiles//gateway.pid +kill -TERM # graceful +kill -KILL # if that fails after a few seconds + gateway start +``` + +### Forcing a hard reset of one service + +```bash +# macOS +launchctl unload ~/Library/LaunchAgents/ai.hermes.gateway-.plist +launchctl load ~/Library/LaunchAgents/ai.hermes.gateway-.plist + +# Linux +systemctl --user restart hermes-gateway-.service +``` + +### Health check + +```bash +hermes doctor # default profile +hermes -p doctor # one profile +```