# Echolot app The production Android client ([spec](../docs/)). Native Kotlin + Jetpack Compose. Multi-module; built bottom-up from a verifiable protocol spine. ## Modules | Module | Type | Status | |---|---|---| | `core-protocol` | pure Kotlin/JVM | **done** — client half of `probe-protocol.md`, verified live against the server | | `core-measurement` | pure Kotlin/JVM | planned — `measurement-schema.md` types | | `core-probe` | Android lib | planned — app-tier probes, ported from `echolot-prober` | | `core-shizuku` | Android lib | planned — dual-path executor (UserService + newProcess fallback) | | `app` | Android app | planned — Compose UI | `core-protocol` is deliberately Android-free so it builds and unit-tests on any JDK (no Android SDK) and can run **integration tests against a live server**. ## core-protocol Implements the control plane (SPKI-pinned enrollment/profile/sessions via `HttpsURLConnection` — Android-API-1 compatible, hostname verification off because trust is the pin), the HKDF-SHA256 session-key schedule, and the binary ELT1 UDP data plane (HMAC gate, ECHO + observation block, MTU probe) — byte-compatible with the Go server. ```sh ./gradlew :core-protocol:test # unit tests (crypto vectors, wire round-trip) scripts/test-fmr.sh # live end-to-end test against the deployed server ``` `test-fmr.sh` mints an enrollment token over SSH, enrolls via the public control plane, computes the SPKI pin from the served cert, and runs `LiveServerTest` — proving the client speaks the wire protocol to the real server (enroll → profile → session → echo+observation → MTU → observations). The live test self-skips when `ECHOLOT_LIVE_*` env vars are absent, so unit runs and CI stay green offline.