Running the Shizuku tier for the first time uploaded every MAC address on the local network to the server at the balanced level - fourteen of them, router and all. The probes embed raw command output verbatim (ip neigh, ip route), which is good evidence and also a complete household device inventory, and the anonymizer could not see it: classification is by field name and whole-value shape, and ip_neigh is one long string that is itself neither a MAC nor an address. measurement-schema.md flagged raw dumps as hard to anonymize and proposed dropping them from exports. Scrubbing is better: identifiers inside unclassified strings are replaced in place with the same pseudonyms used elsewhere, so a MAC appearing in both a parsed field and a raw dump still reads as one device, and the dump stays readable - neighbour-table shape, host count, RFC1918 addresses and vendor prefixes all survive. Dropping it would have protected the same data by destroying the reason for collecting it. One pass, not three: sequential passes re-process their own output. Once a MAC became 78:9a:18:xx:yy:zz the IPv6 pattern matched it - six hex groups separated by colons is an address - and destroyed the vendor prefix the MAC rule had just preserved. Ordered alternation resolves each position once, MAC first. RealDocumentTest runs the anonymizer over a captured run when ECHOLOT_REAL_RUN points at one and fails on any surviving MAC; it self-skips otherwise so no one's network lands in the repo. Against the document that leaked: 14 in, 0 out. Also: the Settings preview button did nothing, reading UiState.history which is empty until the History screen has been opened - same root cause as the "0 run(s)" count. It reads the archive now, and says when there is nothing to show. 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