diff --git a/hosts/h002/flake.lock b/hosts/h002/flake.lock index 7a4e1b4b..e82a2bc2 100644 --- a/hosts/h002/flake.lock +++ b/hosts/h002/flake.lock @@ -78,21 +78,6 @@ "type": "github" } }, - "impermanence": { - "locked": { - "lastModified": 1737831083, - "narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=", - "owner": "nix-community", - "repo": "impermanence", - "rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "impermanence", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1756542300, @@ -1044,7 +1029,6 @@ "common": "common", "de_plasma": "de_plasma", "home-manager": "home-manager_2", - "impermanence": "impermanence", "nixpkgs": "nixpkgs_3", "ros_neovim": "ros_neovim" } diff --git a/hosts/h002/flake.nix b/hosts/h002/flake.nix index 5ff3f647..0a3664d8 100644 --- a/hosts/h002/flake.nix +++ b/hosts/h002/flake.nix @@ -8,8 +8,6 @@ de_plasma.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/de_plasma"; ros_neovim.url = "git+https://git.joshuabell.xyz/ringofstorms/nvim"; - - # impermanence.url = "github:nix-community/impermanence"; }; outputs = @@ -35,7 +33,6 @@ inherit inputs; }; modules = [ - # inputs.impermanence.nixosModules.impermanence inputs.home-manager.nixosModules.default # TODO @@ -52,7 +49,7 @@ ( { lib, ... }: { - boot.loader.grub.device = lib.mkForce "/dev/disk/by-uuid/e8d53d2d-bb73-41d7-bc64-93af7d3a5e7b"; + boot.loader.grub.device = lib.mkForce "/dev/disk/by-id/ata-KINGSTON_SV300S37A120G_50026B773C00F8F4"; } ) inputs.common.nixosModules.hardening @@ -75,9 +72,6 @@ # ) ./hardware-configuration.nix - # ./hardware-mounts.nix - # ./impermanence.nix - # ./impermanence-tools.nix ( { config, diff --git a/hosts/h002/hardware-mounts.nix b/hosts/h002/hardware-mounts.nix deleted file mode 100644 index 5d163572..00000000 --- a/hosts/h002/hardware-mounts.nix +++ /dev/null @@ -1,131 +0,0 @@ -{ - utils, - lib, - ... -}: -let - PRIMARY = "/dev/disk/by-uuid/ca5d2b4d-8964-46c8-99cd-822ac62ac951"; - - SWAP = "/dev/disk/by-uuid/8e3a611e-b9a0-4d42-bc1c-cf1df55d9591"; - - primaryDeviceUnit = "${utils.escapeSystemdPath PRIMARY}.device"; -in -lib.mkMerge [ - # Main filesystems - { - # PRIMARY - fileSystems."/" = { - device = PRIMARY; - fsType = "bcachefs"; - options = [ - "X-mount.subdir=@root" - ]; - }; - fileSystems."/nix" = { - device = PRIMARY; - fsType = "bcachefs"; - options = [ - "X-mount.mkdir" - "X-mount.subdir=@nix" - "relatime" - ]; - }; - fileSystems."/.snapshots" = { - device = PRIMARY; - fsType = "bcachefs"; - options = [ - "X-mount.mkdir" - "X-mount.subdir=@snapshots" - "relatime" - ]; - }; - # (optional) for preservation/impermanence - fileSystems."/persist" = { - device = PRIMARY; - fsType = "bcachefs"; - options = [ - "X-mount.mkdir" - "X-mount.subdir=@persist" - ]; - neededForBoot = true; # NOTE for impermanence only - }; - } - # SWAP (optional) - { swapDevices = [ { device = SWAP; } ]; } - { - # Impermanence fix - boot.initrd.systemd.services.create-needed-for-boot-dirs = { - after = [ - "bcachefs-reset-root.service" - ]; - requires = [ - "bcachefs-reset-root.service" - ]; - serviceConfig.KeyringMode = "shared"; - }; - } - # Reset root for erase your darlings/impermanence/preservation - (lib.mkIf true { - boot.initrd.systemd.services.bcachefs-reset-root = { - description = "Reset bcachefs root subvolume before pivot"; - - after = [ - "initrd-root-device.target" - "cryptsetup.target" - ]; - requires = [ - primaryDeviceUnit - ]; - - before = [ - "sysroot.mount" - ]; - wantedBy = [ - "initrd-root-fs.target" - "sysroot.mount" - "initrd.target" - ]; - - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - KeyringMode = "shared"; - }; - - script = '' - cleanup() { - if [[ ! -e /primary_tmp/@root ]]; then - echo "Cleanup: Creating new @root" - bcachefs subvolume create /primary_tmp/@root - fi - echo "Cleanup: Unmounting /primary_tmp" - umount /primary_tmp || true - } - trap cleanup EXIT - - mkdir -p /primary_tmp - - echo "Mounting ${PRIMARY}..." - if ! mount "${PRIMARY}" /primary_tmp; then - echo "Mount failed. Cannot reset root." - exit 1 - fi - - if [[ -e /primary_tmp/@root ]]; then - mkdir -p /primary_tmp/@snapshots/old_roots - - # Use safe timestamp format (dashes instead of colons) - timestamp=$(date "+%Y-%m-%d_%H-%M-%S") - snap="/primary_tmp/@snapshots/old_roots/$timestamp" - echo "Snapshotting @root to $snap" - bcachefs subvolume snapshot /primary_tmp/@root "$snap" - - echo "Deleting current @root" - bcachefs subvolume delete /primary_tmp/@root - fi - - # Trap handles creating new root and unmount - ''; - }; - }) -] diff --git a/hosts/h002/impermanence-tools.nix b/hosts/h002/impermanence-tools.nix deleted file mode 100644 index 042d0634..00000000 --- a/hosts/h002/impermanence-tools.nix +++ /dev/null @@ -1,76 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: -let - cfg = config.impermanence.tools; - - bcacheImpermanenceBin = pkgs.writeShellScriptBin "bcache-impermanence" ( - builtins.readFile ./impermanence-tools.sh - ); - -in -{ - options.impermanence.tools = { - snapshotRoot = lib.mkOption { - type = lib.types.str; - default = "/.snapshots/old_roots"; - description = "Root directory containing old root snapshots."; - }; - - gc = { - enable = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Enable garbage collection of old root snapshots."; - }; - - keepPerMonth = lib.mkOption { - type = lib.types.int; - default = 1; - description = "Keep at least this many snapshots per calendar month (latest ones)."; - }; - - keepRecentWeeks = lib.mkOption { - type = lib.types.int; - default = 4; - description = "Keep at least one snapshot per ISO week within this many recent weeks."; - }; - - keepRecentCount = lib.mkOption { - type = lib.types.int; - default = 5; - description = "Always keep at least this many most recent snapshots overall."; - }; - }; - }; - - config = { - environment.systemPackages = [ - bcacheImpermanenceBin - pkgs.coreutils - pkgs.findutils - pkgs.diffutils - pkgs.bcachefs-tools - pkgs.fzf - ]; - - systemd.services."bcache-impermanence-gc" = lib.mkIf cfg.gc.enable { - description = "Garbage collect bcachefs impermanence snapshots"; - wantedBy = [ "multi-user.target" ]; - after = [ "multi-user.target" ]; - serviceConfig = { - Type = "oneshot"; - }; - script = '' - exec ${bcacheImpermanenceBin}/bin/bcache-impermanence gc \ - --snapshot-root ${cfg.snapshotRoot} \ - --keep-per-month ${toString cfg.gc.keepPerMonth} \ - --keep-recent-weeks ${toString cfg.gc.keepRecentWeeks} \ - --keep-recent-count ${toString cfg.gc.keepRecentCount} - ''; - }; - }; -} diff --git a/hosts/h002/impermanence-tools.sh b/hosts/h002/impermanence-tools.sh deleted file mode 100644 index bd6d293a..00000000 --- a/hosts/h002/impermanence-tools.sh +++ /dev/null @@ -1,625 +0,0 @@ -#!/usr/bin/env bash -set -eu - -SNAPSHOT_ROOT="/.snapshots/old_roots" -KEEP_PER_MONTH=1 -KEEP_RECENT_WEEKS=4 -KEEP_RECENT_COUNT=5 -DRY_RUN=0 -DIFF_MAX_DEPTH=0 - -usage() { - cat <&2; exit 1; } - SNAPSHOT_ROOT="$1" - ;; - --help|-h) - echo "Usage: bcache-impermanence ls [-nN] [--snapshot-root DIR]" >&2 - exit 0 - ;; - *) - echo "Unknown ls option: $1" >&2 - exit 1 - ;; - esac - shift - done - - local snaps - snaps=$(list_snapshots_desc) - - if [ -z "$snaps" ]; then - echo "No snapshots found in $SNAPSHOT_ROOT" >&2 - exit 1 - fi - - if [ "$count" -gt 0 ] 2>/dev/null; then - printf '%s\n' "$snaps" | head -n "$count" - else - printf '%s\n' "$snaps" - fi -} - -build_keep_set() { - # Prints snapshot names to keep, one per line, based on policies. - local now - now=$(date +%s) - - local snaps - snaps=$(list_snapshots_desc) - - if [ -z "$snaps" ]; then - return 0 - fi - - local tmpdir - tmpdir=$(mktemp -d) - - # Always keep newest KEEP_RECENT_COUNT snapshots. - if [ "$KEEP_RECENT_COUNT" -gt 0 ]; then - printf '%s\n' "$snaps" | head -n "$KEEP_RECENT_COUNT" >"$tmpdir/keep_recent" - fi - - # Per-month: keep up to KEEP_PER_MONTH newest per month. - if [ "$KEEP_PER_MONTH" -gt 0 ]; then - while read -r snap; do - [ -n "$snap" ] || continue - local month - month=${snap%_*} # YYYY-MM-DD - month=${month%-*} # YYYY-MM - local month_file="$tmpdir/month_$month" - local count=0 - if [ -f "$month_file" ]; then - count=$(wc -l <"$month_file") - fi - if [ "$count" -lt "$KEEP_PER_MONTH" ]; then - echo "$snap" >>"$month_file" - fi - done </dev/null || true) - [ -n "$ts" ] || continue - local age - age=$(( now - ts )) - if [ "$age" -gt "$max_age" ]; then - continue - fi - local week - week=$(date -d "${snap%_*} ${snap#*_}" +"%G-%V" 2>/dev/null || true) - [ -n "$week" ] || continue - local week_file="$tmpdir/week_$week" - if [ ! -f "$week_file" ]; then - echo "$snap" >"$week_file" - fi - done <&2; exit 1; } - SNAPSHOT_ROOT="$1" - ;; - --keep-per-month) - shift - [ "$#" -gt 0 ] || { echo "--keep-per-month requires a value" >&2; exit 1; } - KEEP_PER_MONTH="$1" - ;; - --keep-recent-weeks) - shift - [ "$#" -gt 0 ] || { echo "--keep-recent-weeks requires a value" >&2; exit 1; } - KEEP_RECENT_WEEKS="$1" - ;; - --keep-recent-count) - shift - [ "$#" -gt 0 ] || { echo "--keep-recent-count requires a value" >&2; exit 1; } - KEEP_RECENT_COUNT="$1" - ;; - --dry-run) - DRY_RUN=1 - ;; - --help|-h) - echo "Usage: bcache-impermanence gc [--snapshot-root DIR] [--keep-per-month N] [--keep-recent-weeks N] [--keep-recent-count N] [--dry-run]" >&2 - exit 0 - ;; - *) - echo "Unknown gc option: $1" >&2 - exit 1 - ;; - esac - shift - done - - if [ ! -d "$SNAPSHOT_ROOT" ]; then - echo "Snapshot root $SNAPSHOT_ROOT does not exist; nothing to do" >&2 - exit 0 - fi - - local snaps - snaps=$(list_snapshots_desc) - if [ -z "$snaps" ]; then - echo "No snapshots to process" >&2 - exit 0 - fi - - local keep - keep=$(build_keep_set) - - local tmpkeep - tmpkeep=$(mktemp -d) - while read -r k; do - [ -n "$k" ] || continue - : >"$tmpkeep/$k" - done <&2 - else - deleted=$((deleted + 1)) - fi - fi - done <"$children" - fi - - # Build immediate children under current_prefix. - while read -r st rel; do - [ -n "$rel" ] || continue - local sub - if [ -n "$current_prefix" ]; then - case "$rel" in - "$current_prefix") - # Exact match at this level; treat as leaf, not a separate child. - continue - ;; - "$current_prefix"/*) - sub="${rel#"$current_prefix"/}" - ;; - *) - continue - ;; - esac - else - sub="$rel" - fi - - [ -n "$sub" ] || continue - - local child - child="${sub%%/*}" - local child_rel - if [ -n "$current_prefix" ]; then - child_rel="$current_prefix/$child" - else - child_rel="$child" - fi - - if grep -qx "$child_rel" "$seen" 2>/dev/null; then - continue - fi - - echo "$st $child_rel" >>"$children" - echo "$child_rel" >>"$seen" - done <"$diff_list" - - rm -f "$seen" - - if [ ! -s "$children" ]; then - echo "No further differences under ${current_prefix:-/}" >&2 - rm -f "$children" - break - fi - - if ! command -v fzf >/dev/null 2>&1; then - echo "fzf is required for diff browsing" >&2 - rm -f "$children" - break - fi - - local preview_cmd - preview_cmd='sel_rel=$(printf "%s\n" {} | cut -d" " -f2-) -if [ "$sel_rel" = ".." ]; then - echo "[UP] .." - exit 0 -fi -snap_dir="'"$snapshot_dir"'" -a_path="$snap_dir/$sel_rel" -b_path="/$sel_rel" - -if [ ! -e "$a_path" ] && [ -e "$b_path" ]; then - echo "[ADDED] /$sel_rel"; echo - if [ -d "$b_path" ]; then - (cd / && find "$sel_rel" -maxdepth 3 -print 2>/dev/null || true) - else - diff -u /dev/null "$b_path" 2>/dev/null || cat "$b_path" 2>/dev/null || true - fi -elif [ -e "$a_path" ] && [ ! -e "$b_path" ]; then - echo "[REMOVED] /$sel_rel"; echo - if [ -d "$a_path" ]; then - (cd "$snap_dir" && find "$sel_rel" -maxdepth 3 -print 2>/dev/null || true) - else - diff -u "$a_path" /dev/null 2>/dev/null || cat "$a_path" 2>/dev/null || true - fi -else - if [ -d "$a_path" ] && [ -d "$b_path" ]; then - echo "[CHANGED DIR] /$sel_rel"; echo - diff -ru "$a_path" "$b_path" 2>/dev/null || true - else - echo "[CHANGED] /$sel_rel"; echo - diff -u "$a_path" "$b_path" 2>/dev/null || true - fi -fi -' - - local selection - selection=$(FZF_DEFAULT_OPTS="${FZF_DEFAULT_OPTS:-} --ansi --preview-window=right:70%:wrap" \ - fzf --prompt="[bcache-impermanence diff ${current_prefix:-/}] " \ - --preview "$preview_cmd" <"$children") || { - rm -f "$children" - break - } - - rm -f "$children" - - local sel_rel - sel_rel=$(printf "%s\n" "$selection" | cut -d" " -f2-) - - if [ "$sel_rel" = ".." ]; then - if [ -z "$current_prefix" ]; then - break - fi - if printf "%s" "$current_prefix" | grep -q '/'; then - current_prefix="${current_prefix%/*}" - else - current_prefix="" - fi - continue - fi - - # If this selection has descendants, drill down; otherwise treat as leaf and exit. - if grep -q " $sel_rel/" "$diff_list"; then - current_prefix="$sel_rel" - continue - else - # Leaf: user already saw diff in preview; exit. - break - fi - done -} - -cmd_diff() { - local snapshot_name="" - - while [ "$#" -gt 0 ]; do - case "$1" in - -s) - shift - [ "$#" -gt 0 ] || { echo "-s requires a snapshot name" >&2; exit 1; } - snapshot_name="$1" - ;; - --snapshot-root) - shift - [ "$#" -gt 0 ] || { echo "--snapshot-root requires a value" >&2; exit 1; } - SNAPSHOT_ROOT="$1" - ;; - --max-depth) - shift - [ "$#" -gt 0 ] || { echo "--max-depth requires a value" >&2; exit 1; } - DIFF_MAX_DEPTH="$1" - ;; - --help|-h) - echo "Usage: bcache-impermanence diff [-s SNAPSHOT] [--snapshot-root DIR] [--max-depth N] [PATH_PREFIX...]" >&2 - exit 0 - ;; - --*) - echo "Unknown diff option: $1" >&2 - exit 1 - ;; - *) - break - ;; - esac - shift - done - - if [ -z "$snapshot_name" ]; then - snapshot_name=$(latest_snapshot_name || true) - fi - - if [ -z "$snapshot_name" ]; then - echo "No snapshots found for diff" >&2 - exit 1 - fi - - local snapshot_dir - snapshot_dir="$SNAPSHOT_ROOT/$snapshot_name" - - if [ ! -d "$snapshot_dir" ]; then - echo "Snapshot directory $snapshot_dir does not exist" >&2 - exit 1 - fi - - if [ "$#" -eq 0 ]; then - set -- / - fi - - if ! command -v fzf >/dev/null 2>&1; then - echo "fzf is required for diff browsing" >&2 - exit 1 - fi - - # Build list of bind mounts backed by /persist so we can filter them out. - local persist_mounts - persist_mounts=$(awk '$2 ~ "^/persist(/|$)" { print $2 }' /proc/self/mounts || true) - - is_persist_backed() { - local p - p="$1" - if [ -z "$p" ]; then - return 1 - fi - if [ -z "$persist_mounts" ]; then - return 1 - fi - local m - for m in $persist_mounts; do - case "$p" in - "$m"|"$m"/*) return 0 ;; - esac - done - return 1 - } - - local prefixes - prefixes=("$@") - - local tmp - tmp=$(mktemp) - - for prefix in "${prefixes[@]}"; do - case "$prefix" in - /*) : ;; - *) - echo "Path prefix must be absolute: $prefix" >&2 - continue - ;; - esac - - # Skip prefixes that are themselves backed by /persist. - if is_persist_backed "$prefix"; then - continue - fi - - local rel - rel="${prefix#/}" - [ -z "$rel" ] && rel="." - - if [ "$DIFF_MAX_DEPTH" -gt 0 ] 2>/dev/null; then - ( - cd "$snapshot_dir" && find "$rel" -mindepth 1 -maxdepth "$DIFF_MAX_DEPTH" -print 2>/dev/null || true - ) | sed "s/^/A /" >>"$tmp" - - ( - cd / && find "$rel" -mindepth 1 -maxdepth "$DIFF_MAX_DEPTH" -print 2>/dev/null || true - ) | sed "s/^/B /" >>"$tmp" - else - ( - cd "$snapshot_dir" && find "$rel" -mindepth 1 -print 2>/dev/null || true - ) | sed "s/^/A /" >>"$tmp" - - ( - cd / && find "$rel" -mindepth 1 -print 2>/dev/null || true - ) | sed "s/^/B /" >>"$tmp" - fi - done - - if [ ! -s "$tmp" ]; then - echo "No files found under specified prefixes" >&2 - rm -f "$tmp" - exit 1 - fi - - local paths - paths=$(cut -d' ' -f2- "$tmp" | sort -u) - - local diff_list - diff_list=$(mktemp) - - while read -r rel; do - [ -n "$rel" ] || continue - local a_path b_path - a_path="$snapshot_dir/$rel" - b_path="/$rel" - - # Skip paths that reside under a /persist-backed mount in the live system. - if is_persist_backed "$b_path"; then - continue - fi - - local status - if [ ! -e "$a_path" ] && [ -e "$b_path" ]; then - status="added" - elif [ -e "$a_path" ] && [ ! -e "$b_path" ]; then - status="removed" - else - if [ -d "$a_path" ] && [ -d "$b_path" ]; then - if ! diff -rq "$a_path" "$b_path" >/dev/null 2>&1; then - status="changed-dir" - else - continue - fi - else - if ! diff -q "$a_path" "$b_path" >/dev/null 2>/dev/null; then - status="changed" - else - continue - fi - fi - fi - - echo "$status $rel" >>"$diff_list" - done <<<"$paths" - - rm -f "$tmp" - - if [ ! -s "$diff_list" ]; then - echo "No differences found between snapshot $snapshot_name and current system" >&2 - rm -f "$diff_list" - exit 0 - fi - - local initial_prefix="" - if [ "$#" -eq 1 ]; then - initial_prefix="${1#/}" - fi - - browse_diff_tree "$snapshot_name" "$snapshot_dir" "$diff_list" "$initial_prefix" - rm -f "$diff_list" -} - -main() { - if [ "$#" -lt 1 ]; then - usage - exit 1 - fi - - local cmd - cmd="$1" - shift || true - - case "$cmd" in - gc) - cmd_gc "$@" - ;; - ls) - cmd_ls "$@" - ;; - diff) - cmd_diff "$@" - ;; - --help|-h|help) - usage - ;; - *) - echo "Unknown subcommand: $cmd" >&2 - usage - exit 1 - ;; - esac -} - -main "$@" diff --git a/hosts/h002/impermanence.nix b/hosts/h002/impermanence.nix deleted file mode 100644 index d9727cf7..00000000 --- a/hosts/h002/impermanence.nix +++ /dev/null @@ -1,44 +0,0 @@ -{ ... }: -{ - environment.persistence."/persist" = { - enable = true; - hideMounts = true; - directories = [ - "/var/log" - "/var/lib/nixos" - "/var/lib/systemd/coredump" - "/var/lib/systemd/timers" - - "/etc/nixos" - "/etc/ssh" - - "/etc/NetworkManager/system-connections" - "/var/lib/bluetooth" - "/var/lib/NetworkManager" - "/var/lib/iwd" - "/var/lib/fail2ban" - ]; - files = [ - "/etc/machine-id" - ]; - users.luser = { - directories = [ - ".ssh" - ".gnupg" - - ".config/nixos-config" - - ".config/atuin" - ".local/share/atuin" - - ".local/share/zoxide" - - # neovim ros_neovim - ".local/state/nvim_ringofstorms_helium" - ]; - files = [ - - ]; - }; - }; -}