ZeroTrace AirLeak
RPC Reference
The BLE JSON-RPC methods the app uses to control the unit
AirLeak has no serial console and no CLI. The mobile app controls the unit entirely over a Bluetooth link using JSON-RPC 2.0 on the Nordic UART Service (NUS); the capture stream rides a separate dedicated BLE characteristic. You don't normally call these directly, the app's tabs do, but this is the surface they use (src/handlers/, src/transport/rpc_dispatch.cpp).
Most methods require the unit to be licensed (the dispatcher gates them at the Licensed auth level). An unlicensed unit accepts pairing and license activation so the app can bring it online.
Identity
identity.read
Returns the unit's identity: device name, firmware_type (airleak), firmware version, HWID, and the licensed flag. The app reads this on every connect to pick the right device UI and gate licensing.
License
HWID-bound license activation, identical to the other ZeroTrace firmware families.
| Method | Behaviour |
|---|---|
license.request | Begin activation for this HWID |
license.set | Store a license key (the app sends the scanned/typed key) |
license.status | Report current license state |
Capture stays held in setup until a valid key is stored.
Configuration
| Method | Behaviour |
|---|---|
config.get | Return the current settings document (/config.json on LittleFS) plus read-only fields (product name, firmware version, serial, storage) |
config.set | Write one key; rejected if the key isn't a known setting |
See the Settings Reference for every key and default.
Mode
| Method | Behaviour |
|---|---|
mode.read | Return the current mode |
mode.set | Validate, apply, and persist a mode |
Accepted modes: setup, self_test, room_scan, monitor, recon, stalker_hunt, wardrive. The retired mdns_audit / travel values are rejected. See Capture Modes.
State
state.read
A live health snapshot for the app's status readouts:
| Field | Meaning |
|---|---|
uptime_ms | Time since boot |
heap_free | Free internal heap (bytes) |
mode | Current mode |
devices_active | Devices in the live aggregator |
drop_count | Delta-ring overflow count (deltas dropped under backpressure) |
raw_drop_count | Raw advertisements dropped before parsing |
ev_per_sec | Rolling 1 s ingest rate |
lowest_heap | Lowest free heap seen |
safe_mode / safe_mode_triggers | Safe-mode state + trigger count |
scan_duty_pct | Estimated BLE scan duty cycle (window ÷ interval); an upper bound while a phone is connected |
See Heartbeat / state fields for detail.
Capture stream
| Method | Behaviour |
|---|---|
capture.snapshot | Full current device table + the current sequence number |
capture.since | Deltas since a sequence number, or a {reset, snapshot} if the ring wrapped |
capture.subscribe / capture.unsubscribe | Start / stop CBOR delta notifications on the capture characteristic |
The stream is device-centric and coalesced, one logical row per MAC with deltas on meaningful change, not a raw per-advertisement firehose. See Throttle & emit policy.
LED
led.set
Preview an LED color. LED behaviour (startup color, brightness, mode/threat indication) is otherwise driven by config keys, see Settings.
Diagnostics
| Method | Behaviour |
|---|---|
diag.read | Read the on-flash crash / heap / boot diagnostic log |
diag.clear | Clear it |
This persistent log is how post-mortem health (e.g. a heap-exhaustion freeze) is recovered after the fact, since the live capture stream is not buffered.
TraceNet (swarm)
| Method | Behaviour |
|---|---|
node.enable / list / approve / remove / command | Manage ESP-NOW swarm nodes that feed advertisements to this hub |
TraceNet is off by default (swarm_enabled config key). It's surfaced in the app's TraceNet tab and the Drive swarm panel.
Removing a license / wiping settings is handled through the app, not a device command. To clear all settings the app rewrites /config.json to defaults via config.set, and re-licensing is done over the BLE license methods above.