#+TITLE: ZFS pre-pacman snapshot installer step (durable retention) #+DATE: 2026-06-29 #+SOURCE: handoff from the home project, 2026-06-29 * Problem A pacman =PreTransaction= hook snapshots =zroot/ROOT/default@pre-pacman_= before every transaction, but nothing prunes them. Sanoid doesn't manage them (they aren't =autosnap_= names), so they accumulated to 53 on velox between April and the 2026-06-29 health check. Unbounded, they fill the pool over time. * What's actually on velox vs. archsetup The live =/usr/local/bin/zfs-pre-snapshot= is *not* authored by archsetup — =git grep= for its content (=MIN_INTERVAL=, the pre-pacman =LOCKFILE= logic) finds nothing tracked. The =PreTransaction= hooks in the archsetup monolith (~lines 910, 1907, 1942) are the live-update guard, a different hook. The script appears hand-placed on velox. The 2026-01-17 security doc line "ZFS pre-pacman snapshots (already in install-archzfs)" is therefore *out of date* — archsetup does not install this. Incorporating the fix is a NET-NEW installer step, not a patch to an existing one. Correct that stale doc line as part of the work. velox was patched live (pruned to 10, script replaced with the self-pruning version below); live backup at =/usr/local/bin/zfs-pre-snapshot.bak-2026-06-29=. * Proposed installer step In the archzfs / ZFS-on-root install path, gated to ZFS-root installs (velox is the only ZFS daily driver; ratio is btrfs), install: 1. =/etc/pacman.d/hooks/zfs-snapshot.hook= — the =PreTransaction= hook that runs the script. *Not included in the handoff* — source it from velox (=/etc/pacman.d/hooks/zfs-snapshot.hook=) or write it. 2. =/usr/local/bin/zfs-pre-snapshot= — the =KEEP=10= self-pruning version below. Tests live in archsetup, so this wants an archsetup session and a ZFS-root VM test (=make test FS_PROFILE=zfs=), not a cross-project edit from home. * The script (KEEP=10 self-pruning version) #+begin_src bash #!/bin/bash POOL="zroot" DATASET="$POOL/ROOT/default" LOCKFILE="/tmp/.zfs-pre-snapshot.lock" MIN_INTERVAL=60 KEEP=10 # how many pre-pacman snapshots to retain (rollback safety for recent transactions) # Skip if a snapshot was created within the last 60 seconds if [ -f "$LOCKFILE" ]; then last=$(stat -c %Y "$LOCKFILE" 2>/dev/null || echo 0) now=$(date +%s) if (( now - last < MIN_INTERVAL )); then exit 0 fi fi TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S) SNAPSHOT_NAME="pre-pacman_$TIMESTAMP" if zfs snapshot "$DATASET@$SNAPSHOT_NAME"; then echo "Created snapshot: $DATASET@$SNAPSHOT_NAME" touch "$LOCKFILE" # Retention: keep only the most recent $KEEP pre-pacman snapshots, destroy older ones. # Sanoid does not manage these (they aren't autosnap_), so prune them here at creation time. zfs list -H -o name -t snapshot -s creation "$DATASET" 2>/dev/null \ | grep '@pre-pacman_' \ | head -n -"$KEEP" \ | while read -r old; do zfs destroy "$old" && echo "Pruned old snapshot: $old" done else echo "Warning: Failed to create snapshot" >&2 fi #+end_src * Implementation (2026-06-30) - Hook sourced from velox (=/etc/pacman.d/hooks/zfs-snapshot.hook=) and embedded as a heredoc in =configure_pre_pacman_snapshots()=. - Insertion point: a new =configure_pre_pacman_snapshots()= gated on =is_zfs_root=, called from =boot_ux= (the last step) so the hook doesn't fire during the install's own package operations — the first pre-pacman snapshot is the fresh system. The script ships as =scripts/zfs-pre-snapshot= (the =zfs-replicate= pattern), made =ZFS_PRE_*=-env-overridable for testability. - Tests: =tests/zfs-pre-snapshot/= unit-tests the pruning logic against a fake =zfs= (creates, prunes oldest-past-KEEP, ignores non-=pre-pacman_= snapshots, honors the lockfile, warns on snapshot failure); =test_boot.py= asserts the hook + script land on a ZFS install; the orchestrator test pins the new =boot_ux= substep. * Note on the "stale security doc" The 2026-01-17 line "ZFS pre-pacman snapshots (already in install-archzfs)" is *not* stale: that file is an archive generated by install-archzfs (see its header and footer), and the claim is accurate for install-archzfs. The real gap was that archsetup took sanoid from install-archzfs but never ported the pre-pacman hook. This change ports it. The archive is left untouched. * Remaining - ZFS-root VM verification (=make test FS_PROFILE=zfs=) before the task closes.