# M4-D2g — elevation-gated per-subnet code fetch (JIT plan)

**Status:** not started. D2f (multi-subnet + seed transfer) shipped & CI-green at `452358d`;
REQ-PAIR-4/5 `[impl,unit]`. D2g is the LAST D2 task: the local `spt pair show-totp` command
that surfaces a subnet's current code, **gated behind OS privilege elevation** (REQ-PAIR-6),
fetchable per-subnet from any paired node (REQ-PAIR-3). Local-only — no daemon/P2P, so
buildable now without the D4 net wiring.

## Goal

`spt pair show-totp [--subnet <name> | --create-new]` prints the subnet's current 6-digit
code + `otpauth://` URI **only when the process is elevated** (Windows UAC / Linux
root-or-equiv); otherwise it refuses the node-bound fetch and points the user to their
authenticator app (where the seed was provisioned at pairing). Per-subnet because a node
holds N seeds (ADR-0006); `--create-new` mints+names a fresh subnet (sole seed-holder).

## Decisions (from the D2g discussion)

- **Real elevation gate** (todlando is available for admin-enabled testing on this machine).
  Linux: `libc::geteuid() == 0`. Windows: `GetTokenInformation(.., TokenElevation, ..)` via
  the `windows` crate. `cfg`-split; an unknown/unsupported platform returns `Unknown` →
  treated as NOT elevated (fail safe → app fallback). Keep deps leaf-local (the elevation
  util crate only), preserving the dependency-minimal core.
- **Where:** put `is_elevated()` / `Elevation` in the `spt` binary (CLI-adjacent, e.g.
  `crates/spt/src/elevation.rs`) — it gates a CLI command, and only the binary need pull the
  OS deps. The code fetch itself reads `spt-store::SubnetStore` (already there).
- **No-flag behavior:** if the node holds exactly one subnet, use it; if several, **refuse +
  require `--subnet`** (no guessing — same ethos as D3c addressing). Zero subnets → tell the
  user to `--create-new` or pair first.
- **QR:** emit the `otpauth://` URI (TotpSeed::otpauth_uri exists); actual QR *rendering* is
  GUI/later — do NOT pull an image/QR dep now. Mention the URI is importable.

## Pieces (build order)

1. **`elevation.rs`** (REQ-PAIR-6) — `enum Elevation { Elevated, NotElevated, Unknown }` +
   `fn current() -> Elevation`. `#[cfg(unix)]` libc geteuid; `#[cfg(windows)]` windows-crate
   TokenElevation; else Unknown. Unit-test the mapping logic where possible (the OS call
   itself is environment-dependent — gate behind a thin testable seam: a function taking the
   raw elevated-bool and producing the gate decision + message).
2. **`Cmd::Pair { ShowTotp { subnet, create_new } }`** in `cli.rs` — clap subcommand, stable
   arg shapes (REQ-MSG-2 style), parse tests.
3. **Handler** (REQ-PAIR-3) — load SubnetStore; resolve subnet (flag / single / refuse);
   `--create-new` → `create_subnet`; gate on `Elevation::current()`; elevated → print code
   (`seed.code_at(now)`) + `otpauth_uri`; not → print the authenticator-app fallback message.
   Exit codes: success 0; ambiguous/unknown-subnet = usage error; not-elevated = a distinct
   documented non-zero (the fetch was refused, not failed).
4. **Activate REQ-PAIR-3, REQ-PAIR-6** in `traceable-reqs.toml` (`[impl,unit]`; the live
   OS-gate end-to-end is hard to unit-test hermetically — cover the decision/seam in unit,
   note real-elevation verification is manual via todlando / D9).

## Tests
- elevation: the gate-decision seam (elevated bool → show vs fallback) both ways; Unknown →
  fallback; the fallback message names the authenticator app.
- cli parse: `show-totp`, `--subnet x`, `--create-new`, mutually-exclusive flag rejection.
- handler (inject SubnetStore + now + a faked elevation): single-subnet auto-select; multi
  refuses without `--subnet`; `--create-new` mints+shows; code matches `seed.code_at(now)`;
  not-elevated path shows fallback + no code leak.

## NOT in D2g
- Elevated **agent endpoint** path (an elevated agent surfacing the code over IPC) → needs
  the daemon/endpoint ACL (D4/D5); D2g does the node-bound OS-privilege path only.
- QR image rendering → GUI/later.
- REQ-PAIR-7 subnet icon → GUI (D9).

## After D2g
D2 is then COMPLETE (REQ-PAIR-1..6 all `[impl,unit]`). Next: **D3** subnet registry
(REQ-INST-7,9..13 + REQ-HAZARD-REGISTRY-EPOCH-LEASE #8) — see M4-PLAN D3a–f.

## Conventions (carried)
- NO `cargo fmt`. Tag `[impl->REQ-PAIR-{3,6}]` on real evidence, `[unit->…]` on tests.
- `traceable-reqs check` must pass before done. Commit trailer:
  `Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>`.
- CI gotcha: a new push cancels the prior in-flight run — watch the latest sha only.
