Files
serverAppConfigs/docs/Dankclaw/Hermes-WebUI.md
Frank bb0404c310 hermes-webui: add Hermex server as loopback compose service on Dankclaw
- compose: hermes-webui runs bootstrap.py --foreground in the agent image,
  shares /home/dank/hermes-data (same uid mapping as the gateway), publishes
  127.0.0.1:8787 only; password from host .env
- planner/remote deployer: hermes-webui is an isolated Dankclaw service
- tests: deployment integrity seam (loopback, auth, volume, healthcheck,
  planner narrowing, deployer dispatch)
- docs: Hermes-WebUI page with the iOS SSH-tunnel access pattern
2026-08-26 08:17:27 -07:00

4.6 KiB

type, title, description, tags, timestamp
type title description tags timestamp
configuration Hermes-WebUI (Hermex server) The hermes-webui compose service on Dankclaw — the loopback-only API server that the Hermex iPhone app talks to over an SSH tunnel.
hermes
hermes-webui
hermex
dankclaw
ssh-tunnel
2026-09-14

Hermes-WebUI (Hermex server)

hermes-webui is the hermes-webui server (MIT, third-party) that the Hermex iPhone app connects to. It runs the Hermes agent in-process from the shared HERMES_HOME, so it is the same agent — same memory, skills, and sessions — as the Hermes gateway and the Hermes-Dashboard.

Host: Dankclaw — Port: 127.0.0.1:8787 (loopback only) — Ingress: SSH tunnel

Facts

Item Value
Image docker.io/nousresearch/hermes-agent:latest (same as the gateway)
Command python3 /webui/bootstrap.py --foreground
Source checkout /home/dank/hermes-webui on Dankclaw (mounted at /webui)
Port 8787, published to 127.0.0.1 only
Auth HERMES_WEBUI_PASSWORD (host .env; Hermex signs in with it)
Data shares /home/dank/hermes-data with the gateway and dashboard
Health GET /health (compose healthcheck + deployer)

Why a container, not a host process

The gateway's state files are owned by the container-mapped uid (109999, from rootless Podman's 1 → 100000/65536 subuid range). A fresh rootless namespace on Dankclaw maps identically (verified with podman unshare cat /proc/self/uid_map), so a second container using the same image reads and writes the shared volume with exactly the ownership the gateway already uses. A host-side process running as dank (uid 1000) could not read the gateway's 0600 state files.

How it works

  • bootstrap.py creates /webui/.venv on first boot, pip-installs the WebUI and agent requirements, then execs server.py as one foreground process (--foreground). systemd-free: Podman supervises it via restart: unless-stopped.
  • HERMES_WEBUI_SKIP_ONBOARDING=1 keeps the first-run wizard off; a profile already exists in /data from the gateway.
  • The healthcheck start_period is 600 s because the very first boot spends minutes pip-installing into a fresh venv.

Remote access (Hermex on iOS, from anywhere)

Only port 22 is forwarded to Dankclaw at the router, so the webui is reached through an SSH local forward — no extra router ports, no third-party relay (no Tailscale, no Cloudflare):

  1. On the iPhone, open Termius (or any SSH client with local port forwarding) and connect to <dankclaw-public>:22 with the usual key.
  2. On that connection, add a local forward: 127.0.0.1:8787 → 127.0.0.1:8787. The forward runs inside the encrypted SSH session; nothing else on the phone routes through it.
  3. In Hermex, set the server URL to http://localhost:8787 and sign in with the webui password.

The tunnel lives as long as the SSH session on the phone — iOS does not allow a true background daemon, so connect before you use Hermex.

graph LR
    A[Hermex app] -->|http://localhost:8787| B[SSH local forward on iPhone]
    B ==>|encrypted SSH to public IP:22| C[Dankclaw sshd]
    C --> D["hermes-webui 127.0.0.1:8787"]
    D --> E["/home/dank/hermes-data (shared agent state)"]

Deployment

./scripts/deploy.sh dankclaw --service hermes-webui

The planner maps changes to the hermes-webui compose block (and only that block) to this service, so gateway and inference restarts stay isolated. Before first deploy, set a strong HERMES_WEBUI_PASSWORD in the live dankclaw/.env.

Gotchas

  • The /webui mount is rw on purpose: bootstrap.py creates .venv in the repo root. Do not flip it to :ro.
  • Never publish 8787 beyond loopback without keeping the password set — the webui can run agent commands.
  • The webui and the gateway share one data volume. A config change made on one side takes effect when the other restarts (same caveat as the dashboard).
  • bootstrap.py auto-installs a Hermes Agent if it cannot find one; in this image the agent is already present, so that path should never fire. If the log shows an install attempt, the image changed — re-verify the entrypoint.

Citations

See Also