Monitor and debug your Wifi via the cli or in your browser.
Ok, so this project was developed because I was getting constantly kicked off my home wifi and thought there HAD to be a better way to debug this garbage. And none of the damn Mac tools are, frankly, worth a shit. So, here ya go. Have fun with it.
Netdebug opens a dashboard on 127.0.0.1. It watches your throughput, signal, reachability, the Wi-Fi airspace around you, and which apps are using the network — and it keeps a running history. So when something goes wrong (congestion, slowdown, a 2am drop, the band-steer that tanked your call), you can see exactly when and why instead of guessing.
- Loopback only. The dashboard is served on
127.0.0.1and nowhere else. There's no option to put it on your network. - One small binary, no dependencies. Pure Go standard library. The dashboard is a single self-contained page — no CDN, no web fonts, no outside requests.
- Read-only. It measures and reports. It never changes your network settings.
Grab it with Go:
go install github.com/MonkeyIsNull/netdebug@latestOr build from source (nothing to download — it's standard-library only):
git clone https://github.com/MonkeyIsNull/netdebug
cd netdebug
go build -o netdebug .You'll need Go 1.26 or newer.
./netdebug --serveOpen http://127.0.0.1:8099/ in your browser. If that port's taken, netdebug grabs the next free one and prints the real URL. Press Ctrl-C to stop.
The live dashboard (--serve) is the heart of netdebug. It refreshes every second and puts everything on one page:
- Live throughput — your up/down speeds right now, colored by Wi-Fi band so a slowdown that lines up with a band change jumps out.
- Reachability — gateway and internet round-trip time, packet loss, jitter, and DNS health, with a per-target breakdown. It pauses on battery or a locked screen to save power (turn that off with
--no-power-gating). - History — a per-minute and per-hour chart that survives restarts, so you can scroll back through the day.
- Airspace — the Wi-Fi networks around you, how crowded each channel is, and the clearest 80 MHz block to aim for. This is a passive read — netdebug never scans the air or knocks you off your channel.
- Outage journal — a timestamped log of real outages (a single blip is filtered out), each with a best-guess cause.
- Band / channel change journal — a timestamped log of every band-steer and channel hop, so "wait, when did I get moved to 2.4?" is a glance, not a mystery.
- Top talkers — which apps are using the network, ranked by upload. Click one to see what it is and who it's talking to.
- Speed test — a one-click download / upload / latency test, right from the page.
Two optional extras when you serve:
--alerts— native macOS notifications when you drop, get band-steered, or your call quality tanks.--throttle-watch— an occasional, rate-limit-friendly download sample to catch peak-hour ISP throttling.
Besides the live dashboard, netdebug can run a single bottom-up health check of whatever network you're on — DHCP → route → gateway → internet → DNS → web — and tell you the first thing that's broken (that's your problem):
./netdebug # check the current network; saves a report to ./net-tests/
# switch networks by hand in the macOS Wi-Fi menu
./netdebug # check the other one
./netdebug --compare # put the two latest reports side by sideThe result names the first failing layer, for example: "NO UPSTREAM: the gateway answers but the internet doesn't — this network's uplink is down." Add --all (or --signal / --ipv6 / --proxy / --extras) for signal sampling, IPv6 checks, VPN/proxy detection, and more.
There's a
--livemode that switches networks for you, but recent macOS usually blocks it (CoreWLANError -3900), so switching by hand and using--compareis the reliable way.
Each of these runs once, prints to your terminal, and exits — no server:
./netdebug --sample # live up/down rates in the terminal
./netdebug --speed # download / upload / latency
./netdebug --bufferbloat # latency-under-load grade, A–F
./netdebug --airspace # neighbors, channel crowding, clearest block
./netdebug --top-talkers # per-app bandwidth, biggest uploader first
./netdebug --proc <pid> # what a process is and who it's connected to
./netdebug --status # one-line health summary (from a running --serve)
./netdebug --report # daily / weekly rollup from saved historySee examples/ for a fuller tour with sample output.
Want netdebug always on? Install it as a per-user login agent — no root, just your own session:
./netdebug --install-launchd # start now, and at every login
./netdebug --install-launchd --port 9000 # pin a port
./netdebug --launchd-status # is it running / crash-looping?
./netdebug --uninstall-launchd # remove itIt installs to ~/Library/LaunchAgents/com.cobenian.netdebug.plist and logs to ~/Library/Logs/netdebug/netdebug.log (which doesn't rotate, so trim it now and then). Point it at a stable binary path like /usr/local/bin/netdebug — it will refuse a throwaway go run path on purpose.
netdebug folds samples into buckets on disk so your charts survive restarts:
- Location:
~/Library/Application Support/netdebug/history - Kept for: per-minute buckets 48 hours, per-hour buckets 90 days.
- Change it:
--history-dir /abs/path, orhistory_dir/minute_ttl_hours/hour_ttl_daysin a config file.
The outage and roam journals sit alongside as append-only JSONL.
- Loopback only. The server binds
127.0.0.1and re-checks the real socket address after binding, so it can never end up on0.0.0.0,::, or your LAN. There's no flag to change that, and guard tests enforce it. - No sudo. It reads the radio with
system_profilerandifconfig, neverwdutilorsudo, and never prompts for a password. Top talkers usenettopthe same way — if a system process isn't visible to your user, netdebug just says "unavailable" instead of escalating. - Self-contained page. The dashboard HTML has zero external URLs — no CDN, no web fonts, no
<link>, nourl(). Charts are inline SVG, and anything user-facing is written withtextContent, neverinnerHTML.
You don't need a config file — netdebug runs fine on its defaults. To tweak things, copy config.example.json to config.json and pass --config config.json. The example file lists every option.
- macOS on Apple Silicon. Mac-only right now.
- It only looks. No network settings are ever changed.
- macOS hides some radio details. Without Location permission, macOS redacts your SSID and BSSID. netdebug still reports link state honestly, and you can label your network with
--ssid(orssid_labelin config). - Top talkers are per-user. Some system processes need admin rights to identify but we don't do that atm.
- docs/ARCHITECTURE.md — how it's built, the invariants, and a file-by-file map for contributors.
- CHANGELOG.md — what landed, by phase.
- examples/ — common commands and sample output.
- scripts/wifilog.sh — a companion script for digging through macOS Wi-Fi logs after the fact (see scripts/README.md).
go build # compile the binary
go test # run the tests (they run offline — no network needed)
go vet # static checksTests are table-driven over every macOS-output parser and all the decision logic, and they run offline — no network needed.
Issues and pull requests are welcome. Found a bug or have an idea? Open an issue. Want to send a change? Fork, make it, and open a PR — please run gofmt and go test first (CI checks gofmt, go vet, build, and tests on macOS).
MIT — free to use, change, and share. See LICENSE. Copyright (c) 2026 Adam Guyot.





