diff --git a/nix/host-modules/swarm-bao.nix b/nix/host-modules/swarm-bao.nix index 83f77383..282f9687 100644 --- a/nix/host-modules/swarm-bao.nix +++ b/nix/host-modules/swarm-bao.nix @@ -1092,17 +1092,23 @@ in # after `bao operator init`. Skipping rather than failing is also # what makes deleting the token at the end of that procedure safe. unitConfig.ConditionPathExists = baoDeploy.bootstrapTokenFile; + # A store that is up is not necessarily unsealed — under + # `seal = "shamir"` an operator unseals by hand after every + # restart — so early attempts legitimately fail. Bounded, + # because a token that is wrong rather than early would + # otherwise retry forever. + # + # `StartLimit*` are `[Unit]` settings that systemd ignores under + # `[Service]`, which is what these two options render to. The + # interval also has to exceed `RestartSec × burst`, or the window + # closes between attempts and the burst is never reached. + startLimitBurst = 10; + startLimitIntervalSec = 600; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; - # A store that is up is not necessarily unsealed — under - # `seal = "shamir"` an operator unseals by hand after every - # restart — so early attempts legitimately fail. Bounded, - # because a token that is wrong rather than early would - # otherwise retry forever. Restart = "on-failure"; RestartSec = 30; - StartLimitBurst = 10; }; environment.BAO_ADDR = "https://${cfg.domain}:${toString cfg.port}"; script = '' diff --git a/nix/module-eval.nix b/nix/module-eval.nix index d51f92fe..625fc875 100644 --- a/nix/module-eval.nix +++ b/nix/module-eval.nix @@ -536,6 +536,21 @@ let lib.hasInfix "/run/secrets/bao-bootstrap.token" u.script && u.unitConfig.ConditionPathExists == "/run/secrets/bao-bootstrap.token"; } + { + # `StartLimit*` are `[Unit]` settings that systemd ignores under + # `[Service]`, so a bound written into `serviceConfig` renders, deploys + # and does nothing. Asserted where nixpkgs puts it rather than where it + # was written, and the interval is part of the bound: it has to exceed + # `RestartSec × burst` or the window closes between attempts. + name = "the granting unit's start limit lands in [Unit], not [Service]"; + ok = + let + u = baoGrantHere.containers.swarm-bao.config.systemd.services.swarm-bao-controller-policy; + in + toString u.unitConfig.StartLimitBurst == "10" + && toString u.unitConfig.StartLimitIntervalSec == "600" + && !(u.serviceConfig ? StartLimitBurst); + } { # The grants themselves, and the `hive-` prefix is the whole point: # without it the controller can rewrite the policy that constrains it,