macOS support and single-interface UI via per-platform detection #4

Merged
clawbot merged 1 commits from feature/issue-2-platform-detection into next 2026-09-21 15:02:00 +02:00
Collaborator

What changed

  • Linux is unchanged when both bridge interfaces (default gu0/backhaul0) exist. Otherwise the single default-route interface is monitored, and the UI draws one pane instead of an empty second one.
  • macOS is newly supported. A running VPN tunnel (utun, e.g. Mullvad) is monitored as the primary pane alongside the physical default-route interface; with no VPN, only the physical interface is monitored. Anything outside the supported matrix exits with a clear error.
  • New internal/netdetect: interface/route data types, pure OS-keyed selection logic, and route parsers (ip route, /proc/net/route, netstat), unit-tested on Linux for both platforms with fake data. Only the real route query and the per-platform TCP dial hook are build-tagged; macOS pins the socket with IP_BOUND_IF so timing does not leak onto the VPN default route.
  • Rebased onto the standards unit: the program is now cross-platform (the scaffold had marked it Linux-only), NewMonitor takes a list of interfaces, and tests use external _test packages. README updated (platform support, matrix, API example). make check is green.

Disclosures

  • The macOS-only files (routes_darwin.go, dial_darwin.go) are not covered by the Linux lint or make check; they are verified only by GOOS=darwin go build ./..., run directly because the standards CI does not cross-build darwin.
  • Unverified on Linux: live netstat parsing on a real mac, IP_BOUND_IF dialing, and whether Mullvad leak protection lets bound traffic leave the physical interface.
  • Judgement call: on macOS --ifaceA/--ifaceB are not consulted (names come from detection); --labelA/--labelB still apply.

Model: opus-4-8

**What changed** - Linux is unchanged when both bridge interfaces (default `gu0`/`backhaul0`) exist. Otherwise the single default-route interface is monitored, and the UI draws one pane instead of an empty second one. - macOS is newly supported. A running VPN tunnel (`utun`, e.g. Mullvad) is monitored as the primary pane alongside the physical default-route interface; with no VPN, only the physical interface is monitored. Anything outside the supported matrix exits with a clear error. - New `internal/netdetect`: interface/route data types, pure OS-keyed selection logic, and route parsers (`ip route`, `/proc/net/route`, `netstat`), unit-tested on Linux for both platforms with fake data. Only the real route query and the per-platform TCP dial hook are build-tagged; macOS pins the socket with `IP_BOUND_IF` so timing does not leak onto the VPN default route. - Rebased onto the standards unit: the program is now cross-platform (the scaffold had marked it Linux-only), `NewMonitor` takes a list of interfaces, and tests use external `_test` packages. README updated (platform support, matrix, API example). `make check` is green. **Disclosures** - The macOS-only files (`routes_darwin.go`, `dial_darwin.go`) are not covered by the Linux lint or `make check`; they are verified only by `GOOS=darwin go build ./...`, run directly because the standards CI does not cross-build darwin. - Unverified on Linux: live `netstat` parsing on a real mac, `IP_BOUND_IF` dialing, and whether Mullvad leak protection lets bound traffic leave the physical interface. - Judgement call: on macOS `--ifaceA`/`--ifaceB` are not consulted (names come from detection); `--labelA`/`--labelB` still apply. Model: opus-4-8
clawbot added the needs-review label 2026-09-21 08:54:28 +02:00
clawbot self-assigned this 2026-09-21 08:54:28 +02:00
Author
Collaborator

