The docs claimed ntfy keeps push metadata on your own infrastructure, but the default NTFY_SERVER_URL is the public https://ntfy.sh cloud service. Make explicit that push metadata (topic, notification title) passes through ntfy.sh unless you self-host ntfy.
11 KiB
Install — Gateway & App
A step-by-step guide for getting Iris (the Android / Desktop app) talking to your hermes gateway. Written for people who just want to use it, not build it. If you only want the short version, the README has the three commands that matter.
The whole setup has two halves:
- The gateway — a small plugin that runs inside your existing hermes install and opens a door for the app to connect through.
- The app — on your phone or desktop, where you enter the gateway's address and a pairing token.
Old guides? Earlier versions of Iris used a WebSocket on port
8790(ws://…/ws). The transport is now plain HTTP on port8791(see19-http-fallback-transport.md). The app still accepts oldws://URLs and converts them automatically, but new setups should use thehttp://URL printed byhermes gateway setup.
What you need
| Where | What |
|---|---|
| Gateway host (any always-on computer: home server, Raspberry Pi, laptop) | hermes-agent installed with its venv (cd hermes-agent && uv sync) |
| Phone / desktop | Android 8+ or Linux / macOS / Windows |
| Only if you build the app yourself | JDK 17 (+ Android SDK for Android) — see 12-toolchain.md |
Part 1 — Install the gateway plugin (one-time)
Iris is a regular hermes platform plugin, so it installs with the normal
plugin command. This repo is a monorepo (the plugin lives in the
gateway-plugin/ subfolder, next to the app), so you point the installer at
that subfolder with a #subfolder suffix:
hermes plugins install git@gitea.zephyre.one:ARIA/iris_x_hermes.git#gateway-plugin
That's it. The installer clones the repo, copies just the gateway-plugin/
folder into ~/.hermes/plugins/, and asks whether to enable it now (say
yes).
Notes:
-
Any git URL works with the
#gateway-pluginsuffix — e.g.https://gitea.zephyre.one/ARIA/iris_x_hermes.git#gateway-pluginif you prefer HTTPS. -
Developing from a checkout? Skip the install and symlink instead — the plugin then always tracks your working tree:
mkdir -p ~/.hermes/plugins ln -s "$PWD/gateway-plugin" ~/.hermes/plugins/iris -
Check it was picked up:
hermes gateway status # the Iris platform should be listed
Part 2 — Gateway setup (one-time)
Run the interactive setup:
hermes gateway setup
It walks you through five things:
| Prompt | What it means | Default |
|---|---|---|
| Iris pairing token | A long random secret the app must present to connect. Generated for you; stored in ~/.hermes/.env as IRIS_TOKEN. It is printed only once — write it down. |
auto-generated |
| HTTP host | Which network address the gateway listens on. 127.0.0.1 = only this machine. For a phone on your home network, use the machine's LAN IP (e.g. 192.168.1.10). |
127.0.0.1 |
| Port | The port the app connects to. | 8791 |
| Push backend | How offline notifications are delivered: ntfy (default, stays on your own infrastructure) or fcm (Google). See Part 5. |
ntfy |
| TLS | Only asked when no certificate is configured yet: generates a self-signed certificate + key under ~/.hermes/iris/ and stores the paths in .env (IRIS_HTTP_CERT / IRIS_HTTP_KEY), so the gateway serves https://. The app asks you to confirm the printed SHA-256 fingerprint once (like an SSH host key). |
No (Yes if you bound 0.0.0.0) |
When it finishes it prints two things you need for the app:
- Server URL — e.g.
http://192.168.1.10:8791 - Pairing QR + URL — an
iris://pair?…string with a scannable QR code
Then start the gateway:
hermes gateway # (or: hermes gateway restart after changes)
Part 3 — Install the app
Android
Build a debug APK on any machine with JDK 17 + the Android SDK:
cd app
./gradlew :androidApp:assembleDebug
# → app/androidApp/build/outputs/apk/debug/androidApp-debug.apk
Copy the APK to the phone (cable, LAN share, or any file transfer) and open it — Android will ask to allow installs from unknown sources.
Shortcut for developers with a USB-connected phone:
./gradlew :androidApp:installDebug installs it directly.
Desktop
cd app
./gradlew :desktopApp:jpackage # → app/desktopApp/build/…/ (native app, JRE bundled)
On Linux the launcher may print a pure virtual method called warning —
it's a known, harmless jpackage bug (JDK-8348560); the app works fine.
Part 4 — Connect the app
Open the app. The first screen is Connect. You need the Server URL and the pairing token from Part 2.
Option A — Same home network (no encryption, simplest)
Works out of the box on a trusted home network:
- Server URL: the one printed by
hermes gateway setup, e.g.http://192.168.1.10:8791.- On a phone, use the gateway's LAN IP — not
127.0.0.1(that only means "this device" and won't reach your server). - If you set the host to
127.0.0.1during setup, re-runhermes gateway setupand enter the LAN IP instead.
- On a phone, use the gateway's LAN IP — not
- Pairing token: the long token from the setup output (or
grep IRIS_TOKEN ~/.hermes/.envon the gateway host). - Test & Connect.
Android shortcut: the Connect screen has a Scan QR button — point the camera at the QR printed by
hermes gateway setupand the URL + token fill themselves in. (Desktop has no camera, so it's manual entry.)
Option B — Encrypted (TLS) — recommended for anything beyond your LAN
Plain http:// is fine on a home network you trust, but for remote access
you want the traffic encrypted. The gateway can serve https:// itself:
-
Create a certificate + key. Three flavors:
- Generated by setup (easiest):
hermes gateway setupoffers to generate a self-signed certificate for you (see the TLS prompt in Part 2). It writes~/.hermes/iris/iris.crt+iris.key, stores the paths in.env, and prints the SHA-256 fingerprint the app will ask you to confirm. - CA-signed (Let's Encrypt, or your own CA): works out of the box.
- Self-signed manually (e.g.
openssl req -x509 -newkey rsa:2048 -nodes -keyout iris.key -out iris.crt -days 3650 -subj "/CN=iris" -addext "subjectAltName=DNS:iris.example.com,IP:192.168.1.10"): the certificate must carry a SAN entry matching the host you'll type in the app.
- Generated by setup (easiest):
-
Put the paths in
~/.hermes/.envon the gateway host:IRIS_HTTP_CERT=/path/to/iris.crt IRIS_HTTP_KEY=/path/to/iris.key -
hermes gateway restart. -
In the app, use the
https://URL, e.g.https://iris.example.com:8791.
Self-signed certificates: the app won't trust them automatically (by
design). On first connect it shows the certificate's SHA-256 fingerprint and
asks you to confirm it — exactly like an SSH host key. Compare the
fingerprint with the one on the gateway host
(openssl x509 -fingerprint -sha256 -noout -in iris.crt), confirm, and it's
pinned in the app's secure storage from then on. If the certificate ever
changes, you'll be asked to confirm again. No system trust-store installs
needed.
Reaching the gateway from outside your home network
Pick one (in order of preference):
- Tailscale / WireGuard (recommended). Install Tailscale on the gateway
host; the app connects to the stable tailnet IP, e.g.
http://100.x.y.z:8791. No public exposure at all — and since the traffic travels inside the encrypted mesh, plainhttp://is acceptable here. - Reverse proxy / tunnel (Caddy, Cloudflare Tunnel, ngrok). Terminate TLS
at the edge and forward to
127.0.0.1:8791on the gateway host. - Public bind + TLS + strong token (
IRIS_HTTP_HOST=0.0.0.0+ Option B). Last resort — the port is then reachable from the internet; the token and TLS are what protect it.
Part 5 — Push notifications (optional)
Push wakes a backgrounded or offline phone so you see replies even when the app is closed. Nothing is lost either way — on reconnect the app syncs its outbox.
- ntfy (default) — the phone generates its own topic automatically; the
gateway publishes to it. ⚠️ The default server is the public
https://ntfy.shcloud service — push metadata (topic, notification title) passes through ntfy.sh's servers. SetNTFY_SERVER_URLto a self-hosted ntfy to keep push metadata on your own infrastructure — that is the private option (and also more reliable: the publicntfy.shSSE endpoint is flaky). - FCM (opt-in,
IRIS_PUSH_BACKEND=fcm) — standard and reliable, but push metadata (notification title, device token) is routed through Google's servers. Needs a Firebase project +google-services.jsonin the app build. Without it, FCM is inert and ntfy is the path.
Details: 08-push.md.
Gateway options (reference)
Everything is configured in ~/.hermes/.env on the gateway host (or via the
prompts of hermes gateway setup). After changes: hermes gateway restart.
| Variable | What it does | Default |
|---|---|---|
IRIS_TOKEN |
Pairing token the app must present. | — (required) |
IRIS_HTTP_HOST |
Bind address. 127.0.0.1 = local only; LAN IP = same network; 0.0.0.0 = all interfaces. |
127.0.0.1 |
IRIS_HTTP_PORT |
Port the app connects to. | 8791 |
IRIS_HOME_CHANNEL |
Default chat for cron/notification delivery. | default |
IRIS_ALLOWED_USERS |
Comma-separated device ids allowed to connect (empty = token-only auth). | empty |
IRIS_ALLOW_ALL_USERS |
Allow any paired device (dev only). | false |
IRIS_PUSH_BACKEND |
ntfy or fcm. |
ntfy |
IRIS_FCM_SERVICE_ACCOUNT |
Path to Firebase service-account JSON (FCM). | — |
IRIS_FCM_SERVER_KEY |
Legacy FCM server key (fallback). | — |
NTFY_SERVER_URL |
ntfy server. Self-hosting recommended. | https://ntfy.sh |
NTFY_AUTH_TOKEN |
Auth token for a private ntfy topic (real trust boundary). | — |
IRIS_HTTP_CERT / IRIS_HTTP_KEY |
TLS cert/key paths → serves https:// (see Part 4, Option B). |
— |
Security model (tokens, device allowlist, transport):
09-pairing-security.md.
Troubleshooting
| Symptom | Likely cause / fix |
|---|---|
auth failed on connect |
Wrong token. Check IRIS_TOKEN in ~/.hermes/.env (setup prints it only when it generates it). |
| Connection refused | Gateway not running (hermes gateway status); wrong URL (port 8791, LAN IP instead of 127.0.0.1 from a phone); firewall blocking the port. |
| Phone can't reach the gateway | Gateway bound to 127.0.0.1 — re-run hermes gateway setup and set the LAN IP; or the phone is on a different network/VLAN. |
| TLS handshake fails | Cert has no SAN matching the URL host; or the pinned fingerprint is stale after a cert change (re-confirm in the app). |
| Push not arriving | Backend not configured (check ~/.hermes/logs/gateway.log); ntfy.sh flakiness — self-host ntfy. |
| Start over on a phone | adb shell pm clear dev.iris.app wipes the app's pairing state. |
Logs: ~/.hermes/logs/gateway.log or hermes logs --follow.