Replaces the unauthenticated admin mux. Everything but /healthz requires a session, and that is the point: the previous arrangement relied on binding to loopback, which worked exactly until the address changed and then failed silently and publicly. A binding address is a deployment detail, not an access control, and this package does not treat it as one. Two ways in. OIDC through the confidential client, with state and PKCE - PKCE even here, because it costs one hash and closes code interception independently of the secret. And the break-glass password, throttled, for when the IdP is the thing that is broken. Signing in without the admin group is refused with the group named, because "you are not an admin" is a different problem from "your password is wrong" and the remedy is elsewhere. Sessions are MAC-checked cookies: HttpOnly, SameSite=Lax, Secure when TLS is on. CSRF tokens are derived from the session rather than stored, so there is no server-side table to keep in sync, and they are required on every state-changing POST - SameSite already blocks cross-site posts in current browsers, but this is the control that does not depend on the browser being current. Server-rendered with html/template and no JavaScript: the pages are lists and forms, and a framework would add a build step, a dependency tree and an update treadmill to a program that has none of those. The CSP is default-src 'none' accordingly. Pages: overview, devices (with revocation and enrolment-link minting), uploaded runs and a run viewer. Revocations and deletions are logged with who did them. Runs are shown exactly as uploaded, at the privacy level their uploader chose - nothing in the UI can un-redact one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Echolot
Free software for detecting and debugging local network issues from an Android phone — built for people who actually know what a neighbor table is.
Most "wifi analyzer" apps show you signal bars. Echolot aims at the layer where home and office networks actually break: duplicate DHCP servers, broken IPv6 RAs, MTU black holes, NAT64 weirdness, multicast that dies at the AP, DNS that answers differently than it should. It records what it observed, separates observation from interpretation, and exports the whole run so you can argue with it later.
Status: pre-release. The capability prober runs on real hardware; the production app and the probe server are not built yet.
Repository layout
docs/ design docs — the contract for everything below
echolot-prober/ capability prober: validates the no-root feasibility matrix on real devices
The Go probe server and the production app land here as siblings.
Design docs
The three specs are draft-complete and reviewed; treat them as the contract.
| Doc | What it defines |
|---|---|
| docs/feature-catalog-and-feasibility.md | Full feature list + the no-root feasibility matrix |
| docs/measurement-schema.md | Archived/exportable measurement JSON (observation vs finding, two-clock rule, anonymization) |
| docs/probe-protocol.md | Client↔server wire protocol (pinned TLS control plane, binary UDP data plane, STUN, canary DNS) |
| docs/build-status.md | Running log of decisions and next steps |
Privilege tiers
Every result records which tier produced it:
app— no root, no special setup. The bulk of the functionality.shizuku— ADB-shell privileges via wireless pairing, no root. Shipped in v1.root— future optional module.
Licensing
| Part | License | Why |
|---|---|---|
| All code (app, prober, server) | GPL-3.0-or-later | The value here is the platform-API research; copyleft keeps derivative apps free |
docs/ (the specs) |
CC-BY-4.0 | A wire protocol and a measurement format should be implementable by anyone, without license anxiety |
Full texts: LICENSE (GPLv3) and docs/LICENSE (CC BY 4.0).
Sources carry SPDX-License-Identifier headers.
If you want to build a compatible server or client, the protocol and schema docs are deliberately permissive — go ahead.
Building
See echolot-prober/README.md. Short version, from echolot-prober/:
echo "sdk.dir=/path/to/Android/sdk" > local.properties
./gradlew :app:assembleDebug