@@ -163,15 +163,14 @@ info "Building... (5-15 min on first run, downloads packages)"
163163PASS1_LOG=" /tmp/sourceos-enroll-pass1-$( date +%s) .log"
164164nixos-rebuild switch --flake " ${REPO_ROOT} #${HOST} " --impure 2>&1 | \
165165 tee " ${PASS1_LOG} " | \
166- grep -E ' (building|fetching|error|warning|Error|FAILED|activating)' || true
167- # Check exit code of nixos-rebuild (not the grep)
168- if ! grep -q ' activating' " ${PASS1_LOG} " 2> /dev/null; then
169- # activating line may be absent on cached builds; check for explicit failure
170- if grep -qiE ' (error:|FAILED|failed to)' " ${PASS1_LOG} " 2> /dev/null; then
171- die " Pass 1 rebuild failed. Full log: ${PASS1_LOG}
166+ grep --line-buffered -E ' (building|fetching|error|warning|Error|FAILED|activating)' || true
167+ # Capture nixos-rebuild exit code from PIPESTATUS[0] before any other command resets it.
168+ # The `|| true` on the grep does NOT reset PIPESTATUS — it only applies to the grep's own
169+ # exit, not to the pipeline. This is the reliable way to detect nixos-rebuild failure
170+ # without relying on fragile log-pattern matching.
171+ NB1_EXIT=${PIPESTATUS[0]}
172+ [[ $NB1_EXIT -eq 0 ]] || die " Pass 1 rebuild failed (exit ${NB1_EXIT} ). Full log: ${PASS1_LOG}
172173 Check: journalctl -xe | tail -50"
173- fi
174- fi
175174ok " Pass 1 complete ($( elapsed) ) — log: ${PASS1_LOG} "
176175
177176# ── Step 3: Age key ───────────────────────────────────────────────────────────
@@ -373,7 +372,12 @@ step 10 "Build NixOS closure + push to harmonia cache"
373372
374373info " Building builder-aarch64 system closure..."
375374CLOSURE=$( nix build " ${REPO_ROOT} #nixosConfigurations.${HOST} .config.system.build.toplevel" \
376- --no-link --print-out-paths 2>&1 | tail -1)
375+ --no-link --print-out-paths 2> /dev/null)
376+ # nix build returns empty stdout on failure (errors go to stderr). Verify both that
377+ # CLOSURE is non-empty and that the path actually exists in the Nix store.
378+ [[ -n " ${CLOSURE} " && -e " ${CLOSURE} " ]] || \
379+ die " nix build failed — retry with:
380+ nix build ${REPO_ROOT} #nixosConfigurations.${HOST} .config.system.build.toplevel --no-link --show-trace"
377381ok " Built: ${CLOSURE} "
378382
379383# Harmonia must be running before we can push (it starts after pass-2 rebuild).
@@ -388,23 +392,41 @@ info "Activating final configuration..."
388392PASS2_LOG=" /tmp/sourceos-enroll-pass2-$( date +%s) .log"
389393nixos-rebuild switch --flake " ${REPO_ROOT} #${HOST} " --impure 2>&1 | \
390394 tee " ${PASS2_LOG} " | \
391- grep -E ' (building|fetching|error|warning|Error|FAILED|activating)' || true
392- if grep -qiE ' (error:|FAILED|failed to) ' " ${PASS2_LOG} " 2> /dev/null ; then
393- die " Pass 2 rebuild failed. Full log: ${PASS2_LOG}
395+ grep --line-buffered - E ' (building|fetching|error|warning|Error|FAILED|activating)' || true
396+ NB2_EXIT= ${PIPESTATUS[0]}
397+ [[ $NB2_EXIT -eq 0 ]] || die " Pass 2 rebuild failed (exit ${NB2_EXIT} ) . Full log: ${PASS2_LOG}
394398 Check: journalctl -xe | tail -50
395- The previous generation (pass 1) is still bootable."
396- fi
399+ The previous generation (pass 1) is still bootable via the systemd-boot menu."
397400ok " Pass 2 complete ($( elapsed) ) — log: ${PASS2_LOG} "
398401
399- # Now push the closure to the running harmonia cache
402+ # Confirm the activated generation matches the closure we built in step 10.
403+ CURRENT_GEN=$( readlink /run/current-system 2> /dev/null || echo " " )
404+ if [[ -n " ${CURRENT_GEN} " && " ${CURRENT_GEN} " == " ${CLOSURE} " ]]; then
405+ ok " Active generation matches built closure"
406+ elif [[ -n " ${CURRENT_GEN} " ]]; then
407+ info " Active generation: ${CURRENT_GEN} "
408+ info " Built closure: ${CLOSURE} "
409+ warn " Active generation differs from step-10 closure — check: nixos-rebuild list-generations"
410+ fi
411+
412+ # Now push the closure to the running harmonia cache.
413+ # Wait up to 60s for harmonia (it starts during pass-2 activation); this is
414+ # warn-only — a push failure does not block enrollment. sourceos-syncd will
415+ # repopulate the cache on its first sync cycle.
400416info " Pushing NixOS closure to local harmonia cache (http://127.0.0.1:8101)..."
401- sleep 3 # brief pause for harmonia to finish starting
402- if curl -fsSk http://127.0.0.1:8101/nix-cache-info & > /dev/null; then
417+ _HARM_UP=0
418+ for _i in $( seq 1 12) ; do
419+ if curl -fsSk --max-time 5 " http://127.0.0.1:8101/nix-cache-info" & > /dev/null; then
420+ _HARM_UP=1; break
421+ fi
422+ sleep 5
423+ done
424+ if [[ $_HARM_UP -eq 1 ]]; then
403425 nix copy --to " http://127.0.0.1:8101" " ${CLOSURE} " && \
404426 ok " Closure pushed to harmonia cache" || \
405427 warn " nix copy failed — run manually: nix copy --to 'http://127.0.0.1:8101' ${CLOSURE} "
406428else
407- warn " harmonia not yet responding on :8101 — push manually after it starts"
429+ warn " harmonia not responding on :8101 after 60s — push manually after it starts"
408430 warn " nix copy --to 'http://127.0.0.1:8101' ${CLOSURE} "
409431fi
410432
@@ -458,11 +480,23 @@ if systemctl is-active --quiet nginx 2>/dev/null; then
458480 if curl -fsSk http://127.0.0.1:8101/nix-cache-info & > /dev/null; then
459481 ok " Nix cache at http://127.0.0.1:8101 responding"
460482 fi
461- if curl -fsSk http://127.0.0.1:8101/nix-cache-info.minisig & > /dev/null; then
462- ok " minisig endpoint serving correctly"
483+ # Cryptographically verify the minisig, not just that the endpoint responds.
484+ # A stale or wrong-key sig would pass an existence check but fail here.
485+ _SIG_TMP=$( mktemp /tmp/nix-cache-info-XXXXX.minisig)
486+ if curl -fsSk http://127.0.0.1:8101/nix-cache-info.minisig -o " ${_SIG_TMP} " 2> /dev/null && \
487+ [[ -s " ${_SIG_TMP} " ]]; then
488+ if minisign -V -p " ${MINISIGN_PUB} " -m " ${MINISIGN_CACHE_INFO} " -x " ${_SIG_TMP} " & > /dev/null; then
489+ ok " nix-cache-info minisig: signature valid"
490+ else
491+ warn " minisig served but signature is INVALID — re-sign:"
492+ warn " minisign -S -s ${MINISIGN_SEC} -m ${MINISIGN_CACHE_INFO} -x ${MINISIGN_CACHE_INFO_SIG} "
493+ HEALTHY=0
494+ fi
463495 else
464496 warn " minisig not yet at /nix-cache-info.minisig — check nginx + ${MINISIGN_CACHE_INFO_SIG} "
497+ HEALTHY=0
465498 fi
499+ rm -f " ${_SIG_TMP} "
466500fi
467501
468502if [[ $HEALTHY -eq 1 ]]; then
0 commit comments