From a47aa3aff5a54f9c0a7154e703c2ccba6b85aa58 Mon Sep 17 00:00:00 2001 From: Bernardo Magri Date: Sat, 4 Jul 2026 10:07:40 +0100 Subject: [PATCH] =?UTF-8?q?fix(snapshots):=20un-break=20the=20GUI=20?= =?UTF-8?q?=E2=80=94=20polkit=20agent=20+=20launcher,=20not=20an=20overrid?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-diagnosis of the "btrfs-assistant 2.2 segfaults on launch" bug, and the fix is not the planned nixpkgs override: 1. gdb: the crash is btrfs_util_subvolume_iterator_next() — libbtrfsutil's UNPRIVILEGED subvolume-iteration path in btrfs-progs 6.17.1 (upstream-fixed after 6.17.1, kdave/btrfs-progs 886571653). Symbol versions were checked and match: not ABI/link drift. 2. VM A/B on real btrfs: as root it works (exit 0); as a user it segfaults (139). The pkexec launcher runs it as root — so the GUI path was fine all along, except… 3. …the distro shipped NO polkit authentication agent: every pkexec prompt in the session failed silently. That is the actual root cause of "the GUI doesn't open". Shipped: hyprpolkitagent (Hyprland's Qt agent — the prompt is Stylix-themed) via exec-once in hyprland.nix; `nomarchy-menu snapshot` prefers btrfs-assistant-launcher again, with the nomarchy-snapshots fzf flow kept as fallback (and for SSH); checks.snapshot-gui guards the root path + offscreen GUI event loop on a real btrfs volume so a lock bump can't silently regress it. No btrfs-progs patch: all our flows are root-side; the unprivileged fix arrives with a lock bump. Verified: V0 (flake check) + V2 (snapshot-gui check, executed green) + V1 (HM generation builds; hyprland.conf carries the agent exec-once; generated menu passes bash -n with the launcher wiring). Remaining V3 queued: first on-hardware themed polkit prompt. Co-Authored-By: Claude Fable 5 --- agent/BACKLOG.md | 10 ---------- agent/HARDWARE-QUEUE.md | 11 ++++++++--- agent/JOURNAL.md | 18 ++++++++++++++++++ agent/MEMORY.md | 12 ++++++++---- docs/ROADMAP.md | 25 ++++++++++++++++++------- flake.nix | 29 +++++++++++++++++++++++++++++ modules/home/hyprland.nix | 6 ++++++ modules/home/rofi.nix | 14 ++++++++++---- modules/nixos/default.nix | 16 ++++++++++------ 9 files changed, 107 insertions(+), 34 deletions(-) diff --git a/agent/BACKLOG.md b/agent/BACKLOG.md index e7aca8c..0374a7c 100644 --- a/agent/BACKLOG.md +++ b/agent/BACKLOG.md @@ -22,16 +22,6 @@ next, in what order*. ## NOW -### 4. btrfs-assistant segfault — attempt the nixpkgs override fix -ROADMAP § Snapshot browse/restore. btrfs-assistant 2.2 crashes in -`libbtrfsutil.so.1.4.0` (ABI mismatch, confirmed not a VM artifact); the -`nomarchy-snapshots` fzf fallback ships. Try an override aligning its -libbtrfsutil (or a version bump/pin); if upstream nixpkgs has since -fixed it, just verify and re-point the menu. **Done when:** the GUI -launches in the VM test, or the attempt is written up as infeasible -(then re-check on each lock bump). **Verify:** V2 — extend the snapper -VM check to launch the binary. - ### 5. Keyboard layouts — cycle bind + summer-bar parity ROADMAP § Keyboard layouts, remaining bullets: (a) a multi-layout cycle bind (`hyprctl switchxkblayout` current-device, or xkb `grp:` option) diff --git a/agent/HARDWARE-QUEUE.md b/agent/HARDWARE-QUEUE.md index f684b9e..6900c1b 100644 --- a/agent/HARDWARE-QUEUE.md +++ b/agent/HARDWARE-QUEUE.md @@ -25,9 +25,14 @@ QA machine), the **T14s** (webcam case). plug an external keyboard post-login, pick a layout in rofi, confirm it applies per-device only, persists in `settings.keyboard.devices`, and graduates into a `device{}` block on the next rebuild. -- [ ] **Snapshots restore/rollback** — `nomarchy-menu snapshot` → - `nomarchy-snapshots`: browse/diff, restore a file (`undochange`), - and a root-config rollback, each behind the typed-`yes` gate. +- [ ] **Snapshots restore/rollback** — `nomarchy-menu snapshot` now opens + the **btrfs-assistant GUI**: a *themed* polkit prompt must appear + (hyprpolkitagent — first on-hardware outing) and the GUI must open + as root. Also exercise the fzf fallback in a terminal + (`sudo nomarchy-snapshots`): browse/diff, restore a file + (`undochange`), and a root-config rollback behind the typed-`yes` + gate. Bonus check: any other pkexec flow now prompts instead of + silently failing. - [ ] **Update awareness** — with `nomarchy.updates.enable`, let the timer fire (or start the unit): indicator appears only when inputs are behind, notification only on count growth, click opens the upgrade diff --git a/agent/JOURNAL.md b/agent/JOURNAL.md index 791f72f..b789c08 100644 --- a/agent/JOURNAL.md +++ b/agent/JOURNAL.md @@ -17,6 +17,24 @@ Template: --- +## 2026-07-04 — Snapshot GUI un-broken + polkit agent (loop iteration #4) +- **Task:** BACKLOG NOW#4 — btrfs-assistant segfault. +- **Did:** re-diagnosed instead of overriding. gdb: crash is in + libbtrfsutil's *unprivileged* subvolume iteration (btrfs-progs 6.17.1; + upstream fix `886571653` post-6.17.1; symbol versions verified — not + ABI drift). VM A/B: root works (exit 0), user crashes (139). The + launcher runs it as root → GUI fine *if* pkexec can prompt — and the + distro shipped **no polkit agent** (all pkexec silently failed; the + actual root cause of "GUI is dead"). Shipped: hyprpolkitagent + (exec-once, Stylix-themed Qt), menu re-pointed to the launcher with + the fzf flow as fallback, `checks.snapshot-gui` canary (root path + + offscreen event loop on real btrfs). No btrfs-progs patch — root-side + flows don't hit the bug; the fix arrives with a lock bump. +- **Verified:** V0 + V2 (snapshot-gui check, run for real) + V1 (HM + generation renders the agent exec-once; menu bash -n). +- **Pending:** V3 queued — first on-hardware themed polkit prompt. +- **Next suggestion:** NOW#5 (keyboard cycle bind + summer parity). + ## 2026-07-04 — CI is green (run #58) — item 20 main half closed - **Task:** monitor follow-up, no code. Run #58 (the .gitea path fix) **succeeded** — first green CI: the container recipe works on the diff --git a/agent/MEMORY.md b/agent/MEMORY.md index 8ee25c7..12822ba 100644 --- a/agent/MEMORY.md +++ b/agent/MEMORY.md @@ -31,10 +31,14 @@ iteration would otherwise rediscover. - No KVM = slow, not broken; don't read slowness as failure. ## Known-broken / watchlist -- **btrfs-assistant 2.2 segfaults on launch** (nixpkgs 26.05, - `libbtrfsutil.so.1.4.0` ABI mismatch; crashes on hardware too, not a VM - artifact). `nomarchy-snapshots` fzf flow is the shipped fallback; - re-check on every lock bump (§ Snapshot browse/restore; BACKLOG NOW#2). +- **btrfs-assistant "segfault" was unprivileged-only** (re-diagnosed + 2026-07-04): libbtrfsutil's unprivileged subvolume iteration crashes on + btrfs-progs 6.17.1 (upstream-fixed after); **as root it works**, and the + pkexec launcher runs it as root. The real distro bug was **no polkit + agent in the session** (every pkexec failed silently) — hyprpolkitagent + now ships (hyprland.nix exec-once). `checks.snapshot-gui` guards the + root path. Lesson: before "app X is broken", check WHO it runs as — and + whether polkit prompts can render at all (§ Snapshot browse/restore). - **NixOS release bump is a trap:** the discarded attempt (branch deleted 2026-06-22) hit a Hyprland OOM blocker; a redo is a deliberate `v2`, never part of routine lock bumps. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 2d34a9e..59eb5f0 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -744,13 +744,24 @@ how to override it. Items marked ✓ are shipped. change). **The backend is VM-verified** (a full BTRFS+LUKS install: both `root`+`home` configs, a `/home` timeline snapshot taken, `/home/.snapshots` a real subvolume, the oneshot `Result=success`). - - ⚠ **Known bug — btrfs-assistant 2.2 segfaults on launch** in nixpkgs - 26.05: it crashes inside `libbtrfsutil.so.1.4.0` (a library ABI mismatch, - confirmed in the VM regardless of GL/Qt platform — so it's not a VM/GL - artifact and will crash on hardware too). The menu wiring is correct and - the app is installed, but the GUI doesn't open. The snapshot *backend* - (the actual snapshots) is unaffected. Fix path: a nixpkgs override aligning - its libbtrfsutil, or wait for an upstream bump. ✓ **Fallback shipped:** + - ✓ **Resolved — the "btrfs-assistant 2.2 segfault" was unprivileged-only** + (re-diagnosed 2026-07-04): gdb puts the crash in `btrfs_util_subvolume_ + iterator_next()` — libbtrfsutil's *unprivileged* subvolume-iteration path + in btrfs-progs 6.17.1 (upstream-fixed after 6.17.1, kdave/btrfs-progs + `886571653` "re-enable tree search v2 ioctl"; symbol versions were checked + and match, so not an ABI/link issue). **As root it runs fine** — VM-proven + (root `--version` exits 0, unprivileged exits 139) — and the pkexec + launcher runs it as root, so the GUI was never actually broken *when + launched right*. What WAS missing distro-wide: **no polkit authentication + agent**, so every pkexec prompt in the session failed silently — that's + the root cause of "the GUI doesn't open". Fixes: `hyprpolkitagent` + (Hyprland's Qt agent, Stylix-themed) now ships via exec-once in + `hyprland.nix`; the menu prefers `btrfs-assistant-launcher` again with + `nomarchy-snapshots` as fallback; `checks.snapshot-gui` guards the root + path on a real btrfs volume (and the GUI event loop offscreen) against + lock-bump regressions. No btrfs-progs patch: our flows are all root-side, + so the unprivileged fix rides in with a future lock bump. Original + diagnosis kept below for the record. ✓ **Fallback shipped:** the menu now launches `nomarchy-snapshots` (system-side, gated on `nomarchy.system.snapper`) instead — a keyboard-driven snapper browser/restore with no btrfs-assistant dependency. snapper is root-only diff --git a/flake.nix b/flake.nix index b8636c9..94f01eb 100644 --- a/flake.nix +++ b/flake.nix @@ -254,6 +254,35 @@ ''; }; + # Snapshot GUI canary: btrfs-assistant crashes in libbtrfsutil's + # UNPRIVILEGED subvolume iteration (btrfs-progs 6.17.1, fixed + # upstream after 6.17.1) but runs fine as root — which is how the + # menu launches it (pkexec launcher). Guard the root path on a + # real btrfs volume so a lock bump that breaks it fails here, not + # on a user's machine. The GUI event loop is exercised offscreen + # (survives a 5s timeout = no startup crash past the Btrfs scan). + snapshot-gui = pkgs.testers.runNixOSTest { + name = "nomarchy-snapshot-gui"; + nodes.machine = { pkgs, ... }: { + environment.systemPackages = [ pkgs.btrfs-assistant pkgs.btrfs-progs ]; + virtualisation.emptyDiskImages = [ 512 ]; + }; + testScript = '' + machine.wait_for_unit("multi-user.target") + machine.succeed( + "mkfs.btrfs /dev/vdb && mkdir -p /mnt && mount /dev/vdb /mnt " + "&& btrfs subvolume create /mnt/sub1" + ) + # Root (the launcher's effective context): must work. + machine.succeed("QT_QPA_PLATFORM=offscreen btrfs-assistant-bin --version") + # The GUI proper survives startup (timeout kill = alive → 124). + machine.succeed( + "set +e; timeout 5 env QT_QPA_PLATFORM=offscreen btrfs-assistant-bin; " + "[ $? -eq 124 ]" + ) + ''; + }; + # The distroId question (roadmap "Distro branding"): set # distroId = "nomarchy" so /etc/os-release is honest (ID=nomarchy, # ID_LIKE=nixos). The risk is switch-to-configuration's "is this diff --git a/modules/home/hyprland.nix b/modules/home/hyprland.nix index f5bf6fc..af8a3b9 100644 --- a/modules/home/hyprland.nix +++ b/modules/home/hyprland.nix @@ -200,6 +200,12 @@ in # Paint the wallpaper as soon as the session is up (waits for # the daemon internally). "nomarchy-theme-sync wallpaper" + # Polkit authentication agent — without one, EVERY pkexec/polkit + # prompt in the session fails silently (btrfs-assistant-launcher + # was the discovery case). hyprpolkitagent is Hyprland's own Qt + # agent, so the prompt is Stylix-themed like every other Qt + # surface. Its binary lives in libexec (not bin), hence the path. + "${pkgs.hyprpolkitagent}/libexec/hyprpolkitagent" # Waybar is launched here rather than as a systemd user service: bound # to graphical-session.target the unit raced Hyprland's IPC on relogin # and landed in `failed`; exec-once only fires once the compositor is diff --git a/modules/home/rofi.nix b/modules/home/rofi.nix index ea3da46..bca2fb6 100644 --- a/modules/home/rofi.nix +++ b/modules/home/rofi.nix @@ -404,11 +404,17 @@ ${themeRows} exit 0 ;; snapshot) - # btrfs-assistant 2.2 segfaults on 26.05 (libbtrfsutil ABI), so launch - # the keyboard-driven snapper browser instead: a terminal running it - # via sudo (snapper is root-only here — one password prompt), with - # browse/diff, restore (undochange) and rollback, each confirmed. + # Snapshot browse/restore — the btrfs-assistant GUI via its pkexec + # launcher. The "2.2 segfaults on 26.05" diagnosis was HALF right: + # only UNPRIVILEGED runs crash (libbtrfsutil unprivileged subvolume + # iteration, btrfs-progs 6.17.1; fixed upstream after) — as root it + # runs fine, and the launcher runs it as root. The prompt needs the + # session polkit agent (hyprpolkitagent, exec-once in hyprland.nix). + # Fallback: nomarchy-snapshots, the keyboard-driven fzf browser in a + # terminal (kept installed — also nice over SSH). # Self-gated to nomarchy.system.snapper, so it no-ops elsewhere. + command -v btrfs-assistant-launcher >/dev/null 2>&1 \ + && exec btrfs-assistant-launcher command -v nomarchy-snapshots >/dev/null 2>&1 \ || { notify-send "Snapshots" "Snapshot tools unavailable (nomarchy.system.snapper off?)."; exit 0; } exec ${cfg.terminal} -e sudo nomarchy-snapshots ;; diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix index 5d42070..bd240fc 100644 --- a/modules/nixos/default.nix +++ b/modules/nixos/default.nix @@ -344,13 +344,17 @@ in nixos-rebuild switch --flake /etc/nixos#default "$@" '') # The desktop snapshot manager (browse / diff / restore / rollback over - # snapper, elevating via polkit). Kept for when nixpkgs fixes its crash. + # snapper, elevating via polkit) — the primary `nomarchy-menu snapshot` + # target. The "2.2 segfault" is unprivileged-only (libbtrfsutil + # unprivileged subvolume iteration, btrfs-progs 6.17.1, fixed upstream + # after); the pkexec launcher runs it as root, where it works — + # VM-proven, guarded by checks.snapshot-gui. ++ lib.optional cfg.snapper.enable pkgs.btrfs-assistant - # Keyboard-driven snapper browser/restore — the fallback `nomarchy-menu - # snapshot` actually launches while btrfs-assistant 2.2 segfaults on 26.05 - # (libbtrfsutil ABI). Runs as root (snapper is root-only here; the menu - # opens it in a terminal via sudo, one password prompt), fzf to pick, with - # browse/diff (read-only) and typed-`yes` confirmation before any write. + # Keyboard-driven snapper browser/restore — the menu's fallback when the + # GUI is absent, and handy over SSH. Runs as root (snapper is root-only + # here; the menu opens it in a terminal via sudo, one password prompt), + # fzf to pick, with browse/diff (read-only) and typed-`yes` confirmation + # before any write. ++ lib.optional cfg.snapper.enable (pkgs.writeShellApplication { name = "nomarchy-snapshots"; runtimeInputs = with pkgs; [ snapper fzf gawk gnugrep less coreutils systemd ];