#+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 * IMPLEMENTED Status :PROPERTIES: :ID: 79a1075a-4b56-4f25-a861-b69f120a636a :END: - [2026-07-02 Thu] IMPLEMENTED — all six phases shipped (dotfiles 2d9d060, 21db05a, 31ba056, b4010bf, b5c8442; archsetup 0389790 + the wireguard import script): probes, panel Tunnels view, diagnose/doctor route awareness, bar badge, installer swap + operator, velox config migration. Residual human steps filed under todo.org "Manual testing and validation": proton CLI sign-in (per machine) and the first live badge/tunnel round-trip. Ratio picks up the import + package swap on its trip. - [2026-07-02 Thu] DOING — decomposed into six build phases under the todo.org parent (:SPEC_ID: bound); build started same evening per Craig ("tunnels build now + audio-panel spec alongside"). - [2026-07-02 Thu] READY — fused review passed the gate: 4/4 decisions resolved, phases decomposable, claims re-verified live (proton-vpn-cli 1.0.1 in extra, binary =/usr/bin/protonvpn=, no package conflict with the GTK app; =tailscale status --json= shape confirmed on velox — Self/Peer/ CurrentTailnet.Name/MagicDNSSuffix; zero NM wireguard connections yet, seven configs in assets awaiting the phase 6 import). - [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 | implemented | |--------+---------------------------------------------------| | 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). The seven Proton configs in assets/wireguard-config/ import cleanly (=nmcli connection import type wireguard file =, then =connection.autoconnect no= immediately — imports default to autoconnect yes). They use only PrivateKey/Address/DNS + PublicKey/AllowedIPs/Endpoint, no PostUp/PostDown anywhere, so no wg-quick path is needed at all (Craig, 2026-07-02). All are full-tunnel (AllowedIPs 0.0.0.0/0) — the panel should treat them as mutually exclusive. - proton: drive the official proton-vpn-cli (Arch extra repo, v1.0.x, stable since 2026-04) — connect/disconnect/status verbs. It drives NM underneath (python-proton-vpn-network-manager), so the panel still sees connection events through NM. Runtime-exclusive with the GTK app, which gets dropped from the install. The imported NM wireguard configs remain a raw fallback when the CLI/API path is down; the CLI stays primary because the raw configs lack kill switch, port forwarding, and server rotation. ** 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 Part of v1 (Craig, 2026-07-02 — "shouldn't be optional"): a small overlay badge on the net glyph when a tunnel owns the default route. Rides the same route/DNS-ownership detection the diagnostics step adds. * Decisions (Craig) ** DONE Which backends ship in the first pass? CLOSED: [2026-07-02 Thu] Approved (Craig, 2026-07-02): tailscale + NM-managed wireguard. Craig asked whether the wireguard configs can be ported to NM so wg-quick drops out entirely — yes: all seven configs in assets/wireguard-config/ use only the six directives NM imports cleanly (verified 2026-07-02; import command and autoconnect caveat now in the design sketch). wg-quick is out of the spec, not deferred. Proton control is CLI-driven per the Proton decision below, superseding the detection-only recommendation here. ** DONE Tailscale control path: operator flag at install vs net-priv verbs? CLOSED: [2026-07-02 Thu] Approved (Craig, 2026-07-02): =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). ** DONE Does "Tunnels" belong in Connections or its own tab? CLOSED: [2026-07-02 Thu] Approved (Craig, 2026-07-02): a Connections group. A fourth top tab dilutes the V2 nav for three rows. ** DONE Proton VPN: detect-only or drive its CLI? CLOSED: [2026-07-02 Thu] Decided (Craig, 2026-07-02): drive it through a CLI. Research (2026-07-02): Proton shipped an official Linux CLI — first release 2025-11, stable v1.0.0 2026-04, packaged in Arch extra as proton-vpn-cli (1.0.1 at check time), with kill switch, port forwarding, NetShield, server selection, and a status command. It drives NM underneath, so the panel sees its connections through the existing NM event path. Spec changes: the proton backend calls protonvpn connect/disconnect/status instead of device-detection (can_toggle true); archsetup installs proton-vpn-cli and drops proton-vpn-gtk-app (the two can't run concurrently per the project README — untested locally); the imported NM wireguard configs stay as a raw fallback. Sources: [[https://protonvpn.com/support/linux-cli][Proton Linux CLI guide]], [[https://protonvpn.com/support/release-notes-linux-cli][CLI release notes]], [[https://github.com/ProtonVPN/proton-vpn-cli][proton-vpn-cli repo]]. * Implementation phases 1. overlays.py probes (tailscale JSON, nmcli wireguard filter, proton-vpn-cli status) — 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. waybar-net tunnel badge on the net glyph (v1 per the bar-indicator decision), riding phase 3's route-ownership detection; suite coverage. 5. archsetup: tailscale operator flag in the tailscale install step; proton-vpn-cli replaces proton-vpn-gtk-app in the package list; VM test assertions. 6. One-time per-machine migration: import the seven assets/wireguard-config configs into NM with autoconnect off (scriptable; both daily drivers).