An adaptive Wi-Fi reconnect daemon for macOS, built for a laptop that's routinely closed in a bag mid-SSH session.
Two pieces, running together:
wifi-guardian— a loop that watches the current connection and known-network list, reconnects when disconnected (preferring real Wi-Fi over a personal hotspot), and never force-disconnects an already-working session just to chase something marginally better.wifi-keepalive— a tiny Swift/IOKit binary that declareskIOPMUserActiveRemoteevery 60s, which stops macOS from throttling its own background Wi-Fi scanning/joining while the lid is closed.
networksetup -getairportnetwork can report "not associated" on a
perfectly good connection; system_profiler SPAirPortDataType (not
airport, which is gone on modern macOS) and ipconfig getsummary are the
two commands that actually tell the truth here. And disassociating from a
connection you're already on — even to test something "better" — can sever
a live SSH/mosh session for the several seconds it takes to fail over. Both
of those are why this exists instead of a five-line networksetup cron job.
# flake.nix
inputs.wifi-guardian.url = "github:CodeJoule/wifi-guardian";
inputs.wifi-guardian.inputs.nixpkgs.follows = "nixpkgs";
# darwinConfigurations.<host>.modules
[
inputs.wifi-guardian.darwinModules.default
{
services.wifi-guardian = {
enable = true;
user = "yourusername";
};
}
]Then darwin-rebuild switch. It installs as a per-user LaunchAgent
(RunAtLoad + KeepAlive) — no manual nohup, no re-launching after a
reboot or logout.
| Option | Default | |
|---|---|---|
user |
(required) | who the LaunchAgent runs as |
interface |
en0 |
Wi-Fi interface |
hotspotPattern |
iPhone|Hotspot |
regex marking SSIDs as last-resort |
fastIntervalSeconds |
15 |
poll rate while unsettled |
slowIntervalSeconds |
60 |
poll rate once solidly connected |
weakSignalDbm |
-75 |
logged as weak at/below this |
cooldownSeconds |
120 |
backoff after a failed join |
allowLiveHop |
false |
allow disassociating from a working connection to switch — off because that risk isn't worth it on a machine used for live SSH |
~/.local/var/log/wifi-guardian.log — state changes only; silence means
steady state. LaunchAgent stdout/stderr land in /tmp/wifi-guardian.std{out,err}.log.