Auth previously used the shared IRIS_TOKEN as the security principal: a leaked token meant access to all devices, and a compromised device could not be isolated. Gateway: - pairing.py: devices.token column (in-place migration) + revoked denylist table; issue_token (idempotent, 64 hex), token_for, reissue_token, revoke/unrevoke/is_revoked/list_revoked. The token never leaks into device dicts (push fan-out / listings). - http_server.py: auth accepts the shared token (bootstrap/legacy) OR the device's own token (both constant-time); a revoked device_id is rejected with 401 before either comparison. On SSE open (pairing) the per-device token is minted and returned in hello.ack. - protocol.py: hello_ack(..., device_token). - adapter.py: setup flow (hermes gateway setup -> Iris) now offers 'Remove a paired device?' on an existing setup: numbered select menu (last option = exit the removal loop), confirmation, back to the menu for further removals. - tools/iris_devices.py: operator CLI (list / revoke / unrevoke / reissue), stdlib only. App: - SecureStore.deviceToken (Android: EncryptedSharedPreferences; Desktop: second keyring slot iris-device-token / device_token.enc). - HelloAckPayload.deviceToken; GatewayClient stores it on hello and presents it instead of the shared token from then on (live provider in HttpGateway); savePairing/clear wipe it for re-pairing. Docs: 09 §9.3 stretch -> implemented (revocation semantics, both control surfaces), 04 hello.ack example, frames.schema.json, M7 row 13. Tests: 8 new Python tests (issuance, acceptance, revocation, isolation, unrevoke, registry unit x2, setup-flow menu) - 94/94 pass; 2 new Kotlin wire tests - green. Live-verified against a running gateway (hello.ack token matches devices.db; revoke -> 401 even with shared token; unrevoke -> 200; setup TUI both paths).
72 lines
2.5 KiB
YAML
72 lines
2.5 KiB
YAML
name: iris-platform
|
|
label: Iris
|
|
kind: platform
|
|
version: 0.1.0
|
|
description: >
|
|
Native Android / Desktop client gateway adapter for Hermes Agent.
|
|
Runs a WebSocket server inside the gateway; the app connects with a
|
|
pairing token. Supports streaming, reasoning, structured tool events,
|
|
channels/threads, media, FTS5 search, and FCM/ntfy push.
|
|
author: Iris x Hermes
|
|
# ``requires_env`` / ``optional_env`` entries are surfaced in the
|
|
# ``hermes config`` / ``hermes gateway setup`` UI via the platform-plugin
|
|
# env var injector in ``hermes_cli/config.py``.
|
|
requires_env:
|
|
- name: IRIS_TOKEN
|
|
description: "Shared pairing token the app presents on connect"
|
|
prompt: "Iris pairing token"
|
|
password: true
|
|
optional_env:
|
|
- name: IRIS_WS_HOST
|
|
description: "WS bind host (default 127.0.0.1; use 0.0.0.0 for LAN)"
|
|
prompt: "WS host"
|
|
password: false
|
|
- name: IRIS_WS_PORT
|
|
description: "WS port (default 8790)"
|
|
prompt: "WS port"
|
|
password: false
|
|
- name: IRIS_HOME_CHANNEL
|
|
description: "Default chat id for cron/notification delivery (default: default)"
|
|
prompt: "Home channel"
|
|
password: false
|
|
- name: IRIS_ALLOWED_USERS
|
|
description: "Comma-separated allowed device_ids (empty = token-only auth)"
|
|
prompt: "Allowed device ids"
|
|
password: false
|
|
- name: IRIS_ALLOW_ALL_USERS
|
|
description: "Allow any paired device (dev only)"
|
|
prompt: "Allow all devices? (true/false)"
|
|
password: false
|
|
- name: IRIS_PUSH_BACKEND
|
|
description: "Push backend: ntfy (default, keeps metadata off Google) or fcm"
|
|
prompt: "Push backend"
|
|
password: false
|
|
- name: IRIS_FCM_SERVICE_ACCOUNT
|
|
description: "Path to Firebase service-account JSON (FCM HTTP v1)"
|
|
prompt: "FCM service account path"
|
|
password: true
|
|
- name: IRIS_FCM_SERVER_KEY
|
|
description: "Legacy FCM server key (fallback if no service account)"
|
|
prompt: "FCM server key"
|
|
password: true
|
|
- name: NTFY_TOPIC
|
|
description: "ntfy topic for push (when IRIS_PUSH_BACKEND=ntfy)"
|
|
prompt: "ntfy topic"
|
|
password: false
|
|
- name: NTFY_SERVER_URL
|
|
description: "ntfy server URL (default https://ntfy.sh)"
|
|
prompt: "ntfy server URL"
|
|
password: false
|
|
- name: NTFY_AUTH_TOKEN
|
|
description: "ntfy auth token for a private topic (trust boundary)"
|
|
prompt: "ntfy auth token"
|
|
password: true
|
|
- name: IRIS_WS_CERT
|
|
description: "TLS cert path for WSS (optional)"
|
|
prompt: "WSS cert"
|
|
password: false
|
|
- name: IRIS_WS_KEY
|
|
description: "TLS key path for WSS (optional)"
|
|
prompt: "WSS key"
|
|
password: false
|