Networking
Traffic analysis, protocols, local communication, and the network architecture of the Heron device.
Two integration surfaces
Heron has two distinct network surfaces, used by three clients. The split is intentional - setup and ongoing monitoring have different lifecycles and different trust requirements.
| Surface | Used by | Purpose |
|---|---|---|
| USB WebSerial | Onboarding PWA only | First-time setup over USB - identify, challenge, provision |
Local HTTP :8080 |
Dashboard and CLI | Ongoing monitoring on the LAN |
The dashboard and CLI do not use WebSerial and do not authenticate with device identity. They find the Pico on the network and read the HTTP API.
DNS observer
Heron is a passive DNS observer. It runs a local DNS server that forwards every query transparently to upstream resolvers (1.1.1.1 / 8.8.8.8) and logs what it sees. It does not block, redirect, or filter. The buzzer is an alert, not enforcement.
device on LAN ──DNS query──► Heron (:53) ──forward──► 1.1.1.1 / 8.8.8.8
│
└──log──► /dnslog.json (on flash)
USB WebSerial provisioning
Setup runs over USB CDC/ACM at 115200 baud with newline-delimited JSON. Chrome and Edge only - the WebSerial API is not available in Safari or Firefox. Opening the serial port resets the Pico, so the protocol is designed to be restart-safe.
While waiting for commands, the Pico emits a ready beacon every two seconds:
{"status": "ready"}
The PWA waits for this beacon before sending any command. The full onboarding flow runs in five steps:
| Step | Action | System |
|---|---|---|
| connect | WebSerial open, wait for ready, identify | USB serial |
| verify | Cryptographic challenge-response | USB serial + Web Crypto (ECDSA P-256) |
| wifi | Scan networks, provision with SSID and password | USB serial |
| router | DNS setup instructions | USB serial → OUI lookup → router guide |
| done | Unplug to wall power | - |
Local HTTP API
After provisioning, the device exposes an HTTP API on port 8080. No authentication - LAN presence is the only access control. The dashboard and the CLI both talk to this API; they are simply two different clients over the same surface.
Discovery order
- Probe
http://heron.local:8080/health(2-second timeout, mDNS) - Probe the saved IP from
localStorage(dashboard) or~/.heron-host(CLI) - Manual IP entry via the dashboard banner or
--hostflag
Endpoints
GET /health
GET /audit/weekly?since=<ts>&limit=<n>
GET /devices
GET /stats
GET /debug
GET /token
GET /allowlist
PUT /allowlist
DELETE /allowlist/<id>
PUT /devices/<id>
See the API contract for the full request and response shapes, error codes, and the changelog of additions.
mDNS
The device announces itself as heron.local via mDNS - set with network.hostname("heron") in firmware. Any device on the same Wi-Fi can reach it at http://heron.local:8080 without knowing the IP. The dashboard and CLI both prefer this path and fall back to a saved or manual IP when mDNS is unavailable.
Device naming
Tracked devices are identified by source IP. The firmware also sends a NetBIOS Name Service broadcast on UDP 137 to resolve hostnames, falling back to reverse DNS, then to a generated Device #N name. Custom names set in the dashboard persist to /names.json on flash and survive reboots. Maximum 32 characters.
Wi-Fi reconnection
The firmware reconnects to Wi-Fi automatically. A 30-second retry loop in main.py calls connect_to_wifi() whenever wlan.isconnected() returns false. Credentials live in /config.json on flash.
What never happens
- The device never opens an outbound connection to any server other than the upstream DNS resolvers.
- The dashboard never makes a network call to any external service. It talks only to
heron.local. - The onboarding PWA never makes a network call at all - verification is against a public key embedded in the page.