# 12 — Toolchain Setup First-time setup on a machine (verified baseline: CachyOS/Arch, `pacman`, `uv` present, ADB present, no JDK/SDK/Gradle). ## 12.1 JDK 17 ```bash pacman -S jdk17-openjdk java -version # expect 17.x ``` (Compose Multiplatform + current AGP are happy on JDK 17. Use 17 to match the Android toolchain; 21 also works but 17 is the safe floor.) ## 12.2 Android SDK ```bash # cmdline-tools mkdir -p ~/android-sdk/cmdline-tools cd ~/android-sdk/cmdline-tools curl -O https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip unzip commandlinetools-linux-*.zip && mv cmdline-tools latest rm commandlinetools-linux-*.zip export ANDROID_HOME=$HOME/android-sdk export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools sdkmanager --licenses sdkmanager "platform-tools" "platforms;android-34" "build-tools;34.0.0" ``` Persist `ANDROID_HOME`/`PATH` in `~/.bashrc`. ADB is already installed system-wide; `platform-tools` from the SDK is fine too (whichever is first on `PATH`). Create `app/local.properties`: ``` sdk.dir=/home//android-sdk ``` ## 12.3 Gradle No system install — use the project wrapper: ```bash cd app ./gradlew tasks # first run downloads the wrapper distribution ``` (The wrapper version is pinned in `app/gradle/wrapper/gradle-wrapper.properties`.) ## 12.4 hermes environment (for the plugin + running the gateway) ```bash cd hermes-agent uv sync # creates .venv with all core deps (websockets, httpx, …) source .venv/bin/activate hermes --version # sanity ``` - Run the gateway with the plugin: ```bash # install the plugin (dev: symlink) mkdir -p ~/.hermes/plugins ln -s "$PWD/../gateway-plugin" ~/.hermes/plugins/iris hermes gateway status # should list "iris" hermes gateway # run ``` - Tests use hermes's hermetic runner (never bare `pytest`): ```bash scripts/run_tests.sh tests/gateway/test_android.py ``` ## 12.5 Firebase (FCM) — primary push 1. Create a Firebase project (console.firebase.google.com). 2. Add an **Android app** (package = `androidApp` applicationId, e.g. `dev.iris.app`). Download `google-services.json` → `app/androidApp/`. 3. Create a **service account** (Project settings → Service accounts → Generate new private key) → download the JSON. Store its path in `IRIS_FCM_SERVICE_ACCOUNT` (in `~/.hermes/.env`). 4. The app's `FirebaseMessagingService` obtains the FCM token at runtime and registers it via `hello` / `fcm.register`. > Skip Firebase → the default is already ntfy: leave `IRIS_PUSH_BACKEND` unset > (or set it to `ntfy`) and configure `NTFY_TOPIC` / `NTFY_SERVER_URL` > (self-host ntfy or use ntfy.sh). See `08-push.md`. ## 12.6 Environment variables (summary) **Secrets (`~/.hermes/.env`):** ``` IRIS_TOKEN=<64-hex> IRIS_PUSH_BACKEND=ntfy # default; fcm = opt-in (metadata via Google) IRIS_FCM_SERVICE_ACCOUNT=/path/to/service-account.json # IRIS_FCM_SERVER_KEY= # fallback if no service account # NTFY_TOPIC=iris-push # when ntfy # NTFY_SERVER_URL=https://ntfy.sh # IRIS_WS_CERT=/path/cert.pem # WSS # IRIS_WS_KEY=/path/key.pem ``` **Behavioral (`~/.hermes/config.yaml`):** ```yaml gateway: platforms: iris: enabled: true extra: host: 127.0.0.1 # 0.0.0.0 for LAN port: 8790 home_channel: default push_backend: fcm outbox_retention_hours: 72 max_upload_bytes: 104857600 # 100 MB display: platforms: iris: show_reasoning: true reasoning_style: code streaming: true tool_progress: all # gateway sends full data; app controls display ``` ## 12.7 Verify the stack (smoke test) ```bash # 1. gateway up with plugin hermes gateway status | grep -i iris # 2. a raw WS client can pair + echo python - <<'PY' import asyncio, json, websockets async def main(): async with websockets.connect("ws://127.0.0.1:8790/ws") as ws: await ws.send(json.dumps({"v":1,"type":"hello","payload":{ "token":"","device_id":"test","device_name":"probe", "caps":{"min_protocol":1}}})) print("recv:", await ws.recv()) asyncio.run(main()) PY ``` Expect a `hello.ack`. If you get `error {code:"auth"}`, the token/host/port is wrong.