Watch
0
0
Fork
You've already forked hyperhive
0

deploy: move the queue toggle

Same shape as the others. Three of the eight references were prose
rather than code: two assertion messages naming the option to an
operator, and a `defaultText` in the authelia module, whose
`oidc.hiveIdentities` derives from whether the queue runs here.

Also corrects a comment in swarm.nix that this commit's own hunk sits
directly beneath. It said `enableRequiredServices` turns on matrix and
authelia "but NOT nats (nats has no mode that enables it)", which stopped
being true when the queue was added to that switch — before this change,
not by it. The reasoning it introduces is still right and still worth
keeping; only the premise had expired.
This commit is contained in:
atlas 2026-08-30 03:31:32 +02:00 • committed by mara
commit 64e8c62f18
7 changed files with 45 additions and 30 deletions

View file

@ -57,6 +57,10 @@ in
[ "services" "hyperhive" "swarm" "authelia" "enable" ] [ "services" "hyperhive" "swarm" "authelia" "enable" ]
[ "services" "hyperhive" "deploy" "authelia" ] [ "services" "hyperhive" "deploy" "authelia" ]
) )
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "nats" "enable" ]
[ "services" "hyperhive" "deploy" "nats" ]
)
]; ];
options.services.hyperhive.deploy = { options.services.hyperhive.deploy = {
@ -119,6 +123,19 @@ in
''; '';
}; };
nats = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Run the swarm's message queue in a `swarm-nats` container on this
host. A swarm has one queue, so this belongs on the same host as
the rest of the shared services.
Off by default, and off means *absent*: no container is created
and nothing else in the evaluated config changes.
'';
};
controller = lib.mkOption { controller = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false; default = false;

View file

@ -95,7 +95,7 @@ in
# ran its first boot on — so they belong to the mode that asserts this # ran its first boot on — so they belong to the mode that asserts this
# box is the whole deployment, not to the options' own `default`. # box is the whole deployment, not to the options' own `default`.
# #
# Deriving them from `swarm.nats.enable` / `deploy.authelia` # Deriving them from `deploy.nats` / `deploy.authelia`
# inside those defaults is the mixing this file exists to prevent: the # inside those defaults is the mixing this file exists to prevent: the
# option would be describing a deployment shape instead of describing # option would be describing a deployment shape instead of describing
# itself, and "what does all-local turn on?" would stop having one # itself, and "what does all-local turn on?" would stop having one

View file

@ -467,8 +467,8 @@ in
oidc.hiveIdentities = lib.mkOption { oidc.hiveIdentities = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = hyperhiveCfg.swarm.nats.enable; default = deployCfg.nats;
defaultText = lib.literalExpression "services.hyperhive.swarm.nats.enable"; defaultText = lib.literalExpression "services.hyperhive.deploy.nats";
description = '' description = ''
Mint one machine client per hive in Mint one machine client per hive in
{option}`services.hyperhive.swarm.hives`, so each hive can {option}`services.hyperhive.swarm.hives`, so each hive can

View file

@ -539,7 +539,7 @@ in
services.hyperhive.swarm.controller.queue.natsUrl is unset. services.hyperhive.swarm.controller.queue.natsUrl is unset.
It defaults to loopback only when this host also runs the queue It defaults to loopback only when this host also runs the queue
(`services.hyperhive.swarm.nats.enable`). A controller on its (`services.hyperhive.deploy.nats`). A controller on its
own host has to be told where the queue is. own host has to be told where the queue is.
''; '';
} }

View file

