The scheme was already registered and the deep link already worked — it enrolled on arrival, with no confirmation. Now that the web UI offers the link as something to follow, that is one tap between a working enrollment and a replaced one, from a page that might be showing a link minted for a different device entirely. Enrolling is not additive: the new credential replaces the old, and on the previous server this device simply stops reporting. So the link is held and the user is asked, with both server URLs named — the question is "which server", and it cannot be answered without seeing both. The dialog says what survives, because that is the part someone hesitates over: uploads already on the old server stay there, runs stored on the phone are untouched, and the device reappears on the new server as a new device rather than carrying its history across. Enrolling also clears the cached canary zone. It describes the old server's deployment, and querying it against the new one would measure somebody else's zone and file the answer under this network. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Echolot app
The production Android client (spec). 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.
./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.