NodeVault: way out for an org whose key

Task 9901 · Asking · Coder · created · updated

Epic 9747 ("Implement NodeVault on Windows and Mac") shipped the Windows code (#6853), and win-t8703 became capable after an update with no operator step. The live Windows acceptance checks (AC-2/3/5/8/9/10 from Task 9864) were never run. This task removes what blocks them and then runs them. ## Reproduction (Finish 9866, 2026-10-25 ~15:45Z) Signed in to dev (app.dev.jaah.ai) as the Org B admin (`org/console/dev-orgb-login`) and opened Settings → NodeVault. Current key `1a3dc2db8297`, since 10/9/2026. The only node, win-t8703 (Online, fingerprint e5a95a4e5d23), reads "Waiting for a node that holds the key". No node holds the current key and there is no backup. The operator answered Task 9864's question with "Reset Org B NodeVault", but the console has no way to do that. ## Cause - Rotation needs a holder. `apps/api/app/routes/nodevault.py:1010-1012` and `:1059-1061` wrap the new key under the current one (a chain blob), and `:1068-1073` refuse the start with `NO_ONLINE_HOLDER`. - Abandon (`routes/nodevault.py:907-958`) keeps the current key. - Retire (`routes/nodevault.py:1137-1153`) refuses the current key (`ONLY_OLD_KEYS`). - So an org whose only holder is gone has no way to start over in the console. That breaks operator decision D4: "Console should not explain why something can't. It must make it work so that everybody can". Draft #9887, filed outside this epic, describes the same gap. Build it here, then close or link #9887. ## Fix (no new surface beyond one action) 1. In Settings → NodeVault, add a "Start over" action for the org Owner. It is offered only when the current key has no holder: no node has a delivered grant for it, and none is online and holding it. The confirmation names what is lost: every credential sealed under NodeVault becomes unreadable and must be re-entered. Confirming retires the current key and its grants, and leaves the org ready for a fresh Setup. It writes an audit event under Area = NodeVault. 2. The server enforces the same no-holder precondition. Use the existing `_waiting_for_holder` logic at `routes/nodevault.py:249-261`, and refuse with a clear error when a holder exists. 3. Fix the win-t8703 runner slot. A runner checkout on win-t8703 (project win-t4744-probe, task #5270) was refused with "slots declared by admin@jaah.ai, who is not a member of" Org B. Re-declare the slot through the console as an Org B member, so an Org B runner can open a session there. ## Acceptance - Unit and API tests: Start over is refused while a holder exists and succeeds when none does. Afterwards, Setup → Distribute delivers to a capable node. An audit event is written. - Live on dev, Org B: Start over, then Setup (generate, backup, distribute). win-t8703 reads **Delivered**. Screenshot. - Then run Task 9864's Windows live ACs on win-t8703 and cite the output: - AC-2: `jaah nodevault fingerprint` equals the node page and the NodeVault row; `jaah nodevault keys` lists the current hash. - AC-3: a task pinned to win-t8703 fetches a NodeVault credential and reports a match, without printing it. - AC-5: `icacls` on the keystore, and runner `type` → "Access is denied". The widen-and-repair step needs an Administrator shell; record it as deferred if none exists. - AC-8: rotate, then retire. The node shows the new key Delivered and the old hash is gone from `jaah nodevault keys`. - AC-9: Reset identity re-registers the node and re-grants it. - AC-10: with the plan term off, the fetch still works. - `./dc check full` passes in the merge lane, and Jaah Deploy is green. Out of scope: NodeVault crypto, envelope format and the browser key generation (N3). No copy that explains why a node can't hold keys (N4). Rotation's chaining rule stays as it is. Mac live checks stay deferred (OD3: no macOS node on dev). Operator-plane node capability display is Task #9897. Done means: - The Start over action and its server guard are merged, with tests. - win-t8703 reads Delivered in Org B's Settings → NodeVault (screenshot), and AC-2/3/5/8/9/10 output is cited in the report. - Required checks: Jaah Deploy

Outcome

Pull request Pull request

Artifacts

Activity

  • Ready
  • Runner started
  • Awaiting response