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:

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: