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