Show Starlink status lines when Starlink is the non-VPN gateway #3

Open
opened 2026-09-21 08:13:47 +02:00 by clawbot · 3 comments
Collaborator

Owner feature order (sneak, 2026-09-21, chat, verbatim): "we need another issue for adding a couple lines about starlink status (if starlink is detected as the non-vpn gateway)."

Definition of done:

  • When the non-VPN gateway interface's upstream is detected to be a Starlink dish (the standard dish address reachable on the local network, e.g. the dish's well-known endpoint), the UI shows a couple of extra lines with Starlink status (e.g. dish state/obstruction/quality as available from the dish's local status endpoint — implementer proposes the exact fields on this issue before building, kept to a couple of lines per the order).
  • When no Starlink is detected, nothing changes: no extra lines, no probing noise in the UI.
  • Detection and the status fetch are behind a mockable boundary; unit tests need no dish and no live network.
  • README updated with one short paragraph on the Starlink lines and how detection works.

Ordering: after #1 (standards) and #2 (platform/interface detection — this builds on its gateway detection).

model: claude-fable-5

Owner feature order (sneak, 2026-09-21, chat, verbatim): "we need another issue for adding a couple lines about starlink status (if starlink is detected as the non-vpn gateway)." Definition of done: - When the non-VPN gateway interface's upstream is detected to be a Starlink dish (the standard dish address reachable on the local network, e.g. the dish's well-known endpoint), the UI shows a couple of extra lines with Starlink status (e.g. dish state/obstruction/quality as available from the dish's local status endpoint — implementer proposes the exact fields on this issue before building, kept to a couple of lines per the order). - When no Starlink is detected, nothing changes: no extra lines, no probing noise in the UI. - Detection and the status fetch are behind a mockable boundary; unit tests need no dish and no live network. - README updated with one short paragraph on the Starlink lines and how detection works. Ordering: after https://git.eeqj.de/sneak/rtnetmon/issues/1 (standards) and https://git.eeqj.de/sneak/rtnetmon/issues/2 (platform/interface detection — this builds on its gateway detection). model: claude-fable-5
Author
Collaborator

Plan (implementer's brief). One unit, one PR based on next, after #2 lands (it introduces internal/netdetect, whose Select returns the panes; the non-VPN gateway is the physical default-route pane: pane B when there are two panes, the only pane when there is one).

Detection. A Starlink dish always answers at the fixed address 192.168.100.1 from behind its router, on port 9200 (the dish's gRPC status service). Starlink is "the non-VPN gateway" when a TCP connect to 192.168.100.1:9200, bound to the physical pane's interface the same way the TCP probes already bind, succeeds. Probe once at startup and then every minute; when it has never succeeded, nothing is drawn and nothing else is probed (no noise). Not a default-route/gateway-address check: the dish sits behind the Starlink router, so the default gateway is the router, not the dish.

Status fetch. The dish's get_status call on 192.168.100.1:9200. Proposed fields, kept to two lines under the physical pane, the implementer confirms the exact set on this issue before building:

  • line 1: Starlink: <state> uptime <d h m> obstruction <x.x%> alerts <n>
  • line 2: pop ping <ms> / drop <x.x%> down <Mbps> / up <Mbps>
    Refreshed every 5 s, drawn in the pane's colors: red when state is not connected or alerts are present, otherwise default.

Mockable boundary. One small interface with two methods: Probe(ctx) bool and Status(ctx) (Status, error), where Status is a plain struct of the fields above. The real implementation is the only place that knows gRPC and the address; the loop and the rendering take the interface and are unit-tested with a fake. Rendering is a pure function from Status to the two strings.

Dependency decision, made by the implementer and stated in the PR body: the dish speaks gRPC without a published API contract. Two options: (a) vendor the generated Go bindings for get_status only under internal/starlink/pb from a community source, citing the source and the dish firmware they match; (b) a reflection-based dynamic gRPC client. Recommendation: (a), smaller and plain to read; check ~/.claude/GO_PACKAGE_DEFAULTS.md before adding google.golang.org/grpc, and record the decision there if it is new.

Not executable on this Linux host, to be disclosed: any call against a real dish. Tests use the fake only.

README. One short paragraph: the two lines, and that they appear only when the dish answers at 192.168.100.1:9200 over the non-VPN interface.

Model: fable-5-1

Plan (implementer's brief). One unit, one PR based on `next`, after https://git.eeqj.de/sneak/rtnetmon/issues/2 lands (it introduces `internal/netdetect`, whose `Select` returns the panes; the non-VPN gateway is the physical default-route pane: pane B when there are two panes, the only pane when there is one). **Detection.** A Starlink dish always answers at the fixed address `192.168.100.1` from behind its router, on port `9200` (the dish's gRPC status service). Starlink is "the non-VPN gateway" when a TCP connect to `192.168.100.1:9200`, bound to the physical pane's interface the same way the TCP probes already bind, succeeds. Probe once at startup and then every minute; when it has never succeeded, nothing is drawn and nothing else is probed (no noise). Not a default-route/gateway-address check: the dish sits behind the Starlink router, so the default gateway is the router, not the dish. **Status fetch.** The dish's `get_status` call on `192.168.100.1:9200`. Proposed fields, kept to two lines under the physical pane, the implementer confirms the exact set on this issue before building: - line 1: `Starlink: <state> uptime <d h m> obstruction <x.x%> alerts <n>` - line 2: ` pop ping <ms> / drop <x.x%> down <Mbps> / up <Mbps>` Refreshed every 5 s, drawn in the pane's colors: red when state is not connected or alerts are present, otherwise default. **Mockable boundary.** One small interface with two methods: `Probe(ctx) bool` and `Status(ctx) (Status, error)`, where `Status` is a plain struct of the fields above. The real implementation is the only place that knows gRPC and the address; the loop and the rendering take the interface and are unit-tested with a fake. Rendering is a pure function from `Status` to the two strings. **Dependency decision, made by the implementer and stated in the PR body:** the dish speaks gRPC without a published API contract. Two options: (a) vendor the generated Go bindings for `get_status` only under `internal/starlink/pb` from a community source, citing the source and the dish firmware they match; (b) a reflection-based dynamic gRPC client. Recommendation: (a), smaller and plain to read; check `~/.claude/GO_PACKAGE_DEFAULTS.md` before adding `google.golang.org/grpc`, and record the decision there if it is new. **Not executable on this Linux host, to be disclosed:** any call against a real dish. Tests use the fake only. **README.** One short paragraph: the two lines, and that they appear only when the dish answers at `192.168.100.1:9200` over the non-VPN interface. Model: fable-5-1
Author
Collaborator

Confirming the two lines before building (per the plan). Exact fields, drawn under the physical (non-VPN gateway) pane:

Line 1: Starlink: <state> uptime <d h m> obstruction <x.x%> alerts <n>
Line 2: pop ping <ms> / drop <x.x%> down <Mbps> / up <Mbps>

Source fields from the dish get_status response:

  • state — dish state (CONNECTED / SEARCHING / BOOTING / UNKNOWN)
  • uptime — device uptime, shown as days/hours/minutes
  • obstruction — fraction obstructed, shown as a percentage
  • alerts — count of active dish alert flags
  • pop ping — pop-ping latency in ms
  • drop — pop-ping drop rate, shown as a percentage
  • down / up — downlink / uplink throughput, shown in Mbps

The lines turn red when the state is not CONNECTED or any alert is active; otherwise the pane's default color. When no dish answers, neither line is drawn.

Model: opus-4-8

Confirming the two lines before building (per the plan). Exact fields, drawn under the physical (non-VPN gateway) pane: Line 1: `Starlink: <state> uptime <d h m> obstruction <x.x%> alerts <n>` Line 2: ` pop ping <ms> / drop <x.x%> down <Mbps> / up <Mbps>` Source fields from the dish `get_status` response: - state — dish state (CONNECTED / SEARCHING / BOOTING / UNKNOWN) - uptime — device uptime, shown as days/hours/minutes - obstruction — fraction obstructed, shown as a percentage - alerts — count of active dish alert flags - pop ping — pop-ping latency in ms - drop — pop-ping drop rate, shown as a percentage - down / up — downlink / uplink throughput, shown in Mbps The lines turn red when the state is not CONNECTED or any alert is active; otherwise the pane's default color. When no dish answers, neither line is drawn. Model: opus-4-8
Author
Collaborator

Built on #7 (base next).

Two Starlink lines appear under the physical (non-VPN gateway) pane when a dish answers at 192.168.100.1:9200 over that interface: state, uptime, obstruction and alert count; then pop-ping latency and drop rate with downlink/uplink throughput. Red when the dish is not connected or an alert is active. It probes once a minute until a dish answers, then reads status every 5s; otherwise nothing is drawn and nothing is fetched. Detection and the status fetch sit behind a small interface and are tested with a fake — no dish, no network. Field layout is the one confirmed above.

The gRPC dependency decision and the disclosures (nothing run against a real dish; one gosec suppression) are in the PR body.

Model: opus-4-8

Built on https://git.eeqj.de/sneak/rtnetmon/pulls/7 (base `next`). Two Starlink lines appear under the physical (non-VPN gateway) pane when a dish answers at `192.168.100.1:9200` over that interface: state, uptime, obstruction and alert count; then pop-ping latency and drop rate with downlink/uplink throughput. Red when the dish is not connected or an alert is active. It probes once a minute until a dish answers, then reads status every 5s; otherwise nothing is drawn and nothing is fetched. Detection and the status fetch sit behind a small interface and are tested with a fake — no dish, no network. Field layout is the one confirmed above. The gRPC dependency decision and the disclosures (nothing run against a real dish; one gosec suppression) are in the PR body. Model: opus-4-8
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/rtnetmon#3