docs: replace stale 'android' name mentions with 'iris'
CI / Gateway plugin tests (push) Successful in 5m19s
CI / Kotlin tests (android host + desktop) (push) Successful in 6m59s

The plugin is named 'iris' (IrisAdapter, IRIS_HOME_CHANNEL, label Iris),
but several docs still referred to it as the android platform/plugin and
to the product as 'the Android app'. Rename name-mentions to iris/IRIS
and product-mentions to 'Iris app'; keep legitimate OS references
(androidApp, Android SDK, Android 10, androidx, test_android.py, ...).

Also includes pi-lens markdown-lint autofixes (table spacing, trailing
newlines) in the touched files.
This commit is contained in:
ARIA committed 2026-08-24 21:44:02 +02:00
1 parent 597a28050f
commit a61b47a947
15 files changed
+45 -41

No files matched your search

+2 -2
View File
@@ -11,8 +11,8 @@ create.
## Goals
- **Native feel.** Real Android app (Kotlin/Compose), not a WebView. Desktop
app that is the same app, resized for a big screen.
- **Native feel.** Real native app (Iris on Android, Kotlin/Compose), not a
WebView. Desktop app that is the same app, resized for a big screen.
- **First-class gateway citizen.** The app is a hermes *messaging platform*, so
everything the gateway already does "just works": slash commands, cron
delivery, `send_message` routing, coexistence with Telegram/Discord/etc.
+5 -5
View File
@@ -11,7 +11,7 @@
│ │ │ │ │
│ │ ▼ legacy stream callbacks (delta / tool-progress / commentary) │ │
│ │ ┌──────────────────────────────┐ ┌────────────────────────────────┐ │ │
│ │ │ android PLATFORM PLUGIN │◄───────►│ WebSocket SERVER (websockets) │ │ │
│ │ │ IRIS PLATFORM PLUGIN │◄───────►│ WebSocket SERVER (websockets) │ │ │
│ │ │ IrisAdapter │ JSON │ ws://host:8790/ws │ │ │
│ │ │ (BasePlatformAdapter) │ frames │ + media + FCM-token + pairing │ │ │
│ │ │ • send / edit / stream │ └───────────────┬────────────────┘ │ │
@@ -26,7 +26,7 @@
│ │ Google FCM cloud │
▼ │ │ │
┌────────────────────────┐ │ ▼ │
│ ANDROID APP │◄──┴── (wake) ┌──────────┐
│ IRIS APP (ANDROID) │◄──┴── (wake) ┌──────────┐
│ (Kotlin / Compose) │ WSS │ PHONE │
│ • WS client (OkHttp) │◄───────────►│ MIX 2S │
│ • ExoPlayer │ │ (API 29) │
@@ -72,7 +72,7 @@ protocol (`04-wire-protocol.md`) that borrows familiar names (`message.*`,
## Key architectural decisions + rationale
| Decision | Rationale |
|---|---|
| --- | --- |
| **Community-style platform plugin** (`register(ctx)` → `ctx.register_platform`) | Zero hermes-core changes; survives hermes updates; follows `ADDING_A_PLATFORM.md` "Plugin Path". |
| **Single WS transport** for chat, streaming, tools, media, pairing, FCM-token | One connection, one auth, one dependency (`websockets`, already core). Media as chunked binary frames avoids adding an HTTP server. |
| **`websockets` + `httpx` only** | Both are hermes *core* deps → the plugin adds **zero** new Python dependencies (respects hermes supply-chain pinning policy). |
@@ -80,7 +80,7 @@ protocol (`04-wire-protocol.md`) that borrows familiar names (`message.*`,
| **Reasoning split in the adapter** | The gateway prepends reasoning to the final text when `show_reasoning` is on; the adapter splits the stable prefix into a `reasoning` field so the app renders a clean collapsible block. |
| **Channels/threads = `chat_id`/`thread_id`** | The gateway's `SessionSource` already models this; cron delivery already targets `platform:chat_id:thread_id`. We map app concepts onto existing primitives. |
| **SQLite outbox + sync cursor** | Offline delivery + reconnect catch-up without re-reading full history. |
| **Compose Multiplatform** | Desktop is "the Android app, tweaked" → share protocol/state/UI; only platform services + layout differ. |
| **Compose Multiplatform** | Desktop is "the Iris app, tweaked" → share protocol/state/UI; only platform services + layout differ. |
## Data flow (one turn)
@@ -103,4 +103,4 @@ protocol (`04-wire-protocol.md`) that borrows familiar names (`message.*`,
> (not the ACP-only event-native `render_message_event` path). We therefore map
> the legacy `send`/`edit_message`/progress calls to our frames. The exact
> tool-progress vs commentary classification is verified empirically in M2 by
> running the real gateway with a test WS client (see `13-testing.md`).
> running the real gateway with a test WS client (see `13-testing.md`).
+4 -4
View File
@@ -12,7 +12,7 @@ and the canonical example `hermes-agent/plugins/platforms/irc/adapter.py`.
```yaml
name: iris-platform
label: Android
label: Iris
kind: platform
version: 0.1.0
description: >
@@ -24,7 +24,7 @@ author: <you>
requires_env:
- name: IRIS_TOKEN
description: "Shared pairing token the app presents on connect"
prompt: "Android pairing token"
prompt: "Iris pairing token"
password: true
optional_env:
- name: IRIS_WS_HOST
@@ -35,7 +35,7 @@ optional_env:
description: "WS port (default 8790)"
prompt: "WS port"
password: false
- name: ANDROID_HOME_CHANNEL
- name: IRIS_HOME_CHANNEL
description: "Default chat id for cron/notification delivery (default default)"
prompt: "Home channel"
password: false
@@ -97,7 +97,7 @@ def register(ctx):
install_hint="No extra packages needed (websockets + httpx are core deps)",
setup_fn=interactive_setup, # hermes gateway setup flow
env_enablement_fn=_env_enablement, # seed extra + home_channel from env
cron_deliver_env_var="ANDROID_HOME_CHANNEL",
cron_deliver_env_var="IRIS_HOME_CHANNEL",
standalone_sender_fn=_standalone_send, # best-effort out-of-proc cron (stretch)
parse_target_ref_fn=_parse_target_ref, # "iris:<chat>[:<thread>]"
allowed_users_env="IRIS_ALLOWED_USERS",
+2 -2
View File
@@ -23,10 +23,10 @@ gateway identity concepts**.
## 6.2 Default chat
- On first connect, the plugin ensures a **default channel** exists:
`chat_id = ANDROID_HOME_CHANNEL` (default `default`), `kind=default`,
`chat_id = IRIS_HOME_CHANNEL` (default `default`), `kind=default`,
`is_default=true`, name "Default".
- It is also the **cron home channel** (`cron_deliver_env_var=
ANDROID_HOME_CHANNEL`), so `deliver=android` (bare) routes here.
IRIS_HOME_CHANNEL`), so `deliver=iris` (bare) routes here.
- The app opens the default chat on launch.
## 6.3 Threads (toggle for overview)
+1 -1
View File
@@ -1,4 +1,4 @@
# 10 — Android App (Kotlin + Jetpack Compose)
# 10 — Iris App (Android; Kotlin + Jetpack Compose)
Native client. Lives in the Compose Multiplatform project at `app/`; the bulk of
the code is in `app/shared` (commonMain) so the Desktop app reuses it.
+5 -5
View File
@@ -1,13 +1,13 @@
# 11 — Desktop App (Kotlin + Compose Multiplatform)
The desktop app is **the Android app, tweaked for a big screen**. It reuses the
The desktop app is **the Iris app, tweaked for a big screen**. It reuses the
entire `app/shared` module (protocol, network, repositories, state, most UI) and
only adds desktop platform services + a wider default layout.
## 11.1 What's shared vs desktop-specific
| Layer | Shared (commonMain) | Desktop-specific (desktopMain) |
|---|---|---|
| --- | --- | --- |
| Protocol / WS client | ✅ | — |
| Repositories / state | ✅ | — |
| Most Compose UI | ✅ | layout tweaks, keyboard shortcuts |
@@ -62,9 +62,9 @@ only adds desktop platform services + a wider default layout.
- The desktop app connects to the **same** gateway WS server as the phone (the
user's home server / Tailscale). It does **not** spawn its own backend (unlike
hermes's existing Electron desktop, which spawns `hermes serve`) — our
desktop is a pure client of the messaging gateway, matching the Android app.
desktop is a pure client of the messaging gateway, matching the Iris app.
## 11.5 Parity checklist (same functionality as Android)
## 11.5 Parity checklist (same functionality as the Iris app)
- [ ] Streaming, reasoning block, tool cards (3-level verbosity), commentary.
- [ ] Channels + threads + new channel + cron target.
@@ -72,4 +72,4 @@ only adds desktop platform services + a wider default layout.
- [ ] Media attach + live playback (via desktop player).
- [ ] Slash command menu + autocomplete + interactive pickers.
- [ ] Push (tray + OS notifications) + outbox/sync.
- [ ] Pairing/Connect screen (URL + token, WSS cert pin).
- [ ] Pairing/Connect screen (URL + token, WSS cert pin).
+1 -1
View File
@@ -132,7 +132,7 @@ display:
```bash
# 1. gateway up with plugin
hermes gateway status | grep -i android
hermes gateway status | grep -i iris
# 2. a raw WS client can pair + echo
python - <<'PY'
+7 -3
View File
@@ -10,16 +10,18 @@ without the app (critical for verifying frame shapes early).
into the hermes `tests/gateway/test_android.py` pattern when running under
hermes's suite).
- **Run with hermes's hermetic runner** (never bare `pytest`):
```bash
cd hermes-agent
scripts/run_tests.sh tests/gateway/test_android.py
scripts/run_tests.sh # full suite (CI parity)
```
- Coverage to write (behavioral, not change-detector — per hermes test policy):
- `register(ctx)` produces a valid `PlatformEntry` (name, cron env var,
parse_target_ref).
- `check_requirements` / `validate_config` / `is_connected` truth table.
- `_parse_target_ref`: `iris:<chat>`, `iris:<chat>:<thread>`, non-android
- `_parse_target_ref`: `iris:<chat>`, `iris:<chat>:<thread>`, non-iris
→ None.
- **Reasoning split:** given a `show_reasoning`-style final text, `send()`
emits `message {reasoning, text}` correctly; no-prefix → no reasoning field.
@@ -53,12 +55,13 @@ commentary classification and the reasoning prefix) before/while building the
Kotlin client.
```bash
hermes gateway & # with the android plugin
hermes gateway & # with the iris plugin
python gateway-plugin/tests/ws_probe.py --token <IRIS_TOKEN> \
--send "list the files and summarize"
# prints: hello.ack, typing, message.start, message.update…, tool.start, tool.end,
# commentary, message.stop {reasoning,…}, …
```
Use it to lock `04-wire-protocol.md` against reality and to debug the adapter
without waiting for the app.
@@ -102,6 +105,7 @@ adb logcat -d > /tmp/logcat.txt
```
**E2E scenarios (script where possible):**
1. **Pair:** connect screen → enter URL+token → `hello.ack` → main. (Verify auth
leg, not just TCP.)
2. **Text round-trip:** send "hello" → streamed reply appears (message.start →
@@ -155,4 +159,4 @@ adb logcat -d > /tmp/logcat.txt
account can mint a token, and the app's `onNewToken` re-registered after
`pm clear`.
- **Profile leaks:** if tokens look wrong under multiple profiles, verify the
scope-aware secret read (`_get_scoped_secret`) is used.
scope-aware secret read (`_get_scoped_secret`) is used.
+3 -3
View File
@@ -161,11 +161,11 @@ has explicit **acceptance criteria**. Work top-to-bottom; don't skip M0/M1.
- [x] `desktopMain`: tray + OS notifications; `MediaPlayer` actual (mpv/WebView);
`MediaPicker` actual (file dialog); `SecureStore` actual; window mgmt.
- [x] Two-pane default layout; keyboard shortcuts; optional inspector pane.
- [ ] Parity pass vs Android feature checklist (`11-desktop-app.md`).
- [ ] Parity pass vs Iris app feature checklist (`11-desktop-app.md`).
- [x] jpackage builds (Linux first; macOS/Windows as available).
- **Demo:** desktop app pairs to the same gateway; full feature parity; tray
notifications; media plays.
- **Accept:** all Android features work on desktop; tray + shortcuts work;
- **Accept:** all Iris app features work on desktop; tray + shortcuts work;
native binary launches.
- **Status (complete, 2026-08-19):** All `desktopMain` actuals implemented and
verified on Linux (X11). `DesktopSecureStore`: non-secrets in
@@ -274,7 +274,7 @@ in-app scanner and `iris://pair` deep link (closes gap #12, `docs/20`).
build it in M1.
- **M3 (cron) and M5 (push) both touch the outbox** — build the outbox in M3,
extend for push in M5.
- **M6 (desktop) reuses M1–M5 shared code** — do it after the Android features
- **M6 (desktop) reuses M1–M5 shared code** — do it after the Iris app features
are stable so the shared module is settled.
- Parallelizable: plugin (Python) and app (Kotlin) can be worked on
concurrently once the protocol (`04-wire-protocol.md`) is agreed; the probe
+2 -2
View File
@@ -4,7 +4,7 @@
| # | Decision | Choice | Rationale |
| --- | --- | --- | --- |
| 1 | Desktop app tech | **Compose Multiplatform** | Desktop = "the Android app, tweaked"; share protocol/state/UI. |
| 1 | Desktop app tech | **Compose Multiplatform** | Desktop = "the Iris app, tweaked"; share protocol/state/UI. |
| 2 | Push backend | **Both — ntfy default, FCM optional** (issue #10: FCM metadata — title, device token — is routed via Google's servers, contradicting the privacy claim; ntfy keeps it on your own infrastructure) | `IRIS_PUSH_BACKEND`. |
| 3 | Media transport | **Over the WebSocket** | One transport, zero new Python deps; chunked binary frames. |
| 4 | Phone default layout | **User-toggleable, single-pane default** | App-like on phones; auto two-pane on large screens; desktop defaults two-pane. |
@@ -44,7 +44,7 @@ will proceed with unless you say otherwise.
live gateway. If a clean metadata marker exists, prefer it.
2. **Reasoning prefix format stability (M2).** We split on the `code`-style
`💭 **Reasoning:**\n```\n…\n```\n\n` prefix. *Default:* set
`reasoning_style: code` for android and split on that; fallback = no
`reasoning_style: code` for iris and split on that; fallback = no
reasoning field (full text) if the prefix isn't found. Verify in M2.
3. **Per-device tokens vs shared token (M1/M5).** *Default (v1):* shared
`IRIS_TOKEN` + optional `IRIS_ALLOWED_USERS` device allowlist.
+5 -5
View File
@@ -1,7 +1,7 @@
# 18 — Code Review & Lint/LSP Cleanup (alpha → stable)
Comprehensive review of all three components — **gateway plugin**, **Android
app**, and **Desktop app** — performed to take the project from alpha to a
Comprehensive review of all three components — **gateway plugin**, **Iris app
(Android)**, and **Desktop app** — performed to take the project from alpha to a
clean, stable baseline. Each section records what was found, what was fixed,
how it was verified, and what was deliberately left (with rationale).
@@ -50,7 +50,7 @@ findings. Categories:
`hermes_cli.config`, but those live in `hermes_cli.cli_output`; it also imported
a `print_code` that does not exist in hermes at all. The whole import block
raised `ImportError`, which the surrounding `try/except` swallowed, so
`hermes gateway setup` for the android platform **always bailed out early** with
`hermes gateway setup` for the iris platform **always bailed out early** with
"setup helpers unavailable" and never generated a token or prompted for
host/port. Fixed by importing the print helpers from `hermes_cli.cli_output`,
the env helpers from `hermes_cli.config`, and dropping the non-existent
@@ -127,9 +127,9 @@ rule set (`E W F I UP B SIM PL RET C4`) and `line-length = 100`. Changes:
---
## 18.2 Android app (`app/androidApp` + `app/shared`)
## 18.2 Iris app — Android (`app/androidApp` + `app/shared`)
The Android and Desktop apps share the `:shared` KMP module (`commonMain` +
The Iris Android and Desktop apps share the `:shared` KMP module (`commonMain` +
`jvmMain`), so most Kotlin code is covered here and in 18.3.
### 18.2.1 Findings (before)
+1 -1
View File
@@ -315,7 +315,7 @@ interleaved (different languages, no shared surface).
- **QR display in the app** (showing a QR for other devices to scan) —
single-device pairing today; revisit if multi-device lands.
- **`hermes android pair` stretch CLI** (re-issue token + new QR,
- **`hermes iris pair` stretch CLI** (re-issue token + new QR,
`09-pairing-security.md` §9.2 line 48) — separate backlog item.
- **WSS cert pinning** (gap #6) — orthogonal; QR carries `secure=1`
already, pinning is app-side.
+3 -3
View File
@@ -32,7 +32,7 @@ top-to-bottom once, then use the numbered docs as a lookup while implementing.
| 7 | [`07-media.md`](07-media.md) | Media upload/download + playback. |
| 8 | [`08-push.md`](08-push.md) | Push (ntfy default + FCM optional), outbox, sync. |
| 9 | [`09-pairing-security.md`](09-pairing-security.md) | Pairing, auth, security model. |
| 10 | [`10-android-app.md`](10-android-app.md) | When building the Android app. |
| 10 | [`10-android-app.md`](10-android-app.md) | When building the Iris app (Android). |
| 11 | [`11-desktop-app.md`](11-desktop-app.md) | When building the Desktop app. |
| 12 | [`12-toolchain.md`](12-toolchain.md) | First time on a machine (JDK/SDK/uv/Firebase). |
| 13 | [`13-testing.md`](13-testing.md) | Writing tests + on-device ADB workflow. |
@@ -59,9 +59,9 @@ Machine-readable / diagrams:
Python dependencies, zero hermes-core changes.**
2. **`app/androidApp`** — native Kotlin + Jetpack Compose client.
3. **`app/desktopApp`** — Kotlin + Compose Multiplatform client that *shares*
the Android app's code and is "tweaked" for a big screen.
the Iris app's code and is "tweaked" for a big screen.
The Android and Desktop clients live in **one Compose Multiplatform Gradle
The Iris Android and Desktop clients live in **one Compose Multiplatform Gradle
project** (`app/`) with a shared KMP module (`app/shared`).
---
+3 -3
View File
@@ -6,8 +6,8 @@ flowchart TB
AGENT["Agent core<br/>(run_agent.py)"]
SESS["Sessions<br/>(SQLite + FTS5)"]
CRON["Cron scheduler"]
subgraph PLUGIN["android PLATFORM PLUGIN"]
ADAPTER["AndroidAdapter<br/>(BasePlatformAdapter)"]
subgraph PLUGIN["IRIS PLATFORM PLUGIN"]
ADAPTER["IrisAdapter<br/>(BasePlatformAdapter)"]
OUTBOX["Outbox (SQLite)<br/>+ sync cursor"]
PUSH["push.py<br/>FcmBackend / NtfyBackend"]
MEDIA["media.py<br/>cache + chunk stream"]
@@ -33,7 +33,7 @@ flowchart TB
end
subgraph DEVICES["Clients"]
PHONE["ANDROID APP<br/>(Kotlin / Compose)<br/>WS client + ExoPlayer + FCM"]
PHONE["IRIS APP (ANDROID)<br/>(Kotlin / Compose)<br/>WS client + ExoPlayer + FCM"]
DESKTOP["DESKTOP APP<br/>(Compose Multiplatform)<br/>WS client + tray + desktop player"]
end
+1 -1
View File
@@ -56,7 +56,7 @@ hermes gateway # or: hermes gateway restart after config changes
> the LAN, re-run `hermes gateway setup` (or edit `~/.hermes/.env`) and set
> `IRIS_WS_HOST` to the host's LAN IP (e.g. `192.168.1.10`).
## 2. Android app
## 2. Iris app (Android)
Build and install (ADB device connected):