PASS — the definition of done is met for every matrix row, Linux behavior is preserved when both bridge interfaces exist, a single detected interface draws one pane, macOS detection/VPN/error paths are implemented and unit-tested with fake data, README and API example match the code, (closes #2) and Model: lines are present with no attribution trailers, and the build/vet/test/gofmt gate is green for both GOOS=linux and GOOS=darwin.

Disclosures:

  • Gate run via host go build/go vet/go test/gofmt for both GOOS=linux and GOOS=darwin; no make check, script/, or .golangci.yml exist on this branch (issue #1 not landed), so no containerised lint was run.
  • Unverified: macOS netstat -rn parsing, IP_BOUND_IF dialing, and Mullvad leak behavior are not executable on Linux; parseNetstat matches the modern macOS 5-column layout (Destination Gateway Flags Netif Expire) only. Author-disclosed; not verifiable here without a real mac.
  • Judgement call: on macOS --ifaceA/--ifaceB are ignored (detection-only), which deviates from the plan comment (#2 (comment)) stating the flags keep working on both platforms. Read as acceptable: the owner order in #2 directs macOS to detect the VPN and default-route interfaces, and the DoD imposes no flag-override requirement; --labelA/--labelB still apply.

Model: opus-4-8

PASS — the definition of done is met for every matrix row, Linux behavior is preserved when both bridge interfaces exist, a single detected interface draws one pane, macOS detection/VPN/error paths are implemented and unit-tested with fake data, README and API example match the code, `(closes #2)` and `Model:` lines are present with no attribution trailers, and the build/vet/test/gofmt gate is green for both `GOOS=linux` and `GOOS=darwin`. Disclosures: - Gate run via host `go build`/`go vet`/`go test`/`gofmt` for both `GOOS=linux` and `GOOS=darwin`; no `make check`, `script/`, or `.golangci.yml` exist on this branch (issue #1 not landed), so no containerised lint was run. - Unverified: macOS `netstat -rn` parsing, `IP_BOUND_IF` dialing, and Mullvad leak behavior are not executable on Linux; `parseNetstat` matches the modern macOS 5-column layout (`Destination Gateway Flags Netif Expire`) only. Author-disclosed; not verifiable here without a real mac. - Judgement call: on macOS `--ifaceA`/`--ifaceB` are ignored (detection-only), which deviates from the plan comment (https://git.eeqj.de/sneak/rtnetmon/issues/2#issuecomment-95624) stating the flags keep working on both platforms. Read as acceptable: the owner order in https://git.eeqj.de/sneak/rtnetmon/issues/2 directs macOS to detect the VPN and default-route interfaces, and the DoD imposes no flag-override requirement; `--labelA`/`--labelB` still apply. Model: opus-4-8
clawbot added needs-rebase and removed needs-review labels 2026-09-21 09:22:48 +02:00
clawbot added 1 commit 2026-09-21 09:56:54 +02:00
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 now
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 (source address on Linux, IP_BOUND_IF on macOS) are build-tagged. Ping
argument construction is a pure, OS-keyed function. NewMonitor now takes a list
of interfaces.

Model: opus-4-8
clawbot force-pushed feature/issue-2-platform-detection from ef3c373a12 to 71774015db 2026-09-21 09:56:54 +02:00 Compare
clawbot added needs-review and removed needs-rebase labels 2026-09-21 09:58:00 +02:00
Author
Collaborator

Rebased onto the standards unit (#5, now merged into next) and folded this feature into its shape.

Conflicts resolved by hand: the two fixed interfaces became the standards layout while keeping this change's one-or-two-interface model; the ping and packet-loss argument builders and the macOS socket-binding hook now live inside the standards unit's context-carrying probe functions; the UI draws one pane per detected interface; the tests moved to external test packages with small accessor files; and the README overview and file-tree were merged. The program is now built for both Linux and macOS, where the standards scaffold had marked it Linux-only.

Lint findings fixed by changing code, never by suppression: dynamic error text became named error values (wrapped where they carry interface names); the /proc/net/route hex parse now uses the standard library instead of hand-rolled bit math; the external route commands take a timeout; repeated test strings became constants; the one large detection table test was split into per-scenario tests; and blank-line spacing was corrected.

The containerised gate (make check: tests and linter) is green. The macOS-only files are verified by a direct GOOS=darwin go build ./..., since the CI gate does not cross-build them.

Model: opus-4-8

Rebased onto the standards unit (https://git.eeqj.de/sneak/rtnetmon/pulls/5, now merged into `next`) and folded this feature into its shape. Conflicts resolved by hand: the two fixed interfaces became the standards layout while keeping this change's one-or-two-interface model; the ping and packet-loss argument builders and the macOS socket-binding hook now live inside the standards unit's context-carrying probe functions; the UI draws one pane per detected interface; the tests moved to external test packages with small accessor files; and the README overview and file-tree were merged. The program is now built for both Linux and macOS, where the standards scaffold had marked it Linux-only. Lint findings fixed by changing code, never by suppression: dynamic error text became named error values (wrapped where they carry interface names); the `/proc/net/route` hex parse now uses the standard library instead of hand-rolled bit math; the external route commands take a timeout; repeated test strings became constants; the one large detection table test was split into per-scenario tests; and blank-line spacing was corrected. The containerised gate (`make check`: tests and linter) is green. The macOS-only files are verified by a direct `GOOS=darwin go build ./...`, since the CI gate does not cross-build them. Model: opus-4-8
Author
Collaborator

PASS — every row of the supported matrix is implemented and unit-tested with fake data (no live network), Linux draws two panes unchanged when both bridge interfaces exist and one pane otherwise, macOS VPN/physical/error paths follow the owner's order, detection sits behind a pure Select with the host queries as the only per-platform seam, the ip route / /proc/net/route / netstat -rn parsers each have tests, the README and API example match the code, the landing commit ends (closes #2) with Model: lines on commit and PR body and no attribution trailers, and make check is green on the head rebased onto current next.

Disclosures:

  • make check (containerised lint + tests) run green on the PR head; lint and tests both executed uncached.
  • The macOS-only files are not covered by the Linux gate; verified here only by a host GOOS=darwin go build ./..., since CI does not cross-build darwin.
  • Not verifiable without a mac (author-disclosed): live netstat -rn parsing, IP_BOUND_IF dialing, and whether Mullvad leak protection lets bound traffic leave the physical interface.
  • Judgement call: on macOS --ifaceA/--ifaceB are not consulted (names come from detection); consistent with the owner's order and the definition of done, and --labelA/--labelB still apply.
  • Commit message body is ~125 words, marginally above the ~120-word guideline; noted, not failing.

Model: opus-4-8

PASS — every row of the supported matrix is implemented and unit-tested with fake data (no live network), Linux draws two panes unchanged when both bridge interfaces exist and one pane otherwise, macOS VPN/physical/error paths follow the owner's order, detection sits behind a pure `Select` with the host queries as the only per-platform seam, the `ip route` / `/proc/net/route` / `netstat -rn` parsers each have tests, the README and API example match the code, the landing commit ends `(closes #2)` with `Model:` lines on commit and PR body and no attribution trailers, and `make check` is green on the head rebased onto current `next`. Disclosures: - `make check` (containerised lint + tests) run green on the PR head; lint and tests both executed uncached. - The macOS-only files are not covered by the Linux gate; verified here only by a host `GOOS=darwin go build ./...`, since CI does not cross-build darwin. - Not verifiable without a mac (author-disclosed): live `netstat -rn` parsing, `IP_BOUND_IF` dialing, and whether Mullvad leak protection lets bound traffic leave the physical interface. - Judgement call: on macOS `--ifaceA`/`--ifaceB` are not consulted (names come from detection); consistent with the owner's order and the definition of done, and `--labelA`/`--labelB` still apply. - Commit message body is ~125 words, marginally above the ~120-word guideline; noted, not failing. Model: opus-4-8
clawbot merged commit 486a47397c into next 2026-09-21 15:02:00 +02:00
clawbot deleted branch feature/issue-2-platform-detection 2026-09-21 15:02:00 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/rtnetmon#4