#+TITLE: Net Panel — Tailscale, VPN, and WireGuard Interfaces #+AUTHOR: Craig Jennings #+DATE: 2026-07-02 #+TODO: TODO | DONE #+TODO: DRAFT READY DOING | IMPLEMENTED SUPERSEDED CANCELLED * DRAFT Status :PROPERTIES: :ID: 79a1075a-4b56-4f25-a861-b69f120a636a :END: - [2026-07-02 Thu] DRAFT — initial spec from the roam capture "other network interfaces (tailscale, VPNs, wireguard)" filed in todo.org 2026-07-02. * Metadata | Field | Value | |--------+---------------------------------------------------| | Status | draft | |--------+---------------------------------------------------| | Owner | Craig Jennings | |--------+---------------------------------------------------| | Repo | dotfiles (net module); archsetup (packages) | |--------+---------------------------------------------------| | Parent | Waybar network module spec (2026-06-29), V2 panel | |--------+---------------------------------------------------| * Problem The net panel's Connections tab shows what NetworkManager knows: WiFi networks and wired links. The machines also run overlay and tunnel interfaces the panel is blind to: - Tailscale (tailscaled, both daily drivers; the tailnet is how the machines reach each other; not an NM device) - WireGuard configs (assets/wireguard-config/ carries Proton VPN configs; importable as NM connections of type wireguard or run via wg-quick) - Commercial VPN clients (Proton VPN GTK app is installed on velox; owns its own tunnel device) When one of these is up it changes routing, DNS, and reachability — exactly the things the Diagnostics tab reasons about — yet the panel neither shows nor controls them, and the doctor can misattribute a VPN-caused failure to the underlying link. * Goals 1. Visibility: the Connections tab shows overlay/tunnel interfaces with live state (up/down, address, and for tailscale the tailnet peers summary). 2. Control: bring each up or down from the panel row, same interaction shape as Join/Disconnect on WiFi rows (no terminals — V2 contract). 3. Diagnostics awareness: diagnose/doctor know when a tunnel owns the default route or DNS, name it in evidence rows, and stop misattributing its failures to the physical link. Non-goals (this iteration): installing or configuring VPN providers, tailnet ACL management, exit-node selection UI (a "use exit node" affordance can ride a later pass), kill-switch management (tracked separately in the spec's failure catalog). * Design sketch ** Data sources — one probe per backend, engine-side New GTK-free module net/src/net/overlays.py with one probe per backend, each returning the same small dict shape ({kind, name, state, addr, detail, can_toggle}): - tailscale: =tailscale status --json= (rich: self, peers, exit node, health messages). Daemon down → state "stopped". Binary absent → backend absent. - wireguard-nm: =nmcli -t connection show= filtered to type wireguard — up/down via the existing nmcli wrapper (activate/deactivate connection). - wg-quick configs outside NM (files in /etc/wireguard) — read-only listing at most; toggling them requires root and wg-quick; probably defer. - proton-gtk: detect its tunnel device (proton0 / ipv6leakintf) when up; control stays with the Proton app (can_toggle false, detail points at the app) unless Craig prefers driving it via its CLI. ** Panel A fourth Connections group "Tunnels" (after Saved / Available now / Wired) using the existing group-header + row machinery. Row: glyph per kind, name, state caption; primary action Up/Down where can_toggle, else Open app. Tailscale row detail (subtitle or tooltip): tailnet name, peer count online, exit node if any. ** Privileged path - tailscale up/down: needs root or operator — =tailscale set --operator= at install time (archsetup) makes the user an operator, so no sudo needed at runtime. Fallback: the V2 net-priv helper gains tailscale-up/down verbs. - NM wireguard connections: no privilege needed (NM polkit default for the active user). ** Diagnostics awareness - diag gains an "overlay owns default route/DNS" detection step: when the default route or resolv.conf points at a tunnel interface, evidence names it ("default route via tailscale0") and failure classification runs the physical-link checks against the underlying device instead. - doctor: a tunnel-caused egress failure (VPN up but its endpoint dead) classifies fixable with next_action "bring the tunnel down / reconnect", not a WiFi reset. ** Bar indicator Unchanged by default. Optional later: a small overlay badge on the net glyph when a tunnel owns the default route. * Decisions (Craig) ** TODO Which backends ship in the first pass? Recommendation: tailscale + NM-managed wireguard. Proton app detection-only. wg-quick configs deferred. ** TODO Tailscale control path: operator flag at install vs net-priv verbs? Recommendation: =tailscale set --operator=$USER= in archsetup's tailscale step (declarative, no sudo at runtime); net-priv verbs only if operator mode proves insufficient (e.g. up with flags). ** TODO Does "Tunnels" belong in Connections or its own tab? Recommendation: a Connections group. A fourth top tab dilutes the V2 nav for three rows. ** TODO Proton VPN: detect-only or drive its CLI? Recommendation: detect-only first; the app owns reconnect/kill-switch logic. * Implementation phases 1. overlays.py probes (tailscale JSON, nmcli wireguard filter, proton device detection) — pure engine, TDD with fake binaries; =net status= grows an overlays section. 2. Panel Tunnels group + Up/Down wiring through the worker thread; AT-SPI smoke extension. 3. Diagnose/doctor overlay awareness (route/DNS ownership step, classifier rows, evidence text) — TDD against the diag harness. 4. archsetup: tailscale operator flag in the tailscale install step; VM test assertion.