# 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](../README.md) 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`](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](https://github.com/NousResearch/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`](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: ```bash 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: ```bash mkdir -p ~/.hermes/plugins ln -s "$PWD/gateway-plugin" ~/.hermes/plugins/iris ``` - Check it was picked up: ```bash hermes gateway status # the Iris platform should be listed ``` ## Part 2 — Gateway setup (one-time) Run the interactive setup: ```bash 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](#part-5--push-notifications-optional). | `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: ```bash 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: ```bash 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 ```bash 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.) ### 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: 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](#part-2--gateway-setup-one-time)). 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: ```ini 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`](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`](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`.