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 ## Goals
- **Native feel.** Real Android app (Kotlin/Compose), not a WebView. Desktop - **Native feel.** Real native app (Iris on Android, Kotlin/Compose), not a
app that is the same app, resized for a big screen. 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 - **First-class gateway citizen.** The app is a hermes *messaging platform*, so
everything the gateway already does "just works": slash commands, cron everything the gateway already does "just works": slash commands, cron
delivery, `send_message` routing, coexistence with Telegram/Discord/etc. delivery, `send_message` routing, coexistence with Telegram/Discord/etc.
+5 -5
View File
@@ -11,7 +11,7 @@
│ │ │ │ │ │ │ │ │ │
│ │ ▼ legacy stream callbacks (delta / tool-progress / commentary) │ │ │ │ ▼ 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 │ │ │ │ │ │ IrisAdapter │ JSON │ ws://host:8790/ws │ │ │
│ │ │ (BasePlatformAdapter) │ frames │ + media + FCM-token + pairing │ │ │ │ │ │ (BasePlatformAdapter) │ frames │ + media + FCM-token + pairing │ │ │
│ │ │ • send / edit / stream │ └───────────────┬────────────────┘ │ │ │ │ │ • send / edit / stream │ └───────────────┬────────────────┘ │ │
@@ -26,7 +26,7 @@
│ │ Google FCM cloud │ │ │ Google FCM cloud │
▼ │ │ │ ▼ │ │ │
┌────────────────────────┐ │ ▼ │ ┌────────────────────────┐ │ ▼ │
│ ANDROID APP │◄──┴── (wake) ┌──────────┐ │ IRIS APP (ANDROID) │◄──┴── (wake) ┌──────────┐
│ (Kotlin / Compose) │ WSS │ PHONE │ │ (Kotlin / Compose) │ WSS │ PHONE │
│ • WS client (OkHttp) │◄───────────►│ MIX 2S │ │ • WS client (OkHttp) │◄───────────►│ MIX 2S │
│ • ExoPlayer │ │ (API 29) │ │ • ExoPlayer │ │ (API 29) │
@@ -72,7 +72,7 @@ protocol (`04-wire-protocol.md`) that borrows familiar names (`message.*`,
## Key architectural decisions + rationale ## Key architectural decisions + rationale
| Decision | 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". | | **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. | | **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). | | **`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. | | **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. | | **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. | | **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) ## 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 > (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 > the legacy `send`/`edit_message`/progress calls to our frames. The exact
> tool-progress vs commentary classification is verified empirically in M2 by > 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 ```yaml
name: iris-platform name: iris-platform
label: Android label: Iris
kind: platform kind: platform
version: 0.1.0 version: 0.1.0
description: > description: >
@@ -24,7 +24,7 @@ author: <you>
requires_env: requires_env:
- name: IRIS_TOKEN - name: IRIS_TOKEN
description: "Shared pairing token the app presents on connect" description: "Shared pairing token the app presents on connect"
prompt: "Android pairing token" prompt: "Iris pairing token"
password: true password: true
optional_env: optional_env:
- name: IRIS_WS_HOST - name: IRIS_WS_HOST
@@ -35,7 +35,7 @@ optional_env:
description: "WS port (default 8790)" description: "WS port (default 8790)"
prompt: "WS port" prompt: "WS port"
password: false password: false
- name: ANDROID_HOME_CHANNEL - name: IRIS_HOME_CHANNEL
description: "Default chat id for cron/notification delivery (default default)" description: "Default chat id for cron/notification delivery (default default)"
prompt: "Home channel" prompt: "Home channel"
password: false password: false
@@ -97,7 +97,7 @@ def register(ctx):
install_hint="No extra packages needed (websockets + httpx are core deps)", install_hint="No extra packages needed (websockets + httpx are core deps)",
setup_fn=interactive_setup, # hermes gateway setup flow setup_fn=interactive_setup, # hermes gateway setup flow
env_enablement_fn=_env_enablement, # seed extra + home_channel from env 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) standalone_sender_fn=_standalone_send, # best-effort out-of-proc cron (stretch)
parse_target_ref_fn=_parse_target_ref, # "iris:<chat>[:<thread>]" parse_target_ref_fn=_parse_target_ref, # "iris:<chat>[:<thread>]"
allowed_users_env="IRIS_ALLOWED_USERS", allowed_users_env="IRIS_ALLOWED_USERS",
+2 -2
View File
@@ -23,10 +23,10 @@ gateway identity concepts**.
## 6.2 Default chat ## 6.2 Default chat
- On first connect, the plugin ensures a **default channel** exists: - 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". `is_default=true`, name "Default".
- It is also the **cron home channel** (`cron_deliver_env_var= - 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. - The app opens the default chat on launch.
## 6.3 Threads (toggle for overview) ## 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 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. 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) # 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 entire `app/shared` module (protocol, network, repositories, state, most UI) and
only adds desktop platform services + a wider default layout. only adds desktop platform services + a wider default layout.
## 11.1 What's shared vs desktop-specific ## 11.1 What's shared vs desktop-specific
| Layer | Shared (commonMain) | Desktop-specific (desktopMain) | | Layer | Shared (commonMain) | Desktop-specific (desktopMain) |
|---|---|---| | --- | --- | --- |
| Protocol / WS client | ✅ | — | | Protocol / WS client | ✅ | — |
| Repositories / state | ✅ | — | | Repositories / state | ✅ | — |
| Most Compose UI | ✅ | layout tweaks, keyboard shortcuts | | 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 - 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 user's home server / Tailscale). It does **not** spawn its own backend (unlike
hermes's existing Electron desktop, which spawns `hermes serve`) — our 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. - [ ] Streaming, reasoning block, tool cards (3-level verbosity), commentary.
- [ ] Channels + threads + new channel + cron target. - [ ] 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). - [ ] Media attach + live playback (via desktop player).
- [ ] Slash command menu + autocomplete + interactive pickers. - [ ] Slash command menu + autocomplete + interactive pickers.
- [ ] Push (tray + OS notifications) + outbox/sync. - [ ] 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 ```bash
# 1. gateway up with plugin # 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 # 2. a raw WS client can pair + echo
python - <<'PY' 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 into the hermes `tests/gateway/test_android.py` pattern when running under
hermes's suite). hermes's suite).
- **Run with hermes's hermetic runner** (never bare `pytest`): - **Run with hermes's hermetic runner** (never bare `pytest`):
```bash ```bash
cd hermes-agent cd hermes-agent
scripts/run_tests.sh tests/gateway/test_android.py scripts/run_tests.sh tests/gateway/test_android.py
scripts/run_tests.sh # full suite (CI parity) scripts/run_tests.sh # full suite (CI parity)
``` ```
- Coverage to write (behavioral, not change-detector — per hermes test policy): - Coverage to write (behavioral, not change-detector — per hermes test policy):
- `register(ctx)` produces a valid `PlatformEntry` (name, cron env var, - `register(ctx)` produces a valid `PlatformEntry` (name, cron env var,
parse_target_ref). parse_target_ref).
- `check_requirements` / `validate_config` / `is_connected` truth table. - `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. → None.
- **Reasoning split:** given a `show_reasoning`-style final text, `send()` - **Reasoning split:** given a `show_reasoning`-style final text, `send()`
emits `message {reasoning, text}` correctly; no-prefix → no reasoning field. 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. Kotlin client.
```bash ```bash
hermes gateway & # with the android plugin hermes gateway & # with the iris plugin
python gateway-plugin/tests/ws_probe.py --token <IRIS_TOKEN> \ python gateway-plugin/tests/ws_probe.py --token <IRIS_TOKEN> \
--send "list the files and summarize" --send "list the files and summarize"
# prints: hello.ack, typing, message.start, message.update…, tool.start, tool.end, # prints: hello.ack, typing, message.start, message.update…, tool.start, tool.end,
# commentary, message.stop {reasoning,…}, … # commentary, message.stop {reasoning,…}, …
``` ```
Use it to lock `04-wire-protocol.md` against reality and to debug the adapter Use it to lock `04-wire-protocol.md` against reality and to debug the adapter
without waiting for the app. without waiting for the app.
@@ -102,6 +105,7 @@ adb logcat -d > /tmp/logcat.txt
``` ```
**E2E scenarios (script where possible):** **E2E scenarios (script where possible):**
1. **Pair:** connect screen → enter URL+token → `hello.ack` → main. (Verify auth 1. **Pair:** connect screen → enter URL+token → `hello.ack` → main. (Verify auth
leg, not just TCP.) leg, not just TCP.)
2. **Text round-trip:** send "hello" → streamed reply appears (message.start → 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 account can mint a token, and the app's `onNewToken` re-registered after
`pm clear`. `pm clear`.
- **Profile leaks:** if tokens look wrong under multiple profiles, verify the - **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); - [x] `desktopMain`: tray + OS notifications; `MediaPlayer` actual (mpv/WebView);
`MediaPicker` actual (file dialog); `SecureStore` actual; window mgmt. `MediaPicker` actual (file dialog); `SecureStore` actual; window mgmt.
- [x] Two-pane default layout; keyboard shortcuts; optional inspector pane. - [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). - [x] jpackage builds (Linux first; macOS/Windows as available).
- **Demo:** desktop app pairs to the same gateway; full feature parity; tray - **Demo:** desktop app pairs to the same gateway; full feature parity; tray
notifications; media plays. 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. native binary launches.
- **Status (complete, 2026-08-19):** All `desktopMain` actuals implemented and - **Status (complete, 2026-08-19):** All `desktopMain` actuals implemented and
verified on Linux (X11). `DesktopSecureStore`: non-secrets in 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. build it in M1.
- **M3 (cron) and M5 (push) both touch the outbox** — build the outbox in M3, - **M3 (cron) and M5 (push) both touch the outbox** — build the outbox in M3,
extend for push in M5. 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. are stable so the shared module is settled.
- Parallelizable: plugin (Python) and app (Kotlin) can be worked on - Parallelizable: plugin (Python) and app (Kotlin) can be worked on
concurrently once the protocol (`04-wire-protocol.md`) is agreed; the probe concurrently once the protocol (`04-wire-protocol.md`) is agreed; the probe
+2 -2
View File
@@ -4,7 +4,7 @@
| # | Decision | Choice | Rationale | | # | 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`. | | 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. | | 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. | | 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. live gateway. If a clean metadata marker exists, prefer it.
2. **Reasoning prefix format stability (M2).** We split on the `code`-style 2. **Reasoning prefix format stability (M2).** We split on the `code`-style
`💭 **Reasoning:**\n```\n…\n```\n\n` prefix. *Default:* set `💭 **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. 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 3. **Per-device tokens vs shared token (M1/M5).** *Default (v1):* shared
`IRIS_TOKEN` + optional `IRIS_ALLOWED_USERS` device allowlist. `IRIS_TOKEN` + optional `IRIS_ALLOWED_USERS` device allowlist.
+5 -5
View File
@@ -1,7 +1,7 @@
# 18 — Code Review & Lint/LSP Cleanup (alpha → stable) # 18 — Code Review & Lint/LSP Cleanup (alpha → stable)
Comprehensive review of all three components — **gateway plugin**, **Android Comprehensive review of all three components — **gateway plugin**, **Iris app
app**, and **Desktop app** — performed to take the project from alpha to a (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, clean, stable baseline. Each section records what was found, what was fixed,
how it was verified, and what was deliberately left (with rationale). 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 `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 a `print_code` that does not exist in hermes at all. The whole import block
raised `ImportError`, which the surrounding `try/except` swallowed, so 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 "setup helpers unavailable" and never generated a token or prompted for
host/port. Fixed by importing the print helpers from `hermes_cli.cli_output`, 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 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. `jvmMain`), so most Kotlin code is covered here and in 18.3.
### 18.2.1 Findings (before) ### 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) — - **QR display in the app** (showing a QR for other devices to scan) —
single-device pairing today; revisit if multi-device lands. 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. `09-pairing-security.md` §9.2 line 48) — separate backlog item.
- **WSS cert pinning** (gap #6) — orthogonal; QR carries `secure=1` - **WSS cert pinning** (gap #6) — orthogonal; QR carries `secure=1`
already, pinning is app-side. 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. | | 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. | | 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. | | 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. | | 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). | | 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. | | 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.** Python dependencies, zero hermes-core changes.**
2. **`app/androidApp`** — native Kotlin + Jetpack Compose client. 2. **`app/androidApp`** — native Kotlin + Jetpack Compose client.
3. **`app/desktopApp`** — Kotlin + Compose Multiplatform client that *shares* 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`). 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)"] AGENT["Agent core<br/>(run_agent.py)"]
SESS["Sessions<br/>(SQLite + FTS5)"] SESS["Sessions<br/>(SQLite + FTS5)"]
CRON["Cron scheduler"] CRON["Cron scheduler"]
subgraph PLUGIN["android PLATFORM PLUGIN"] subgraph PLUGIN["IRIS PLATFORM PLUGIN"]
ADAPTER["AndroidAdapter<br/>(BasePlatformAdapter)"] ADAPTER["IrisAdapter<br/>(BasePlatformAdapter)"]
OUTBOX["Outbox (SQLite)<br/>+ sync cursor"] OUTBOX["Outbox (SQLite)<br/>+ sync cursor"]
PUSH["push.py<br/>FcmBackend / NtfyBackend"] PUSH["push.py<br/>FcmBackend / NtfyBackend"]
MEDIA["media.py<br/>cache + chunk stream"] MEDIA["media.py<br/>cache + chunk stream"]
@@ -33,7 +33,7 @@ flowchart TB
end end
subgraph DEVICES["Clients"] 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"] DESKTOP["DESKTOP APP<br/>(Compose Multiplatform)<br/>WS client + tray + desktop player"]
end 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 > 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`). > `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): Build and install (ADB device connected):