Hermex

Set up Hermex

Run, expose, connect

Hermex is a native iPhone client for a server you run yourself: the open-source hermes-webui (MIT). Three moves and you’re steering it from your phone — about 15 minutes, no cloud account required.

What you’ll need

  • A machine you control to run the server — macOS, Linux, or Windows via WSL2 — with Python 3.11+ and git.
  • A Hermes Agent setup — if it’s missing, the bootstrap below offers to install it for you.
  • An iPhone with Hermex installed (free on the App Store).

Run your server

Clone hermes-webui and run its bootstrap. It finds (or installs) Hermes Agent, sets up a Python environment, starts the server, and walks you through first-run onboarding:

git clone https://github.com/nesquena/hermes-webui.git
cd hermes-webui
python3 bootstrap.py

By default the server listens on 127.0.0.1:8787 — local-only until you expose it in step 02. To relaunch later, run ./start.sh from the repo.

Set a password

Auth is off by default for localhost, but you’re about to put this on a network — set a password before exposing it (Hermex asks for it when you connect). Either flip it on in the server’s Settings panel, or via environment variable:

HERMES_WEBUI_PASSWORD=your-secret ./start.sh

Keep it running (optional)

For an always-on box, the bundled launcher runs it as a background daemon:

./ctl.sh start    # background daemon
./ctl.sh status   # uptime, bound host/port, health
./ctl.sh stop

Verify

curl http://127.0.0.1:8787/health

Make it reachable

Your iPhone needs a way to reach the server. Pick one path — both end with a URL you’ll type into Hermex:

Option A — Cloudflare Tunnel (public HTTPS hostname)

Cloudflare Tunnel dials out from your machine to Cloudflare’s edge, so nothing is port-forwarded and the server can stay bound to 127.0.0.1. TLS terminates at Cloudflare — Hermex sees real HTTPS. To try it instantly (prints a temporary trycloudflare.com URL that changes on every run):

brew install cloudflared   # or see Cloudflare's install docs
cloudflared tunnel --url http://localhost:8787

For a permanent hostname on a domain you’ve added to Cloudflare, create a named tunnel:

cloudflared tunnel login
cloudflared tunnel create hermes
cloudflared tunnel route dns hermes hermes.yourdomain.com
cloudflared tunnel run --url http://localhost:8787 hermes

Your server URL is https://hermes.yourdomain.com. The hostname is public, so the password from step 01 is required here, not optional.

Option B — Tailscale (private network)

Tailscale puts your server and iPhone on the same private WireGuard network — nothing is exposed to the public internet. Install it on both devices, sign in to the same tailnet, then start the server listening on all interfaces with a password:

HERMES_WEBUI_HOST=0.0.0.0 HERMES_WEBUI_PASSWORD=your-secret ./start.sh
tailscale ip -4   # your server's Tailscale address

Your server URL is http://<tailscale-ip>:8787. Plain HTTP is fine here — Tailscale encrypts everything on the wire with WireGuard, and Hermex accepts Tailscale’s 100.x addresses for exactly this setup. One precision: binding to 0.0.0.0 also answers on your local network, not just the tailnet — that password is doing real work.

Connect Hermex

  1. Install Hermex from the App Store and open it on your iPhone.
  2. Enter your Server URL from step 02 — the Cloudflare hostname or the Tailscale address.
  3. Tap Test Connection. Hermex pings your server’s /health endpoint and confirms it can reach it.
  4. Enter the password you set in step 01 and sign in. Your server URL is stored in the iOS Keychain, on your phone; the password isn’t stored after login — Hermex talks only to your server and collects no data.

Troubleshooting

The app can’t reach the server

Work outward. On the server itself, check curl http://127.0.0.1:8787/health (and ./ctl.sh status if you run the daemon). If that works, test the public URL from another device’s browser. Whichever hop fails is the one to fix.

The tunnel URL stopped working

Quick tunnels (trycloudflare.com) get a new URL every time cloudflared restarts — switch to a named tunnel for a stable hostname. For named tunnels, check cloudflared tunnel info hermes and make sure cloudflared is still running.

The Tailscale address doesn’t respond

Confirm both devices are signed into the same tailnet and show as connected in the Tailscale app. Then make sure the server was started with HERMES_WEBUI_HOST=0.0.0.0 — the default binding is localhost-only, which Tailscale can’t reach.

Sign-in keeps failing

The password in Hermex must match HERMES_WEBUI_PASSWORD on the server (or the one set in its Settings panel). If you set the variable inline, remember it only applies to that launch — daemon restarts need it set again or persisted in the server’s .env.

Port 8787 is already taken

Find the culprit with lsof -i :8787, or move the server with HERMES_WEBUI_PORT=9000 ./start.sh (update your tunnel/URL to match).

Long responses cut off mid-stream

The server keeps streams alive with heartbeats, but Cloudflare’s free tier can drop a connection that produces nothing for ~100 seconds. Reopen the session to pick the conversation back up — nothing is lost on the server.

Still stuck? message me on X — include what /health returns locally and which path (Cloudflare or Tailscale) you’re on. Server-side issues live upstream at nesquena/hermes-webui.