feat(swarm-authelia): expose prometheus metrics and declare the scrape target

The endpoint was off, so nothing reported on the swarm's own SSO. Enabling
it alone would have added no data — the scraper that reads it only landed
with the swarm-tier prometheus receiver.

Loopback only, like the main listener and for a stronger reason: this
endpoint authenticates nothing and reports request volumes and outcomes for
every login on the swarm.

metricsPort is an option rather than a literal because every swarm container
shares the host netns, so two services picking the same port do not conflict
at build time — one loses at runtime with nothing in any log. 9959 is
upstream's default and is unclaimed across nix/.
This commit is contained in:
atlas 2026-08-19 21:45:07 +02:00 committed by mara
commit 31fa891a4e

View file

@ -339,6 +339,26 @@ in
'';
};
metricsPort = lib.mkOption {
type = lib.types.port;
default = 9959;
description = ''
TCP port authelia serves its Prometheus metrics on, bound to
loopback. Upstream's default, kept so an operator reading
authelia's documentation finds what they expect.
A separate port from {option}`port` because it is a separate
listener with a different audience: the main one is proxied by
the gateway and reachable from the swarm, this one is scraped by
the collector on this host and by nothing else.
Every swarm container shares the host network namespace, so
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.
'';
};
domain = lib.mkOption {
type = lib.types.str;
# Under the SWARM domain, like the forge and matrix: a swarm has one
@ -804,6 +824,12 @@ in
# bridge at that wrong answer.
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}";
# This swarm-ui quick-links entry, same guard as the vhost/DNS name
# above (only the host actually running the container claims it —
# see `services.hyperhive.swarm.controller.links`'s description for
@ -1110,6 +1136,16 @@ in
};
log.level = "info";
# Prometheus exposition for the swarm collector to scrape.
# Loopback only, like the main listener above and for a
# stronger reason: this endpoint has no authentication of its
# own, and it reports request volumes and outcomes for every
# SSO login on the swarm.
telemetry.metrics = {
enabled = true;
address = "tcp://127.0.0.1:${toString cfg.metricsPort}";
};
# `watch` is load-bearing, not a convenience: authelia reads
# this file once at startup, and `swarm-authelia-bridge` writes
# it to create agent identities while being unable to restart