Echolot is the German word for an echo sounder — an instrument that emits a ping and reads what comes back — and the UI now looks like one instead of like a dashboard. The three big-number stat cards went first: that layout is the stock answer for any admin page, and it told a network engineer nothing they could act on. Palette is a water column rather than a neutral near-black, with a desaturated sea-green return for the accent. Verdict colours come from the domain, so they carry meaning rather than decorate. No web fonts — the CSP forbids loading anything and shipping font files would trade what makes this a single pleasant binary for a typeface — so the character comes from treatment: machine-set headings, tracked small caps, hairlines. The one ornament is a trace of returns across time on the runs page, one bar per run coloured by verdict, oldest to newest. It is real data, pure CSS, and it is what an echo sounder actually draws. The mobile fix is the same idea rather than a fallback. Tables become label-and-value records with dotted leaders, which is how a sounding log prints and is easier to read on a phone than any table that scrolls sideways. Above 46rem every row shares one grid so the columns agree by construction; the first attempt used table-cell and each row wrapped independently, which produces a table that does not align — a list paying for borders. Found by rendering it rather than reading the CSS: the trace stranded itself against the right edge when runs were few, "Open run" broke across two lines, equal columns wrapped device names while a one-digit count kept a quarter of the row, and the sign-in page carried no wordmark at all, so you arrived somewhere that never said what it was. Co-Authored-By: Claude Opus 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