From f8dd7374560ed997b13b30881369f1a39ba9b9ed Mon Sep 17 00:00:00 2001 From: atlas Date: Sun, 13 Sep 2026 00:57:59 +0200 Subject: [PATCH] swarm: run the agent queue credential reader before hive-c0re Ruled: swarm-bao-queue-agent.service must run before hive-c0re.service and be wanted (not required) by it, so no agent container renders ahead of the reader's attempt at its credential. An unreachable store delays hive-c0re's start by the reader's own start-limit window rather than failing it outright. Refs #4314 --- docs/swarm/secrets.md | 5 ++++- nix/host-modules/glue-queue-agent-credential.nix | 16 +++++++++++----- nix/module-eval.nix | 16 ++++++++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/docs/swarm/secrets.md b/docs/swarm/secrets.md index 764fef47..6a3dde95 100644 --- a/docs/swarm/secrets.md +++ b/docs/swarm/secrets.md @@ -163,7 +163,10 @@ The **second reader** is the agent containers' queue credential: the client id it authenticates, because that's how a queue client reads them — the secret by path, the id by value. No local fallback exists, and none is possible, so absent files mean this hive's agents don't connect, which is what -a swarm looks like before the publisher on the authelia host has run. +a swarm looks like before the publisher on the authelia host has run. The +reader runs before `hive-c0re.service` and is wanted (not required) by it, so +an agent container never renders ahead of the credential; an unreachable store +delays the daemon's start rather than failing it. ⚠️ **Service↔store mTLS is its own trust domain.** A credential you must already hold to authenticate can't be fetched from the thing it authenticates diff --git a/nix/host-modules/glue-queue-agent-credential.nix b/nix/host-modules/glue-queue-agent-credential.nix index ebb94f52..81e3c350 100644 --- a/nix/host-modules/glue-queue-agent-credential.nix +++ b/nix/host-modules/glue-queue-agent-credential.nix @@ -103,11 +103,17 @@ in ]; wants = lib.optionals baoDeploy.enable [ "container@${baoCfg.machine}.service" ]; requires = lib.optionals baoDeploy.enable [ "swarm-bao-pki.service" ]; - # Ordered `Before=` nothing, because the consumer does not exist yet. - # Agent containers are created at runtime, so no static unit name can be - # named here anyway; the slice that bind-mounts these files in adds the - # edge through hive-c0re's `container@h-.service` drop-in. - wantedBy = [ "multi-user.target" ]; + # Ordered before hive-c0re, so no agent container renders ahead of an + # attempt at its credential. `Wants=`, not `Requires=`: a store this + # unit can't reach delays hive-c0re's start by its own start-limit + # window (`TimeoutStartSec`, retried up to `startLimitBurst` times + # below) rather than failing it — hive-c0re starts once that window + # elapses, whatever credential is or isn't on disk by then. + before = [ "hive-c0re.service" ]; + wantedBy = [ + "multi-user.target" + "hive-c0re.service" + ]; path = [ baoDeploy.package pkgs.coreutils diff --git a/nix/module-eval.nix b/nix/module-eval.nix index 052226cf..a35580c0 100644 --- a/nix/module-eval.nix +++ b/nix/module-eval.nix @@ -1226,6 +1226,22 @@ let name = "the queue credential reader's journal reaches the collector"; ok = builtins.elem "swarm-bao-queue-agent" baoRemoteReader.services.hyperhive.swarm.otel.journaldUnits; } + { + # No agent container may render before this unit has had its attempts, + # and the edge that guarantees it must delay hive-c0re rather than sink + # it: an unreachable store is this unit's `Restart=on-failure` window, + # not a reason for the daemon that renders every agent to fail its own + # start. + name = "the queue credential reader orders before hive-c0re and is wanted, not required, by it"; + ok = + let + u = baoRemoteReader.systemd.services.swarm-bao-queue-agent; + in + builtins.elem "hive-c0re.service" (u.before or [ ]) + && builtins.elem "hive-c0re.service" (u.wantedBy or [ ]) + && !(builtins.elem "hive-c0re.service" (u.requiredBy or [ ])) + && !(builtins.elem "hive-c0re.service" (u.requires or [ ])); + } { # The doctrine three glue files state, as a property a rewrite has to # keep: a client is defined by holding a certificate the store accepts,