Milestone branch next for merge into main. Every unit on it passed an independent review before being squash-merged.
On the branch
Repo standards adoption (#1): script/ entrypoints with the Makefile as thin shims, containerised lint and tests, CI workflow, vendored .golangci.yml, REPO_POLICIES.md, TODO.md; all lint findings fixed. Behavior unchanged.
macOS support and single-interface UI (#2): Linux with both bridge interfaces is unchanged; otherwise the default-route interface alone is monitored and the UI draws one pane. On macOS a connected VPN tunnel (utun) is the primary pane next to the physical default-route interface, or the physical interface alone. Anything outside that matrix exits with an error.
Starlink status lines (#3): two lines under the physical pane, red on dish disconnect or an active alert. Detection is a TCP probe to the dish endpoint bound to the physical interface, once a minute until a dish answers, then get_status every 5s; no dish means nothing drawn and no RPC. Minimal vendored proto bindings for get_status only; adds pinned grpc/protobuf. Not run against a real dish: fake-backed tests only.
Version stamp (#10): rtnetmon logs its version (the git tag or short commit) on the first startup line, from make build and from a plain docker build . alike; .git goes into the build context without its config.
CI runs (#12): script/bootstrap refreshes the apt package lists before its first install, so the CI job gets past the bootstrap on the runner image.
Your macOS VPN report (#8): a tunnel is the VPN pane only while it carries the default route (a default row, or 0/1 plus 128.0/1). Idle Tailscale or a tunnel left by a disconnected client no longer turns the display red.
TODO.md brought up to date (#13): who merges where, what is on next, and what waits on you.
What to know before merging or deploying
make lint, make test and make check need a Docker daemon.
The personal copy/run rsync deploy targets were removed from the Makefile; make run runs the binary locally.
Not verified on a real mac (no mac in the fleet): live netstat -rn parsing, the VPN rule above, IP_BOUND_IF socket binding, and whether Mullvad leak protection lets bound traffic leave the physical interface. A first run on your mac is the test.
On macOS --ifaceA/--ifaceB are ignored (interfaces come from detection); --labelA/--labelB still apply.
Waiting on the owner
The merge into main. The milestone is complete and nothing else is in flight.
A first run on your mac, as above.
Model: fable-5-1 (original); fable-5 (updates); opus-5-5 (version stamp, CI fix, VPN fix, current revision)
Milestone branch `next` for merge into `main`. Every unit on it passed an independent review before being squash-merged.
**On the branch**
- Repo standards adoption (https://git.eeqj.de/sneak/rtnetmon/issues/1): `script/` entrypoints with the Makefile as thin shims, containerised lint and tests, CI workflow, vendored `.golangci.yml`, `REPO_POLICIES.md`, `TODO.md`; all lint findings fixed. Behavior unchanged.
- macOS support and single-interface UI (https://git.eeqj.de/sneak/rtnetmon/issues/2): Linux with both bridge interfaces is unchanged; otherwise the default-route interface alone is monitored and the UI draws one pane. On macOS a connected VPN tunnel (`utun`) is the primary pane next to the physical default-route interface, or the physical interface alone. Anything outside that matrix exits with an error.
- Starlink status lines (https://git.eeqj.de/sneak/rtnetmon/issues/3): two lines under the physical pane, red on dish disconnect or an active alert. Detection is a TCP probe to the dish endpoint bound to the physical interface, once a minute until a dish answers, then `get_status` every 5s; no dish means nothing drawn and no RPC. Minimal vendored proto bindings for `get_status` only; adds pinned `grpc`/`protobuf`. Not run against a real dish: fake-backed tests only.
- Version stamp (https://git.eeqj.de/sneak/rtnetmon/issues/10): rtnetmon logs its version (the git tag or short commit) on the first startup line, from `make build` and from a plain `docker build .` alike; `.git` goes into the build context without its `config`.
- CI runs (https://git.eeqj.de/sneak/rtnetmon/issues/12): `script/bootstrap` refreshes the apt package lists before its first install, so the CI job gets past the bootstrap on the runner image.
- Your macOS VPN report (https://git.eeqj.de/sneak/rtnetmon/issues/8): a tunnel is the VPN pane only while it carries the default route (a `default` row, or `0/1` plus `128.0/1`). Idle Tailscale or a tunnel left by a disconnected client no longer turns the display red.
- `TODO.md` brought up to date (https://git.eeqj.de/sneak/rtnetmon/issues/13): who merges where, what is on `next`, and what waits on you.
**What to know before merging or deploying**
- `make lint`, `make test` and `make check` need a Docker daemon.
- The personal copy/run rsync deploy targets were removed from the Makefile; `make run` runs the binary locally.
- Not verified on a real mac (no mac in the fleet): live `netstat -rn` parsing, the VPN rule above, `IP_BOUND_IF` socket binding, and whether Mullvad leak protection lets bound traffic leave the physical interface. A first run on your mac is the test.
- On macOS `--ifaceA`/`--ifaceB` are ignored (interfaces come from detection); `--labelA`/`--labelB` still apply.
**Waiting on the owner**
- The merge into `main`. The milestone is complete and nothing else is in flight.
- A first run on your mac, as above.
Model: fable-5-1 (original); fable-5 (updates); opus-5-5 (version stamp, CI fix, VPN fix, current revision)
clawbot
self-assigned this 2026-09-21 09:22:47 +02:00
Standard scaffold: script/ entrypoints with the Makefile as thin shims, a Dockerfile whose lint and test phases gate the build, a Gitea CI workflow running script/cibuild, REPO_POLICIES.md, TODO.md, .editorconfig, .dockerignore, LICENSE, wider .gitignore. .golangci.yml is byte-identical to the canonical copy in the prompts repo.
211 lint findings fixed in code: package globals became functions/fields and a cobra command constructor, magic numbers became named constants, ctx is threaded into the probes, monitor loops split. Tests moved to external _test packages with export_test.go. Behavior unchanged.
Readers will trip over: make lint/test/check now need a Docker daemon; the personal rsync copy/run targets are gone and make run runs locally.
Disclosure: four //nolint:gosec remain on the fixed-argv ping/curl calls and the operator-chosen log file, as the reference repo annotates the same class.
Disclosure: module path left as-is.
Model: opus-4-8 (implementation); fable-5-1 (merge)
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)
clawbot
removed their assignment 2026-09-21 15:02:29 +02:00
sneak
was assigned by clawbot2026-09-21 15:02:29 +02:00
Detection is a TCP connect to the dish endpoint bound to the physical
interface; once a dish answers, get_status is read every 5s and two
status lines render under the physical pane, red on disconnect or
alert. No dish: nothing drawn, no RPC. Minimal vendored proto bindings
for get_status only. Independent review passed (PR 7 comment 100012).
Model: opus-4-8 (implementation and review); model: claude-fable-5
(merge)
Not merge-ready: the CI check has never passed on next. All three runs of script/cibuild in CI failed within seconds: 7dd4ac7, 486a473, and the head 769511d (re-run 2026-09-22 23:47 UTC, the build step failed after 5 seconds). The same script passes locally on 769511d, so the CI run itself is broken for this repo, not the code. merge-ready and the assignment stay off until a CI run executes and passes. The fix is queued for when rtnetmon resumes from the priority pause.
Model: opus-5-5
Not merge-ready: the CI check has never passed on `next`. All three runs of `script/cibuild` in CI failed within seconds: `7dd4ac7`, `486a473`, and the head `769511d` (re-run 2026-09-22 23:47 UTC, the build step failed after 5 seconds). The same script passes locally on `769511d`, so the CI run itself is broken for this repo, not the code. `merge-ready` and the assignment stay off until a CI run executes and passes. The fix is queued for when rtnetmon resumes from the priority pause.
Model: opus-5-5
rtnetmon had no version. main.Version is now set at link time and logged
in the first startup line. `make build` and `make dev` set it from
`git describe --tags --always` unless VERSION is given. The Dockerfile
takes it from the VERSION build argument when one is given, otherwise
from `git describe --tags --always` of the .git the build context now
carries, and fails the build if the context carries .git and no version
comes out. .dockerignore follows the canonical copy: .git is sent,
.git/config, which can hold a credential, is not.
Model: opus-5-5
The CI job image ships with empty apt package lists, so the bootstrap's
`apt-get install -y golang` could not find the package and every CI run
stopped there. On the apt path the bootstrap now runs `apt-get update`
once, before the first install, and not at all when nothing needs
installing.
Model: opus-5-5
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
The workflow lines now say who merges where: clawbot squash-merges
reviewed issue PRs into next, only sneak merges next into main, and a
deploy is a squash commit from main onto prod, never automatic.
Status lists what is on next today, each item linked to its issue: the
repo standards, interface detection on Linux and macOS, the macOS VPN
detection fix, the Starlink status lines, the version stamp, and CI
running script/cibuild. Next Step is the owner's merge of the milestone
PR and a first run on a real Mac.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Milestone branch
nextfor merge intomain. Every unit on it passed an independent review before being squash-merged.On the branch
script/entrypoints with the Makefile as thin shims, containerised lint and tests, CI workflow, vendored.golangci.yml,REPO_POLICIES.md,TODO.md; all lint findings fixed. Behavior unchanged.utun) is the primary pane next to the physical default-route interface, or the physical interface alone. Anything outside that matrix exits with an error.get_statusevery 5s; no dish means nothing drawn and no RPC. Minimal vendored proto bindings forget_statusonly; adds pinnedgrpc/protobuf. Not run against a real dish: fake-backed tests only.make buildand from a plaindocker build .alike;.gitgoes into the build context without itsconfig.script/bootstraprefreshes the apt package lists before its first install, so the CI job gets past the bootstrap on the runner image.defaultrow, or0/1plus128.0/1). Idle Tailscale or a tunnel left by a disconnected client no longer turns the display red.TODO.mdbrought up to date (#13): who merges where, what is onnext, and what waits on you.What to know before merging or deploying
make lint,make testandmake checkneed a Docker daemon.make runruns the binary locally.netstat -rnparsing, the VPN rule above,IP_BOUND_IFsocket binding, and whether Mullvad leak protection lets bound traffic leave the physical interface. A first run on your mac is the test.--ifaceA/--ifaceBare ignored (interfaces come from detection);--labelA/--labelBstill apply.Waiting on the owner
main. The milestone is complete and nothing else is in flight.Model: fable-5-1 (original); fable-5 (updates); opus-5-5 (version stamp, CI fix, VPN fix, current revision)
make checkon branchnextat commit769511dpassed (lint: 0 issues; all tests pass with -race -cover).model: claude-opus-4-8
Not merge-ready: the CI check has never passed on
next. All three runs ofscript/cibuildin CI failed within seconds:7dd4ac7,486a473, and the head769511d(re-run 2026-09-22 23:47 UTC, the build step failed after 5 seconds). The same script passes locally on769511d, so the CI run itself is broken for this repo, not the code.merge-readyand the assignment stay off until a CI run executes and passes. The fix is queued for when rtnetmon resumes from the priority pause.Model: opus-5-5
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.