- 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
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. |
|
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.pycreates/webui/.venvon first boot, pip-installs the WebUI and agent requirements, then execsserver.pyas one foreground process (--foreground). systemd-free: Podman supervises it viarestart: unless-stopped.HERMES_WEBUI_SKIP_ONBOARDING=1keeps the first-run wizard off; a profile already exists in/datafrom the gateway.- The healthcheck
start_periodis 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):
- On the iPhone, open Termius (or any SSH client with local port
forwarding) and connect to
<dankclaw-public>:22with the usual key. - 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. - In Hermex, set the server URL to
http://localhost:8787and 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
/webuimount is rw on purpose:bootstrap.pycreates.venvin the repo root. Do not flip it to:ro. - Never publish
8787beyond 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.pyauto-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
- hermes-webui README (env vars,
--foreground,/health): https://github.com/nesquena/hermes-webui - Hermex setup model (server URL + password; Cloudflare/Tailscale are optional, not required): https://hermexapp.com/
See Also
- Hermes — the agent and its Signal gateway
- Hermes-Dashboard — the LAN browser management UI
- Dankclaw Stack