A utun tunnel counted as the VPN whenever it was up with a routable
IPv4 address, so idle Tailscale or a tunnel left behind by a
disconnected client became the VPN pane and its probes failed. A
tunnel is now the VPN only while it carries the IPv4 default route in
netstat: an unscoped default row, or both 0/1 and 128.0/1 rows on it.
A default row scoped to a tunnel (flag I) does not count; on the
physical interface it still does, since a VPN holding the default
leaves the physical default scoped. Tests feed netstat text in the
macOS layout through the parser into Select. Not run on a real Mac.
Model: opus-5-5
Linux runs exactly as before when both bridge interfaces exist. When they do
not, the lone default-route interface is monitored, and a single interface
draws a single UI pane instead of an empty second one.
macOS is newly supported: a running VPN tunnel (utun) is monitored as the
primary pane alongside the physical default-route interface, or the physical
interface alone when no VPN is up.
Detection lives in internal/netdetect: interface/route data types, pure
selection logic keyed on OS name, and route parsers, all unit-tested on Linux
for both platforms. Only the real route query and the per-platform TCP dial
binding are build-tagged. NewMonitor now takes a list of interfaces.
Not verified on a real mac: live netstat parsing, IP_BOUND_IF dialing, Mullvad leak protection.
Model: opus-4-8 (implementation); fable-5-1 (landing commit)