# 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 status` — `nmcli 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.
