Files
Panama/docs/superpowers/specs/2026-08-24-network-sharing-redesign.md
T

8.8 KiB

Network & Sharing redesign — the last punt dies

Approved mock: home-mocks/network.html (scratchpad, :8642). Spec wins over mock on conflict.

Goals

  1. Connections becomes a full network manager: per-connection details (IP/DNS/gateway/MAC), forget + autoconnect + MAC randomization, native VPN card with import, hotspot, airplane mode, enterprise Wi-Fi join, system proxy. The "Owned by Fedora" card and both GNOME punt rows are removed.
  2. Firewall gains its add side: allow service/port flow, per-connection zone dropdowns, default zone, zone browser — all existing helper verbs plus one new read-only one.
  3. Printers: per-printer defaults (paper size, two-sided) and job hold/release.
  4. Sharing: copy/structure polish only (error banner into a card, honest Samba row, RDP credential flow copy without naming the terminal in user-facing text).
  5. Search stops lying ("Managed by GNOME Settings" entries die; duplicate Printers entry deduped; new entries for VPN, hotspot, airplane, proxy, IP address).

Non-goals: Samba share management (not installed here — the row explains what installing unlocks), PPD/vendor drivers (contract-banned), WWAN (no hardware), per-connection static IP editing (view-only details this phase).

New helper: scripts/panama-network (pinned verbs)

Python, the panama-vpn/panama-sharing discipline: validated inputs, JSON out, bounded timeouts, mutations return fresh state. Input validation: connection/SSID names against a conservative charset, file paths must exist and end in .conf/.ovpn for import.

  • details <connection>{ ip4, gateway, dns: [], mac, macRandomized }never secrets.
  • forget <connection>nmcli connection delete.
  • set-autoconnect <connection> <bool>.
  • set-mac-random <connection> <bool>wifi.cloned-mac-address random|permanent; the JSON notes a reconnect is needed to take effect.
  • import-vpn <file>nmcli connection import type wireguard|openvpn file … (type by extension); returns the imported connection's name.
  • hotspot start <ssid> / hotspot stop / hotspot statusnmcli device wifi hotspot; the generated password is read back via nmcli device wifi show-password and returned once for the UI to display alongside a QR (reuse the panama-wifi-qr pipeline if trivial, else text-only this phase).
  • join-enterprise — SSID + eap profile (peap-mschapv2 | ttls-pap) + identity on argv, password on stdin, never argv. Mechanism: probe for python NM gi bindings (gi.require_version('NM','1.0')) and use D-Bus AddConnection when present; else fall back to a scripted nmcli connection edit session over stdin (which keeps the secret out of ps). Agent A picks after probing and documents the choice in the script header.
  • proxy get / proxy set <mode> [host port | pac-url] — gsettings org.gnome.system.proxy (mode none|manual|auto; manual sets http+https+socks host/port together this phase).
  • airplane status / airplane set <bool> — rfkill, matching the existing keybind path.

Services (A)

  • services/NetworkTools.qml (new singleton) wraps panama-network: cached detailsFor (connection) (refreshed on page open + active-connection change), forget, setAutoconnect, setMacRandom, importVpn(path), hotspot state/start/stop, proxyMode+setters, airplaneOn+toggle, joinEnterprise(...) (password handed through a Process stdin write), busy/lastError. Test seam: PANAMA_NETWORK_HELPER env override for the helper path.
  • services/Connectivity.qml stays pure-native (contract-pinned no-shell-out): add setWifiEnabled(bool) and setBluetoothEnabled(bool) wrappers (native writes) so the page toggles stop bypassing the service; add objectName-friendly derived state if needed.
  • services/Firewall.qml + scripts/panama-firewall: new read-only verb zone-info <zone> → services/ports/summary for the zone browser; service exposes zoneInfo(zone) with a small cache. Existing verbs untouched.
  • services/Printers.qml + scripts/panama-printers: new verbs get-options <printer> (lpoptions: media, sides — curated keys only), set-option <printer> <key> <value> (validated against a closed key/value vocabulary: media=Letter|A4|Legal, sides=one-sided|two-sided-long-edge|two-sided-short-edge), hold <job> / release <job> (python3-cups). Mutations return fresh snapshots.
  • services/Vpn.qml untouched (import lands through NetworkTools; the list/toggle stays as-is, now also consumed by the page).

