From 2727d88a1298d653ffa347ead4fd7d9adafa4b06 Mon Sep 17 00:00:00 2001 From: oqyude Date: Sat, 10 Oct 2026 16:31:41 +0300 Subject: [PATCH] =?UTF-8?q?fix(storage-guard):=20remove=20RequiresMountsFo?= =?UTF-8?q?r=20=E2=80=94=20was=20causing=20auto-remount?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CRITICAL BUG #2 in storage guard, caught by live test v8 on sapphira (2026-10-10). RequiresMountsFor=/home/ooyude/External in the unit file told systemd that the service depends on the mount. When the mount was gone and the service was started, systemd's dependency resolver AUTOMATICALLY REMOUNTED the filesystem to satisfy the dependency — THEN checked ConditionPathIsMountPoint. The condition saw the just-remounted filesystem and evaluated to true. Service started on empty external storage. Guard completely bypassed. This is a subtle interaction: - ConditionPathIsMountPoint is a TEST (true/false evaluation) - RequiresMountsFor is a DEPENDENCY (systemd must make it true) A guard should be a TEST, not a dependency that makes the test trivially pass. Remove the dependency. The condition alone is sufficient for both boot-time and runtime checks: - Boot: mount unit starts via local-fs.target, condition is true - Runtime: if mount disappears, condition becomes false on next start attempt. Without RequiresMountsFor, systemd doesn't auto-recover, so the guard fires. Ordering should be expressed via After= in the consumer's systemd.services block (not in the shared helper), e.g.: systemd.services.postgresql.after = [ "home-ooyude-External.mount" ]; Live test v8 sequence (before this fix): 1. umount /home/ooyude/External → OK, gone from /proc/mounts 2. findmnt /home/ooyude/External → exit=1, not in table 3. systemctl start postgresql → STARTED (guard bypassed) 4. journal: no ConditionPath error (service started successfully) This is the second guard bug found by live testing in this session. The first one was the '!' prefix inversion. Both were invisible to nix eval, both only visible at runtime. Lesson reinforced: guards MUST be live-tested, not just statically evaluated. Recovery: v8 test reverted state via emergency_recovery trap (remount + restart all services). System healthy at 10/10. --- lib/xlib/helpers.nix | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/xlib/helpers.nix b/lib/xlib/helpers.nix index fc4b11b..e02fa98 100644 --- a/lib/xlib/helpers.nix +++ b/lib/xlib/helpers.nix @@ -178,7 +178,13 @@ in # or merge with an existing serviceConfig: # serviceConfig = xlib.helpers.mkStorageGuard xlib // { ...other fields... }; mkStorageGuard = xlib: { - RequiresMountsFor = [ xlib.dirs.server-home ]; + # Guard via ConditionPathIsMountPoint ONLY. We intentionally do + # NOT add RequiresMountsFor: that creates a dependency that causes + # systemd to auto-remount the filesystem when starting the unit, + # which defeats the guard (tested and confirmed: v8 test on + # sapphira, 2026-10-10). Ordering should be expressed via After= + # on the .mount unit in the consumer's systemd.services block, + # not via a guard-creating dependency here. ConditionPathIsMountPoint = [ xlib.dirs.server-home ]; }; }