@ -49,7 +49,7 @@ let
hostPath = name: "${secretDir}/${name}"; hostPath = name: "${secretDir}/${name}";
# The responder needs all three credentials. Gating on them rather than # The responder needs all three credentials. Gating on them rather than
# on `cfg.enable` keeps a half-configured hive at "queue up, denying # on `deployCfg.nats` keeps a half-configured hive at "queue up, denying
# everyone" instead of "unit crash-looping on a missing file". # everyone" instead of "unit crash-looping on a missing file".
# #
# In auto mode the seeds are minted on this host before the container # In auto mode the seeds are minted on this host before the container
@ -210,18 +210,9 @@ in
# per-service vhost pattern do not apply. # per-service vhost pattern do not apply.
options.services.hyperhive.swarm.nats = { options.services.hyperhive.swarm.nats = {
enable = lib.mkOption { # `enable` moved to `services.hyperhive.deploy.nats` — see
type = lib.types.bool; # ./deploy.nix. What stays here is what the queue IS: its domain,
default = false; # ports, accounts and callout wiring.
description = ''
Run the swarm's message queue in a `swarm-nats` container on this
host. A swarm has one queue, so this belongs on the same host as
the rest of the shared services.
Off by default, and off means *absent*: no container is created
and nothing else in the evaluated config changes.
'';
};
# ⚠️ Deliberately NO `package` option, unlike this module's siblings. # ⚠️ Deliberately NO `package` option, unlike this module's siblings.
# `services.nats` upstream does not expose one — it resolves # `services.nats` upstream does not expose one — it resolves
@ -408,7 +399,7 @@ in
}; };
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf deployCfg.nats {
# The responder as well as the server: a denial reaches the client as a # The responder as well as the server: a denial reaches the client as a
# timeout, so the server's own log is the only place it is an error. # timeout, so the server's own log is the only place it is an error.
services.hyperhive.swarm.otel.journaldUnits = [ services.hyperhive.swarm.otel.journaldUnits = [
@ -423,7 +414,7 @@ in
# several layers from "the operator never set the issuer". # several layers from "the operator never set the issuer".
assertion = cfg.autoGenerateCallout || cfg.calloutIssuerPublicKey != ""; assertion = cfg.autoGenerateCallout || cfg.calloutIssuerPublicKey != "";
message = '' message = ''
services.hyperhive.swarm.nats.enable requires services.hyperhive.deploy.nats requires
nats.calloutIssuerPublicKey — the public half of the account nats.calloutIssuerPublicKey — the public half of the account
nkey that signs user JWTs for this queue. nkey that signs user JWTs for this queue.
@ -440,7 +431,7 @@ in
# somebody connects. # somebody connects.
assertion = cfg.autoGenerateCallout || cfg.calloutUserPublicKey != ""; assertion = cfg.autoGenerateCallout || cfg.calloutUserPublicKey != "";
message = '' message = ''
services.hyperhive.swarm.nats.enable requires services.hyperhive.deploy.nats requires
nats.calloutUserPublicKey — the public half of the user nkey nats.calloutUserPublicKey — the public half of the user nkey
the auth-callout responder authenticates as. the auth-callout responder authenticates as.
@ -485,7 +476,7 @@ in
{ {
assertion = autheliaUrl != null; assertion = autheliaUrl != null;
message = '' message = ''
services.hyperhive.swarm.nats.enable requires services.hyperhive.deploy.nats requires
services.hyperhive.swarm.authelia.url — the queue authenticates services.hyperhive.swarm.authelia.url — the queue authenticates
clients by validating tokens that authelia issued. clients by validating tokens that authelia issued.
@ -684,7 +675,7 @@ in
# door nobody can open, which is the deliberate interim state. # door nobody can open, which is the deliberate interim state.
# #
# ⚠️ It is gated on the seeds being configured rather than on # ⚠️ It is gated on the seeds being configured rather than on
# `cfg.enable`, so a half-configured hive gets a running, # `deployCfg.nats`, so a half-configured hive gets a running,
# refusing queue instead of a unit that crash-loops on a # refusing queue instead of a unit that crash-loops on a
# missing file. A queue that denies everyone is a legible # missing file. A queue that denies everyone is a legible
# failure; a restart loop is not. # failure; a restart loop is not.

View file

@ -60,7 +60,6 @@ in
# derives `nats.autoGenerateCallout` from the all-local mode, so that # derives `nats.autoGenerateCallout` from the all-local mode, so that
# mode was minting the queue's callout nkeys and then never starting # mode was minting the queue's callout nkeys and then never starting
# the queue they authenticate against. # the queue they authenticate against.
nats.enable = lib.mkDefault swarmCfg.enableRequiredServices;
# The metrics pair and the log store used to derive here too. They now # The metrics pair and the log store used to derive here too. They now
# live under `deploy.*` (below, and ./deploy.nix) because "does THIS # live under `deploy.*` (below, and ./deploy.nix) because "does THIS
@ -89,6 +88,9 @@ in
# at whoever runs it. # at whoever runs it.
config.services.hyperhive.deploy.authelia = lib.mkDefault swarmCfg.enableRequiredServices; config.services.hyperhive.deploy.authelia = lib.mkDefault swarmCfg.enableRequiredServices;
# The queue. Same rule: once per swarm, optional.
config.services.hyperhive.deploy.nats = lib.mkDefault swarmCfg.enableRequiredServices;
# The metrics pair, deriving together on purpose: a store with no UI is # The metrics pair, deriving together on purpose: a store with no UI is
# unreadable and a UI with no store is empty, so there is no sensible # unreadable and a UI with no store is empty, so there is no sensible
# deployment that takes one and not the other from this switch. An # deployment that takes one and not the other from this switch. An

View file

@ -80,18 +80,23 @@ let
# ONE condition for all three of them, deliberately. # ONE condition for all three of them, deliberately.
# #
# 🩸 They were three independent conditions first, and that was wrong in # 🩸 They were three independent conditions first, and that was wrong in
# a way only an eval gate finds: `enableRequiredServices` turns on # a way only an eval gate finds: at the time `enableRequiredServices`
# matrix and authelia but NOT nats (nats has no mode that enables it — # turned on matrix and authelia but NOT nats, so an ordinary all-local
# see the auto-deploy question on the swarm-queue issue), so an ordinary # hive resolved authelia's two coordinates and not the queue URL. Two of
# all-local hive resolved authelia's two coordinates and not the queue # three set is exactly what the assertion below rejects, so every
# URL. Two of three set is exactly what the assertion below rejects, so # `enableAllLocalDefaults` hive would have stopped evaluating.
# every `enableAllLocalDefaults` hive would have stopped evaluating. #
# (The queue does derive from that switch now — ./swarm-required-services.nix
# — so the original asymmetry is gone. The reasoning below is why the
# single predicate stays anyway: it makes the partial state
# unrepresentable rather than merely detected, which does not depend on
# which services happen to derive together this month.)
# #
# Deriving all three from one predicate makes the partial state # Deriving all three from one predicate makes the partial state
# unrepresentable rather than merely detected: a default set is all or # unrepresentable rather than merely detected: a default set is all or
# nothing, and the assertion is then only ever about what an operator # nothing, and the assertion is then only ever about what an operator
# typed. # typed.
queueLocal = swarmCfg.nats.enable && deployCfg.authelia && cfg.hiveName != null; queueLocal = deployCfg.nats && deployCfg.authelia && cfg.hiveName != null;
in in
{ {
options.services.hyperhive.swarm.hives = lib.mkOption { options.services.hyperhive.swarm.hives = lib.mkOption {