diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index add171c..f50b156 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -295,10 +295,16 @@ how to override it. Items marked ✓ are shipped. right-click toggles DND). Turning DND off confirms with a toast; turning it on is silent (notifications are suppressed — the bell-off glyph is the cue). -- **Snapshot browse/restore UX:** snapper already takes BTRFS timeline - snapshots (`nomarchy.system.snapper`); surface them from the desktop — a - rofi menu (or btrfs-assistant) to browse/diff/restore and boot-from- - snapshot — so rollback isn't CLI-only. +- ✓ **Snapshot browse/restore UX:** `nomarchy-menu snapshot` (in the SUPER+M + picker, self-gated on snapper) launches **btrfs-assistant** — the + desktop browse/diff/restore/rollback UI, elevating via polkit (the safe + choice for a destructive, root-only op vs a fat-fingerable rofi rollback). + Shipped system-side gated on `nomarchy.system.snapper`. Also extended + snapper to snapshot **/home** by default (hourly 5 / daily 7 / weekly 4) + when it's its own BTRFS subvolume; a boot oneshot creates the required + `/home/.snapshots` subvolume if missing (covers existing installs, no disko + change). Remaining (optional): a keyboard-driven rofi browse for quick + glances; boot-from-snapshot needs a systemd-boot equivalent of grub-btrfs. - **Update awareness:** updates are manual today (`sys-update`/`home-update`); add a Waybar indicator / notification when flake inputs are stale or a new nixpkgs rev is available (optionally with a pending-change count). Augments, diff --git a/modules/home/rofi.nix b/modules/home/rofi.nix index 4f8c6c4..0dd5824 100644 --- a/modules/home/rofi.nix +++ b/modules/home/rofi.nix @@ -172,6 +172,14 @@ let && notify-send "Do Not Disturb off" "Notifications resumed." exit 0 ;; + snapshot) + # btrfs-assistant: snapshot browse / diff / restore / rollback over + # snapper, elevating via polkit. Shipped system-side with + # nomarchy.system.snapper; self-gate so the entry no-ops elsewhere. + command -v btrfs-assistant >/dev/null 2>&1 \ + || { notify-send "Snapshots" "btrfs-assistant isn't installed (BTRFS snapshots off?)."; exit 0; } + exec btrfs-assistant ;; + "") entries=( "󰀻 Apps" @@ -188,6 +196,8 @@ let "󰭹 Ask Claude" "󰂛 Do Not Disturb" ) + # Snapshots only when btrfs-assistant is installed (BTRFS + snapper). + command -v btrfs-assistant >/dev/null 2>&1 && entries+=("󰋚 Snapshots") # Power-profile picker only when this is a laptop (battery present) # running power-profiles-daemon — matches the Waybar indicator's # self-gating; desktops and the TLP backend don't show it. @@ -211,12 +221,13 @@ let *Keybindings*) exec "$0" keybinds ;; *Ask*) exec "$0" ask ;; *"Do Not Disturb"*) exec "$0" dnd ;; + *Snapshots*) exec "$0" snapshot ;; *"Power profile"*) exec "$0" power-profile ;; *Power*) exec "$0" power ;; esac ;; *) - echo "usage: nomarchy-menu [power|power-profile|theme|clipboard|calc|files|emoji|web|network|bluetooth|capture|keybinds|ask|dnd]" >&2 + echo "usage: nomarchy-menu [power|power-profile|theme|clipboard|calc|files|emoji|web|network|bluetooth|capture|keybinds|ask|dnd|snapshot]" >&2 exit 64 ;; esac ''; diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix index 99d6a85..37358db 100644 --- a/modules/nixos/default.nix +++ b/modules/nixos/default.nix @@ -185,7 +185,7 @@ in # is a clean no-op rather than a failing timer. services.snapper.configs = lib.mkIf (cfg.snapper.enable && (config.fileSystems."/".fsType or "") == "btrfs") - { + ({ root = { SUBVOLUME = "/"; TIMELINE_CREATE = true; @@ -196,6 +196,39 @@ in TIMELINE_LIMIT_MONTHLY = "0"; TIMELINE_LIMIT_YEARLY = "0"; }; + } + # /home is user data — snapshot it too when it's its own BTRFS subvolume + # (the installer's @home). Keep the recent hourlies for oops-recovery + # plus dailies, and a month of weeklies; "at least daily" by default. + // lib.optionalAttrs ((config.fileSystems."/home".fsType or "") == "btrfs") { + home = { + SUBVOLUME = "/home"; + TIMELINE_CREATE = true; + TIMELINE_CLEANUP = true; + TIMELINE_LIMIT_HOURLY = "5"; + TIMELINE_LIMIT_DAILY = "7"; + TIMELINE_LIMIT_WEEKLY = "4"; + TIMELINE_LIMIT_MONTHLY = "0"; + TIMELINE_LIMIT_YEARLY = "0"; + }; + }); + + # snapper needs a `.snapshots` subvolume inside each tracked subvolume. + # The installer made one for / (@snapshots → /.snapshots) but not /home, + # so create a nested one there if missing (idempotent via the condition; + # excluded from /home snapshots since BTRFS snapshots are non-recursive). + # Runs at boot so it also fixes already-installed machines — no disko + # change needed. + systemd.services.nomarchy-home-snapshots-subvol = lib.mkIf + (cfg.snapper.enable && (config.fileSystems."/home".fsType or "") == "btrfs") { + description = "Create /home/.snapshots for snapper if missing"; + wantedBy = [ "multi-user.target" ]; + before = [ "snapper-timeline.service" "snapper-cleanup.service" ]; + unitConfig.ConditionPathExists = "!/home/.snapshots"; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.btrfs-progs}/bin/btrfs subvolume create /home/.snapshots"; + }; }; # ── Fonts ──────────────────────────────────────────────────────── @@ -285,7 +318,10 @@ in --cleanup-algorithm number echo "Rebuilding..." nixos-rebuild switch --flake /etc/nixos#default "$@" - ''); + '') + # The desktop snapshot manager (browse / diff / restore / rollback over + # snapper, elevating via polkit) — what `nomarchy-menu snapshot` launches. + ++ lib.optional cfg.snapper.enable pkgs.btrfs-assistant; # Don't let boot entries fill the ESP over the years. boot.loader.systemd-boot.configurationLimit = lib.mkDefault 10;