From 6982b2ec14e5b58410a2028491e3a39f2aebd660 Mon Sep 17 00:00:00 2001 From: atlas Date: Sat, 10 Oct 2026 13:31:17 +0200 Subject: [PATCH] swarm-secret-publish: drop RemainAfterExit so the path unit re-runs it swarm-secret-publish.service was Type=oneshot with RemainAfterExit=true. After its first successful run it stayed active (exited), so every later start from swarm-secret-publish.path (PathChanged on authelia's client secret dir) was a no-op: a secret minted or replaced after that first run never reached the store, and nothing logged it. Without RemainAfterExit the unit goes inactive after each run, so each PathChanged event starts a fresh publish. Same shape as swarm-bao-forge-publisher in hive-forge/default.nix. No unit requires, wants, orders after, binds to or is part of swarm-secret-publish.service, so nothing depended on it staying active. This also covers the first-boot race where the publisher runs before authelia's first mint: the mint writes the file, and the path unit now starts the publisher again. A module-eval case pins it: the path unit targets the service, the service is a oneshot, and RemainAfterExit is unset or false. Closes #5032 --- nix/host-modules/swarm-secret-publisher.nix | 3 ++- nix/module-eval/secret-publisher.nix | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/nix/host-modules/swarm-secret-publisher.nix b/nix/host-modules/swarm-secret-publisher.nix index ef5cf948..ad1582e3 100644 --- a/nix/host-modules/swarm-secret-publisher.nix +++ b/nix/host-modules/swarm-secret-publisher.nix @@ -177,9 +177,10 @@ in baoDeploy.package pkgs.coreutils ]; + # ⚠️ No `RemainAfterExit`. A oneshot that stays `active (exited)` turns + # every later start from the path unit above into a no-op. serviceConfig = { Type = "oneshot"; - RemainAfterExit = true; # Bounded here rather than left to systemd's default, so the number a # boot waits on is in the file that waits. A sealed store answers on # the port and never answers the write. diff --git a/nix/module-eval/secret-publisher.nix b/nix/module-eval/secret-publisher.nix index 4ee353ee..78780429 100644 --- a/nix/module-eval/secret-publisher.nix +++ b/nix/module-eval/secret-publisher.nix @@ -379,6 +379,20 @@ let && !(m.systemd.services ? swarm-secret-publish) && !(m.systemd.paths ? swarm-secret-publish); } + { + # The path unit's start is a no-op while its target is `active (exited)`, + # so a secret minted after the first run would never be published. + # `Type` is read too, so a missing `serviceConfig` cannot pass the arm. + name = "the publisher's path unit starts a oneshot that does not remain active"; + ok = + let + m = secretPublisherHere; + sc = m.systemd.services.swarm-secret-publish.serviceConfig; + in + m.systemd.paths.swarm-secret-publish.pathConfig.Unit == "swarm-secret-publish.service" + && sc.Type == "oneshot" + && !(sc.RemainAfterExit or false); + } ]; in runGroup "secret-publisher" cases