Files
ARIA b065d1783b
CI / Gateway plugin tests (push) Successful in 8m58s
CI / Kotlin tests (android host + desktop) (push) Failing after 13m32s
Docs: clarify ntfy privacy — default server is public ntfy.sh
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.
2026-08-27 09:23:44 +02:00

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:

  1. The gateway — a small plugin that runs inside your existing hermes install and opens a door for the app to connect through.
  2. 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 port 8791 (see 19-http-fallback-transport.md). The app still accepts old ws:// URLs and converts them automatically, but new setups should use the http:// URL printed by hermes 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-plugin suffix — e.g. https://gitea.zephyre.one/ARIA/iris_x_hermes.git#gateway-plugin if 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:

  1. 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.1 during setup, re-run hermes gateway setup and enter the LAN IP instead.
  2. Pairing token: the long token from the setup output (or grep IRIS_TOKEN ~/.hermes/.env on the gateway host).
  3. Test & Connect.

Android shortcut: the Connect screen has a Scan QR button — point the camera at the QR printed by hermes gateway setup and the URL + token fill themselves in. (Desktop has no camera, so it's manual entry.)

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:

  1. Create a certificate + key. Three flavors:

    • Generated by setup (easiest): hermes gateway setup offers 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.
  2. Put the paths in ~/.hermes/.env on the gateway host:

    IRIS_HTTP_CERT=/path/to/iris.crt
    IRIS_HTTP_KEY=/path/to/iris.key
    
  3. hermes gateway restart.

  4. 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, plain http:// is acceptable here.
  • Reverse proxy / tunnel (Caddy, Cloudflare Tunnel, ngrok). Terminate TLS at the edge and forward to 127.0.0.1:8791 on 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.sh cloud service — push metadata (topic, notification title) passes through ntfy.sh's servers. Set NTFY_SERVER_URL to a self-hosted ntfy to keep push metadata on your own infrastructure — that is the private option (and also more reliable: the public ntfy.sh SSE 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.json in 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.