RadioChron is a Rust Wi-Fi diagnostics and BLE history engine with an unattended agent, streaming Node API, MCP and desktop surfaces. It records what the radio actually did — associations, roams, drops, reason codes — and returns verdicts, not data dumps. A snapshot says “−58 dBm, fine”. The chronicle says why it wasn't fine ten minutes ago.
# Rust library $ cargo add radiochron # Standalone Node library $ npm install radiochron # Unattended durable collector $ cargo install radiochron-agent # Separate MCP server package $ claude mcp add radiochron -- npx -y radiochron-mcp
RadioChron reads the Windows WLAN event log as structured, locale-independent data — numeric event IDs and reason codes, never localized message text — and separates roams from faults by ConnectionId continuity: a roam reuses the session id, a genuine reconnect allocates a new one. Security rekeys, resume-from-sleep and same-network roaming are suppressed instead of counted as failures.
{
"id": "reconnect_loop",
"severity": "critical",
"title": "5 connection attempts and 5 failures in 120s",
"detail": { "attempts": 5, "failures": 5, "successes": 0,
"reason_codes": [229396, 229396, 229378] },
"caveat": "Roams and rekeys are excluded by requiring distinct
ConnectionIds and real failures. Reason codes are
undocumented observations; treat unfamiliar ones as
unknown rather than meaningful."
}
caveat — the honest reason it might be wrong.
A bare severity invites over-trust; error bars are part of the payload.A stable link polled every 2 seconds is 43,200 identical log lines a day. The chronicle
writes one associated line and goes quiet until something
changes: a roam, a drop, a signal shift beyond dB-hysteresis, a reason code in the event
log. Storage is versioned append-only JSONL with built-in rotation — greppable, deduplication-safe, and a
hard disk ceiling of ~4 MiB by default, sized for embedded flash, not workstations.
// pluggable Sink trait — the shipped sink is JSONL + rotation. // a SQLite sink is ~30 lines in *your* crate; bundling one would // drag in a C compile and break the bare-rustup build. let sink = JsonlSink::open("chronicle.jsonl", RotationPolicy::default())?; let mut rec = Recorder::new(sink, RecorderOptions::default()); rec.run_for(Duration::from_secs(3600))?; // or own the loop: rec.step()
Field devices lose Wi-Fi constantly and can't say why. The Rust options that exist either
shell out to netsh/iw and parse localized text, or resolve to a
51-crate tree that doesn't build on a bare rustup toolchain at all — its transitive
dependencies want mingw or the Visual C++ build tools. On an embedded image that's the
difference between adding a dependency and rebuilding the OS.
RadioChron hand-writes its FFI and resolves system libraries at
runtime. No binding crates, no import libraries, no C. A device that only needs association
state compiles just that: features are granular by capability. Linux talks to nl80211
over Generic Netlink directly — no iw parsing.
# a sensor that only reports link state pulls exactly that: radiochron = { version = "0.4", default-features = false, features = ["status"] } # portable no_std BLE history; ESP-IDF can add the real NimBLE collector: radiochron = { version = "0.4", default-features = false, features = ["embedded", "ble"] } radiochron-esp-idf = { version = "0.2", features = ["nimble"] } # status · scan · analyze · sample · history · record · connectivity · ble
radiochron — collectors, 802.11 analysis, BLE identity/history
detectors and the chronicle. What an IoT agent, ESP-IDF adapter, exporter or CLI embeds.
The BLE model remains no_std + alloc. MIT/Apache-2.0.
Source.radiochron-mcp — seventeen portable typed tools + six resources
over stdio; Windows adds WLAN AutoConfig history. diagnose_incident orchestrates
the chain and ble_scan observes native WinRT, BlueZ, or CoreBluetooth radio.
MIT licensed.
Source.radiochron-agent — an opt-in native Wi-Fi/BLE collector for
gateways and edge boxes. It writes every versioned event to a durable offline spool
before MQTT QoS 1 or OTLP export, exposes Prometheus health, and supports signed
configuration/OTA without adding transport dependencies to core.
Install and configure.radiochron-js; builds cover Windows x64, Intel Mac and Apple Silicon.
Downloads and details.Capture a baseline, inspect Wi-Fi and Bluetooth radios through the same pan/zoom/fullscreen RF map, merge radio evidence with paired or connected OS devices, correlate rotating private addresses with explicit confidence, and explore exact retained scans and 1/7/30-day patterns without sending telemetry anywhere.










All screenshots use invented SSIDs and beacons, locally administered MAC addresses and documentation-only IP ranges.
| tool | answers |
|---|---|
diagnose_incident | one compact Wi-Fi, connectivity, history, chronicle and optional native BLE incident report |
wifi_history | “why did it drop earlier” — reconnect loops, an AP failing key exchange, credential mismatch // the headline |
wifi_analyze | findings, not records: contention, weak signal, band-steering candidates — 802 bytes where the raw list costs 41 KB |
wifi_sample | “is it stable” — RSSI swing, roam count, drops over a bounded window |
wifi_status | the association right now: SSID, BSSID, PHY, rates, RSSI |
wifi_networks | nearby BSS with band, channel, dBm and security flags; summary by default |
wifi_scan | ask the driver for a fresh scan |
connectivity_diagnose | separate radio, authentication, IP/DHCP, DNS, TCP and explicitly configured Internet reachability |
chronicle_start | start the rotating local change-only recorder |
chronicle_stop | stop and flush the recorder |
chronicle_status | recorder state, path and latest error |
chronicle_recent | recent changes across active and rotated JSONL files |
ble_identify / ble_observe | opaque identity, history and caveated persistence, co-travel, clone or flood evidence from caller-supplied observations |
ble_scan | bounded native advertisement scan without GATT connection; updates the same process-local history |
ble_histories / ble_evaluate | process-local recurrence and explicit disappearance evaluation |
ble_tracker_reset | clear local BLE history and apply allowlist, expected-device and detector policy |
Deliberately absent: saved Wi-Fi keys, MAC changes, adapter
restarts, LAN sweeps. An autonomous model must not be able to leak a credential or drop you
off the network. Calling them returns -32601.
netsh wlan show wlanreport
already groups three days of history into sessions. If a human reading a screen is your
workflow, use them — they're good.no_std + alloc BLE identity/history detectors, ESP-IDF/NimBLE adapter, and native WinRT/BlueZ/CoreBluetooth scans through Node, MCP, Agent and DesktopAF_PACKET capture on Linux, BPF capture on macOS and ESP-IDF promiscuous mode; retention is bounded and configurable, because a collector must not accumulate its surroundings forever