English Β· δΈζ
Your Mac hears more than it tells you.
A macOS terminal listening post for Wi-Fi, BLE, link health, and the RF environment.
BLE view (press n to cycle through Wi-Fi / BLE / Bonjour) β Connected peripherals on top, Advertising devices below, each labelled with its public-format identification.
Events modal (press m to open) β last 100 roam / RF-stir / latency / loss / link events, per-AP Ο baseline, last-hour Ο sparkline.
macOS perceives a lot of signal around your Mac β Wi-Fi networks coming and going, BLE devices broadcasting nearby, gateway latency stretching, RF noise rising β and its built-in UI shows almost none of it. Apple's Wi-Fi panel reports the current signal and nothing else. Bluetooth Settings shows what you've paired, never what's around. macOS has no surface at all for "is my gateway healthy" or "did something just change in the room."
diting fills that gap. It runs in your terminal as a four-panel TUI on top of the same APIs Apple uses internally:
- Wi-Fi visibility. Every BSSID in range, grouped by physical
AP. Plain-language diagnostics on top of dense scan data β
visible-BSSID counts, channel crowding, least-crowded channel
hints, current-link health, a roam score with reasons. Roam
events get logged as they happen, tagged
[band switch on <AP>]vs[inter-AP roam]. - BLE deep identification. Two sections: Connected peripherals
you're using right now (AirPods, Magic Keyboard, Apple Watch β
devices that don't advertise and so are invisible to plain BLE
scanners), and Advertising devices broadcasting nearby β
identified as
AirTag,iBeacon,Eddystone-URL,Tile,SmartTag,iPhone,Mac,Apple Watch,HomePodinstead of the "Apple, Inc. (anonymous) Find My" wall. Pressi(or click a row) on any list view β Wi-Fi, BLE, or Bonjour β for a detail modal: every field the snapshot carries, decoded payloads, RSSI history sparklines and distance estimates where the data permits. - Link health. Continuous gateway + WAN probes. The
Linkrow readsgw 12 ms Β· 0% Β· WAN 18 ms Β· 0% Β· jitter 3 msso a -55 dBm AP that looks fine reads correctly as bad when upstream is dropping packets. - RF environment monitor. Rolling RSSI variance per AP with a
stable/activequalifier (calibrate toquietwithditing calibrate). Surfaces "something changed" without making a presence claim β correlation, never causation. NOT Wi-Fi sensing β seedocs/explainers/wifi-sensing.mdfor what diting deliberately does not claim. - Unified events log. Roam / RF stir / latency spike / loss
burst / link state β all five event types stream into one ring
buffer. Press
mfor a full-screen browser of the last 100; usediting monitorfor headless JSONL output to a Home Assistant pipeline or atail -Faudit window.
For instance: you walk between rooms, your Mac stays glued to the
AP it associated with five hours ago at -75 dBm β even though
there's a new -45 dBm one within reach broadcasting the same SSID.
Zoom stutters and you blame the Wi-Fi. Apple's panel won't tell you
which AP you're on; diting will, and the c binding cycles the
radio so macOS re-runs auto-join and reassociates with the strongest
BSSID. Same path as menu-off-then-on, in one keystroke.
- Diagnose home or office network issues. When Zoom is bad β is
it RSSI? gateway? WAN? a noisy channel? someone hammering the
uplink? The Diagnostics panel +
Linkrow + Events strip narrow it down without you reading raw packets. - Find Bluetooth things around you. What IoT is in this room?
Where's that AirTag? The BLE list resolves vendor + protocol on
every advertising device; the detail modal's RSSI sparkline lets
you walk a target down by signal strength. Anonymous adverts
(vendor + RSSI only, no name) are gated by a 5-second presence
window before they fire a
seenevent, which kills single-packet ghost flicker in dense RF environments while preserving real walk-bys; tune with--ble-presence-gate DURATION(0to capture every ephemeral advert). - See who's on your Wi-Fi. The fourth panel (cycle to it via
n) lists every host on your local subnet β IP, MAC, vendor, hostname, Bonjour name. ARP cache + ICMP sweep; no router login needed. Default /24 sweep;DITING_LAN_INVENTORY_WIDE=1unlocks a /22 sweep for wider home subnets. - Catch anomalous signals. Latency spikes, loss bursts,
unexplained RF variance β diting names what changed and when.
Long-running sessions land in
--logJSONL for after-the-fact analysis withditing analyze. - See patterns across weeks of sessions. Point
diting analyzeat multiple JSONL files (shell glob) with an optional--since 7dwindow and it surfaces the patterns single-session reports miss: an hour-of-day chart, a dayΓhour heatmap, a per-network event-volume ranking, a daily trend with 7-day rolling average, and a top-contributors block β which BSSIDs / BLE devices / LAN hosts caused the most churn over the window. The JSONL log itself captures BLE / Bonjour / LAN transition events alongside the Wi-Fi state, so the aggregations work over the full event vocabulary. - Hand it to ChatGPT or Claude for richer interpretation.
diting analyze --for-llmwrites a Markdown report + a paste- ready analyst prompt; drag the report into chat.openai.com or claude.ai, paste the prompt, get back pattern clustering and hypothesis-ranking. Add--anonymizeto scrub SSIDs / BSSIDs / RFC1918 IPs / hostnames / BLE identifiers before pasting into a public LLM. The handleβoriginal mapping prints to your terminal only β never into the bundle. - (Future) Room-presence sensing. Long-term, hardware-assisted flagship. See Roadmap.
diting carries a notion of where the user is right now. Four scenes ship today, each tuned for one class of environment:
| Scene | When to use | What it changes |
|---|---|---|
home (default) |
apartment / own Wi-Fi, β€ ~15 BLE devices, single AP | BLE presence gate 5 s β kills 0 s ghost flicker but keeps brief contacts |
office |
corp floor, enterprise Wi-Fi, dense BLE + many BSSIDs | BLE presence gate 15 s β absorbs the Continuity RPA churn baseline |
public |
cafe / train / plane / public Wi-Fi | BLE presence gate 30 s β almost everything is passers-by |
audit |
actively investigating (security research, debug, forensics) | BLE presence gate 0 s β record every advert |
When you don't pass --scene and don't set DITING_SCENE, diting picks the scene itself by inspecting the active Wi-Fi connection at startup. The rules are simple, deterministic, and run on local state only β no probes, no phone-home:
- Enterprise auth (WPA2 Enterprise / WPA3 Enterprise / 802.1X) β
office - β₯ 30 visible BSSIDs in the most recent CoreWLAN scan β
office - otherwise β
home
public stays opt-in (captive-portal detection without active probing is unreliable). When the auto-detect runs, diting prints a one-line banner to stderr explaining what it picked and why:
$ diting
auto-detected scene: office (WPA2 Enterprise auth)
Suppress the banner with DITING_SCENE_QUIET=1.
For networks you visit regularly, copy scenes.example.yaml to scenes.yaml (git-ignored) and map SSID β scene:
networks:
- ssid: HomeNet
scene: home
- ssid: Meituan
scene: office
# Use gateway_mac when SSID is reused across networks (eduroam):
- gateway_mac: 14:51:7e:71:5a:1a
scene: officeA yaml hit wins over the auto-detect. The banner becomes:
pinned scene: office (matched "Meituan" in scenes.yaml)
Override the file location with DITING_SCENES_FILE=/path/to/scenes.yaml.
CLI flag and env var still take precedence over scenes.yaml and the heuristic:
diting --scene office # this session
DITING_SCENE=office diting # persistent (e.g. shell rc)
The active scene is tagged into the JSONL session header
(session_meta) so diting analyze can group cross-session
aggregations by scene, and the --for-llm bundle injects the
scene's baseline expectation into the prompt template β the LLM
reads office-mode noise as "expected baseline" rather than
"anomalous", and home-mode novelty as "interesting" rather than
"signal in the bin".
--ble-presence-gate D continues to override the scene's gate
when you want fine control for one session.
diting (θ°ε¬) is a mythical beast in Chinese Buddhist lore β the divine mount of KαΉ£itigarbha Bodhisattva (ε°θηθ©θ¨). It is said to hear every sound in heaven, on earth, and across the ten directions; one ear pressed to the ground, it can tell truth from falsehood, virtue from sin, and the present from the past. Your Mac sits at the centre of a smaller ten directions of its own β Wi-Fi networks coming and going, BLE devices whispering nearby, upstream packets quietly dropping β and, left to itself, it never relays a word of any of it.
tianer (倩θ³) β literally "heavenly ear" β is the ear behind 倩θ³ι, one of the Six Supernormal Powers (ε η₯ι) in Buddhist tradition: the faculty of clairaudient hearing, by which sounds too far, too faint, or too hidden for ordinary ears can still be made out. θ°ε¬'s reputation for hearing all ten directions rests on this faculty β the beast is the listener, but ε€©θ³ is the ear it listens through.
curl -fsSL https://raw.githubusercontent.com/chenchaoyi/diting/main/install.sh | bash
ditingOne command. No Python, no uv, no Xcode Command Line Tools on
your machine β the installer downloads a self-contained binary
plus the helper bundle and drops them in
~/.local/share/diting/ and ~/Library/Application Support/diting/.
On first run the helper opens a small status window and walks you
through three macOS permission prompts in order β Location β Bluetooth
β Notifications β one at a time. Click Allow on each and the TUI
launches with full SSID, BSSID, and BLE data, plus diting-branded
notifications when the watchdog detects an anomaly.
Why the helper? macOS 14.4+ redacts SSID and BSSID to None unless the calling process has Location Services. A Python CLI launched from Terminal cannot get on that list, but a tiny
.appbundle can.ditingshells out to it for scan data and gets the real values back. Press?inside the TUI for the full story.
Pin a specific release:
DITING_VERSION=v0.10.0 curl -fsSL https://raw.githubusercontent.com/chenchaoyi/diting/main/install.sh | bashRequires Python 3.11+ and uv, plus the Xcode Command Line Tools (the helper bundle is built from Swift sources on first launch).
git clone git@github.com:chenchaoyi/diting.git
cd diting
uv sync
make helper # one-time: build + sign the Swift helper bundle
open helper/diting-tianer.app # one-time: grant Location β Bluetooth β Notifications
uv run ditinguv run diting and the curl-installed diting can coexist on the
same machine β the developer flow keeps picking up the in-repo
helper, the installed binary uses its own copy under Application
Support.
Run diting analyze <log.jsonl> against a --log-produced
JSONL to get a rule-based report β heuristics that name what
went wrong (Frequent inter-AP roams, Real packet loss observed, Repeated disassociations, etc.) plus a connection
timeline and an actionable TODO list per insight.
Point it at multiple files (shell glob) with an optional
--since DURATION filter to surface patterns single-session
reports can't:
diting analyze 'diting-*.jsonl' --since 30dβ¦produces (on top of the per-session block):
- Scope header β file count, observed span, active filter
- Events by hour-of-day β 24-row ASCII bar chart
- Day Γ hour heatmap β 7Γ24 density grid using
βββββ βββso weekend mornings and weekday lunch hours pop out visually - Top networks β events per associated BSSID, ranked
- Daily trend β per-day total + 7-day rolling average
- Top contributors β three sub-rankings: BSSIDs by
roam + RF-stir count; BLE identifiers by
seencount (catches privacy-rotating devices); LAN hosts by DHCP-rotation count
--since accepts 30d / 7d / 24h / 90m / 60s.
Single-file no---since invocations keep the original
per-session layout verbatim β the cross-session blocks only
render when the user is genuinely doing a multi-session view.
diting analyze 'diting-*.jsonl' --since 30d --for-llmWrites a paste-ready bundle to ./diting-llm-<timestamp>/:
report.mdβ Markdown rendition of the same analysis the terminal produces, with tables for ranked data, fenced code blocks for the ASCII charts, and a glossary section defining diting-specific terms so the LLM doesn't have to guess.prompt.txtβ a paste-ready analyst prompt that asks the LLM to identify the top patterns the data supports, name likely root causes + supporting evidence, suggest follow-up investigations, and label any inferences as "hypothesis" rather than "fact".
The CLI then prints a four-step paste workflow (open
chat.openai.com / claude.ai β drag-drop the .md β paste
the prompt β submit). No API key, no telemetry, no upload β
diting writes the files locally and the user controls who
sees them.
Add --anonymize when pasting into a public LLM:
diting analyze 'diting-*.jsonl' --since 30d --for-llm --anonymizeSSIDs / BSSIDs / RFC1918 IPs / hostnames / BLE identifiers /
LAN MACs get replaced with stable handles (SSID_1, AP_1,
IP_1, HOST_1, BLE_1, MAC_1). Public IPs (8.8.8.8,
1.1.1.1) and vendor names (Apple, Inc., Cisco Systems)
pass through unchanged. The handleβoriginal mapping prints
to terminal stdout only β never into the bundle β so you can
decode the LLM's references later without leaking the mapping
into the chat.
uv run diting --lang zh # force Chinese
DITING_LANG=zh uv run diting # via env varWith no override, diting autodetects the system locale β
LANG=zh_CN.UTF-8 defaults to Chinese; everything else stays English.
| Key | Action |
|---|---|
q |
quit |
p |
pause / resume polling |
r |
force a rescan now (CoreWLAN ~5 s throttle still applies) |
s |
cycle sort β Wi-Fi: by AP β by signal; Bonjour: service β by-host |
n |
cycle Nearby view: Wi-Fi BSSIDs β BLE β Bonjour β LAN |
c |
force re-roam β cycle Wi-Fi off/on so macOS re-picks the strongest BSSID |
m |
open / close the Events modal β last 100 roam / stir / latency / loss / link events |
? |
open / close the in-app help screen |
b |
open / close Wi-Fi Basics: SSID, BSSID, channel, band, security, roam score |
j |
(in the Wi-Fi detail modal) join the inspected SSID β previously-saved networks confirm via Touch ID (or login password on Macs without a sensor) and join silently; new networks get a native macOS password prompt. Not hitless: a cross-SSID switch tears the current connection down for ~2-5 s. Enterprise / 802.1X is refused with a hint. |
watch, once, monitor, and calibrate subcommands run
diting without the TUI:
uv run diting once # snapshot of current connection, exit
uv run diting watch # streaming text events until Ctrl+C
uv run diting monitor # headless JSONL events to stdout
uv run diting monitor --out events.jsonl # append JSONL to a file
uv run diting monitor --notify # macOS Notification Centre alerts on high-confidence events
uv run diting calibrate # 5 min "empty room" RSSI baseline β ./diting-baseline.jsonThe monitor subcommand is the long-run / Home Assistant
integration target β every roam, RF stir, latency spike, loss
burst, and link-state change emits one well-formed JSON line. The
schema lives in
docs/specs/v0.7.0-network-ground-truth-and-environment-monitor.md.
diting works fine without any AP-name configuration β every
BSSID gets an auto-clustered label like ?AB:CD:EF so radios of the
same physical AP group together visually, and roam classification
between APs still works.
If you want human-readable AP names (2F-living instead of
?40:fe:95) in the scan list and roam log, drop a file at
./aps.yaml (next to the executable / the cloned repo's
aps.example.yaml):
aps:
- name: 1F-bedroom
mgmt_mac: 40:fe:95:8a:3c:07
- name: 2F-living
mgmt_mac: 40:fe:95:8a:3c:54
- name: 3F-attic
mgmt_mac: bc:22:47:ca:79:46diting then renders 2F-living (5G) (40:fe:95:8a:3c:58) in
place of the raw BSSID, and roam events read [band switch on 2F-living: 5G β 2.4G] or [inter-AP roam].
Where the mgmt MACs come from. Most controllers (H3C, Aruba,
Ubiquiti, Cisco, ASUS mesh, β¦) expose only an AP-level management
MAC per access point, not the per-radio BSSIDs each AP actually
broadcasts. Read those off the controller's AP list page β
typically at the controller's web UI under "Access Points" / "AP
ε葨" / "Devices" β then paste them into aps.yaml with whatever
spatial labels make sense to you.
When to skip this entirely. On enterprise / shared / unfamiliar
networks where you can't access the controller, just don't create
aps.yaml. The auto-cluster labels (?AB:CD:EF) already correctly
group every radio of one physical AP under a single label β you
lose the friendly name, but every other feature works.
If your AP vendor randomises per-radio MACs (rare; some Cisco
Meraki SKUs), add a radio_overrides map mapping specific BSSIDs
to AP names. See aps.example.yaml.
Set DITING_INVENTORY=/some/path/aps.yaml to load the file from
somewhere other than the current working directory.
| Variable | Default | Effect |
|---|---|---|
DITING_LANG |
autodetected | UI language: en or zh. Equivalent to --lang. |
DITING_INVENTORY |
./aps.yaml (CWD-relative) |
Path to the AP-aliases YAML. The file is optional; if absent, diting uses auto-cluster labels. |
DITING_HELPER |
searched in /Applications, ~/Applications, repo helper/ |
Path to the diting-tianer.app bundle or its binary. |
DITING_SCAN_INTERVAL |
7 |
Seconds between scans. CoreWLAN throttles around 5 s, so values below ~6 yield empty scans every other call. Floor 3. |
DITING_LATENCY_WAN_TARGET |
autodetected from scutil --dns |
IP for the WAN latency anchor. Default picks the first non-gateway nameserver from SCDynamicStoreCopyValue("State:/Network/Global/DNS"); if the only configured DNS is the gateway, the WAN probe is skipped and the diagnostic line reads WAN n/a (DNS == gateway). Override to pin an explicit IP (e.g. 1.1.1.1 for networks that allow it). |
DITING_LAN_INVENTORY_WIDE |
unset | When set to 1, the LAN view sweeps a /22 (1022 hosts) around your interface IP instead of the default /24 (254 hosts). Useful on home subnets wider than /24; on corporate /16+ VLANs the sweep is still capped at /22 around your IP. |
Some neighbours' SSIDs come back (hidden). That's the 802.11
hidden-SSID bit β the AP is broadcasting normally, just with the
SSID information element blanked. BSSID, channel, signal, and
capabilities are all still visible. Hidden β undetectable.
Tx Rate and Max Link Speed may diverge. Apple's
transmitRate (current data rate, can include frame aggregation)
and maximumLinkSpeed (radio capability ceiling at the negotiated
PHY/MCS/NSS) come from different CoreWLAN APIs; "current β€ max" is
not guaranteed. The Connection panel shows both with a footnote.
The Diagnostics panel is a guide, not an RF survey tool. Channel recommendations and roam scores are estimated from the BSSIDs visible to CoreWLAN in the latest scan. They reward stronger RSSI, better SNR, cleaner bands, and less crowded channels, and they penalize open networks and security mismatches. Treat them as "where to look next" hints rather than as Apple's official roaming decision.
OPEN means no Wi-Fi-layer password/encryption. Captive portals
can still ask for login after association, but the radio link itself
is open. The Nearby BSSIDs panel marks these rows so you can assess
guest networks and accidentally-open SSIDs quickly.
Without the helper, the Nearby BSSIDs scan list is fully redacted.
RSSI, channel, band, and width still come through, but every SSID
shows (redacted) and every BSSID (redacted). The Connection
panel itself is unaffected β diting reads SSID and BSSID for
the current AP through a separate SCDynamicStore tunnel that
macOS forgot to redact.
BLE devices rotate their identifier for privacy. The same
physical device (an AirTag, a phone, an Apple Watch) appears under
multiple CoreBluetooth UUIDs over time. diting's fuzzy merger
collapses obvious duplicates into one row by matching (vendor_id, name) plus an RSSI window, and shows a (merged N) badge on the
combined entry, but the heuristic is conservative β anonymous
beacons (no vendor, no name) are never merged because conflating
them would silently remove signal. Expect to see one or two extra
rows per rotating device when names disagree.
BLE range is short (~10 m vs Wi-Fi's ~30 m), so the BLE list will feel "smaller" than the Wi-Fi scan even on a busy floor.
macOS hides the underlying BLE MAC. CoreBluetooth gives only
a per-host UUID; vendor identification goes through the
manufacturer-data company ID field exclusively. diting decodes
the public portions of Apple Continuity (the Nearby Info
device-class nibble β iPhone / iPad / Mac / Apple TV /
HomePod / Apple Watch) and the Find My / iBeacon signatures,
but the encrypted payloads (lock state, AirDrop, Music-playing,
Handoff session info) stay opaque. Per-model identification
(iPhone 14 vs 15) is not in any public ad packet β anyone
claiming to do that is reading proprietary GATT services after
connecting, which diting will not do.
The Environment line is not Wi-Fi sensing. diting sits in
Tier 0 of the Wi-Fi-sensing capability ladder: rolling RSSI variance
on the data CoreWLAN already exposes. The line surfaces a binary
stable / active (or quiet after diting calibrate)
qualifier β never people-counting, never motion-with-pose, never
breathing rate. Channel State Information (the data the academic
sensing literature actually uses) is not exposed by macOS, and even
where it is exposed (ESP32, Intel 5300 under Linux) the Tier-3+
demos require a research stack, not a pip install. See
docs/explainers/wifi-sensing.md
for the full story; the Environment line is the live example of
what diting honestly does with RSSI.
Connected peripherals have no RSSI. retrieveConnectedPeripherals
returns the devices currently associated with the Mac
(AirPods you're listening to, Magic Keyboard you're typing on),
but reading their signal mid-session would require readRSSI()
against an active connection β an invasive perturbation diting
deliberately avoids. The Connected section shows β for the
signal column and sorts alphabetically by name.
disassociate() is unreliable for forcing a roam. Earlier
versions of diting used iface.disassociate() for the c
binding; on 802.1X enterprise networks it would tear down the link
and macOS would not auto-rejoin. Cycling power via
setPower(false) then setPower(true) mirrors the Wi-Fi-menu
off/on path and reliably triggers full auto-join with Keychain
credentials.
Contributing? See DEVELOPMENT.md for the SDD
workflow, capability index, local development commands, bilingual
discipline, and an implementation deep-dive (BSSID resolution,
channel handling, pluggable backend).
Version history lives in CHANGELOG.md.
Three buckets: near-term gets actively worked, mid-term is on the queue with a clear shape, further out is direction without a timeline. No specific dates β diting is a personal project; the ordering is intent.
- mDNS / Bonjour LAN inventory. A
n-toggleable third view alongside Wi-Fi / BLE listing every Sonos, Apple TV, HomePod, NAS, printer, AirDrop-capable Mac, HomeKit hub, Time Capsule, and other service-advertising peer. Answers "what's on my network and is it alive" with a much richer answer than ARP. - Anomaly watchdog mode. Headless long-runs that push macOS
Notification Centre alerts on high-confidence events (stir,
loss burst, latency spike). Today's
diting monitor --notifyis the seed; it grows configurable thresholds and per-event silence windows. - Per-device proximity compass. When the BLE detail modal is open and a row is selected, render a "getting warmer / colder" signal-strength compass. Walk down an AirTag, Tile, or any advertising device by RSSI gradient.
- Cellular state, when the Mac silicon exposes it. A few Mac
models have cellular modems; tethered iPhone state is broadly
available via
pymobiledevice3-style access. Surface signal bars + carrier + technology when present, gracefully omit otherwise.
- Investigate / scenario mode. A guided entry β
diting troubleshoot zoom,diting find <name>β that walks a non-power-user through the relevant panels with plain-English conclusions. Keeps the dashboard view for power users. - JSONL session replay.
diting replay <file.jsonl>feeds a prior log back through the TUI as if events were happening live, for after-the-fact incident review. - Trend graphs in the TUI. RSSI / latency / channel-util over time, time-on-AP per BSSID. Builds on the existing JSONL log.
- Auto-roam mode. Gated, conservative. When a clearly-better same-SSID candidate persists β₯ N seconds, cycle the radio automatically β sticky-AP pain hands-free.
- Pin-a-BSSID join. Extend the
jaction on the Wi-Fi detail modal so the user can deliberately associate to one specific BSSID within an ESS (right now CoreWLAN'sassociate(toNetwork:password:)accepts the SSID and the OS picks the BSSID β fine for normal use, useless for "is it this specific radio that's flaky?"). Likely path: temporarily disable auto-join + 802.11r/k/v roaming for the duration of the association, or fall through to a per-BSSIDCWConfigurationprofile. Diagnostics value β lets a user A/B their two ceiling APs without walking between rooms.
- Room-presence sensing β flagship. Move the RF environment
monitor from "something changed" to "someone entered the
living room". This is hard; Tier-3+ sensing requires CSI (not
exposed by macOS) or a small auxiliary hardware probe. Long-
term, hardware-assisted, deliberate. See
docs/explainers/wifi-sensing.mdfor the honest read on what's possible. - Dedicated edge-hardware companion. Pair diting with a small
always-on box (Raspberry Pi-class) for the two things a
foreground macOS TUI inherently can't do: 24/7 persistent
observation (a stranger joined the LAN at 3 a.m. for five
minutes β your Mac was asleep) and Wi-Fi sensing on a
stationary radio in monitor mode (prerequisite for Tier-1+
in the sensing explainer). Separate product / codebase
(Linux + Python), macOS TUI stays the front end and subscribes
to the edge box over Bonjour. See also
docs/explainers/lan-inventory-arp.mdfor the LAN-inventory half that doesn't need the edge box. Any-device LAN inventory (ARP-based).[shipped] A fourth panel listing every host on the local /24 β IP, MAC, vendor (via OUI), hostname (via reverse DNS), Bonjour cross-reference, first/last seen. Cycle to it vian(fourth view). Default /24 sweep; setDITING_LAN_INVENTORY_WIDE=1for a /22 sweep on wider home subnets. Design lives indocs/explainers/lan-inventory-arp.md.- Optional menu-bar app for ambient awareness without keeping a terminal open.
- Linux backend.
nl80211viapyroute2or shelling out toiw scan. Architecturally already abstracted behindWiFiBackend; just unimplemented. - Continuity / Personal Hotspot / iCloud Private Relay state. Mac-specific integrations surfaced in Diagnostics where they're load-bearing for "why is my network weird right now".
- MAC-OUI vendor names come from the IEEE Registration Authority MA-L (24-bit) registry. The bundled snapshot in
src/diting/data/*_ouis.jsonis refreshed per release viauv run python scripts/refresh_ouis.py, which pulls the canonical CSV fromhttps://standards-oui.ieee.org/oui/oui.csv.
MIT. See LICENSE.