UI (B)

ConnectivityPage.qml rebuilt: Wired card (connection row expands to a details KV grid); Wi-Fi card (toggle via Connectivity.setWifiEnabled, connected network expands to details + autoconnect + MAC-randomize + QR share + Forget with a two-stage confirm; other networks keep Join/inline password; enterprise networks get an inline join form — auth dropdown, identity, password field, optional CA file path, Connect; hotspot row at the bottom); VPN card (list from Vpn.qml with per-VPN toggles, empty state + Import row → file path entry or zenity-free inline TextField this phase); Bluetooth card (toggle via service wrapper); Radios & proxy card (airplane toggle, proxy dropdown expanding to manual host/port or PAC URL fields). The "Owned by Fedora" card is deleted. Page gains objectName: "connectivity". KV details grid is a new small component (ConnectionDetails.qml) with mono values.

FirewallPage.qml: existing exposure/remove flows untouched; "Allow something new" row expanding to a kind dropdown (Named service / Port) + validated TextField + Allow button + the "permanent rule, the system will ask for your password" caption; Zones card gains per-connection zone dropdowns (setZone, with a confirm that names the interface), default zone dropdown (setDefaultZone), and a zone-browser chip row driving zoneInfo into a detail line. Only render zones[0]'s rules card per current behavior, but the zones card now lists every active zone.

SharingPage.qml: error banner moves into the This-machine card; Remote-login card compresses port/password-mode/connect-with into row details; RDP credentials copy becomes "Set in a terminal so the password never passes through Panama" (kitty stays the mechanism); Samba row explains "install it and this becomes a switch".

PrintersPage.qml: expanded printer gains Paper size + Two-sided dropdowns (from get-options, written via set-option); queue rows gain Hold/Release next to Cancel; the two duplicate "Search the network" rows merge into the single empty-state card per mock.

Search & docs (C)

Delete the three "Managed by GNOME Settings" connectivity entries and the duplicate Printers entry. Add (page connectivity): VPN, Import a VPN, Hotspot, Airplane mode, Network proxy, IP address, Forget a Wi-Fi network, Enterprise Wi-Fi. Keep existing printers/firewall/sharing entries. Docs regen: none needed (no schema keys) — verify.

Contracts (C — write, never run)

  • connectivity-contract: still native-only — verify the no-shell-out pin survives (all nmcli lives in NetworkTools/panama-network); add needles for the service-wrapper toggles.
  • New network-tools-contract: stubbed-nmcli fixtures (the vpn-contract pattern): details excludes secrets, forget/autoconnect argv shapes, import type-by-extension, enterprise password arrives via stdin and NEVER argv (assert the argv builder), proxy gsettings calls, airplane rfkill.
  • firewall-contract: extend for the add flow (additions are non-destructive, no confirm required; zone changes ARE consequence-bearing — pin that changing a connection's zone names the interface in its confirm), zone-info read-only.
  • printers-contract: closed option vocabulary pinned (no arbitrary lpadmin -o passthrough), hold/release verbs, driverless pins untouched.
  • gnome-handoff-contract: the network exclusion reason ("Panama has no VPN or per-connection routing") is now false — move network and wifi into OWNED so no page may punt to them, and verify nothing does.
  • sharing-contract, settings-pages-contract: needles reconciled.
  • Backlog: Phase 9 section.

Agent ownership (parallel)

  • A: scripts/panama-network (new), scripts/panama-firewall, scripts/panama-printers, services/NetworkTools.qml (new), services/Connectivity.qml, services/Firewall.qml, services/Printers.qml.
  • B: the four pages + new components (+ qmldir).
  • C: services/SettingsSearch.qml, contracts above, backlog, README count line only if count changes.

B programs against the pinned verbs/APIs; A must not change them without updating this spec. Live-desktop rules apply to everyone: no test runs, no harness boots, valid QML/Python at every save, and no live mutations — no nmcli writes, no firewall-cmd writes, no lpadmin, no rfkill, no gsettings writes; read-only probes only.