hyperhive — swarm options
services.hyperhive.swarm.authelia.agentClientSuffix
Suffix appended to a hive’s own client id to name the client its
agent containers present — agents on hive alpha all present
hive-alpha-agent.
Read-only for the same reason as hiveClientPrefix, and read by the
same consumer with the same failure mode: a split spelling denies
every agent as a timeout.
A suffix on the hive’s id rather than a prefix of its own, because
agent-alpha reads as the agent named alpha — which is precisely
what this identity does not say.
One id per hive rather than per agent, because agents are created at runtime and a per-agent client would make creating one a config change plus a reload. The consequence is that this identity says which hive an agent belongs to and never which agent — the broker cannot tell two agents on one hive apart.
Type: string (read only)
Default:
"-agent"
Declared by:
services.hyperhive.swarm.authelia.bridgePort
TCP port swarm-authelia-bridge listens on, loopback-bound
(127.0.0.1:${bridgePort}) — one above authelia’s own default
port (9091), outside hyperhive’s other claimed ranges.
Not published on its own: the authelia vhost proxies
/bridge/ to it, and that is the address swarm-controller
uses from any host. The bridge answers only
swarm-controller’s OIDC client.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
9092
Declared by:
services.hyperhive.swarm.authelia.domain
Public hostname for the SSO provider — the sub-domain shape the
forge and matrix already use, under the swarm’s domain because a
swarm has one SSO provider. Must be the name browsers
actually visit: it is the authelia_url the session cookie is
validated against.
⚠️ Unlike the forge and matrix names, this one carries no
migration pin: nothing depends on the previous
auth.${services.hyperhive.domain} yet, so it moves outright.
Type: string
Default:
"auth.${services.hyperhive.swarm.domain}"
Example:
"login.example.com"
Declared by:
services.hyperhive.swarm.authelia.hiveClientPrefix
Prefix of the OAuth2 client id minted for each hive in
services.hyperhive.swarm.hives — the client for hive alpha is
hive-alpha.
Read-only for the same reason as machine and unit: it is what
this module produces, published so a consumer does not carry a
second copy.
The consumer that matters is the queue’s auth-callout responder, which decides which hive a connection is by stripping this prefix off the introspected client id. Split the two spellings and every hive is denied — as a timeout, indistinguishable from a hive that simply has not reported.
Type: string (read only)
Default:
"hive-"
Declared by:
services.hyperhive.swarm.authelia.machine
Name of the nixos-container authelia runs in. Read-only: it is
what this module declares, published so callers of
systemctl -M and /var/lib/nixos-containers/<name> do not
have to hardcode it.
Type: string (read only)
Default:
"swarm-authelia"
Declared by:
services.hyperhive.swarm.authelia.metricsPort
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 port because it is a separate
listener with a different audience: the main one serves logins and
is reachable from the swarm, this one carries no authentication of
its own. The swarm collector scrapes it, from whichever host it
runs on, as https://${domain}/metrics: the gateway’s
= /metrics location proxies to this port only after authelia
has accepted the collector’s token for that URL.
⚠️ 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.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
9959
Declared by:
services.hyperhive.swarm.authelia.oidc.clients
OIDC relying parties this provider will issue tokens to. Declaring one turns the provider on; the default empty list leaves this module exactly as it was — a session provider and nothing else.
⚠️ There is deliberately no secret here. A client secret has
two holders in two containers (authelia keeps a hash, the
relying party the plaintext), and
services.authelia.instances.<n>.settings is rendered into the
nix store, which is world-readable and permanent. So this
option carries only the parts that are safe to evaluate: the
secret is minted on first boot and never passes through a nix
expression. See docs/swarm/ for what goes where.
Type: list of (submodule)
Default:
[ ]
Declared by:
services.hyperhive.swarm.authelia.oidc.clients.*.accessTokenSignedResponseAlg
Signing algorithm for this client’s access tokens.
null leaves authelia on its default, which issues an
opaque token (authelia_at_…) — a database handle that
carries no claims and means nothing to anyone but this
provider.
Set RS256 when the resource server verifies the token
itself rather than asking this provider about it: that
yields an RFC 9068 JWT (at+jwt) carrying aud, iss
and client_id, verifiable against /jwks.json with no
round trip.
⚠️ This is what makes a token readable by an OIDC-verifying consumer at all. A resource server given an opaque token is not misconfigured — it is structurally unable to verify it, and says so in terms that point at the verifier rather than at the token’s format.
Type: null or one of “none”, “RS256”
Default:
null
Example:
"RS256"
Declared by:
services.hyperhive.swarm.authelia.oidc.clients.*.audience
Audiences (aud) this client is permitted to request a
token for. Empty means it asks for none, which is the
right answer for a client whose resource server does not
distinguish callers.
⚠️ Registering an audience only permits it — the value
lands in a token when the client asks for it at the
token endpoint, and a client that does not send
audience= receives a token with aud: [] however
complete this list looks. Measured against authelia
4.39.20: the config reads exactly right and the resource
server rejects every token, because a config that grants
and a request that claims are two separate acts.
Requesting an audience that is not listed here is
refused with invalid_target, which is what makes this
usable as a boundary rather than a label: a client cannot
mint a token for a resource slot that is not its own.
Type: list of string
Default:
[ ]
Example:
[
"hive-alpha"
]
Declared by:
services.hyperhive.swarm.authelia.oidc.clients.*.bearerAuthz
Grant this client the authelia.bearer.authz scope, so it
may present its access token to authelia’s authz endpoint
and be authorised by an access_control rule — how a
scraper reaches a service published behind the gateway.
A named capability rather than a free-form scopes list,
for the same reason kind derives the rest: authelia
refuses some scope/grant combinations outright (openid
with client_credentials among them), and a list would
make those combinations expressible again. This admits the
one value that is legal here and nothing else.
Note: Setting this obliges two other options: a non-empty
audience, and a tokenEndpointAuthMethod of
client_secret_basic, client_secret_jwt or
private_key_jwt. Authelia checks the same thing, but only
in its preStart validator — which means a violation
builds and deploys cleanly and then fails to restart,
taking swarm SSO down. The host that runs authelia asserts
both at evaluation, where a wrong value costs nothing.
Type: boolean
Default:
false
Example:
true
Declared by:
services.hyperhive.swarm.authelia.oidc.clients.*.description
Human-readable name, shown on authelia’s consent screen. This is the string a person reads when deciding whether to hand an application their identity, so it should name the application rather than the protocol.
Type: string
Example:
"HyperHive forge"
Declared by:
services.hyperhive.swarm.authelia.oidc.clients.*.id
OAuth2 client id, as the relying party knows itself.
Type: string
Example:
"forgejo"
Declared by:
services.hyperhive.swarm.authelia.oidc.clients.*.kind
Whether a human logs in through this client, or a daemon authenticates as itself.
interactive is the authorization-code flow: a browser is
redirected, a person authenticates, the client receives an
id-token. machine is client_credentials: there is
nobody to redirect and no identity to assert but the
client’s own, so it receives an access token and no
id-token.
native is a public client a person runs on their own
machine (RFC 8252): no secret, authorization code with
PKCE through a loopback redirectUris entry, and a refresh
token. Its access token carries authelia.bearer.authz
for its audience, so the gateway admits it as that person.
This is declared rather than inferred from an empty
redirectUris, because authelia permits only the grants a
client names — omitting grant_types yields
authorization-code alone, and a daemon then fails at the
token endpoint with unauthorized_client rather than at
evaluation.
Type: one of “interactive”, “machine”, “native”
Default:
"interactive"
Example:
"machine"
Declared by:
services.hyperhive.swarm.authelia.oidc.clients.*.redirectUris
Exact callback URLs the provider will redirect to. Matched literally by authelia — a trailing-slash difference is a rejected login, not a warning.
Meaningless for kind = "machine", which is asserted
rather than silently ignored.
Type: list of string
Default:
[ ]
Example:
[
"https://forge.example.com/user/oauth2/authelia/callback"
]
Declared by:
services.hyperhive.swarm.authelia.oidc.clients.*.tokenEndpointAuthMethod
How this client proves its identity at the token
endpoint. null leaves authelia on its own default
(client_secret_basic), which is what every client that
does not say otherwise gets.
Set it when the relying party’s implementation differs,
because authelia enforces the registered method rather
than accepting whatever arrives. tuwunel sends
client_secret_post, and against a client registered for
basic the result is a 401 from /api/oidc/token after
a successful consent — the login looks like it worked
right up to the last hop, and neither the redirect nor
the secret is at fault.
⚠️ null is NOT accepted on a client with bearerAuthz.
Measured against authelia 4.39.20: under that scope the
method must be stated: omitting it is refused with
must be configured as 'client_secret_basic', … but it's configured as an empty string. The sentence above is
true of an ordinary client and false of that one: the
host that runs authelia asserts at evaluation that a
bearerAuthz client states client_secret_basic,
client_secret_jwt or private_key_jwt.
Type: null or one of “client_secret_basic”, “client_secret_post”, “client_secret_jwt”, “private_key_jwt”, “none”
Default:
null
Example:
"client_secret_post"
Declared by:
services.hyperhive.swarm.authelia.oidc.hiveIdentities
Mint machine clients per hive in
services.hyperhive.swarm.hives, so each hive can
authenticate to swarm services as itself: hive-<name> for the
hive’s own daemons, and hive-<name>-agent for the agent containers
running on it.
Two clients rather than one because they are not the same principal — a hive’s daemons run on the host and an agent runs in a container the host hands a credential to, so a swarm service has to be able to grant them different things. It is one client per hive on the agent side, not per agent: agents are created at runtime, and a per-agent client would make creating one a config change plus an authelia reload. The cost is that agents on a hive are indistinguishable from each other, tracked as a follow-up rather than papered over.
On by default: a swarm’s hives have identities, and that is a fact about the swarm rather than about any one host. It does not depend on whether the queue runs on this machine, so the answer is the same on every host of one swarm — set it false for a swarm whose hives authenticate to nothing.
The clients are inert until something authenticates with them: each is a client id and a secret sitting on this host. What delivers a secret to a hive that is not this host is a separate problem and deliberately not solved here.
Type: boolean
Default:
true
Declared by:
services.hyperhive.swarm.authelia.port
TCP port authelia listens on. 9091 is upstream’s default and sits outside hyperhive’s claimed ranges (dashboard 7000, forge 3000, matrix 8008, every agent in 8100…8999 via FNV-1a hash).
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
9091
Declared by:
services.hyperhive.swarm.authelia.unit
authelia’s systemd unit inside the container. Read-only, and derived from the instance name exactly like the unit itself.
Type: string (read only)
Default:
"authelia-swarm.service"
Declared by:
services.hyperhive.swarm.authelia.url
Base URL clients are sent to for authentication — the half of this module that exists on every hive, not just the one running the container.
Names domain, and does not ask whether this host
runs the container: a swarm has one SSO provider, so every hive
addresses the same name and resolution decides where it is —
dnsmasq locally on the host serving the vhost, the real network
anywhere else. There is no loopback-vs-remote branch to get
wrong, the same way services.hyperhive.swarm.otel.domain
has none.
Not having SSO is not a supported deployment: every swarm has an
IdP, so this is never null.
Type: string
Default:
"https://${domain}"
Example:
"https://auth.example.com"
Declared by:
services.hyperhive.swarm.bao.domain
Name the store is reached on. A sibling of the swarm’s other
service names, not a child of any hive domain: an authority whose
nameConstraints permit one hive’s domain cannot issue for a sibling
of it, so the shape of this name decides which authorities could ever
sign for the store. That is a property of the name, not a choice of
issuer — this module makes no such choice.
Type: string
Default:
"bao.${services.hyperhive.swarm.domain}"
Declared by:
services.hyperhive.swarm.bao.machine
Container name. Read-only: the name appears in host paths and in
machinectl, so it is a fact other modules may read rather than a knob.
Type: string (read only)
Default:
"swarm-bao"
Declared by:
services.hyperhive.swarm.bao.otel.clientId
OAuth2 client id the collector inside the store’s container authenticates as, and — self-referentially, the shape every hive’s client already uses — the audience it asks its token for.
Its own, not the swarm collector’s and not
swarm-controller’s. One identity per principal: this forwarder
runs wherever the store runs, which is not where either of those
two runs, and the receiver it pushes to
(swarm.otel.storeProducerName) admits this id alone.
Swarm-wide and read-only because three hosts have to agree on it:
authelia registers the client
(glue-swarm-bao-otel-oidc-client.nix), the swarm collector checks
the audience (swarm-otel.nix), and the store’s host reads the
minted secret back out of the store under a path composed from it.
Two spellings present as a healthy-looking 401.
Type: string (read only)
Default:
"swarm-bao-collector"
Declared by:
services.hyperhive.swarm.bao.otel.telemetryPort
Port the collector inside the store’s container serves its own metrics on — queue depth, refused and dropped samples, exporter failures. How you find out that telemetry is being lost, so it is worth keeping rather than switching off.
⚠️ Deliberately neither 8888 nor 8889. 8888 is the collector
binary’s built-in default, which the hive tier
(services.hyperhive.otel.telemetryPort) already binds, and
8889 is the swarm tier’s
(services.hyperhive.swarm.otel.telemetryPort). This
container runs with privateNetwork = false, so all three share the
host’s network namespace whenever they are co-located — and unlike
the OTLP ports this one appears nowhere in either config when it is
left undeclared, so nothing that compares configured ports can see
the clash. The second collector to start simply dies with
bind: address already in use.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
8890
Declared by:
services.hyperhive.swarm.bao.port
TCP port the store listens on. Upstream’s own default, kept so an operator reading OpenBao documentation finds what they expect.
Swarm-wide because a client has to know it to reach the store, and
the same port on every listener: which addresses the store answers
on is the running host’s business
(services.hyperhive.deploy.bao.extraListenAddresses), but
which port it answers on is something the whole swarm agrees.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
8200
Declared by:
services.hyperhive.swarm.bao.ui.domain
Name the gateway serves the store’s browser UI on, to members of
authelia’s admins group only. Swarm-wide because authelia’s host
writes the access rule for it and the store’s host serves it.
Must differ from services.hyperhive.swarm.bao.domain: that
name is the mutual-TLS endpoint every reader dials, and it has no
vhost.
Type: string
Default:
"bao-ui.${services.hyperhive.swarm.domain}"
Declared by:
services.hyperhive.swarm.bao.ui.oidc.clientId
OAuth2 client id the store’s oidc auth method logs browser users
in as, at authelia.
Swarm-wide and read-only because two hosts have to agree on it:
authelia registers the client (glue-bao-ui-oidc-client.nix) and
mints its secret, and the store’s host reads that secret back out of
the store under a path composed from this id.
Type: string (read only)
Default:
"swarm-bao-ui"
Declared by:
services.hyperhive.swarm.bao.ui.oidc.redirectUri
Where authelia sends the browser back to after an OIDC login, and the
URI both authelia and the store’s oidc role match exactly.
The format is the OpenBao UI’s own route,
/ui/vault/auth/<mount>/oidc/callback, with the mount oidc. The
UI composes it from the page’s origin, so it only matches when the
gateway serves the UI on port 443.
Type: string (read only)
Default:
"https://${services.hyperhive.swarm.bao.ui.domain}/ui/vault/auth/oidc/oidc/callback"
Declared by:
services.hyperhive.swarm.builder.clientId
The OAuth2 client id the swarm builder presents to the swarm queue. Read-only: the host running authelia registers it, the queue’s auth-callout responder grants it the build subject, and the secret publisher carries its secret to the store, all from this one value.
Type: string (read only)
Default:
"swarm-builder"
Declared by:
services.hyperhive.swarm.controller.links
Quick links to swarm-wide services, surfaced by the swarm UI’s
links menu (GET /api/links). Same shape and same
zero-code-change-to-extend idea as hyperhive.dashboardLinks
(nix/agent-modules/dashboard-links.nix), one level up: rather
than one fixed list, definitions merge with the list-merge idiom
services.hyperhive.gateway.localNames uses, so an operator can
add arbitrary extra entries here directly with no swarm-controller
or swarm-ui change.
The Forge, Authelia, Grafana, Metrics and Logs entries come from
services.hyperhive.swarm.<service>.domain, the Bao entry from
services.hyperhive.swarm.bao.ui.domain, and the Matrix entry
from services.hyperhive.swarm.matrix.gatewayHost when it is set,
so they are present whichever host runs each service.
swarm-ui.nix contributes its own API-docs entry, since the
swarm UI runs wherever the controller does.
Read only on the host that runs the controller.
Type: list of (submodule)
Default:
[ ]
Example:
[ { label = "Wiki"; icon = "📖"; url = "https://wiki.example.com/"; } ]
Declared by:
services.hyperhive.swarm.controller.links.*.icon
Optional icon emoji or short glyph.
Type: string
Default:
""
Declared by:
services.hyperhive.swarm.controller.links.*.label
Display label for the link.
Type: string
Declared by:
services.hyperhive.swarm.controller.links.*.url
Full URL.
Type: string
Declared by:
services.hyperhive.swarm.controller.queue.natsUrl
Where the controller reaches the swarm queue.
Defaults to the queue’s name, which is the same URL on every host: the queue’s own host resolves it locally, and any other through the operator’s DNS.
Empty means unset, which the assertion below refuses — a controller with no queue is not a lighter controller.
Type: string
Default:
"tls://${swarm.nats.domain}:${swarm.nats.port}"
Example:
"tls://nats.example.com:4222"
Declared by:
services.hyperhive.swarm.controller.queue.tokenEndpoint
The OIDC token endpoint the controller mints its own access token from.
Derived from swarm.authelia.url, which is the half of the
authelia module that exists on every host — so this is already
correct for a remote provider as long as that URL is set.
Type: string
Default:
"${swarm.authelia.url}/api/oidc/token"
Declared by:
services.hyperhive.swarm.controller.queueClientId
The OAuth2 client id the controller presents to the swarm queue. Read-only: it is the client the host running authelia registers, whichever host runs the controller, published so the auth-callout responder can be told which client may read every hive’s key without repeating the string.
The responder decides that from a client id, and a client id it does not recognise is denied. A denial reaches a NATS client as a timeout rather than an error, and a controller that cannot read looks exactly like a swarm where no hive has reported yet — so a drift between these two spellings is invisible at the point it is introduced and misattributed everywhere it shows up.
Type: string (read only)
Default:
"swarm-controller"
Declared by:
services.hyperhive.swarm.controller.staleAfterSeconds
How old a hive’s last status snapshot may be before
GET /api/hives/status reports it as stale rather than fresh.
This is a statement about how often hives publish, not about how patient a reader is — set it above the publishing cadence or every hive reads stale between offers. It is an option and not a constant precisely because that cadence is a property of the deployment.
Freshness is derived when the endpoint is read, never stored, so changing this takes effect for the next request; no hive has to re-publish anything.
Type: positive integer, meaning >0
Default:
120
Declared by:
services.hyperhive.swarm.domain
DNS domain of the wider swarm this hive belongs to. Each hive
occupies its own sub-domain of it, which is why every entry in
services.hyperhive.swarm.hives defaults its domain to
<name>.<swarm.domain> — set this plus
services.hyperhive.hiveName, list the hives by name, and no
hive in the swarm states an address at all.
Required on every host that runs a hyperhive service (a
services.hyperhive.deploy.* service, the gateway, the bridge,
otel or the snapshot store), with the same value on every host
of the swarm, and deliberately not
defaulted. A guessed swarm domain is a wrong hostname that
evaluates cleanly and deploys, which is worse than an eval
failure telling an operator to write down the one address their
swarm answers to.
Type: null or string
Default:
null
Example:
"darkest.space"
Declared by:
services.hyperhive.swarm.forge.domain
Public hostname for the forge. Doubles as both the forgejo
DOMAIN setting (clone URLs forgejo advertises) AND the
gateway vhost server-name (sub-domain routing — see
docs/networking/gateway.md).
Defaults to forge.${services.hyperhive.swarm.domain} — the
swarm’s domain, not this hive’s, because a swarm runs one
forge that every hive in it talks to.
⚠️ To use the per-hive name forge.${services.hyperhive.domain}
instead, pin it here. Certificates follow either way: the
store’s swarm-services role issues for the configured names,
not for a fixed tree.
Set to a full hostname (git.example.com,
forge.internal.lan, etc.) for a bespoke vhost shape — the
full domain goes here, no separate sub-domain-label option.
Type: string
Default:
"forge.${services.hyperhive.swarm.domain}"
Example:
"git.example.com"
Declared by:
services.hyperhive.swarm.forge.httpPort
TCP port the forge serves HTTP on. Default 3000 sits outside hyperhive’s claimed ranges (dashboard 7000, every agent in 8100…8999 via FNV-1a hash). Change this if you already have another forgejo bound to 3000.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
3000
Declared by:
services.hyperhive.swarm.forge.publicUrl
Browser-facing forge URL the dashboard uses to build clickable
forge links (the H0M3 Forge tile, per-agent-row forge links) —
sourced into every agent container + hive-c0re as
HIVE_FORGE_PUBLIC_URL.
Defaults to https://${cfg.domain}, the gateway vhost. When
null, the dashboard hides forge links rather than
guessing one — see docs/web-ui/dashboard.md::H0M3 page for
the rationale (a link built from the operator’s own browser
hostname + a container port is only an accident away from
wrong on any deployment that isn’t plain localhost).
Type: null or string
Default:
"https://${domain}"
Example:
"https://forge.example.com"
Declared by:
services.hyperhive.swarm.forge.rootUrl
Override the auto-derived forgejo ROOT_URL. When null
(default), ROOT_URL is https://${cfg.domain}/. The gateway
always terminates TLS (self-signed is the implicit floor when no
gateway.tls.certDir / ACME is set), so the forge is always
advertised over https. A non-canonical gateway.httpsPort is
appended as :<port>.
Set this only for a genuinely bespoke shape (e.g. an external
reverse proxy on a different host/path). Must end with / per
forgejo’s ROOT_URL contract.
Type: null or string
Default:
null
Example:
"https://forge.example.com/"
Declared by:
services.hyperhive.swarm.forge.sshPort
TCP port the forge’s built-in SSH server listens on. Kept off
22 so it doesn’t clash with the host’s openssh. Agents push
with ssh -p <sshPort> git@<domain>:<owner>/<repo>.git.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
2222
Declared by:
services.hyperhive.swarm.forge.sso.clientId
OAuth2 client id this forge identifies itself with. Must match
the id of the corresponding entry in
services.hyperhive.swarm.authelia.oidc.clients.
Type: string
Default:
"forgejo"
Declared by:
services.hyperhive.swarm.forge.sso.redirectUri
OAuth2 callback authelia sends the browser back to, and the URI it matches exactly.
Read-only: forgejo derives it from its own ROOT_URL and the
login source’s name, so it is a fact other modules read rather
than a knob. The glue that registers this client wherever
authelia runs reads it from here instead of restating the
format.
⚠️ With services.hyperhive.swarm.forge.rootUrl unset,
ROOT_URL follows the gateway’s httpsPort, which is
per-host. An authelia host that is not the forge’s host renders
the forge’s callback only if the two agree on it; set rootUrl
if they do not.
Type: string (read only)
Default:
"${ROOT_URL}user/oauth2/authelia/callback"
Declared by:
services.hyperhive.swarm.grafana.domain
Name the gateway serves this on. A sibling of the swarm’s other
service names, so the swarm-services sub-CA can issue for it — see
hive-tls.nix for why a service name being a sibling rather than a
child decides which CA may sign it.
⚠️ Changing this changes the OAuth redirect URI, which authelia matches exactly. Both sides move together because both derive from this option; an operator who pins one by hand breaks the login.
Type: string
Default:
"grafana.${services.hyperhive.swarm.domain}"
Declared by:
services.hyperhive.swarm.grafana.machine
Container name. Read-only: the name appears in host paths and in
machinectl, so it is a fact other modules may read rather than a
knob.
Type: string (read only)
Default:
"swarm-grafana"
Declared by:
services.hyperhive.swarm.grafana.metricsPort
Loopback port on which the gateway’s nginx re-serves Grafana’s
/metrics, and nothing else, so the swarm’s collector can scrape it.
⚠️ This is nginx’s port, not Grafana’s. Grafana still claims none —
see services.hyperhive.deploy.grafana.socketDir for why that
matters. A prometheus scrape target is a host:port, and it cannot
address a unix socket; rather than undo the socket decision, the one
endpoint a scraper needs gets a listener of its own.
Bound to loopback and unauthenticated, which is the same posture every
other entry in
services.hyperhive.swarm.otel.scrapeTargets has: those
targets are trusted by proximity rather than by credential.
Deliberately not the published grafana.<domain> vhost, which
would put an authorization decision in front of a scrape: that vhost
answers /metrics with 403, because Grafana serves it without
authentication.
The number itself is arbitrary and free today;
state/eval-port-collisions.sh is what keeps it that way, since a
second claim on a port in this shared namespace produces no bind error
and nothing in any log.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
9095
Declared by:
services.hyperhive.swarm.grafana.oidc.clientId
The authelia OIDC client id. Names the application rather than
the protocol, per the convention in
services.hyperhive.swarm.authelia.oidc.clients.
Type: string
Default:
"swarm-grafana"
Declared by:
services.hyperhive.swarm.grafana.oidc.redirectUri
OAuth callback authelia sends the browser back to, and the URI it matches exactly.
Read-only, like services.hyperhive.swarm.grafana.machine
and for the same reason: Grafana derives it from its own
root_url (<root_url>/login/generic_oauth), so it is a fact
other modules may read rather than a knob. The glue that registers
this client wherever authelia runs reads it from here instead of
restating the format — a second spelling of it is a silently
rejected login.
Type: string (read only)
Default:
"https://${services.hyperhive.swarm.grafana.domain}/login/generic_oauth"
Declared by:
services.hyperhive.swarm.grafana.oidc.role
Grafana org role every SSO user is assigned.
Admin by default, and that is a considered default rather than
a permissive one: the login form is disabled whenever SSO is
configured, so this is the only way anyone reaches Grafana —
a Viewer default would produce a swarm nobody can administer.
Passing authelia already means being an operator of this swarm;
its user store is the small, swarmctl-managed one.
Lower it if a swarm ever grows read-only operators, which is a one-line change here.
Type: one of “Viewer”, “Editor”, “Admin”
Default:
"Admin"
Example:
"Editor"
Declared by:
services.hyperhive.swarm.hives
Every hive in this swarm, keyed by hiveName — including this
host’s own hive. The same attrset is meant to be identical on
every host in the swarm, so it can be written once and shared;
services.hyperhive.hiveName is what makes a given host read it
as “me and four others” rather than “five peers”.
Each entry needs no fields at all in the conventional case: a
hive’s domain defaults to <name>.<swarm.domain>, so the whole
directory is usually a list of names.
It must contain an entry for hiveName, which is asserted — this
host’s own address is read out of it (it is where
services.hyperhive.domain derives from), and a hive that lists
everyone but itself would otherwise derive its own peer set as
everything and peer with itself.
Type: attribute set of (submodule)
Default:
{ }
Example:
{
edge = {
domain = "edge.elsewhere.example";
wireguardAddress = "10.100.0.2/32";
};
pr1ma = {
wireguardAddress = "10.100.0.1/32";
wireguardEndpoint = "203.0.113.1:51820";
};
}
Declared by:
services.hyperhive.swarm.hives.<name>.domain
Public DNS domain this hive occupies — used for
swarm-controller’s hive roster, agent identity
(qualified agent@domain labels), and Matrix federation
discovery.
Defaults to <name>.<swarm.domain>, the convention every
hive in a swarm follows, so a conventional directory is
names only. Set it for a hive that is addressed by
something else.
Type: string
Default:
"${name}.${services.hyperhive.swarm.domain}"
Example:
"lab.example.com"
Declared by:
services.hyperhive.swarm.hives.<name>.wireguardAddress
IP address (with prefix) of this hive’s host on the
WireGuard mesh. Used as the allowedIPs for its
WireGuard config entry (./swarm-wireguard.nix), so
intra-swarm traffic can route over the mesh address
rather than the public domain. Required to include
a hive in the mesh (entries missing this field are
silently excluded from wg-hive).
Type: null or string
Default:
null
Example:
"10.100.0.2/32"
Declared by:
services.hyperhive.swarm.hives.<name>.wireguardEndpoint
WireGuard endpoint for this hive in host:port form.
Null = this hive has no reachable endpoint, so the tunnel
is initiated from the other side.
Reads like a fact about the relationship and is not: it says whether this hive can be dialled, which every other hive in the swarm needs the same answer to.
Type: null or string
Default:
null
Example:
"203.0.113.1:51820"
Declared by:
services.hyperhive.swarm.hives.<name>.wireguardPublicKey
WireGuard public key for this hive’s host. Required when
services.hyperhive.deploy.wireguard.enable = true and
you want this hive reachable over the mesh. Null = TLS-
only peering (public internet, no mesh tunnel).
Type: null or string
Default:
null
Example:
"base64pubkey="
Declared by:
services.hyperhive.swarm.matrix.adminSync.ignoreUsers
Full matrix user ids swarm-controller’s operator admin sync never
revokes homeserver admin from. The sync revokes admin only from
accounts it made admin itself, once their owner leaves authelia’s
admins group; an account listed here keeps admin regardless.
Type: list of string matching the pattern @[^:[:space:]]+:[^[:space:]]+
Default:
[ ]
Example:
[
"@mara:example.org"
]
Declared by:
services.hyperhive.swarm.matrix.allowEncryption
Server-side switch for matrix end-to-end encryption — sets
tuwunel’s allow_encryption. Off by default: on the hive-internal
homeserver the operator already controls the transport, so server
E2EE adds key-management overhead (cross-signing, device
verification, undecryptable-message recovery) without a clear
threat-model win for the common single-hive case. Turn on when
agents join encrypted rooms on external / federated homeservers,
or when the operator wants message contents opaque to the
homeserver admin. Independent of the agent matrix client, which
always supports decryption so it can read encrypted rooms it is
invited to regardless of this flag; this option only governs
whether THIS homeserver permits room encryption.
Type: boolean
Default:
false
Declared by:
services.hyperhive.swarm.matrix.apiUrl
Client-server API base URL hive-c0re itself uses to
provision matrix (register agent users, create the hive space
and chat room, invite members). Distinct from the agent-facing
hyperhive.matrix.url, which is the gateway vhost handed to
each agent’s hive-matrix-daemon.
Defaults to https:// on the homeserver’s gateway vhost
(gatewayHost), the same name on every hive whichever host runs
tuwunel, and null when there is no vhost. hive-c0re logs in
with the hive’s matrix sender token over this URL, so it crosses
the network whenever hive-c0re is not on the homeserver’s host:
an http:// value sends that token in cleartext. hive-c0re
verifies the gateway’s certificate against the hive trust bundle
(system CAs plus the swarm and services roots).
null means hive-c0re has no homeserver to provision against
and matrix provisioning no-ops. There is deliberately no
fallback compiled into the daemon: an address baked into the
binary is one that builds fine and then talks to the wrong
machine.
Type: null or string
Default:
if services.hyperhive.swarm.matrix.gatewayHost == null then null
else "https://${services.hyperhive.swarm.matrix.gatewayHost}"
Example:
"https://matrix.example.com"
Declared by:
services.hyperhive.swarm.matrix.gatewayHost
Public hostname for the matrix homeserver behind the gateway.
Defaults to chat.${services.hyperhive.swarm.domain} — the
swarm’s domain, because a swarm runs one homeserver. Set to
null to skip the gateway vhost (tuwunel stays direct on
httpPort). See docs/networking/gateway.md for the vhost map + matrix
discovery flow, and the federation port-8448 caveat at the
bottom of that doc.
⚠️ gatewayHost and serverName are different things, and
they carry very different costs. gatewayHost is the API
listener hostname (where nginx proxies /_matrix/*) and is
free to change: it is a routing detail clients rediscover
through .well-known. serverName is the matrix-identifier
domain embedded irrevocably in every user and room id —
adopting a new one is a different homeserver, not a rename.
Both defaults sit under the swarm domain, but only this
one is safe to change on a running deployment.
To use the per-hive name matrix.${services.hyperhive.domain}
instead, pin it here.
Type: null or string
Default:
"chat.${services.hyperhive.swarm.domain}"
Example:
"matrix.example.com"
Declared by:
services.hyperhive.swarm.matrix.httpPort
TCP port tuwunel serves the matrix client-server API on.
Default 8008 is the matrix-spec well-known port. Sits
outside hyperhive’s claimed ranges (dashboard 7000, every
agent in 8100…8999 via FNV-1a hash). Federation listens on
federationPort separately.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
8008
Declared by:
services.hyperhive.swarm.matrix.serverName
Matrix server_name — the host part of every user ID
(@argus:<server_name>) and room ID minted on this
homeserver. CRITICAL: must be stable from day one because
it’s embedded irrevocably in the identifiers.
Defaults to services.hyperhive.swarm.domain (the bare swarm
domain), because a swarm runs one homeserver — tying its
identity to a single hive’s domain would make relocating the
container between hives look like a different homeserver.
Combined with the .well-known/matrix/{client,server} routes
the gateway serves at that domain, clients auto-discover the
actual matrix endpoint without needing a subdomain. Override
here only if you need a different server_name shape (e.g.
chat.example.org for a bespoke hostname).
Changing this cannot be undone by rebuilding. The default
is the swarm domain, so a homeserver that minted ids under
another value (matrix.${services.hyperhive.domain} or the
bare hive domain) must pin that value, e.g.
services.hyperhive.swarm.matrix.serverName =
config.services.hyperhive.domain; # or "matrix.${…domain}"
before rebuilding. Adopting a new server_name does not rename
the old users and rooms — it strands them, because their ids
still name a homeserver that no longer answers.
Type: null or string
Default:
null
Example:
"chat.example.org"
Declared by:
services.hyperhive.swarm.matrix.sso.clientId
OAuth2 client id this homeserver identifies itself with. The host running authelia registers it, whichever host runs the homeserver.
The homeserver’s host reads the secret it pairs with out of the
swarm secret store, into
services.hyperhive.deploy.matrix.sso.clientSecretFile.
Type: string
Default:
"tuwunel"
Declared by:
services.hyperhive.swarm.matrix.sso.redirectUri
SSO callback authelia sends the browser back to, and the URI it matches exactly. Format-locked by tuwunel: the callback host must point directly at the homeserver, and the path is fixed.
Read-only: the homeserver’s config and its authelia client entry
both read it from here. null when gatewayHost is, because the
homeserver then has no name a browser can reach.
Type: null or string (read only)
Default:
"https://${services.hyperhive.swarm.matrix.gatewayHost}/_matrix/client/unstable/login/sso/callback/${services.hyperhive.swarm.matrix.sso.clientId}"
Declared by:
services.hyperhive.swarm.name
Human-readable name of the wider swarm this hive belongs to.
Hives at different DNS domains can share a swarm name when
they federate together. Exposed to agents as
HYPERHIVE_SWARM_NAME; surfaced in the dashboard chrome and
per-agent system prompt when set.
Type: null or string
Default:
null
Example:
"constellat1on"
Declared by:
services.hyperhive.swarm.nats.clientId
OAuth2 client id the queue’s authentication path identifies itself with. The host running authelia registers it, whichever host runs the queue, and the queue’s host reads its secret out of the swarm secret store.
Type: string
Default:
"swarm-nats"
Declared by:
services.hyperhive.swarm.nats.domain
Name every client reaches the queue on, as
tls://<domain>:<port>, and the only name its certificate carries.
A sibling of the swarm’s other service names, for the reason
services.hyperhive.swarm.bao.domain gives.
The host running the queue answers it through gateway.localNames.
Every other hive resolves it through the operator’s upstream DNS,
which is where a multi-host swarm needs a record for it.
Not in services.hyperhive.swarm.serviceDomains: that list
is the names a gateway vhost fronts, and nginx fronts nothing here.
Type: string
Default:
"nats.${services.hyperhive.swarm.domain}"
Declared by:
services.hyperhive.swarm.nats.metricsPort
Port the Prometheus exporter serves NATS’s metrics on, bound to loopback for the swarm collector to scrape.
⚠️ This and monitorPort are two more claims on a port
space every swarm container shares — they run in this container but
privateNetwork = false, so a collision with any other hyperhive
service is a runtime coin toss over which process gets the port,
with nothing in any log saying so. Both defaults are upstream’s
own (nats-server 8222, prometheus-nats-exporter 7777) and
neither is claimed elsewhere in this repo, checked when they were
added.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
7777
Declared by:
services.hyperhive.swarm.nats.monitorPort
Port NATS serves its monitoring endpoint on, bound to loopback.
Not a metrics endpoint: the server has no Prometheus format of its
own. This serves /varz, /connz, /routez as JSON, and the
exporter below is what translates it — which is why enabling the
exporter without this produces a process that starts cleanly and
scrapes nothing.
⚠️ Loopback, and the exporter is the only intended reader. The
endpoint is unauthenticated and /connz names every connected
client, so the address it binds is the whole access control. Do
not widen it, and do not put it behind a gateway vhost expecting
that to add one.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
8222
Declared by:
services.hyperhive.swarm.nats.port
TCP port the queue listens on. 4222 is upstream’s default and sits outside hyperhive’s claimed ranges (dashboard 7000, forge 3000, matrix 8008, every agent in 8100…8999 via FNV-1a hash).
Opened on the bridge interface, for agent containers, and on
wg-hive when this host is on the mesh, for other hives. Never
host-wide. TLS only: a client that does not speak it is refused.
Unlike monitorPort and metricsPort, the
address is not what bounds who may use this port: the queue’s
auth_callout refuses every client it cannot identify.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
4222
Declared by:
services.hyperhive.swarm.otel.audience
Every audience this collector’s OAuth2 client is permitted to present a token for — the scrape targets it reads with a credential, plus the stores it pushes to. Published read-only so ./glue-swarm-otel-oidc-client.nix can register the client wherever authelia runs without restating the derivation: this option and that registration are the same fact seen from two hosts, and a second formula for it would be free to drift from this one.
Type: list of string (read only)
Default:
[
"https://auth.invalid/metrics"
"https://forge.invalid/metrics"
"https://logs.invalid/metrics"
"https://metrics.invalid/metrics"
"https://logs.invalid/insert/opentelemetry/v1/logs"
"https://metrics.invalid/opentelemetry/api/v1/push"
]
Declared by:
services.hyperhive.swarm.otel.clientId
OAuth2 client id this collector authenticates as. Published so
authelia’s access_control rules can name it without carrying a
second copy of the string, exactly as
services.hyperhive.swarm.authelia.hiveClientPrefix is published
for the queue’s responder.
Two spellings drifting apart is not a build failure: authelia refuses a rule naming an unregistered client in its startup validator, so the swarm’s SSO service fails to restart — long after the change that caused it evaluated cleanly.
Type: string (read only)
Default:
"swarm-collector"
Declared by:
services.hyperhive.swarm.otel.domain
Name the gateway serves this on. A sibling of the swarm’s other
service names, so the swarm-services sub-CA can issue for it — see
hive-tls.nix for why a service name being a sibling rather than a
child decides which CA may sign it.
This is what the hive tier’s exporter reaches — the hive collector is a plain producer against this name exactly like every other client of a swarm service, resolved locally by dnsmasq on a co-located host and over the real network otherwise. There is no separate loopback-vs-remote knob to get wrong: everything in this swarm addresses its siblings by name.
Type: string
Default:
"otel.${services.hyperhive.swarm.domain}"
Declared by:
services.hyperhive.swarm.otel.machine
Name of the nixos-container this collector runs in — also the
machinectl name, so other modules may read it rather than
repeating the literal.
Type: string (read only)
Default:
"swarm-otel"
Declared by:
services.hyperhive.swarm.otel.port
First port of this collector’s receiver range. Every hive in
services.hyperhive.swarm.hives gets its own
authenticated receiver — that is what makes the hive label
unforgeable — so the range is one port per hive, starting here, in
sorted-name order.
⚠️ Internal. No client is ever told a port: a hive reaches its own
receiver as https://${domain}/<hive>, and the gateway routes on
that path. So adding a hive, which renumbers the ones after it, is
harmless — nginx is rendered from this same evaluation and moves
with it.
⚠️ Deliberately not 4318, the OTLP/HTTP default, because the
hive tier already uses it (services.hyperhive.otel.collector.port)
and every swarm container shares the host’s network namespace. Two
listeners claiming one port on one host is not a build failure —
it is a runtime coin toss over which one gets it, with nothing in
any log saying so. On the host running this collector, an
assertion rejects a derived range that overlaps any other port
this module or the hive tier declares, which is as far as a module
can see.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
4319
Declared by:
services.hyperhive.swarm.otel.producerName
The swarm-tier producer’s own component/path/authenticator name
— the same reserved literal this module’s collector components
(otlp/swarm, oidc/swarm, resource/swarm, metrics/swarm)
are already named after internally. Published as an option so a
sibling module (glue-controller-env.nix) can address the receiver
by name — https://${domain}/${producerName}/ — instead of
repeating the string. Read-only for the same reason
clientId/machine/domain are: two spellings of a name that
has to match on both ends is a mismatch waiting to happen.
Type: string (read only)
Default:
"swarm"
Declared by:
services.hyperhive.swarm.otel.producerPort
Port the swarm tier’s own OTLP/HTTP receiver for a
swarm-level producer (swarm-controller’s vcs/jobq counters
today) listens on, at 127.0.0.1. Reached through the gateway at
https://${domain}/${producerName}/, same shape as a hive’s
own receiver — a swarm-level producer may not share a host with
this collector (the swarm services need not run on the same host
as the swarm controller), so loopback-only reachability is not a
supported shape here, as it is not for a hive.
Authenticated by oidc/${producerName}, checking for the
audience services.hyperhive.swarm.controller.queueClientId
requests — not the per-hive hiveClientPrefix namespace, because
a swarm-level producer is explicitly not a hive (see that
option’s own doc comment). The swarm label this receiver’s
samples get is still the one the receiver’s own existence
establishes, same as before — there is no hive identity being
forged or attributed here, just a caller proving it is the one
principal allowed to push into this pipeline.
⚠️ Deliberately NOT derived from port + (number of hives): that
range grows every time a hive is added, and a fixed offset from
it would silently start colliding once the hive count caught up.
Kept as its own reserved value instead, and the port-collision
assertion still rejects a real collision (including one hive
growth eventually causes) rather than starting a collector that
quietly drops one receiver’s samples.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
4390
Declared by:
services.hyperhive.swarm.otel.publishedScrapeTargets
Prometheus endpoints this collector scrapes by name, with a
credential, as <job name> = "<url>".
Entries are declared swarm-wide: from this module’s config,
unconditionally, keyed on the service’s swarm.<svc>.domain, so
every host evaluates the same set. Unlike scrapeTargets, an entry
is not declared under the service’s own enable: the collector
reads this list on its host and authelia’s registration reads it on
authelia’s host, and neither need be the service’s host. A swarm
that does not run the service anywhere scrapes a name that answers
nothing, which shows as a failing job rather than a missing one.
The collector’s OAuth2 client derives its permitted audiences from these URLs, and authelia derives from each one the access rule that admits only that client to it, so a target and the authorisation to reach it are one declaration. Two lists that must agree would be a drift to maintain, and its failure mode is the bad one: a target whose audience was forgotten authenticates against nothing and reads as a scrape failure rather than a config mistake.
⚠️ A full URL, not host:port. Authelia validates a bearer
token against the address being requested, so the string here is
also the audience the token is minted for; a near miss (a trailing
slash, http for https) presents as a valid token rejected at
the target, several layers from its cause.
⚠️ Deliberately separate from scrapeTargets. An entry there is
trusted because the scraper and the target share a host — that
option is loopback-and-unauthenticated by contract. An entry here
is trusted because it presents a credential. One shape for both
would leave a reader unable to tell which of those a given target
relies on.
Type: attribute set of string
Default:
{ }
Example:
{ forge = "https://forge.example.com/metrics"; }
Declared by:
services.hyperhive.swarm.otel.scrapeTargets
Prometheus exposition endpoints this collector scrapes, as
<job name> = "<host>:<port>".
A path and query may follow the port —
"127.0.0.1:8202/v1/sys/metrics?format=prometheus" — for an
exporter that does not serve /metrics. Both are optional and
omitted when absent, so a bare host:port is scraped exactly as
before.
A service declares its own entry, from its own module, under its
own enable. Do not assemble the list here: an entry then exists
only where the service that named it runs, so a target is never
declared on a host that does not serve it. An entry that also needs
the collector on the same host (the loopback address is only honest
then) is declared from a glue module instead, which reads both
switches: grafana and nats are declared by
glue-otel-scrape-targets.nix.
⚠️ That constrains the TARGET, not the SCRAPER, and the difference
is a silent gap. Nothing here puts the collector on the same host.
Services are co-located by default — swarm-required-services.nix
derives their enable flags from one lib.mkDefault — not by
construction, and an operator may split them.
When they are split the target is simply never scraped: the service’s host declares an entry no local collector reads, and the collector’s host never enabled that service so has no entry at all. No error, no eval failure, no warning.
No assertion can catch this. Two hosts are separate NixOS evaluations with no shared context, so neither can see what the other runs. A service that would be seriously wrong to lose should say so in its own contribution, because saying it is the only mechanism available.
Samples land in a swarm-level pipeline that stamps swarm and
never hive: a swarm service does not belong to a hive, and
hive stays a property of which authenticated receiver accepted a
push, not something a scrape can acquire.
Empty by default, in which case no scrape receiver, processor or pipeline is emitted at all — an enabled scraper with nothing to scrape is the inert configuration this option exists to avoid.
Type: attribute set of string
Default:
{ }
Example:
{ forgejo = "127.0.0.1:3000"; }
Declared by:
services.hyperhive.swarm.otel.storeProducerName
Path and component name of the store forwarder’s own receiver
(otlp/${storeProducerName}, oidc/${storeProducerName}).
Published so swarm-bao.nix addresses the receiver by name rather
than repeating the string, exactly as producerName above is
published for glue-controller-env.nix.
Read-only and derived from the store’s container name: two spellings of a name both ends have to agree on is a 404 on a request that authenticated perfectly.
Type: string (read only)
Default:
services.hyperhive.swarm.bao.machine
Declared by:
services.hyperhive.swarm.otel.storeProducerPort
Port the receiver for the secret store’s own forwarder listens
on, at 127.0.0.1. Reached through the gateway at
https://${domain}/${storeProducerName}/, the same
path-per-producer shape a hive and the swarm-tier producer above
both use.
Its own receiver, and not a second sender into
services.hyperhive.swarm.otel.producerPort, because an
oidc authenticator checks exactly ONE audience: that receiver
admits the audience swarm-controller registers for itself, and
the store’s forwarder is a different principal with a client of its
own. Sharing the route would mean sharing that client — which is
the thing the store exists to make unnecessary.
⚠️ A reserved value rather than an offset from
services.hyperhive.swarm.otel.port, for the reason stated
on producerPort: that range grows with the hive count and would
eventually walk into any fixed offset from it. The port-collision
assertion covers this port too.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
4391
Declared by:
services.hyperhive.swarm.otel.telemetryPort
Port this collector serves its own metrics on — queue depth, refused and dropped samples, exporter failures. How you find out that telemetry is being lost, so it is worth keeping rather than switching off.
⚠️ Deliberately not 8889’s neighbour 8888, which is the
collector’s built-in default and therefore what the hive tier
already binds. Two collectors share a network namespace whenever
they are co-located, and unlike the OTLP port this one appears
nowhere in either config — it is a default inside the binary, so
nothing that compares configured ports can see the clash. The
second collector to start simply dies with
bind: address already in use.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
8889
Declared by:
services.hyperhive.swarm.review-classifier.clientId
The OAuth2 client id the review classifier presents to the swarm queue. Read-only: the host running authelia registers it, the queue’s auth-callout responder grants it the try-out subject, and the secret publisher carries its secret to the store, all from this one value.
Type: string (read only)
Default:
"swarm-review-classifier"
Declared by:
services.hyperhive.swarm.snapshotStore.address
Mesh address of the swarm’s snapshot store — the single
btrfs receive endpoint every hive in this swarm pushes agent
snapshots to. Bare IP, no prefix.
There is exactly one store per swarm, not one per peer: the receiver keys destinations by agent, so an agent that migrates between hives keeps a single unbroken incremental chain. Per-hive stores would split that chain in two, which is the case the store exists to serve.
Null means this swarm has no store configured, and pushing fails
saying so rather than guessing an address. Set it on every hive
that pushes; the receiving host separately sets
services.hyperhive.snapshotStore.enable.
Type: null or string
Default:
null
Example:
"10.100.0.1"
Declared by:
services.hyperhive.swarm.snapshotStore.port
TCP port the swarm’s snapshot store listens on. Must match the
receiving host’s services.hyperhive.snapshotStore.port.
Defaulted (unlike address) because it is a shared convention
both sides read from the same option docs — whereas an address
is deployment-specific and cannot be guessed.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
51821
Declared by:
services.hyperhive.swarm.statusPublish.tokenEndpoint
The swarm IdP’s OAuth2 token endpoint. This hive mints a
client_credentials access token there and presents it when
connecting to the queue, which authenticates it as
hive-<hiveName> — the client
nix/host-modules/swarm-authelia.nix already declares for
every entry in services.hyperhive.swarm.hives.
Type: null or string
Default:
"${swarm.authelia.url}/api/oidc/token"
Example:
"https://auth.example.com/api/oidc/token"
Declared by:
services.hyperhive.swarm.victorialogs.domain
Name the gateway serves this on, behind the same authelia
auth_request gate as the swarm UI’s own vhost. A sibling of the
swarm’s other service names, so the swarm-services sub-CA can
issue for it — see hive-tls.nix for why a service name being a
sibling rather than a child decides which CA may sign it, and
swarm.nix’s serviceDomains' for where this name has to be
registered for that to actually happen.
Type: string
Default:
"logs.${services.hyperhive.swarm.domain}"
Declared by:
services.hyperhive.swarm.victorialogs.machine
Container name. Read-only: the name appears in host paths and in
machinectl, so it is a fact other modules may read rather than a
knob.
Type: string (read only)
Default:
"swarm-victorialogs"
Declared by:
services.hyperhive.swarm.victorialogs.port
Loopback port the store listens on. Upstream’s default, kept because there is no reason to move it and a familiar number is one less thing an operator has to look up.
⚠️ Every swarm container shares the host’s network namespace, so
this is a swarm-wide claim rather than a per-container one — two
modules picking the same number collide at runtime with no bind
error and nothing in any log. state/eval-port-collisions.sh
checks the class.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
9428
Declared by:
services.hyperhive.swarm.victoriametrics.domain
Name the gateway serves this on. A sibling of the swarm’s other
service names, so the swarm-services sub-CA can issue for it — see
hive-tls.nix for why a service name being a sibling rather than a
child decides which CA may sign it.
Type: string
Default:
"metrics.${services.hyperhive.swarm.domain}"
Declared by:
services.hyperhive.swarm.victoriametrics.machine
Container name. Read-only: the name appears in host paths and in
machinectl, so it is a fact other modules may read rather than a
knob.
Type: string (read only)
Default:
"swarm-victoriametrics"
Declared by: