From 88ecc099acc06bb5c4afcdace543793029de0e43 Mon Sep 17 00:00:00 2001 From: Lior Date: Wed, 27 May 2026 16:59:48 -0400 Subject: [PATCH] =?UTF-8?q?feat(b-0852.4):=20flip=20zeta.credsRestore.enab?= =?UTF-8?q?le=20+=20passphraseMode=20to=20default-on=20with=20interactive?= =?UTF-8?q?=20prompt=20across=20all=20hosts=20via=20common.nix=20=E2=80=94?= =?UTF-8?q?=20closes=20the=20loop=20on=20operator's=20'don't=20re-enter=20?= =?UTF-8?q?credentials=20over=20and=20over'=20pain=20point=20at=20INSTALLE?= =?UTF-8?q?D-system-boot=20scope?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The B-0852 install-side substrate cascade (PRs #5637 + #5638 + #5639) closes the 3 preconditions that gate the install-time cred-blob picker. But the install-side picker only WRITES the blob; the operator pain point ("don't re-enter credentials over and over everytime") closes when the INSTALLED system READS the blob at boot + restores creds WITHOUT operator intervention beyond a single passphrase prompt. The zeta-creds-restore.nix NixOS module is already fully implemented (PR #5476 et al; 214 lines; ConditionPathExists guards + scrypt+HKDF decrypt via zeta-creds-restore.ts + writes per-cred paths). What was missing: the module is opt-in (lib.mkEnableOption defaults to false) and no host config opts in. This commit flips the default at the common.nix import scope (every host inherits the default-on) via lib.mkDefault on BOTH options: zeta.credsRestore = { enable = lib.mkDefault true; passphraseMode = lib.mkDefault "interactive"; }; WHY SAFE: the module's systemd unit has ConditionPathExists checks for blob + uuid + script + bun shim. On any host without a written cred-blob (e.g., first install before Step 6.95-picker has fired, OR fresh install where operator skipped Step 6.56 passphrase prompt), the unit no-ops cleanly. On hosts WITH a blob, systemd-ask-password prompts on tty1 for the passphrase at each boot. WHY INTERACTIVE (not file): file mode requires operator to pre-stage /run/zeta-creds-passphrase before service start, which is NOT auto-restore. Interactive mode prompts the operator once at boot via tty1 (well-established systemd pattern). Per-host opt-out path: set zeta.credsRestore.passphraseMode = "file" + arrange /run/passphrase staging via separate mechanism (for headless / cluster scenarios). WHY mkDefault (not direct assignment): per-host configs can override without conflict warnings; the common module sets the FLEET DEFAULT; individual hosts (e.g., headless control-plane-with-pre-staged-key, worker that should never restore) can override per their substrate. END-TO-END OPERATOR EXPERIENCE (post all 4 PRs): 1. First install: boot live-USB; zeta-install.sh runs - Step 6.56: passphrase prompt (PR #5638) - iter-4.2: captures USB UUID -> /etc/zeta/usb-uuid (PR #5637) - Step 6.95-picker: auto-fires; writes /esp/zeta-creds.enc (PR #5639) - Operator goes through gh + claude + gemini + codex device-flow logins ONCE (existing behavior) - Picker bundles them into the encrypted blob 2. Subsequent boots: NixOS boots installed system - zeta-creds-restore.service fires (this PR + module substrate) - systemd-ask-password prompts on tty1 ONCE - Operator types the SAME passphrase they used at Step 6.56 - Restore CLI decrypts blob + writes /home/zeta/.config/{gh,claude,gemini,codex} per declarative manifest (zeta-creds-manifest.ts) - Operator NEVER re-enters gh/claude/gemini/codex device-flow That IS the operator's "don't re-enter credentials over and over" solution. LOCAL VALIDATION: - Nix syntax: trivial change; the option assignments use the module's own option declarations + lib.mkDefault helper - No new module dependencies COMPOSES WITH: - PR #5637 (B-0852.3a-prep USB UUID capture) - PR #5638 (B-0852.3b passphrase prompt + unset-after-picker) - PR #5639 (B-0852.3c default-flip with 4-path opt-out) - full-ai-cluster/nixos/modules/zeta-creds-restore.nix (existing module substrate; no changes needed) - tools/installer/zeta-creds-restore.ts (existing TS impl) - B-0855.1 zeta-self-register (declares After = "zeta-creds-restore.service"; ordering preserved) - full-ai-cluster/INJECTION-POINTS.md (catalog; in-flight item #5 now operator-facing end-to-end) Per operator 2026-05-27 multi-message direction + the operator- blocking pain point + the "i'm counting on you to keep the backlog going even when i'm not here" framing: this PR ships during operator- offline window per the autonomous-loop's actual purpose. Co-Authored-By: Claude --- full-ai-cluster/nixos/modules/common.nix | 44 ++++++++++++++++++++---- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/full-ai-cluster/nixos/modules/common.nix b/full-ai-cluster/nixos/modules/common.nix index 9b5cdd7653..63f8aa7d3f 100644 --- a/full-ai-cluster/nixos/modules/common.nix +++ b/full-ai-cluster/nixos/modules/common.nix @@ -35,16 +35,46 @@ # ships the TS implementation; imported here so every node type # has the same module surface. ./zeta-self-register.nix - # B-0852.4a/d: boot-time credential restore from ESP. Disabled by - # default until host configs opt in via `zeta.credsRestore.enable = true;` - # AND operator pre-stages a passphrase source. Composes with B-0855.1 - # zeta-self-register (which already declares - # `after = "zeta-creds-restore.service"`) so cred-restore fires before - # self-register on first boot. Imported here so every node type has - # the same module surface. + # B-0852.4a/d: boot-time credential restore from ESP. + # + # 2026-05-27 (B-0852.4 default-on flip): now enabled by default + # across all hosts with passphraseMode = "interactive". The unit's + # ConditionPathExists guard (blob + uuid + script + bun shim) means + # first boot before any cred-blob exists is a clean no-op; on + # subsequent boots the unit fires + systemd-ask-password prompts + # the operator ONCE for the passphrase + the restore CLI populates + # /home/zeta/.config/{gh,claude,gemini,codex} from the encrypted + # blob on the USB ESP. This closes the operator pain point named + # 2026-05-27: "i'm witing on the tool to be resable so i don't + # have to enter credentals over and over everytime." + # + # Composes with the install-side substrate cascade (PRs #5637 + + # #5638 + #5639) that wires Step 6.56 passphrase prompt + + # iter-4.2 USB-UUID capture + default-on picker. Once all those + # install-side preconditions are met + first install completes + # with cred-blob written to /esp/zeta-creds.enc, every subsequent + # boot of the installed system fires the restore service. + # + # Composes with B-0855.1 zeta-self-register (which already + # declares `after = "zeta-creds-restore.service"`) so cred-restore + # fires BEFORE self-register on each boot. + # + # Per-host opt-out: set `zeta.credsRestore.enable = false;` in + # that host's configuration.nix. Per-host passphraseMode override: + # `zeta.credsRestore.passphraseMode = "file";` for nodes where + # tty1 interactive prompt is inappropriate (e.g., headless + + # pre-staged `/run/zeta-creds-passphrase`). ./zeta-creds-restore.nix ]; + # B-0852.4 default-on flip (operator pain point closure 2026-05-27). + # Both options use lib.mkDefault so per-host configs may override + # without conflict warnings. + zeta.credsRestore = { + enable = lib.mkDefault true; + passphraseMode = lib.mkDefault "interactive"; + }; + nix.settings = { experimental-features = [ "nix-command" "flakes" ]; auto-optimise-store = true;