--- type: configuration title: Hermes-WebUI (Hermex server) description: The hermes-webui compose service on Dankclaw — the loopback-only API server that the Hermex iPhone app talks to over an SSH tunnel. tags: - hermes - hermes-webui - hermex - dankclaw - ssh-tunnel timestamp: 2026-09-14 --- # Hermes-WebUI (Hermex server) `hermes-webui` is the [hermes-webui](https://github.com/nesquena/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 `: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. ```mermaid 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 - 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]]