# Hermes Agent for bob — single-container "recommended s6" mode. # The image runs `hermes gateway run` under s6 supervision, with the dashboard # web UI supervised alongside in the SAME container when HERMES_DASHBOARD is set # (per the image's own startup guidance). Running both in one container keeps # them in one network namespace, so the dashboard can reach the gateway exactly # as upstream's host-networked compose assumes — which two bridge-networked # containers could not. # # Command must be explicit: the image default (no command) is the interactive # CLI, which exits without a TTY under systemd and crash-loops. # # Setup: sudo install -d -o 10000 -g 10000 /var/lib/hermes ; drop config.yaml # (provider: custom -> http://hanzalova.internal:31313/v1) ; daemon-reload ; start. # Dashboard publishes to the LAN at :5100 (agent-zero=5080, open-webui=5090). # ⚠ The dashboard stores provider API keys and has NO auth — trusted LAN only; # front it with an authenticating reverse proxy for any wider exposure. # AutoUpdate=registry keeps it current via the enabled podman-auto-update.timer. [Unit] Description=Hermes Agent (gateway + dashboard) After=network-online.target Wants=network-online.target [Container] Image=git.lair.cafe/lair/hermes:latest ContainerName=hermes AutoUpdate=registry Exec=gateway run PublishPort=5100:9119 Volume=/var/lib/hermes:/opt/data:Z Environment=HERMES_UID=10000 Environment=HERMES_GID=10000 # Enable the supervised dashboard web UI inside this container (binds 0.0.0.0:9119). Environment=HERMES_DASHBOARD=1 # The image fails closed: it refuses a non-loopback (0.0.0.0) dashboard bind # unless OAuth is configured OR --insecure is opted in. We expose on the trusted # LAN without auth (the chosen "LAN port like the others" tradeoff), so opt in. # ⚠ Anyone on the LAN can reach the API-key-storing UI. To switch to real auth, # drop this and set HERMES_DASHBOARD_OAUTH_CLIENT_ID (+ portal) instead. Environment=HERMES_DASHBOARD_INSECURE=1 [Service] Restart=always TimeoutStartSec=300 [Install] WantedBy=default.target