# M12 W4 — doyle design ruling (subnet QR + self-elevation)

> doyle 2026-06-14, gating todlando's W4 JIT (M12-W4-PLAN.md). Privilege-escalation feature →
> security conditions stated explicitly + a mandatory hazard REQ. Primitives verified vs source
> before ruling.

## Verified against source
- `elevation::rerun_command` enforces the **absolute exe path** (secure_path-proof, KH 5.10) — test `sudo_rerun_uses_absolute_path_not_bare_name` asserts `starts_with("sudo /")`.
- `try_auto_elevate` (cli.rs:3065) re-execs `sudo <current_exe_str> <invocation_args>` — **verbatim args**, no widening.
- `ELEVATION_PROVEN` flag + `current()` (elevation.rs:164) short-circuit to `Elevated` once proven → **loop-guard foundation exists**.
- `render_qr` (cli.rs:2964) renders the `otpauth://` URI on create / show-code / join. T4.1 largely shipped.

The matrix builds on sound primitives. Rulings:

## Q1 — T4.1 QR: verify + close the join gap; "window" = inline terminal QR, NO GUI. ✓
Confirm `cmd_subnet_join` renders the QR (not only the code) post-join; add the `render_qr` call if missing, else no-op. No GUI milestone exists — reading "window" as a GUI surface would invent scope. (The "window" in the *milestone title* belongs to T4.2's elevated console, not a QR surface.)

## Q2 — Windows UAC child-window: `runas` on the abs-exe DIRECTLY, child self-pauses, NO stdout marshaling.
`ShellExecuteW` with the `runas` verb on **`<abs-exe> <verbatim-args>`** — **not** a `cmd /k` wrapper (a `cmd /k` leaves an interactive *privileged shell* open = needless escalated surface). The elevated child detects it is the elevated re-launch, does the work, prints the result + "You can close this window," and **pauses for a keypress** so the user reads it. The original process prints "Elevated terminal launched…" and exits 0.
- **Security (binding): never pipe / capture the elevated child's stdout back across the privilege boundary.** The unprivileged parent must not depend on privileged output. The child is self-contained.
- Verbatim args + absolute exe path (same secure-path discipline as the sudo leg, applied to the Windows launcher).

## Q3 — Linux desktop order: confirmed; argv-array, never a shell string.
interactive-TTY → inline `sudo` (existing) → else `DISPLAY` ∧ `pkexec` → `pkexec` → else `DISPLAY` ∧ terminal-emulator → `x-terminal-emulator -e sudo …` (fallback list: x-terminal-emulator → gnome-terminal → konsole → xterm) → else → print the absolute-path sudo hint. pkexec preferred (native polkit GUI auth, clean stdio, no extra window).
- **Security (binding): pass argv as an ARRAY to pkexec / the terminal-emulator / runas — never a shell-interpolated command string.** A crafted id / path / arg must not be able to inject a second command. (`Command::new(...).args([...])`, not `sh -c "<interpolated>"`.)

## Q4 — pure decision seam: APPROVED.
`decide_elevation_path(os, elevation, interactive_tty, has_display, has_pkexec, has_term_emulator) -> ElevatePath { AlreadyElevated, InlineSudo, UacWindow, Pkexec, TerminalEmulator, PrintHint }`, pure + unit-tested across the matrix; each variant's actual launch is the impure manual-verify leg (REQ-PAIR-6 precedent). Generalizes `should_auto_elevate`. 
- **Required matrix coverage: `AlreadyElevated` returned whenever `current()==Elevated` (or `ELEVATION_PROVEN`), on EVERY os** — this is the loop-safety guarantee expressed in the testable seam. The elevated child re-enters, sees `Elevated`, and never re-elevates.

## Q5 — REQ granularity: NEW REQ-ELEVATE-1. ✓
Cross-platform self-elevating re-launch is a distinct mechanism reused by every gated command (not subnet-specific). New **REQ-ELEVATE-1** (decision matrix + per-OS window launch + inline-notice UX). doc+impl+unit, **no int** (launch is manual-verify).

## Q6 — Security posture: YES, add a hazard REQ. NON-NEGOTIABLE.
A privilege-escalation feature MUST carry a `REQ-HAZARD-*` with conformance tests. Add **REQ-HAZARD-SELF-ELEVATE** (required: unit; the hazard = a self-elevation that widens scope, hijacks PATH, injects via a shell string, or loops). The invariant:

> Self-elevation re-runs the EXACT original invocation with the **absolute exe path** — never widening privilege scope, never adding/altering args, never via a PATH-resolved bare name, never via a shell-interpolated string (argv-array only); the elevated child drops state back to the user (composes with the 5.7 de-elevation) and **never re-elevates** (loop-safe via `AlreadyElevated`).

Unit conformance (extends the existing abs-path test across the new launchers):
1. Every launcher's command uses the absolute exe path + verbatim args (Win `runas`, pkexec, terminal-emulator, sudo).
2. `decide_elevation_path` → `AlreadyElevated` when elevated, every os (loop-safety).
3. Launch argv is an array, not a shell string (no-injection — assert the constructed argv, no `sh -c`).

The existing REQ-HAZARD-SUDO-SECURE-PATH covers the Unix abs-path-under-sudo facet; REQ-HAZARD-SELF-ELEVATE covers the cross-platform verbatim / no-widen / no-shell-injection / loop-safe facets. The "print hint" floor must also print the **absolute-path** command (copy-paste stays secure_path-proof).

## Scope confirmation
W4 is mostly T4.2's Windows + Linux-desktop launch arms over the existing pure-seam pattern — smaller than W2. Agreed.

## Binding gate conditions (I verify all three)
1. **Loop-safety** proven in the pure seam: `AlreadyElevated` short-circuit on every os.
2. **Verbatim + absolute-path + argv-array** across ALL launchers (the REQ-HAZARD-SELF-ELEVATE tests).
3. **No stdout marshaling** across the privilege boundary (Windows elevated child self-contained).

Build uncommitted on m12-w1-bringup-rc. I gate (reproduce traceable + read the matrix + hazard tests) before commit. Suite + clippy -D + traceable EXIT=0. Operator calls the commit.
