# M12 Wave 4 — subnet QR + self-elevating window — JIT plan

> todlando 2026-06-14, after the W2 + follow-on batch gate (PASS, committed
> 6e76f62 / eb74b18). doyle: "proceed to W4 subnet QR + self-elevating window;
> ping a design-check JIT like the others." **DESIGN-CHECK PENDING — build only
> after doyle's ruling.** The load-bearing unknown is T4.2 (self-elevating
> *window*): cross-platform + a security surface (spawning elevated processes /
> UAC). Spec: `M12-PLAN.md` Wave 4 + `M12-CANDIDATES.md`.

## Scope (M12-PLAN.md Wave 4)
- **T4.1** `spt subnet create/join/show-code`: surface a **QR code** of the TOTP
  seed (on create AND join).
- **T4.2** **Self-elevating behavior**: run normally if interactive + elevated;
  else re-run self in a self-elevating window with inline stdout ("Elevated
  terminal launched… / You can close this window"). Cross-platform — Win UAC
  window · Linux+desktop pkexec/polkit or x-terminal-emulator · Linux+TTY inline
  sudo · headless/no-TTY print the command for the human. Scope elevation to only
  the steps that need it (service/firewall/privileged-port; subnet-create itself
  is unprivileged-to-run but reveals a secret, so it is gated).

## What EXISTS today (investigation 2026-06-14)
- **QR already renders** (M7/M8, REQ-SUBNET-1): `render_qr` (cli.rs:2964) encodes
  the `otpauth://` URI as a unicode-half-block terminal QR. Printed on **create**
  (`cmd_subnet_create`), **show-code** (`cmd_subnet_show_code`), and the **join**
  flow (`cmd_subnet_join`). So T4.1's "QR on create AND join" is **largely already
  shipped** — the open question is whether anything is actually missing (Q1).
- **Elevation is PARTIALLY built** (`spt/src/elevation.rs`, REQ-PAIR-6 /
  REQ-HAZARD-SUDO-SECURE-PATH):
  - `current()` probe (Win token / Unix euid, fail-safe `Unknown`); `gate_for`
    (Show/Fallback); `fallback_message`.
  - `current_style()` (Unix `Sudo` / Win `AdminTerminal`); `rerun_command`
    (absolute-path sudo line, secure_path-proof); `should_auto_elevate` (Unix ∧
    interactive ∧ NotElevated).
  - `cli.rs::try_auto_elevate` ACTUALLY re-execs `sudo <abs-exe> <args>` on an
    **interactive Unix TTY** (the elevated child does the work; `main` de-elevates
    back via the 5.7 drop). `with_elevation_hint` prints the absolute-path sudo
    command otherwise. Consumed by `cmd_subnet_create` + `cmd_subnet_show_code`.
- **So the W4 T4.2 DELTA is the rest of the matrix:** today only **Unix +
  interactive-TTY** auto-elevates (inline sudo); **Windows prints "run as
  administrator" with NO actual UAC re-launch**; **Linux desktop without a TTY**
  (DISPLAY but no terminal) has no path; **headless** falls to the printed hint
  (Unix only). T4.2 = add the **Windows UAC window**, the **Linux desktop
  pkexec/terminal-emulator** path, and a **headless print** that covers Windows too.
- De-elevation seam exists (`spt-daemon::deelevate`, REQ-HAZARD-ELEVATED-DAEMON-SPAWN)
  — the elevated child drops back to the user for state safety; the
  `ELEVATION_PROVEN` flag keeps the gate authorized post-drop. The self-elevating
  re-launch is the INVERSE direction (unprivileged → elevated) and must compose
  with this drop without a loop (the elevated child reads `Elevated`, never
  re-elevates).

## Investigation TODOs (read before coding — gated on doyle scope)
1. **`cmd_subnet_join` (cli.rs:4260)**: confirm the QR actually renders on the
   joiner's side post-join (or only the code). Close any T4.1 gap if present.
2. **Windows `ShellExecuteW` "runas"**: the verb that triggers UAC + launches an
   elevated process. It opens a SEPARATE window; the elevated child's stdout does
   NOT come back to the original console. Determine the child-window-stays-open
   mechanism (so the user sees output): launch `cmd.exe /k <exe> <args>` under
   runas, or `<exe> <args>` with a trailing pause. windows-sys already in the dep
   set (`Win32_UI_Shell` feature may need adding).
3. **Linux desktop**: detect `DISPLAY`/`WAYLAND_DISPLAY`; prefer `pkexec` (polkit
   GUI auth, inherits stdio) when present, else `x-terminal-emulator -e sudo …`
   (Debian alternative; fallbacks: gnome-terminal/konsole). Decide the probe order
   + the "no path → print hint" floor.
4. **"Inline stdout" semantics**: confirm the intended UX — the ORIGINAL process
   prints "Elevated terminal launched…", the elevated CHILD window prints the work
   + "You can close this window." (The original cannot capture the elevated
   child's output across the privilege boundary on Windows.)
5. **Loop-safety + de-elevation interplay**: the elevated child must run the work
   then the 5.7 drop, and must NOT re-trigger self-elevation. Verify the
   `ELEVATION_PROVEN`/`current()==Elevated` path short-circuits any re-elevation.

## Open design questions for doyle
1. **T4.1 — is the QR already done?** The terminal QR renders on create +
   show-code + (apparently) join. Is T4.1 satisfied once I confirm the join path
   (Q-TODO 1), treating "window" as the inline terminal QR? Or does "window" imply
   a separate/GUI surface (no GUI milestone exists → I'd defer that)? Recommend:
   **T4.1 = verify + close any join-QR gap; "window" = the inline terminal QR**,
   no GUI work.
2. **Windows UAC child-window mechanism (Q-TODO 2/4).** Recommend: `ShellExecuteW`
   with the `runas` verb launching `<abs-exe> <args>` in a NEW elevated console
   that STAYS OPEN (a trailing "press any key" / the child prints "You can close
   this window"); the original prints "Elevated terminal launched…" and exits 0.
   Confirm the stays-open approach (child-pause vs `cmd /k`) + that we never try to
   pipe the elevated child's stdout back.
3. **Linux desktop elevation order (Q-TODO 3).** Recommend probe order:
   interactive-TTY → inline `sudo` (today); else DISPLAY ∧ `pkexec` → pkexec; else
   DISPLAY ∧ a terminal-emulator → `x-terminal-emulator -e sudo`; else → print the
   sudo hint. Confirm the order + the terminal-emulator fallback list.
4. **The pure decision seam.** Recommend extending `elevation.rs` with a pure
   `decide_elevation_path(os, elevation, interactive_tty, has_display, has_pkexec,
   has_term_emulator) -> ElevatePath { AlreadyElevated, InlineSudo, UacWindow,
   Pkexec, TerminalEmulator, PrintHint }`, unit-tested across the matrix; each
   variant's actual launch is the impure leg (manual-verify, REQ-PAIR-6 precedent).
   This generalizes today's `should_auto_elevate`. Confirm shape.
5. **REQ granularity.** T4.2 self-elevation is bigger than today's
   REQ-HAZARD-SUDO-SECURE-PATH (Unix sudo) and distinct from REQ-PAIR-6 (the
   show/fallback gate) and REQ-SUBNET-4 (which mutations are gated). Recommend a
   NEW **REQ-ELEVATE-1** (cross-platform self-elevating re-launch: the decision
   matrix + the per-OS window launch + inline-notice UX). Or extend REQ-SUBNET-4.
   Your call (lean: new REQ — it is a distinct mechanism reused by every gated
   command, not subnet-specific). doc+impl+unit (no int — launch is manual-verify).
6. **Security posture.** Self-elevation spawns a privileged process from an
   unprivileged one — the user's UAC/polkit/sudo prompt IS the consent gate (we
   never bypass it). We re-run the EXACT same invocation (no added privilege
   scope), absolute-exe-path (secure_path / no PATH hijack), and the elevated child
   de-elevates state back. Any KNOWN-HAZARDS entry to add (e.g. "self-elevation
   re-runs verbatim, never widens args; child drops state to the user")?

## Tentative tasks (pending doyle ruling — shapes will shift on Q1–Q6)
- **T4.0** Add the REQ to `traceable-reqs.toml` first (rule 3) — REQ-ELEVATE-1 (or
  extend REQ-SUBNET-4) per Q5. doc+impl+unit.
- **T4.1** Confirm/close the join-QR gap (likely tiny or a no-op).
- **T4.2a** `elevation.rs`: the pure `decide_elevation_path` matrix +
  `ElevatePath` enum (extends `should_auto_elevate`). Heavy unit tests.
- **T4.2b** The per-OS impure launchers: Windows `ShellExecuteW("runas")` elevated
  console (windows-sys `Win32_UI_Shell`); Linux `pkexec` + `x-terminal-emulator`
  spawns; reuse the Unix inline-sudo path; the headless/print floor (Windows too).
  Wire into the gated commands (replace the bare `try_auto_elevate` call sites with
  the matrix dispatch). Inline-notice UX ("Elevated terminal launched…").
- **T4.2c** Doc: CONTEXT.md elevation section + any KNOWN-HAZARDS entry (Q6).
- Activate stages as evidence lands. Full suite + clippy -D + traceable EXIT=0.
  W4 gate (doyle). Then **W5 whoami** → **W2.5 attach/kick** → full M12 gate.

## Crystallized design (2026-06-14, post-ruling — doyle GO, `M12-W4-RULING.md`)
doyle verified the primitives first (rerun_command abs-path test-asserted, try_auto_elevate
verbatim args, ELEVATION_PROVEN loop-guard, render_qr on create/show/join). All 6 Qs ruled +
a mandatory hazard REQ added. **Build straight from this.**
- **Q1 T4.1:** verify + close the join-QR gap only; "window" = the inline terminal QR (the
  milestone-title "window" refers to T4.2's elevated console, NOT a QR surface); NO GUI.
- **Q2 Windows UAC:** `ShellExecuteW` `"runas"` on `<ABS-EXE>` + **verbatim args DIRECTLY** —
  NOT `cmd /k` (a `/k` leaves a privileged interactive shell open = needless surface). The
  child detects the elevated-relaunch, does the work, prints "You can close this window" +
  **PAUSES for a keypress**; the original prints "Elevated terminal launched…" and exits 0.
  **BINDING: never pipe/capture the elevated child's stdout back across the privilege
  boundary** — the child is self-contained.
- **Q3 Linux order (confirmed):** TTY → inline `sudo`; else `DISPLAY` ∧ `pkexec` → pkexec
  (preferred: native polkit, clean stdio); else `DISPLAY` ∧ a terminal-emulator →
  `x-terminal-emulator -e sudo` (fallbacks gnome-terminal / konsole / xterm); else print the
  abs-path hint. **BINDING: pass argv as an ARRAY to pkexec/term/runas/sudo — NEVER a
  shell-interpolated string, no `sh -c`** (a crafted id/path must not inject a 2nd command).
- **Q4 pure seam (approved):** `decide_elevation_path(...) -> ElevatePath { AlreadyElevated,
  InlineSudo, UacWindow, Pkexec, TerminalEmulator, PrintHint }`; matrix unit-tested, launch =
  impure manual-verify. **REQUIRED coverage: `AlreadyElevated` returned when
  `current()==Elevated` / `ELEVATION_PROVEN` on EVERY os** = loop-safety proven in the seam.
- **Q5 REQ:** NEW **REQ-ELEVATE-1** (cross-platform self-elevating re-launch; reused by every
  gated command, not subnet-specific). doc+impl+unit, NO int.
- **Q6 SECURITY — MANDATORY hazard REQ:** NEW **REQ-HAZARD-SELF-ELEVATE** (required: unit).
  Invariant: self-elevation re-runs the EXACT invocation with the ABSOLUTE exe path — never
  widens scope, never adds/alters args, never a PATH-resolved bare name, never a
  shell-interpolated string (argv-array only); the elevated child drops state back (composes
  with the 5.7 de-elevate) and NEVER re-elevates (loop-safe via `AlreadyElevated`). Unit
  conformance: (1) every launcher uses abs-path + verbatim args (Win runas / pkexec / term /
  sudo); (2) decide → `AlreadyElevated` when elevated on every os; (3) argv is an array, not a
  shell string (assert the constructed argv; no `sh -c`). Existing REQ-HAZARD-SUDO-SECURE-PATH
  = the Unix abs-path facet; this = the cross-platform verbatim / no-widen / no-inject /
  loop-safe invariant. The print-hint floor prints the ABS-path command too.
- **BINDING GATE (doyle verifies all 3):** (1) loop-safety `AlreadyElevated` short-circuit on
  every os; (2) verbatim + abs-path + argv-array across ALL launchers (the hazard tests); (3)
  no stdout marshaling across the privilege boundary.

## BUILT — sweep GREEN, awaiting doyle gate (2026-06-14, todlando)
All on `m12-w1-bringup-rc`, UNCOMMITTED (operator calls the commit).
- **T4.0** REQ-ELEVATE-1 (doc+impl+unit) + REQ-HAZARD-SELF-ELEVATE (unit) added to `traceable-reqs.toml`.
- **T4.1** join-QR: `cmd_subnet_join` success path now renders code + `otpauth://` URI + terminal QR (reuses `decide_show_code`, the joiner is elevated post-gate). [impl->REQ-PAIR-3/REQ-SUBNET-1]
- **T4.2a** pure seam in `elevation.rs`: `Os`, `ElevatePath{AlreadyElevated,InlineSudo,UacWindow,Pkexec,TerminalEmulator,PrintHint}`, `decide_elevation_path` + argv builders (`sudo_argv`/`pkexec_argv`/`terminal_argv`/`windows_runas_params`/`print_hint_command`). Old `ElevateStyle`/`current_style`/`rerun_command`/`should_auto_elevate` REMOVED (superseded → would dead-code under clippy -D); REQ-HAZARD-SUDO-SECURE-PATH evidence re-anchored onto `sudo_argv`+`print_hint_command`+their tests.
- **T4.2b** impure launchers in `cli.rs`: `try_auto_elevate` now dispatches the matrix; `launch_uac_window` (`ShellExecuteW("runas")`, cfg(windows)), `pkexec`/terminal-emulator spawns, `program_on_path`/`first_terminal_emulator`/`has_display` probes, `pause_elevated_console_if_fresh` (Windows sole-console `GetConsoleProcessList` → keypress-pause; no-op elsewhere) at the 3 gated-cmd tails. windows-sys features +Win32_UI_Shell/+Win32_UI_WindowsAndMessaging/+Win32_System_Console.
- **T4.2c** docs: CONTEXT.md "self-elevating re-launch" section (`<!-- [doc->REQ-ELEVATE-1] -->`); KNOWN-HAZARDS 5.11 `[REQ-HAZARD-SELF-ELEVATE]` + 5.10 mapping refreshed.
- **GATE EVIDENCE (the 3 binding conditions):** (1) loop-safety — `already_elevated_short_circuits_on_every_os` asserts `AlreadyElevated` across os×tty×display×pkexec×term grid; (2) verbatim+abs-path+argv-array — `launcher_argv_is_absolute_exe_plus_verbatim_args` + `launchers_never_shell_interpolate_a_crafted_arg` (incl. Windows MSVC-quote no-`cmd /c`); (3) no stdout marshaling — `launch_uac_window` never captures child output (doc-asserted; child self-pauses).
- **SWEEP:** `cargo build -p spt` clean · `cargo clippy -p spt --all-targets -- -D warnings` clean · `cargo test -p spt --bin spt` **155 passed / 0 failed** (incl. 9 elevation unit tests) · `traceable-reqs check` **EXIT=0** (REQ-ELEVATE-1 +doc+impl+unit, REQ-HAZARD-SELF-ELEVATE +impl+unit OK).
- **NOT manual-verified** (impure legs, REQ-PAIR-6 precedent): the actual UAC `runas` window, `pkexec`/terminal-emulator spawns, the Windows console-pause — need a real elevated desktop (todlando/operator D9-style check).

## Notes
- The actual elevated launch is environment-dependent (UAC/polkit/sudo/TTY) — the
  DECISION matrix is the unit-tested seam, the launch is the manual-verify leg
  (REQ-PAIR-6 OS-probe precedent). No `int` stage on T4.2.
- Compose with the existing 5.7 de-elevation drop — re-launch is the inverse edge;
  must be loop-safe (elevated child never re-elevates).
- Build UNCOMMITTED until the operator calls the commit (W2's pre-auth was a
  one-off; the follow-on batch was operator-relayed commit too).
