From e062fdfeff80996d3d8abc8bb62dbca89e45d41c Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 30 Aug 2026 07:42:07 -0700 Subject: [PATCH] hermes: gateway dials the signal-cli daemon over loopback on host networking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gateway unit ran on dankclaw_default and could not reach the host daemon: from that network only DNAT'd published ports accept connections to the LAN address. The compose era ran the gateway with network_mode: host, so the daemon was reachable; the unit silently changed topology at takeover and broke the adapter. Host networking + a loopback dial removes the dependency entirely — the gateway is an outbound-only client and publishes nothing. --- dankclaw/compose.yaml | 12 +++++++----- dankclaw/hermes/quadlet/hermes.container | 15 +++++++++------ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/dankclaw/compose.yaml b/dankclaw/compose.yaml index 14d065c..2575785 100644 --- a/dankclaw/compose.yaml +++ b/dankclaw/compose.yaml @@ -52,6 +52,7 @@ services: hermes: image: docker.io/nousresearch/hermes-agent:latest container_name: hermes + network_mode: host restart: unless-stopped # The image's default s6 entrypoint runs the interactive TUI, which exits # without a TTY and crash-loops the container. Override it to run the @@ -62,11 +63,12 @@ services: HERMES_DATA_DIR: /data # Point hermes's config/.env at the mounted data dir (persistent). HERMES_HOME: /data - # Signal adapter -> the host signal-cli daemon (:8080, host process). - # From container space the host LAN IP only accepts connections to - # published ports (netavark DNAT); un-forwarded host ports refuse - # instantly. The gateway alias is the verified working path (2026-08-30). - SIGNAL_HTTP_URL: "http://host.containers.internal:8080" + # Signal adapter -> the signal-cli daemon, a HOST process listening on + # :8080. The service runs with network_mode: host below, so it dials + # loopback directly; from a bridge network only DNAT'd published ports + # accept connections to the host LAN IP, and un-forwarded ports refuse + # instantly (verified live 2026-08-30). + SIGNAL_HTTP_URL: "http://127.0.0.1:8080" SIGNAL_ACCOUNT: "${SIGNAL_ACCOUNT}" SIGNAL_ALLOWED_USERS: "${SIGNAL_ALLOWED_USERS}" ports: diff --git a/dankclaw/hermes/quadlet/hermes.container b/dankclaw/hermes/quadlet/hermes.container index 8f32fc7..45e1803 100644 --- a/dankclaw/hermes/quadlet/hermes.container +++ b/dankclaw/hermes/quadlet/hermes.container @@ -10,10 +10,13 @@ # (same splice pattern proven live on authentik-worker). The gateway drops to # the unprivileged hermes user (uid 10000) at runtime; /data is world-writable # on purpose so that unmapped uid can write agent.log and its state there. -# SIGNAL_HTTP_URL uses the gateway alias, not the host LAN IP: from container -# space on this network only DNAT'd published ports accept connections to the -# LAN address; un-forwarded host ports refuse instantly (verified live -# 2026-08-30). The alias resolves to the network's own gateway IP. +# +# Host networking: the adapter dials the signal-cli daemon, a HOST process +# listening on :8080. The compose era dialed the host LAN IP from a bridge +# network, where only DNAT'd published ports accept connections to that +# address — un-forwarded host ports refuse instantly (verified live +# 2026-08-30). A host-network client dials 127.0.0.1:8080 with no such +# dependency, and the gateway publishes nothing inbound. [Unit] Description=Hermes agent gateway container @@ -24,11 +27,11 @@ Wants=network-online.target ContainerName=hermes Image=localhost/hermes-agent:latest Pull=never -Network=dankclaw_default +NetworkMode=host EnvironmentFile=/home/dank/serverAppConfigs/dankclaw/.env Environment=HERMES_DATA_DIR=/data Environment=HERMES_HOME=/data -Environment=SIGNAL_HTTP_URL=http://host.containers.internal:8080 +Environment=SIGNAL_HTTP_URL=http://127.0.0.1:8080 Environment=SIGNAL_ACCOUNT=${SIGNAL_ACCOUNT} Environment=SIGNAL_ALLOWED_USERS=${SIGNAL_ALLOWED_USERS} Volume=/home/dank/hermes-data:/data