# Iris × Hermes A chat app for [hermes-agent](https://github.com/NousResearch/hermes-agent): a **native Android app** and a **desktop app** (Linux, macOS, Windows) — both built from one shared Kotlin codebase (Compose Multiplatform). Iris pairs with your running `hermes gateway` over a private, token-authenticated connection and gives you a Telegram-quality chat experience with your personal agent: streaming replies, visible reasoning, structured tool activity, channels, threads, media, search, and push notifications. ## Features - **Native Hermes-Gateway integration** — your hermes → gateway → Iris app - **Absolute Privacy!** — everything stays on your own infrastructure (push: ntfy by default; FCM is opt-in and routes push metadata via Google — see [Push notifications](#push-notifications)) - **100 MB file uploads by default** — configurable on the gateway via `max_upload_bytes` (see [Media](docs/07-media.md) §7.7); all limits are set on the gateway side (hermes), not in the app - **No 4,096-character message limit like Telegram** — messages travel over your own gateway (hard frame-body cap: 1 MiB) - **Full markdown support** — tables, checkmarks, bold, inline code, code blocks + syntax highlighting… - **HTML Artifact Preview** — agent-sent HTML/CSS/JS rendered in an in-app WebView - **All settings live in the app**, not in hermes `config.yml`! Change everything on the fly. - **Channels** — create channels to keep track of your reports, cronjobs, webhook calls - **Pin channels, rename them, change the color/icon** - **Customize how your chat should look like** — color? Check! Images? Check! - **Reasoning collapse/expandable** in the chat bubble - **Change the default behavior** of tool & reasoning verbosity - **Threads** — create your own, or let the AI create them with a title - **Search messages from everywhere** ## How it works ``` hermes-agent ──> hermes gateway ──(HTTP :8791)──> Iris app (Android / Desktop) ``` - `gateway-plugin/` is a hermes **platform plugin** (`android`). It runs inside the `hermes gateway` process and opens an HTTP server the apps connect to. Zero new Python dependencies, zero hermes-core changes. - `app/` is one Compose Multiplatform Gradle project: `:shared` (KMP, most of the code), `:androidApp` (native Kotlin + Jetpack Compose client), `:desktopApp` (the same app, tweaked for a big screen). - The app is a first-class hermes *messaging platform*, so everything the gateway already does just works: slash commands, cron delivery, `send_message` routing, coexistence with Telegram/Discord/etc. - Push notifications: ntfy (default) or FCM (opt-in). ## Push notifications Push wakes a backgrounded/offline device; on reconnect the app syncs the outbox, so nothing is lost. - **ntfy (default)** — push metadata stays on your own infrastructure (self-hosted ntfy recommended). This is the backend for truly private communication. - **FCM (opt-in, `IRIS_PUSH_BACKEND=fcm`)** — standard/reliable, but FCM push metadata (notification title, device token) is routed through **Google's servers**. If you want truly private communication, use ntfy instead. Setup: [`docs/install.md`](docs/install.md); details: [`docs/08-push.md`](docs/08-push.md). ## Install the gateway Three commands on the machine where hermes runs: ```bash cd hermes-agent && uv sync # 1. hermes with its venv (separate project, not this repo) # 2. install the Iris plugin — the #gateway-plugin suffix points the # installer at the plugin subfolder of this monorepo hermes plugins install git@gitea.zephyre.one:ARIA/iris_x_hermes.git#gateway-plugin # 3. generate the pairing token + server URL, then run the gateway hermes gateway setup hermes gateway ``` `hermes gateway setup` prints the **server URL** and **pairing token / QR** the app needs on its Connect screen. All options (LAN binding, TLS, push, device allowlist) and the full app pairing walkthrough: [`docs/install.md`](docs/install.md). ## Build from source ### Prerequisites | Where | You need | | --- | --- | | Gateway host | [hermes-agent](https://github.com/NousResearch/hermes-agent) with its venv (`uv sync`) | | Android build machine | JDK 17, Android SDK (`sdk.dir` in `app/local.properties` or `ANDROID_HOME`), ADB with a connected device | | Desktop build machine | JDK 17 only | No system Gradle needed — both apps use the project wrapper (`./gradlew`). ### 1. Gateway (on the gateway host) If you're developing from a checkout, skip `hermes plugins install` and symlink the plugin so it always tracks your working tree: ```bash cd hermes-agent && uv sync mkdir -p ~/.hermes/plugins ln -s "$PWD/gateway-plugin" ~/.hermes/plugins/iris hermes gateway status # should list the Iris platform hermes gateway setup # generates IRIS_TOKEN, prints server URL + pairing QR hermes gateway # run the gateway ``` (Otherwise see [Install the gateway](#install-the-gateway) above.) ### 2. Android app ```bash cd app ./gradlew :androidApp:installDebug # build + install on the connected ADB device # or just build the APK: ./gradlew :androidApp:assembleDebug # → app/androidApp/build/outputs/apk/debug/ ``` ### 3. Desktop app ```bash cd app ./gradlew :desktopApp:run # dev run ./gradlew :desktopApp:jpackage # native app-image (bundles the JRE) ``` ### 4. Pair On the app's **Connect** screen: 1. **Server URL** — `http://:8791` (printed by `hermes gateway setup`). 2. **Pairing token** — from the setup output, or `IRIS_TOKEN` in `~/.hermes/.env` on the gateway host. 3. **Test & Connect.** Notes: - **Android** has a **Scan QR** button that reads the QR printed by `hermes gateway setup` and pre-fills URL + token; desktop uses manual entry. - The default bind is `127.0.0.1` (desktop on the same machine only). For a phone on the LAN, set `IRIS_HTTP_HOST` to the gateway's LAN IP. - Remote access: Tailscale/WireGuard, or a reverse proxy/tunnel with TLS (`IRIS_HTTP_CERT` / `IRIS_HTTP_KEY`). Full walkthrough, push setup (ntfy/FCM), TLS, and troubleshooting: [`docs/install.md`](docs/install.md). ## Contributing Contributions are welcome! Before you start: 1. **Read the docs.** The full reference library is in [`docs/`](docs/README.md) — start with [`docs/00-overview.md`](docs/00-overview.md), then follow the numbered docs (architecture, wire protocol, plugin design, testing, …). 2. **Know the layout.** | Path | What | | --- | --- | | `gateway-plugin/` | Python hermes platform plugin (`android`); `protocol.py` is the frame source of truth | | `app/shared` | KMP module with most of the client code (shared by Android + Desktop) | | `app/androidApp` | Thin Android shell (package `dev.iris.app`) | | `app/desktopApp` | Thin desktop shell | | `docs/` | Numbered reference library | 3. **Run the tests.** - Python (gateway plugin): `cd hermes-agent && scripts/run_tests.sh tests/gateway/test_android.py` (never bare `pytest` — hermes's runner sandboxes `HERMES_HOME`). - Kotlin: `cd app && ./gradlew :shared:testDebugUnitTest` - Live check (gateway must be running): `gateway-plugin/tests/ws_probe.py` and `gateway-plugin/tests/e2e.py` — see [`gateway-plugin/tests/README.md`](gateway-plugin/tests/README.md). 4. **Keep the protocol in sync.** `gateway-plugin/protocol.py`, `app/shared/.../protocol/Protocol.kt`, and `docs/protocol/frames.schema.json` must always agree. 5. **Pre-commit hooks** are configured (`.pre-commit-config.yaml`); run `pre-commit install` once after cloning. Open an issue first for anything big, then send a pull request. ## License Apache License 2.0 — see [LICENSE](LICENSE).