fix(snapshots): un-break the GUI — polkit agent + launcher, not an override
All checks were successful
Check / eval (push) Successful in 3m4s
All checks were successful
Check / eval (push) Successful in 3m4s
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 <noreply@anthropic.com>
This commit is contained in:
@@ -22,16 +22,6 @@ next, in what order*.
|
|||||||
|
|
||||||
## NOW
|
## 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
|
### 5. Keyboard layouts — cycle bind + summer-bar parity
|
||||||
ROADMAP § Keyboard layouts, remaining bullets: (a) a multi-layout cycle
|
ROADMAP § Keyboard layouts, remaining bullets: (a) a multi-layout cycle
|
||||||
bind (`hyprctl switchxkblayout` current-device, or xkb `grp:` option)
|
bind (`hyprctl switchxkblayout` current-device, or xkb `grp:` option)
|
||||||
|
|||||||
@@ -25,9 +25,14 @@ QA machine), the **T14s** (webcam case).
|
|||||||
plug an external keyboard post-login, pick a layout in rofi, confirm
|
plug an external keyboard post-login, pick a layout in rofi, confirm
|
||||||
it applies per-device only, persists in `settings.keyboard.devices`,
|
it applies per-device only, persists in `settings.keyboard.devices`,
|
||||||
and graduates into a `device{}` block on the next rebuild.
|
and graduates into a `device{}` block on the next rebuild.
|
||||||
- [ ] **Snapshots restore/rollback** — `nomarchy-menu snapshot` →
|
- [ ] **Snapshots restore/rollback** — `nomarchy-menu snapshot` now opens
|
||||||
`nomarchy-snapshots`: browse/diff, restore a file (`undochange`),
|
the **btrfs-assistant GUI**: a *themed* polkit prompt must appear
|
||||||
and a root-config rollback, each behind the typed-`yes` gate.
|
(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
|
- [ ] **Update awareness** — with `nomarchy.updates.enable`, let the timer
|
||||||
fire (or start the unit): indicator appears only when inputs are
|
fire (or start the unit): indicator appears only when inputs are
|
||||||
behind, notification only on count growth, click opens the upgrade
|
behind, notification only on count growth, click opens the upgrade
|
||||||
|
|||||||
@@ -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
|
## 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)
|
- **Task:** monitor follow-up, no code. Run #58 (the .gitea path fix)
|
||||||
**succeeded** — first green CI: the container recipe works on the
|
**succeeded** — first green CI: the container recipe works on the
|
||||||
|
|||||||
@@ -31,10 +31,14 @@ iteration would otherwise rediscover.
|
|||||||
- No KVM = slow, not broken; don't read slowness as failure.
|
- No KVM = slow, not broken; don't read slowness as failure.
|
||||||
|
|
||||||
## Known-broken / watchlist
|
## Known-broken / watchlist
|
||||||
- **btrfs-assistant 2.2 segfaults on launch** (nixpkgs 26.05,
|
- **btrfs-assistant "segfault" was unprivileged-only** (re-diagnosed
|
||||||
`libbtrfsutil.so.1.4.0` ABI mismatch; crashes on hardware too, not a VM
|
2026-07-04): libbtrfsutil's unprivileged subvolume iteration crashes on
|
||||||
artifact). `nomarchy-snapshots` fzf flow is the shipped fallback;
|
btrfs-progs 6.17.1 (upstream-fixed after); **as root it works**, and the
|
||||||
re-check on every lock bump (§ Snapshot browse/restore; BACKLOG NOW#2).
|
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
|
- **NixOS release bump is a trap:** the discarded attempt
|
||||||
(branch deleted 2026-06-22) hit a Hyprland OOM blocker; a redo is a
|
(branch deleted 2026-06-22) hit a Hyprland OOM blocker; a redo is a
|
||||||
deliberate `v2`, never part of routine lock bumps.
|
deliberate `v2`, never part of routine lock bumps.
|
||||||
|
|||||||
@@ -744,13 +744,24 @@ how to override it. Items marked ✓ are shipped.
|
|||||||
change). **The backend is VM-verified** (a full BTRFS+LUKS install: both
|
change). **The backend is VM-verified** (a full BTRFS+LUKS install: both
|
||||||
`root`+`home` configs, a `/home` timeline snapshot taken, `/home/.snapshots`
|
`root`+`home` configs, a `/home` timeline snapshot taken, `/home/.snapshots`
|
||||||
a real subvolume, the oneshot `Result=success`).
|
a real subvolume, the oneshot `Result=success`).
|
||||||
- ⚠ **Known bug — btrfs-assistant 2.2 segfaults on launch** in nixpkgs
|
- ✓ **Resolved — the "btrfs-assistant 2.2 segfault" was unprivileged-only**
|
||||||
26.05: it crashes inside `libbtrfsutil.so.1.4.0` (a library ABI mismatch,
|
(re-diagnosed 2026-07-04): gdb puts the crash in `btrfs_util_subvolume_
|
||||||
confirmed in the VM regardless of GL/Qt platform — so it's not a VM/GL
|
iterator_next()` — libbtrfsutil's *unprivileged* subvolume-iteration path
|
||||||
artifact and will crash on hardware too). The menu wiring is correct and
|
in btrfs-progs 6.17.1 (upstream-fixed after 6.17.1, kdave/btrfs-progs
|
||||||
the app is installed, but the GUI doesn't open. The snapshot *backend*
|
`886571653` "re-enable tree search v2 ioctl"; symbol versions were checked
|
||||||
(the actual snapshots) is unaffected. Fix path: a nixpkgs override aligning
|
and match, so not an ABI/link issue). **As root it runs fine** — VM-proven
|
||||||
its libbtrfsutil, or wait for an upstream bump. ✓ **Fallback shipped:**
|
(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
|
the menu now launches `nomarchy-snapshots` (system-side, gated on
|
||||||
`nomarchy.system.snapper`) instead — a keyboard-driven snapper
|
`nomarchy.system.snapper`) instead — a keyboard-driven snapper
|
||||||
browser/restore with no btrfs-assistant dependency. snapper is root-only
|
browser/restore with no btrfs-assistant dependency. snapper is root-only
|
||||||
|
|||||||
29
flake.nix
29
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
|
# The distroId question (roadmap "Distro branding"): set
|
||||||
# distroId = "nomarchy" so /etc/os-release is honest (ID=nomarchy,
|
# distroId = "nomarchy" so /etc/os-release is honest (ID=nomarchy,
|
||||||
# ID_LIKE=nixos). The risk is switch-to-configuration's "is this
|
# ID_LIKE=nixos). The risk is switch-to-configuration's "is this
|
||||||
|
|||||||
@@ -200,6 +200,12 @@ in
|
|||||||
# Paint the wallpaper as soon as the session is up (waits for
|
# Paint the wallpaper as soon as the session is up (waits for
|
||||||
# the daemon internally).
|
# the daemon internally).
|
||||||
"nomarchy-theme-sync wallpaper"
|
"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
|
# Waybar is launched here rather than as a systemd user service: bound
|
||||||
# to graphical-session.target the unit raced Hyprland's IPC on relogin
|
# to graphical-session.target the unit raced Hyprland's IPC on relogin
|
||||||
# and landed in `failed`; exec-once only fires once the compositor is
|
# and landed in `failed`; exec-once only fires once the compositor is
|
||||||
|
|||||||
@@ -404,11 +404,17 @@ ${themeRows}
|
|||||||
exit 0 ;;
|
exit 0 ;;
|
||||||
|
|
||||||
snapshot)
|
snapshot)
|
||||||
# btrfs-assistant 2.2 segfaults on 26.05 (libbtrfsutil ABI), so launch
|
# Snapshot browse/restore — the btrfs-assistant GUI via its pkexec
|
||||||
# the keyboard-driven snapper browser instead: a terminal running it
|
# launcher. The "2.2 segfaults on 26.05" diagnosis was HALF right:
|
||||||
# via sudo (snapper is root-only here — one password prompt), with
|
# only UNPRIVILEGED runs crash (libbtrfsutil unprivileged subvolume
|
||||||
# browse/diff, restore (undochange) and rollback, each confirmed.
|
# 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.
|
# 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 \
|
command -v nomarchy-snapshots >/dev/null 2>&1 \
|
||||||
|| { notify-send "Snapshots" "Snapshot tools unavailable (nomarchy.system.snapper off?)."; exit 0; }
|
|| { notify-send "Snapshots" "Snapshot tools unavailable (nomarchy.system.snapper off?)."; exit 0; }
|
||||||
exec ${cfg.terminal} -e sudo nomarchy-snapshots ;;
|
exec ${cfg.terminal} -e sudo nomarchy-snapshots ;;
|
||||||
|
|||||||
@@ -344,13 +344,17 @@ in
|
|||||||
nixos-rebuild switch --flake /etc/nixos#default "$@"
|
nixos-rebuild switch --flake /etc/nixos#default "$@"
|
||||||
'')
|
'')
|
||||||
# The desktop snapshot manager (browse / diff / restore / rollback over
|
# 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
|
++ lib.optional cfg.snapper.enable pkgs.btrfs-assistant
|
||||||
# Keyboard-driven snapper browser/restore — the fallback `nomarchy-menu
|
# Keyboard-driven snapper browser/restore — the menu's fallback when the
|
||||||
# snapshot` actually launches while btrfs-assistant 2.2 segfaults on 26.05
|
# GUI is absent, and handy over SSH. Runs as root (snapper is root-only
|
||||||
# (libbtrfsutil ABI). Runs as root (snapper is root-only here; the menu
|
# here; the menu opens it in a terminal via sudo, one password prompt),
|
||||||
# opens it in a terminal via sudo, one password prompt), fzf to pick, with
|
# fzf to pick, with browse/diff (read-only) and typed-`yes` confirmation
|
||||||
# browse/diff (read-only) and typed-`yes` confirmation before any write.
|
# before any write.
|
||||||
++ lib.optional cfg.snapper.enable (pkgs.writeShellApplication {
|
++ lib.optional cfg.snapper.enable (pkgs.writeShellApplication {
|
||||||
name = "nomarchy-snapshots";
|
name = "nomarchy-snapshots";
|
||||||
runtimeInputs = with pkgs; [ snapper fzf gawk gnugrep less coreutils systemd ];
|
runtimeInputs = with pkgs; [ snapper fzf gawk gnugrep less coreutils systemd ];
|
||||||
|
|||||||
Reference in New Issue
Block a user