hermes: CDP browser daemon + MCP client wiring for the agent

Adds a digest-pinned headless-shell Chrome DevTools Protocol daemon as a
boot-persistent quadlet unit (chrome-devtools-protocol.container). The
entrypoint override drops the image's socat front so the CDP endpoint binds
loopback-only on 127.0.0.1:9222 in the host network namespace; only the two
host-network consumers (Signal gateway, webui server) can drive it.

hermes-webui flips Network=dankclaw_default -> Network=host so its agent
process shares the daemon's namespace (0.0.0.0:8787 bind is LAN-facing as
before; no exposure change). deploy_hermes installs the new unit and starts
it if inactive without restarting in-flight browser sessions.
This commit is contained in:
Frank committed 2026-08-31 16:25:28 -07:00
1 parent f1dd586fbb
commit 207235420c
4 files changed
+74 -10

No files matched your search

+12 -8
View File
@@ -1,4 +1,4 @@
# Quadlet unit: Hermex webui (rootless, boot-persistent).
# Quadlet unit: Hermex webui (Hermex) container (rootless, boot-persistent).
#
# Install path on dankclaw: ~/.config/containers/systemd/hermes-webui.container
# Unit name: hermes-webui.service (container name: hermes-webui)
@@ -7,10 +7,16 @@
# foreground mode: bootstrap.py creates .venv inside the mounted checkout,
# installs WebUI + agent deps, then execs server.py as a single foreground
# process. It reads HERMES_HOME=/data, so it drives the SAME agent state
# (memory, skills, sessions) as the gateway container. Published on the LAN
# interface only (192.168.0.143:8787): password-gated (HERMES_WEBUI_PASSWORD
# in the host .env, git-ignored), so LAN clients connect directly and the
# SSH tunnel (phone -> Dankclaw:22) remains the sole off-network ingress.
# (memory, skills, sessions) as the gateway container.
#
# Host networking on purpose: the agent's chrome-devtools MCP client spawns
# npx chrome-devtools-mcp with --browserUrl=http://127.0.0.1:9222, and the CDP
# daemon binds loopback-only in the host network namespace (see
# chrome-devtools-protocol.container). Only processes sharing that namespace
# can drive the browser; a bridge-network client could not reach it at all.
# The server therefore listens on 0.0.0.0:8787 of the host netns (LAN-facing as
# before); access control is the password gate, and the SSH tunnel
# (phone -> Dankclaw:22) remains the sole off-network ingress path.
[Unit]
Description=Hermes webui (Hermex) container
@@ -19,9 +25,8 @@ Description=Hermes webui (Hermex) container
ContainerName=hermes-webui
Image=localhost/hermes-agent:latest
Pull=never
Network=dankclaw_default
Network=host
EnvironmentFile=/home/dank/serverAppConfigs/dankclaw/.env
Environment=HERMES_HOME=/data
Environment=HERMES_WEBUI_HOST=0.0.0.0
Environment=HERMES_WEBUI_PORT=8787
Environment=HERMES_WEBUI_PASSWORD=${HERMES_WEBUI_PASSWORD}
@@ -30,7 +35,6 @@ Environment=HERMES_WEBUI_PASSWORD=${HERMES_WEBUI_PASSWORD}
Environment=HERMES_WEBUI_SKIP_ONBOARDING=1
Volume=/home/dank/hermes-data:/data
Volume=/home/dank/hermes-webui:/webui
PublishPort=192.168.0.143:8787:8787
Exec=python3 /webui/bootstrap.py --foreground
# JSON exec form on purpose: this quadlet generator joins a bare HealthCmd
# into ONE argv element with \x20 escapes and then shell-executes it — the