This release focuses on robustness and privilege-model hardening, driven by an independent architecture review of the app's core repair loop and privileged-helper security model.
Security: sudoers surface reduced
route add/route delete no longer run through a broad NOPASSWD: /sbin/route sudoers grant. They're now two validated subcommands (route-add, route-delete) on the existing root-owned ipv6monitor-pf wrapper, which validates the interface and gateway in root-context code before building the route command - the same hardening the RA-protection feature already had.
Action required for existing installs: the old sudoers rule does nothing for this version. Run:
sudo Scripts/apply-sudoers-update.shThis reinstalls the wrapper, replaces your /etc/sudoers.d/ipv6monitor with the wrapper-only rule (backing up the old one first), and verifies it works without a password prompt. Until this is run, automated route repair will fail on the next route loss.
Fixes
- Closed a real cross-queue data race in the core monitoring loop:
currentInterface/routerIPare now consistently confined to the app's serial repair queue on both the write and read side (menu, connectivity window, health-poll timer), matching the discipline already used for RA Protection's state. - Removed a deadlock-prone shell helper. A legacy process-execution path could hang forever on large command output (e.g. a big
ndp -anneighbor cache); both callers now use the hardened, concurrent-drain runner. - Fixed silently-disabled snapshot rate-limiting after a successful auto-repair. The episode-tracking state that throttles forensic snapshots during route flapping never restarted its clock after a successful repair - the most common recovery path - so a rapid string of losses right after a repair could re-trigger full disk-writing snapshots. The route-loss/repair state machine has been extracted into a small, independently unit-tested
RouteEpisodeTracker. - Fixed a hazard where the app could delete a live VPN's default route. The stale-foreign-route cleanup added for a prior incident used an overly broad condition; it's now tightened so a route is only removed when the foreign interface is both inactive and has no real global IPv6 address (a live
utun/VPN interface never satisfies both).
Also in this release
MECHANICS.md- a maintained architecture map of the codebase.Scripts/tests/run-all.sh- runs the full test suite in one command.- Unified three duplicate process-execution implementations into one shared, hardened runner.
All changes were verified with a full local build + test suite pass, two independent Codex review passes (the second confirmed a fix to a race the first pass had found), and a live test of the new route-add/route-delete path against the real routing table (delete -> verify loss -> repair -> verify recovery, plus interface/gateway input-validation checks).
Note
The bundled app is ad-hoc signed (no paid Apple Developer account). On first launch you may need to right-click -> Open, or remove the quarantine attribute. Building from source (see README) is equally supported.