From 541dd298206648212d8a02e22bc9f81f9a926e05 Mon Sep 17 00:00:00 2001 From: atlas Date: Mon, 24 Aug 2026 12:30:52 +0200 Subject: [PATCH] fix(swarm-authelia): only declare the scrape target where a collector reads it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The comment claimed the option's rule keeps scraper and target on one host by construction. It does not. Both services default from enableRequiredServices via mkDefault, which is an invitation to override rather than a guarantee, so co-location is a property of the auto-deployed topology and not of the module. Gating the target on the collector's own enable makes the loopback address honest: a host running authelia without a collector no longer declares a target nothing can read. That absence was the part worth fixing, because it is silent — no error, no metrics, nothing in a log to notice. This does not make authelia scrapeable from another host. That needs the endpoint published under a name with a certificate and an audience, which is separate work; the option's docs now say so where someone splitting the two would read it. --- nix/host-modules/swarm-authelia.nix | 33 +++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/nix/host-modules/swarm-authelia.nix b/nix/host-modules/swarm-authelia.nix index 09c7c5f5..b3c9a027 100644 --- a/nix/host-modules/swarm-authelia.nix +++ b/nix/host-modules/swarm-authelia.nix @@ -43,6 +43,7 @@ let swarmDomain = hyperhiveCfg.swarm.domain; uiCfg = hyperhiveCfg.swarm.ui; forgeCfg = hyperhiveCfg.swarm.forge; + otelCfg = hyperhiveCfg.swarm.otel; # Group an account must hold to reach operator-only surfaces. Named # here because this module writes the rule that enforces it and @@ -356,6 +357,16 @@ in two services defaulting to the same port do not conflict at build time — one simply loses at runtime, with nothing in any log. Check a new value against the others before changing this. + + ::: {.note} + Loopback means this endpoint is only reachable by a collector on + the *same host*, so the scrape target is declared only when one is + enabled here. Run the swarm's collector elsewhere and authelia's + metrics are simply not collected — no error, and nothing in a log + to say so. Making them reachable across hosts is a different piece + of work: the endpoint would have to be published under a name, + with a certificate and an audience. + ::: ''; }; @@ -825,10 +836,24 @@ in services.hyperhive.gateway.localNames = [ cfg.domain ]; # Declared here rather than in the collector's module, per the option's - # own rule: an entry exists only where the service that named it runs, - # which is what keeps scraper and target on one host by construction - # rather than by the all-local deployment happening to co-locate them. - services.hyperhive.swarm.otel.scrapeTargets.authelia = "127.0.0.1:${toString cfg.metricsPort}"; + # own rule: an entry exists only where the service that named it runs. + # + # Gated on the collector's `enable` as well, and that second condition is + # what makes the loopback address honest. Both services default from + # `swarm.enableRequiredServices` — but `mkDefault` is an invitation to + # override, not a guarantee, so "they are on the same host" is a property + # of the auto-deployed topology rather than of the module. Without this + # gate, a host running authelia and no collector would declare a target + # nothing can read, and the absence would be silent: no error, no metrics, + # nothing to notice. + # + # It does not make authelia scrapeable from ANOTHER host — that needs the + # endpoint published under a name with a cert and an audience, which is a + # different piece of work. This only stops the config asserting a + # collection that is not happening. + services.hyperhive.swarm.otel.scrapeTargets = lib.mkIf otelCfg.enable { + authelia = "127.0.0.1:${toString cfg.metricsPort}"; + }; # This swarm-ui quick-links entry, same guard as the vhost/DNS name # above (only the host actually running the container claims it —