nix: gate hive-c0re on deploy.hive-controller.enable, drop hyperhive.enable
`services.hyperhive.enable` and `services.hyperhive.c0re.enable` are gone. One switch, `services.hyperhive.deploy.hive-controller.enable` (default false, as the old toggle was), now gates hive-c0re and hive-priv. Both old paths are `mkRenamedOptionModule` shims in deploy.nix, so a host config that still sets either evaluates as before and gets a rename warning. Every other read of the old toggle is resolved, including the 29 made through the `hyperhiveCfg`/`hiveCfg` aliases: - Dropped: each swarm service and its glue keeps only its own deploy toggle (authelia, bao and its PKI glue, grafana, victorialogs, victoriametrics, the secret publisher, swarm-ca, the OIDC client rows, the controller/nats/matrix-ctl/publisher/services-issuer identities), the forge, and the `domain` deprecation warning. - To deploy.hive-controller.enable: the queue-agent credential reader and its assertion, which feed hive-c0re and write under its state dir, plus their policy-order entry; the network identity assertions; hive-tls's two writes into hive-c0re's environment. - hive-tls runs where the gateway runs self-signed (`gateway.enable && useSelfSigned`), not on every host. - The matrix appservice-token reader and its assertion stay on `deploy.matrix.enable` plus their client-identity checks. They read deploy.matrix's token file and registration script; their deploy.bao inputs are the client-half options a hive sets to read a store it does not run, so gating on deploy.bao.enable would drop the tested remote-reader case. - The `hiveName` assertion moves from hive-network.nix to hyperhive.nix and fires wherever the hive, the store or the homeserver runs: each turns the name into an identifier with no fallback. On a host with `deploy.allSwarmServices` and no hive, the documented services-host recipe, authelia, bao, grafana, victorialogs, victoriametrics, the OIDC client rows and the hive CA now render; before, the old toggle being off left them out. Refs #4500
This commit is contained in:
parent
ed2ec52fe5
commit
3202cde704
43 changed files with 292 additions and 162 deletions
|
|
@ -77,7 +77,7 @@ Minimal `flake.nix` for a host that runs hive-c0re:
|
||||||
modules = [
|
modules = [
|
||||||
hyperhive.nixosModules.default # hive-c0re + hive-forge + hive-gateway in one import
|
hyperhive.nixosModules.default # hive-c0re + hive-forge + hive-gateway in one import
|
||||||
({ ... }: {
|
({ ... }: {
|
||||||
services.hyperhive.enable = true;
|
services.hyperhive.deploy.hive-controller.enable = true;
|
||||||
# services.hyperhive.c0re.operatorPronouns = "they/them"; # default: "she/her"
|
# services.hyperhive.c0re.operatorPronouns = "they/them"; # default: "she/her"
|
||||||
|
|
||||||
# ... rest of your host config
|
# ... rest of your host config
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ listener on `bridgeIp` is on the host's bridge interface.
|
||||||
```nix
|
```nix
|
||||||
{
|
{
|
||||||
services.hyperhive = {
|
services.hyperhive = {
|
||||||
enable = true;
|
deploy.hive-controller.enable = true;
|
||||||
hiveName = "pr1ma";
|
hiveName = "pr1ma";
|
||||||
swarm.domain = "darkest.space";
|
swarm.domain = "darkest.space";
|
||||||
swarm.hives.pr1ma = { }; # -> domain = pr1ma.darkest.space
|
swarm.hives.pr1ma = { }; # -> domain = pr1ma.darkest.space
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ The store host is a swarm member like any other: it gets an entry in
|
||||||
[swarm/](../swarm/README.md) for the mesh itself.
|
[swarm/](../swarm/README.md) for the mesh itself.
|
||||||
|
|
||||||
Note that `deploy.wireguard.enable` gates the mesh, **not**
|
Note that `deploy.wireguard.enable` gates the mesh, **not**
|
||||||
`c0re.enable` --- a store host runs no hive and would otherwise get no
|
`deploy.hive-controller.enable` --- a store host runs no hive and would otherwise get no
|
||||||
`wg-hive` interface at all.
|
`wg-hive` interface at all.
|
||||||
|
|
||||||
## Pointing a hive at it
|
## Pointing a hive at it
|
||||||
|
|
|
||||||
|
|
@ -299,9 +299,9 @@ migrating agent keeps one unbroken incremental chain. See
|
||||||
|
|
||||||
`services.hyperhive.deploy.swarm-controller.enable` runs the `swarm-controller`
|
`services.hyperhive.deploy.swarm-controller.enable` runs the `swarm-controller`
|
||||||
daemon on this host. **Off by default and deliberately not derived from
|
daemon on this host. **Off by default and deliberately not derived from
|
||||||
`services.hyperhive.enable`**: a swarm has one controller, so enabling it
|
`services.hyperhive.deploy.hive-controller.enable`**: a swarm has one
|
||||||
is a statement about swarm topology, not about whether the operator has
|
controller, so enabling it states a fact about swarm topology, not about
|
||||||
installed hyperhive. Every hive runs `hive-c0re` (the agents on that host); one
|
whether this host runs a hive. Every hive runs `hive-c0re` (the agents on that host); one
|
||||||
hive additionally runs this (what's true across hives).
|
hive additionally runs this (what's true across hives).
|
||||||
|
|
||||||
What it serves, why it's a unix socket rather than a port, and the
|
What it serves, why it's a unix socket rather than a port, and the
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@
|
||||||
# an operator override still wins. Intended usage:
|
# an operator override still wins. Intended usage:
|
||||||
#
|
#
|
||||||
# imports = [ hyperhive.nixosModules.default ];
|
# imports = [ hyperhive.nixosModules.default ];
|
||||||
# services.hyperhive.enable = true;
|
# services.hyperhive.deploy.hive-controller.enable = true;
|
||||||
#
|
#
|
||||||
default =
|
default =
|
||||||
{ lib, pkgs, ... }:
|
{ lib, pkgs, ... }:
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ let
|
||||||
};
|
};
|
||||||
boot.loader.grub.enable = false;
|
boot.loader.grub.enable = false;
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "25.11";
|
||||||
services.hyperhive.enable = lib.mkForce false;
|
services.hyperhive.deploy.hive-controller.enable = lib.mkForce false;
|
||||||
services.hyperhive.deploy.matrix.enable = lib.mkForce false;
|
services.hyperhive.deploy.matrix.enable = lib.mkForce false;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
# The full hyperhive host stack, pulled together in one place — this
|
# The full hyperhive host stack, pulled together in one place — this
|
||||||
# is what the flake exports as `nixosModules.default` (wrapped with
|
# is what the flake exports as `nixosModules.default` (wrapped with
|
||||||
# the package/source wiring; see flake.nix). One import covers
|
# the package/source wiring; see flake.nix). One import covers
|
||||||
# everything; `services.hyperhive.enable = true` turns the stack on.
|
# everything; `services.hyperhive.deploy.hive-controller.enable = true`
|
||||||
|
# runs a hive on this host.
|
||||||
#
|
#
|
||||||
# Every swarm needs the forge — hive-c0re mirrors every agent's applied
|
# Every swarm needs the forge — hive-c0re mirrors every agent's applied
|
||||||
# config repo into it and it's the canonical store for the meta flake
|
# config repo into it and it's the canonical store for the meta flake
|
||||||
|
|
@ -9,7 +10,7 @@
|
||||||
# runs it (derived from `deploy.allSwarmServices`, see
|
# runs it (derived from `deploy.allSwarmServices`, see
|
||||||
# ./swarm-required-services.nix). hive-matrix is opt-in (off by default). All
|
# ./swarm-required-services.nix). hive-matrix is opt-in (off by default). All
|
||||||
# subsystems rely on `services.hyperhive.domain`, which is required
|
# subsystems rely on `services.hyperhive.domain`, which is required
|
||||||
# (asserted in hive-network.nix) whenever hyperhive is enabled.
|
# (asserted in hive-network.nix) wherever this host runs a hive.
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./hyperhive.nix
|
./hyperhive.nix
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,15 @@ in
|
||||||
[ "services" "hyperhive" "swarm" "statusPublish" "clientSecretFile" ]
|
[ "services" "hyperhive" "swarm" "statusPublish" "clientSecretFile" ]
|
||||||
[ "services" "hyperhive" "deploy" "hive-controller" "statusPublish" "clientSecretFile" ]
|
[ "services" "hyperhive" "deploy" "hive-controller" "statusPublish" "clientSecretFile" ]
|
||||||
)
|
)
|
||||||
|
# Both old spellings of "run a hive here" forward to the one switch.
|
||||||
|
(lib.mkRenamedOptionModule
|
||||||
|
[ "services" "hyperhive" "enable" ]
|
||||||
|
[ "services" "hyperhive" "deploy" "hive-controller" "enable" ]
|
||||||
|
)
|
||||||
|
(lib.mkRenamedOptionModule
|
||||||
|
[ "services" "hyperhive" "c0re" "enable" ]
|
||||||
|
[ "services" "hyperhive" "deploy" "hive-controller" "enable" ]
|
||||||
|
)
|
||||||
(lib.mkRenamedOptionModule
|
(lib.mkRenamedOptionModule
|
||||||
[ "services" "hyperhive" "swarm" "otel" "enable" ]
|
[ "services" "hyperhive" "swarm" "otel" "enable" ]
|
||||||
[ "services" "hyperhive" "deploy" "swarm-otel" "enable" ]
|
[ "services" "hyperhive" "deploy" "swarm-otel" "enable" ]
|
||||||
|
|
@ -452,9 +461,9 @@ in
|
||||||
Run the swarm's metrics UI on this host.
|
Run the swarm's metrics UI on this host.
|
||||||
|
|
||||||
Off by default and not derived from
|
Off by default and not derived from
|
||||||
{option}`services.hyperhive.enable`: a swarm has one Grafana, so
|
{option}`services.hyperhive.deploy.hive-controller.enable`: a swarm
|
||||||
running it is a decision about this host rather than about
|
has one Grafana, so running it is a decision about this host rather
|
||||||
whether hyperhive is installed.
|
than about whether it runs a hive.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -570,6 +579,21 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
hive-controller.enable = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
example = true;
|
||||||
|
description = ''
|
||||||
|
Run a hive on this host: the `hive-c0re` coordinator that owns every
|
||||||
|
agent container here, its `hive-priv` helper, and the bridge,
|
||||||
|
resolver and gateway the agents hang off.
|
||||||
|
|
||||||
|
Every hive turns this on. A host that only runs the swarm's shared
|
||||||
|
services ({option}`services.hyperhive.deploy.allSwarmServices`)
|
||||||
|
leaves it off.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
swarm-controller.enable = lib.mkOption {
|
swarm-controller.enable = lib.mkOption {
|
||||||
type = lib.types.bool;
|
type = lib.types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
|
|
@ -577,9 +601,9 @@ in
|
||||||
Run the swarm-controller daemon on this host.
|
Run the swarm-controller daemon on this host.
|
||||||
|
|
||||||
Off by default and deliberately not derived from
|
Off by default and deliberately not derived from
|
||||||
{option}`services.hyperhive.enable`: a swarm has one controller,
|
{option}`services.hyperhive.deploy.hive-controller.enable`: a swarm
|
||||||
so running it is a decision about this host rather than about
|
has one controller, so running it is a decision about this host
|
||||||
whether hyperhive is installed.
|
rather than about whether it runs a hive.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,22 +31,21 @@ let
|
||||||
readersHere = {
|
readersHere = {
|
||||||
# ./glue-matrix-bao-token.nix
|
# ./glue-matrix-bao-token.nix
|
||||||
swarm-bao-matrix-token =
|
swarm-bao-matrix-token =
|
||||||
hyperhiveCfg.enable
|
havePair baoDeploy.matrixTokenClientCertFile baoDeploy.matrixTokenClientKeyFile
|
||||||
&& havePair baoDeploy.matrixTokenClientCertFile baoDeploy.matrixTokenClientKeyFile
|
|
||||||
&& deployCfg.matrix.enable;
|
&& deployCfg.matrix.enable;
|
||||||
# ./glue-queue-agent-credential.nix
|
# ./glue-queue-agent-credential.nix
|
||||||
swarm-bao-queue-agent =
|
swarm-bao-queue-agent =
|
||||||
hyperhiveCfg.enable
|
deployCfg.hive-controller.enable
|
||||||
&& havePair baoDeploy.queueAgentClientCertFile baoDeploy.queueAgentClientKeyFile;
|
&& havePair baoDeploy.queueAgentClientCertFile baoDeploy.queueAgentClientKeyFile;
|
||||||
# ./swarm-grafana.nix
|
# ./swarm-grafana.nix
|
||||||
swarm-bao-grafana-oidc = hyperhiveCfg.enable && deployCfg.grafana.enable;
|
swarm-bao-grafana-oidc = deployCfg.grafana.enable;
|
||||||
# ./swarm-otel.nix
|
# ./swarm-otel.nix
|
||||||
swarm-bao-otel-oidc =
|
swarm-bao-otel-oidc =
|
||||||
deployCfg.swarm-otel.enable
|
deployCfg.swarm-otel.enable
|
||||||
&& havePair baoDeploy.otelOidcClientCertFile baoDeploy.otelOidcClientKeyFile;
|
&& havePair baoDeploy.otelOidcClientCertFile baoDeploy.otelOidcClientKeyFile;
|
||||||
# ./swarm-bao.nix: its block is gated on the store, which `orderAfterPolicy`
|
# ./swarm-bao.nix: its block is gated on the store, which `orderAfterPolicy`
|
||||||
# already checks.
|
# already checks.
|
||||||
swarm-bao-forwarder-oidc = hyperhiveCfg.enable;
|
swarm-bao-forwarder-oidc = true;
|
||||||
# ./swarm-nats.nix: the queue's TLS leaf, not a secret, but the same wait.
|
# ./swarm-nats.nix: the queue's TLS leaf, not a secret, but the same wait.
|
||||||
swarm-bao-nats-tls = deployCfg.nats.enable;
|
swarm-bao-nats-tls = deployCfg.nats.enable;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -43,9 +43,10 @@ let
|
||||||
|
|
||||||
# What a reader calls itself to the store. The hive's name, because a bao
|
# What a reader calls itself to the store. The hive's name, because a bao
|
||||||
# cert-auth role matches on the CN — this is an interface, not a label.
|
# cert-auth role matches on the CN — this is an interface, not a label.
|
||||||
# No fallback: `hiveName` is asserted set for every hyperhive host, which is
|
# No fallback: `hiveName` is asserted set wherever `deploy.bao.enable` is
|
||||||
# the same condition this file's `config` is gated on. A fallback here reads
|
# (./hyperhive.nix), the same condition this file's `config` is gated on. A
|
||||||
# as a second supported spelling and there is no such thing.
|
# fallback here reads as a second supported spelling and there is no such
|
||||||
|
# thing.
|
||||||
clientCn = hyperhiveCfg.hiveName;
|
clientCn = hyperhiveCfg.hiveName;
|
||||||
|
|
||||||
# $1 dir $2 basename $3 CN $4 SAN or "" $5 EKU
|
# $1 dir $2 basename $3 CN $4 SAN or "" $5 EKU
|
||||||
|
|
@ -71,7 +72,7 @@ let
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf (hyperhiveCfg.enable && deployCfg.bao.enable) {
|
config = lib.mkIf deployCfg.bao.enable {
|
||||||
services.hyperhive.deploy.bao = {
|
services.hyperhive.deploy.bao = {
|
||||||
serverCertFile = lib.mkDefault "${pkiDir}/server.pem";
|
serverCertFile = lib.mkDefault "${pkiDir}/server.pem";
|
||||||
serverKeyFile = lib.mkDefault "${pkiDir}/server-key.pem";
|
serverKeyFile = lib.mkDefault "${pkiDir}/server-key.pem";
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ let
|
||||||
pkiDir = if haveMintedPki then builtins.dirOf baoDeploy.clientCertFile else null;
|
pkiDir = if haveMintedPki then builtins.dirOf baoDeploy.clientCertFile else null;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf (hyperhiveCfg.enable && deployCfg.swarm-controller.enable && haveMintedPki) {
|
config = lib.mkIf (deployCfg.swarm-controller.enable && haveMintedPki) {
|
||||||
services.hyperhive.deploy.swarm-controller = {
|
services.hyperhive.deploy.swarm-controller = {
|
||||||
baoClientCertFile = lib.mkDefault "${pkiDir}/controller.pem";
|
baoClientCertFile = lib.mkDefault "${pkiDir}/controller.pem";
|
||||||
baoClientKeyFile = lib.mkDefault "${pkiDir}/controller-key.pem";
|
baoClientKeyFile = lib.mkDefault "${pkiDir}/controller-key.pem";
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ let
|
||||||
forgeCfg = hyperhiveCfg.swarm.forge;
|
forgeCfg = hyperhiveCfg.swarm.forge;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf (hyperhiveCfg.enable && deployCfg.authelia.enable) {
|
config = lib.mkIf deployCfg.authelia.enable {
|
||||||
# One declaration, two readers. The forge knows its own callback URL;
|
# One declaration, two readers. The forge knows its own callback URL;
|
||||||
# making the operator restate it in authelia's client list would be a
|
# making the operator restate it in authelia's client list would be a
|
||||||
# second source of truth for a string whose mismatch is a silent
|
# second source of truth for a string whose mismatch is a silent
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ let
|
||||||
grafanaCfg = hyperhiveCfg.swarm.grafana;
|
grafanaCfg = hyperhiveCfg.swarm.grafana;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf (hyperhiveCfg.enable && deployCfg.authelia.enable) {
|
config = lib.mkIf deployCfg.authelia.enable {
|
||||||
# One declaration, two readers. Grafana's callback URL is format-locked to
|
# One declaration, two readers. Grafana's callback URL is format-locked to
|
||||||
# its own root URL, and the read-only option it is taken from is where that
|
# its own root URL, and the read-only option it is taken from is where that
|
||||||
# format is spelled — restating it here would be a second source of truth
|
# format is spelled — restating it here would be a second source of truth
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,8 @@ let
|
||||||
# pieces; this literal is the nix half of that one agreement.
|
# pieces; this literal is the nix half of that one agreement.
|
||||||
#
|
#
|
||||||
# `hiveName` has no fallback here for the reason ./glue-bao-tls.nix gives at
|
# `hiveName` has no fallback here for the reason ./glue-bao-tls.nix gives at
|
||||||
# its own use of it: it is asserted set for every hyperhive host.
|
# its own use of it: it is asserted set on every host that runs the
|
||||||
|
# homeserver.
|
||||||
tokenPath = "secret/swarm/hives/${hyperhiveCfg.hiveName}/matrix/appservice-token";
|
tokenPath = "secret/swarm/hives/${hyperhiveCfg.hiveName}/matrix/appservice-token";
|
||||||
|
|
||||||
# A literal, not an option — ./hive-matrix.nix names its container
|
# A literal, not an option — ./hive-matrix.nix names its container
|
||||||
|
|
@ -94,7 +95,7 @@ in
|
||||||
# is a hive that has no store, which is supported. So this one additionally
|
# is a hive that has no store, which is supported. So this one additionally
|
||||||
# requires `hiveReaderIdentity`: the host demonstrably reads the store, and
|
# requires `hiveReaderIdentity`: the host demonstrably reads the store, and
|
||||||
# named every option but this pair.
|
# named every option but this pair.
|
||||||
(lib.mkIf (hyperhiveCfg.enable && deployCfg.matrix.enable && hiveReaderIdentity) {
|
(lib.mkIf (deployCfg.matrix.enable && hiveReaderIdentity) {
|
||||||
assertions = [
|
assertions = [
|
||||||
{
|
{
|
||||||
assertion = haveClientIdentity;
|
assertion = haveClientIdentity;
|
||||||
|
|
@ -124,7 +125,7 @@ in
|
||||||
];
|
];
|
||||||
})
|
})
|
||||||
|
|
||||||
(lib.mkIf (hyperhiveCfg.enable && haveClientIdentity && deployCfg.matrix.enable) {
|
(lib.mkIf (haveClientIdentity && deployCfg.matrix.enable) {
|
||||||
# Same rule as the unit's own gate: this reader exists on a host that has a
|
# Same rule as the unit's own gate: this reader exists on a host that has a
|
||||||
# client identity and a homeserver, which is not every host that runs the
|
# client identity and a homeserver, which is not every host that runs the
|
||||||
# store, so the store's module cannot name it.
|
# store, so the store's module cannot name it.
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ let
|
||||||
pkiDir = if haveMintedPki then builtins.dirOf baoDeploy.clientCertFile else null;
|
pkiDir = if haveMintedPki then builtins.dirOf baoDeploy.clientCertFile else null;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf (hyperhiveCfg.enable && deployCfg.matrix.enable && haveMintedPki) {
|
config = lib.mkIf (deployCfg.matrix.enable && haveMintedPki) {
|
||||||
services.hyperhive.deploy.matrix = {
|
services.hyperhive.deploy.matrix = {
|
||||||
ctlBaoClientCertFile = lib.mkDefault "${pkiDir}/matrix-ctl.pem";
|
ctlBaoClientCertFile = lib.mkDefault "${pkiDir}/matrix-ctl.pem";
|
||||||
ctlBaoClientKeyFile = lib.mkDefault "${pkiDir}/matrix-ctl-key.pem";
|
ctlBaoClientKeyFile = lib.mkDefault "${pkiDir}/matrix-ctl-key.pem";
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ let
|
||||||
pkiDir = if haveMintedPki then builtins.dirOf baoDeploy.clientCertFile else null;
|
pkiDir = if haveMintedPki then builtins.dirOf baoDeploy.clientCertFile else null;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf (hyperhiveCfg.enable && deployCfg.nats.enable && haveMintedPki) {
|
config = lib.mkIf (deployCfg.nats.enable && haveMintedPki) {
|
||||||
services.hyperhive.deploy.nats = {
|
services.hyperhive.deploy.nats = {
|
||||||
baoClientCertFile = lib.mkDefault "${pkiDir}/nats.pem";
|
baoClientCertFile = lib.mkDefault "${pkiDir}/nats.pem";
|
||||||
baoClientKeyFile = lib.mkDefault "${pkiDir}/nats-key.pem";
|
baoClientKeyFile = lib.mkDefault "${pkiDir}/nats-key.pem";
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,10 @@
|
||||||
# into one that is refused, which reaches the agent as a timeout.
|
# into one that is refused, which reaches the agent as a timeout.
|
||||||
#
|
#
|
||||||
# 📌 This runs wherever a client identity is configured, NOT only where the
|
# 📌 This runs wherever a client identity is configured, NOT only where the
|
||||||
# store is — the rule ./glue-matrix-bao-token.nix states in full. There is no
|
# store is — the rule ./glue-matrix-bao-token.nix states in full — and
|
||||||
# "this hive runs agents" condition to gate it on as well: agent containers
|
# wherever this host runs a hive (`deploy.hive-controller.enable`): the
|
||||||
# are created at runtime by hive-c0re, so every hyperhive host is a host that
|
# credential is for the agents hive-c0re creates here at runtime, and the unit
|
||||||
# may run one.
|
# writes under hive-c0re's own state directory.
|
||||||
{
|
{
|
||||||
pkgs,
|
pkgs,
|
||||||
lib,
|
lib,
|
||||||
|
|
@ -74,7 +74,8 @@ let
|
||||||
# one agreement.
|
# one agreement.
|
||||||
#
|
#
|
||||||
# `hiveName` has no fallback here for the reason ./glue-bao-tls.nix gives at
|
# `hiveName` has no fallback here for the reason ./glue-bao-tls.nix gives at
|
||||||
# its own use of it: it is asserted set for every hyperhive host.
|
# its own use of it: it is asserted set wherever
|
||||||
|
# `deploy.hive-controller.enable` is.
|
||||||
credentialPath = "secret/swarm/hives/${hyperhiveCfg.hiveName}/queue/agent";
|
credentialPath = "secret/swarm/hives/${hyperhiveCfg.hiveName}/queue/agent";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|
@ -110,11 +111,11 @@ in
|
||||||
#
|
#
|
||||||
# Shaped after ./swarm-grafana.nix's `haveClientIdentity` assertion — the
|
# Shaped after ./swarm-grafana.nix's `haveClientIdentity` assertion — the
|
||||||
# same refusal, named to this principal's own pair. Where Grafana's gate
|
# same refusal, named to this principal's own pair. Where Grafana's gate
|
||||||
# is `deploy.grafana.enable`, this reader has no toggle of its own to
|
# is `deploy.grafana.enable`, this reader's is the hive's own: every hive
|
||||||
# check: every hive runs one for its own agents, so reading the store at
|
# runs one for its own agents, so on a hive, reading the store at all is
|
||||||
# all is what asks for it. Hence `hiveReaderIdentity` alone — a host
|
# what asks for it. Hence `hiveReaderIdentity` — a host holding no hive
|
||||||
# holding no hive leaf has no store to read and nothing is missing.
|
# leaf has no store to read and nothing is missing.
|
||||||
(lib.mkIf (hyperhiveCfg.enable && hiveReaderIdentity) {
|
(lib.mkIf (deployCfg.hive-controller.enable && hiveReaderIdentity) {
|
||||||
assertions = [
|
assertions = [
|
||||||
{
|
{
|
||||||
assertion = haveClientIdentity;
|
assertion = haveClientIdentity;
|
||||||
|
|
@ -149,7 +150,7 @@ in
|
||||||
];
|
];
|
||||||
})
|
})
|
||||||
|
|
||||||
(lib.mkIf (hyperhiveCfg.enable && haveClientIdentity) {
|
(lib.mkIf (deployCfg.hive-controller.enable && haveClientIdentity) {
|
||||||
# Same rule as the unit's own gate: this reader exists on any host holding
|
# Same rule as the unit's own gate: this reader exists on any host holding
|
||||||
# a client identity, which is not every host that runs the store, so the
|
# a client identity, which is not every host that runs the store, so the
|
||||||
# store's module cannot name it.
|
# store's module cannot name it.
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,7 @@ let
|
||||||
pkiDir = if haveMintedPki then builtins.dirOf baoDeploy.clientCertFile else null;
|
pkiDir = if haveMintedPki then builtins.dirOf baoDeploy.clientCertFile else null;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config =
|
config = lib.mkIf (deployCfg.swarm-secret-publisher.enable && haveMintedPki) {
|
||||||
lib.mkIf (hyperhiveCfg.enable && deployCfg.swarm-secret-publisher.enable && haveMintedPki)
|
|
||||||
{
|
|
||||||
services.hyperhive.deploy.swarm-secret-publisher = {
|
services.hyperhive.deploy.swarm-secret-publisher = {
|
||||||
baoClientCertFile = lib.mkDefault "${pkiDir}/secret-publisher.pem";
|
baoClientCertFile = lib.mkDefault "${pkiDir}/secret-publisher.pem";
|
||||||
baoClientKeyFile = lib.mkDefault "${pkiDir}/secret-publisher-key.pem";
|
baoClientKeyFile = lib.mkDefault "${pkiDir}/secret-publisher-key.pem";
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ let
|
||||||
pkiDir = if haveMintedPki then builtins.dirOf baoDeploy.clientCertFile else null;
|
pkiDir = if haveMintedPki then builtins.dirOf baoDeploy.clientCertFile else null;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf (hyperhiveCfg.enable && haveMintedPki) {
|
config = lib.mkIf haveMintedPki {
|
||||||
services.hyperhive.deploy.hive-controller.tls = {
|
services.hyperhive.deploy.hive-controller.tls = {
|
||||||
baoClientCertFile = lib.mkDefault "${pkiDir}/services-issuer.pem";
|
baoClientCertFile = lib.mkDefault "${pkiDir}/services-issuer.pem";
|
||||||
baoClientKeyFile = lib.mkDefault "${pkiDir}/services-issuer-key.pem";
|
baoClientKeyFile = lib.mkDefault "${pkiDir}/services-issuer-key.pem";
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ let
|
||||||
baoCfg = hyperhiveCfg.swarm.bao;
|
baoCfg = hyperhiveCfg.swarm.bao;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf (hyperhiveCfg.enable && deployCfg.authelia.enable) {
|
config = lib.mkIf deployCfg.authelia.enable {
|
||||||
# One declaration, two readers: `clientId` is a read-only option
|
# One declaration, two readers: `clientId` is a read-only option
|
||||||
# ./swarm-bao.nix owns, and ./swarm-otel.nix builds this principal's
|
# ./swarm-bao.nix owns, and ./swarm-otel.nix builds this principal's
|
||||||
# authenticator audience from the same option.
|
# authenticator audience from the same option.
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ let
|
||||||
otelCfg = hyperhiveCfg.swarm.otel;
|
otelCfg = hyperhiveCfg.swarm.otel;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf (hyperhiveCfg.enable && deployCfg.authelia.enable) {
|
config = lib.mkIf deployCfg.authelia.enable {
|
||||||
# One declaration, two readers: `clientId` and `audience` are read-only
|
# One declaration, two readers: `clientId` and `audience` are read-only
|
||||||
# options ./swarm-otel.nix derives from the scrape/push targets it owns,
|
# options ./swarm-otel.nix derives from the scrape/push targets it owns,
|
||||||
# so this file states neither formula a second time.
|
# so this file states neither formula a second time.
|
||||||
|
|
|
||||||
|
|
@ -153,11 +153,11 @@ in
|
||||||
name = "hive-c0re";
|
name = "hive-c0re";
|
||||||
consumers = [ "hive-c0re" ];
|
consumers = [ "hive-c0re" ];
|
||||||
hostUnit = true;
|
hostUnit = true;
|
||||||
enable = cfg.enable;
|
enable = config.services.hyperhive.deploy.hive-controller.enable;
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf config.services.hyperhive.deploy.hive-controller.enable {
|
||||||
# This module is the host's only knowledge that agent containers
|
# This module is the host's only knowledge that agent containers
|
||||||
# exist: they hang off the bridge, resolve through dnsmasq, and their
|
# exist: they hang off the bridge, resolve through dnsmasq, and their
|
||||||
# UIs — plus the operator dashboard — are served by the gateway.
|
# UIs — plus the operator dashboard — are served by the gateway.
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,10 @@
|
||||||
{
|
{
|
||||||
pkgs,
|
pkgs,
|
||||||
lib,
|
lib,
|
||||||
config,
|
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
options.services.hyperhive.c0re = {
|
options.services.hyperhive.c0re = {
|
||||||
enable = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = config.services.hyperhive.enable;
|
|
||||||
defaultText = lib.literalExpression "config.services.hyperhive.enable";
|
|
||||||
description = "Enable hive-c0re coordinator daemon (auto-enabled by services.hyperhive.enable).";
|
|
||||||
};
|
|
||||||
package = lib.mkOption {
|
package = lib.mkOption {
|
||||||
type = lib.types.package;
|
type = lib.types.package;
|
||||||
defaultText = lib.literalExpression "hyperhive.packages.\${system}.default";
|
defaultText = lib.literalExpression "hyperhive.packages.\${system}.default";
|
||||||
|
|
|
||||||
|
|
@ -459,7 +459,7 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf (config.services.hyperhive.enable && deployCfg.forgejo.enable) {
|
config = lib.mkIf deployCfg.forgejo.enable {
|
||||||
# Same principle as the vhost below — this service's own surface lives
|
# Same principle as the vhost below — this service's own surface lives
|
||||||
# with the service. The SSO source is named alongside forgejo because
|
# with the service. The SSO source is named alongside forgejo because
|
||||||
# its failure mode is a login that silently falls back, not an error.
|
# its failure mode is a login that silently falls back, not an error.
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,8 @@ let
|
||||||
# saying so, as the token path below.
|
# saying so, as the token path below.
|
||||||
#
|
#
|
||||||
# `hiveName` has no fallback here for the reason ./glue-matrix-bao-token.nix
|
# `hiveName` has no fallback here for the reason ./glue-matrix-bao-token.nix
|
||||||
# gives at its own use of it: it is asserted set for every hyperhive host.
|
# gives at its own use of it: it is asserted set on every host that runs the
|
||||||
|
# homeserver.
|
||||||
hiveLocalpart = "hive-${toString config.services.hyperhive.hiveName}";
|
hiveLocalpart = "hive-${toString config.services.hyperhive.hiveName}";
|
||||||
|
|
||||||
# The `as_token`, and the `hs_token` the spec requires alongside it. Both
|
# The `as_token`, and the `hs_token` the spec requires alongside it. Both
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ in
|
||||||
imports = [
|
imports = [
|
||||||
(lib.mkRemovedOptionModule [ "services" "hyperhive" "network" "isolateContainers" ] ''
|
(lib.mkRemovedOptionModule [ "services" "hyperhive" "network" "isolateContainers" ] ''
|
||||||
Network isolation is the only mode and is always on whenever
|
Network isolation is the only mode and is always on whenever
|
||||||
hyperhive is enabled; the shared-netns path was removed. Remove
|
the hive runs; the shared-netns path was removed. Remove
|
||||||
the setting.
|
the setting.
|
||||||
'')
|
'')
|
||||||
(lib.mkRemovedOptionModule [ "services" "hyperhive" "network" "upstreamDns" ] ''
|
(lib.mkRemovedOptionModule [ "services" "hyperhive" "network" "upstreamDns" ] ''
|
||||||
|
|
@ -148,11 +148,12 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkMerge [
|
config = lib.mkMerge [
|
||||||
# Identity checks belong to hyperhive being on, not to the bridge
|
# Identity checks belong to the hive being on, not to the bridge
|
||||||
# being up: a hive with no domain is misconfigured either way, and
|
# being up: a hive with no domain is misconfigured either way, and
|
||||||
# moving them under the bridge gate would hide them on exactly the
|
# moving them under the bridge gate would hide them on exactly the
|
||||||
# hosts that are hardest to debug.
|
# hosts that are hardest to debug. `hiveName` is asserted in
|
||||||
(lib.mkIf config.services.hyperhive.enable {
|
# ./hyperhive.nix, where more than the hive depends on it.
|
||||||
|
(lib.mkIf config.services.hyperhive.deploy.hive-controller.enable {
|
||||||
# This message is only useful if an operator can actually reach
|
# This message is only useful if an operator can actually reach
|
||||||
# it, and an assertion competes with every eager default that
|
# it, and an assertion competes with every eager default that
|
||||||
# reads the value it guards: option defaults that interpolate the
|
# reads the value it guards: option defaults that interpolate the
|
||||||
|
|
@ -196,16 +197,6 @@ in
|
||||||
`services.hyperhive.domain` for you.
|
`services.hyperhive.domain` for you.
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
{
|
|
||||||
assertion = config.services.hyperhive.hiveName != null;
|
|
||||||
message = ''
|
|
||||||
hyperhive requires services.hyperhive.hiveName to be set —
|
|
||||||
it is this hive's label within the swarm, and the leftmost
|
|
||||||
part of the domain it is addressed by
|
|
||||||
(`<hiveName>.<swarm.domain>`), not only a display name.
|
|
||||||
Set it (`services.hyperhive.hiveName = "pr1ma";`).
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ let
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf config.services.hyperhive.deploy.hive-controller.enable {
|
||||||
# Socket unit for hive-priv — the narrow root helper that executes
|
# Socket unit for hive-priv — the narrow root helper that executes
|
||||||
# privileged operations on behalf of hive-c0re. Systemd creates and
|
# privileged operations on behalf of hive-c0re. Systemd creates and
|
||||||
# holds `/run/hive/priv.sock` before the first connection arrives.
|
# holds `/run/hive/priv.sock` before the first connection arrives.
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,11 @@ let
|
||||||
|
|
||||||
# The host-managed hive CA is the trust anchor for self-signed mode.
|
# The host-managed hive CA is the trust anchor for self-signed mode.
|
||||||
# It is only stood up when the gateway actually serves a self-signed
|
# It is only stood up when the gateway actually serves a self-signed
|
||||||
# cert: the gateway must be in self-signed mode. `domain` is required
|
# cert: the gateway must run here and be in self-signed mode. The
|
||||||
# (asserted in hive-network.nix), so the leaf SANs always have a
|
# self-signed condition is the gateway module's single source of truth
|
||||||
# domain to derive from. The self-signed condition is the gateway
|
# (`gateway.useSelfSigned`): true when neither an operator cert
|
||||||
# module's single source of truth (`gateway.useSelfSigned`): true when
|
# (`tls.certDir`) nor ACME is set.
|
||||||
# neither an operator cert (`tls.certDir`) nor ACME is set.
|
active = gatewayCfg.enable && gatewayCfg.useSelfSigned;
|
||||||
active = hyperhiveCfg.enable && gatewayCfg.useSelfSigned;
|
|
||||||
|
|
||||||
# How this hive's CA comes into existence when it is missing — and it
|
# How this hive's CA comes into existence when it is missing — and it
|
||||||
# is one of exactly two things, chosen by config rather than by what
|
# is one of exactly two things, chosen by config rather than by what
|
||||||
|
|
@ -1022,7 +1021,14 @@ in
|
||||||
# bundle. It is the ANCHOR bundle rather than `ca.pem` for the reason
|
# bundle. It is the ANCHOR bundle rather than `ca.pem` for the reason
|
||||||
# spelled out where the bundle is written above; no key path is ever
|
# spelled out where the bundle is written above; no key path is ever
|
||||||
# exposed (an agent that could read one could mint trusted certs).
|
# exposed (an agent that could read one could mint trusted certs).
|
||||||
systemd.services.hive-c0re.environment.HIVE_TLS_CA_PATH = "${cfg.stateDir}/trust-bundle.pem";
|
#
|
||||||
|
# ⚠️ Every line here that names another daemon's unit is gated on that
|
||||||
|
# daemon's own enable: the gateway also runs on hosts with no hive and no
|
||||||
|
# controller, and defining an environment key on a unit that does not
|
||||||
|
# exist CREATES a unit fragment for it — inert (no `ExecStart`, empty
|
||||||
|
# `wantedBy`, never activated), and it evaluates and builds clean.
|
||||||
|
systemd.services.hive-c0re.environment.HIVE_TLS_CA_PATH =
|
||||||
|
lib.mkIf hyperhiveCfg.deploy.hive-controller.enable "${cfg.stateDir}/trust-bundle.pem";
|
||||||
|
|
||||||
# The same anchor, named for the swarm-queue clients that need it when
|
# The same anchor, named for the swarm-queue clients that need it when
|
||||||
# they mint a token from authelia over TLS. Declared HERE, beside the
|
# they mint a token from authelia over TLS. Declared HERE, beside the
|
||||||
|
|
@ -1038,18 +1044,11 @@ in
|
||||||
# variable name. The anchor was never missing; nothing pointed the queue
|
# variable name. The anchor was never missing; nothing pointed the queue
|
||||||
# client at it.
|
# client at it.
|
||||||
#
|
#
|
||||||
# Set unconditionally within this module's `active` guard, exactly like
|
# Where there is no hive CA this module contributes nothing at all, and
|
||||||
# the line above: where there is no hive CA this module contributes
|
# the clients then fall back to the platform roots — which is correct for
|
||||||
# nothing at all, and the clients then fall back to the platform roots —
|
# a swarm fronted by a public certificate.
|
||||||
# which is correct for a swarm fronted by a public certificate.
|
systemd.services.hive-c0re.environment.HIVE_C0RE_OIDC_CA_FILE =
|
||||||
systemd.services.hive-c0re.environment.HIVE_C0RE_OIDC_CA_FILE = "${cfg.stateDir}/trust-bundle.pem";
|
lib.mkIf hyperhiveCfg.deploy.hive-controller.enable "${cfg.stateDir}/trust-bundle.pem";
|
||||||
# ⚠️ Gated, where the hive-c0re line above is not, and the asymmetry is
|
|
||||||
# the point: hive-c0re runs on every hive, the controller runs on one.
|
|
||||||
# Defining an environment key on a unit that does not exist CREATES a
|
|
||||||
# unit fragment for it — inert (no `ExecStart`, empty `wantedBy`, never
|
|
||||||
# activated) but present on every non-controller hive with a CA. Caught
|
|
||||||
# in review on this PR; it evaluates and builds clean either way, which
|
|
||||||
# is exactly why it needed a reviewer rather than a check.
|
|
||||||
systemd.services.swarm-controller.environment.SWARM_CONTROLLER_OIDC_CA_FILE =
|
systemd.services.swarm-controller.environment.SWARM_CONTROLLER_OIDC_CA_FILE =
|
||||||
lib.mkIf hyperhiveCfg.deploy.swarm-controller.enable "${cfg.stateDir}/trust-bundle.pem";
|
lib.mkIf hyperhiveCfg.deploy.swarm-controller.enable "${cfg.stateDir}/trust-bundle.pem";
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
# Top-level, cross-cutting hyperhive options: the master enable
|
# Top-level, cross-cutting hyperhive options: the hive's identity
|
||||||
# switch, the hive's identity (domain + display names), and hive-wide
|
# (domain + display names) and hive-wide feature toggles read by several
|
||||||
# feature toggles read by several subsystem modules. Imported by the
|
# subsystem modules. Imported by the ./default.nix aggregator.
|
||||||
# ./default.nix aggregator.
|
|
||||||
{
|
{
|
||||||
lib,
|
lib,
|
||||||
config,
|
config,
|
||||||
|
|
@ -22,13 +21,9 @@ in
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
# Top-level hyperhive enable flag. When true, automatically enables
|
|
||||||
# hive-c0re and the on-by-default hyperhive subsystems.
|
|
||||||
options.services.hyperhive.enable = lib.mkEnableOption "hyperhive — the agent swarm coordinator";
|
|
||||||
|
|
||||||
# Canonical hive DNS domain shared by every subsystem that needs a
|
# Canonical hive DNS domain shared by every subsystem that needs a
|
||||||
# stable hostname. Typed nullOr (default null) so the option always
|
# stable hostname. Typed nullOr (default null) so the option always
|
||||||
# exists, but it's REQUIRED whenever hyperhive is enabled — an
|
# exists, but it's REQUIRED wherever this host runs a hive — an
|
||||||
# assertion in hive-network.nix fails eval when it's unset, since
|
# assertion in hive-network.nix fails eval when it's unset, since
|
||||||
# matrix bakes it in on first boot and the gateway/forge/agent URLs all
|
# matrix bakes it in on first boot and the gateway/forge/agent URLs all
|
||||||
# derive from it (no safe default). Full identity-surface
|
# derive from it (no safe default). Full identity-surface
|
||||||
|
|
@ -61,7 +56,8 @@ in
|
||||||
stable name (currently: `services.hyperhive.swarm.matrix.serverName`
|
stable name (currently: `services.hyperhive.swarm.matrix.serverName`
|
||||||
derives from this, defaulting to
|
derives from this, defaulting to
|
||||||
`matrix.''${services.hyperhive.domain}` when `serverName` is
|
`matrix.''${services.hyperhive.domain}` when `serverName` is
|
||||||
null). **Required** when `services.hyperhive.enable` — eval fails
|
null). **Required** when
|
||||||
|
`services.hyperhive.deploy.hive-controller.enable` — eval fails
|
||||||
with a helpful message if it's unset (it's baked into matrix on
|
with a helpful message if it's unset (it's baked into matrix on
|
||||||
first boot and drives the gateway/forge/agent URLs, with no safe
|
first boot and drives the gateway/forge/agent URLs, with no safe
|
||||||
default; changing it later is destructive). Exposed to agents as
|
default; changing it later is destructive). Exposed to agents as
|
||||||
|
|
@ -88,9 +84,7 @@ in
|
||||||
# nothing — measured, after this warning fired on the conventional
|
# nothing — measured, after this warning fired on the conventional
|
||||||
# case. `mkOptionDefault` is priority 1500, so anything lower is a
|
# case. `mkOptionDefault` is priority 1500, so anything lower is a
|
||||||
# definition someone actually wrote (100 plain, 1000 mkDefault).
|
# definition someone actually wrote (100 plain, 1000 mkDefault).
|
||||||
config.warnings =
|
config.warnings = lib.optional (options.services.hyperhive.domain.highestPrio < 1500) ''
|
||||||
lib.optional (hiveCfg.enable && options.services.hyperhive.domain.highestPrio < 1500)
|
|
||||||
''
|
|
||||||
services.hyperhive.domain is set explicitly and is deprecated. This
|
services.hyperhive.domain is set explicitly and is deprecated. This
|
||||||
hive's address belongs in the swarm directory, which every hive in
|
hive's address belongs in the swarm directory, which every hive in
|
||||||
the swarm shares a copy of:
|
the swarm shares a copy of:
|
||||||
|
|
@ -123,7 +117,8 @@ in
|
||||||
`services.hyperhive.hiveName`, list the hives by name, and no
|
`services.hyperhive.hiveName`, list the hives by name, and no
|
||||||
hive in the swarm states an address at all.
|
hive in the swarm states an address at all.
|
||||||
|
|
||||||
**Required** when `services.hyperhive.enable`, and deliberately
|
**Required** when
|
||||||
|
`services.hyperhive.deploy.hive-controller.enable`, and deliberately
|
||||||
not defaulted: there is no fallback worth having. A guessed
|
not defaulted: there is no fallback worth having. A guessed
|
||||||
swarm domain is a wrong hostname that evaluates cleanly and
|
swarm domain is a wrong hostname that evaluates cleanly and
|
||||||
deploys, which is worse than an eval failure telling an
|
deploys, which is worse than an eval failure telling an
|
||||||
|
|
@ -144,7 +139,10 @@ in
|
||||||
example = "pr1ma";
|
example = "pr1ma";
|
||||||
description = ''
|
description = ''
|
||||||
Human-readable name of this single-host hive instance.
|
Human-readable name of this single-host hive instance.
|
||||||
**Required** when `services.hyperhive.enable`. Distinct from
|
**Required** when this host runs a hive, the swarm's secret store
|
||||||
|
or its homeserver
|
||||||
|
(`services.hyperhive.deploy.hive-controller.enable`,
|
||||||
|
`deploy.bao.enable`, `deploy.matrix.enable`). Distinct from
|
||||||
`services.hyperhive.domain` (the machine-addressable DNS name)
|
`services.hyperhive.domain` (the machine-addressable DNS name)
|
||||||
but no longer merely cosmetic: a hive occupies
|
but no longer merely cosmetic: a hive occupies
|
||||||
`<hiveName>.<swarm.domain>`, so this is the label the hive is
|
`<hiveName>.<swarm.domain>`, so this is the label the hive is
|
||||||
|
|
@ -154,6 +152,28 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Each of these deployments turns `hiveName` into an identifier with no
|
||||||
|
# fallback: hive-c0re's OIDC client and matrix localpart, the CN of the
|
||||||
|
# store's `client.pem` (./glue-bao-tls.nix), and the `hives/<name>` path
|
||||||
|
# the matrix token and queue credential readers fetch. A null there renders
|
||||||
|
# as an empty string that evaluates and deploys.
|
||||||
|
config.assertions = [
|
||||||
|
{
|
||||||
|
assertion =
|
||||||
|
!(
|
||||||
|
hiveCfg.deploy.hive-controller.enable || hiveCfg.deploy.bao.enable || hiveCfg.deploy.matrix.enable
|
||||||
|
)
|
||||||
|
|| hiveCfg.hiveName != null;
|
||||||
|
message = ''
|
||||||
|
hyperhive requires services.hyperhive.hiveName to be set on a host
|
||||||
|
that runs a hive, the swarm's secret store or its homeserver — it is
|
||||||
|
this hive's label within the swarm, and the leftmost part of the
|
||||||
|
domain it is addressed by (`<hiveName>.<swarm.domain>`), not only a
|
||||||
|
display name. Set it (`services.hyperhive.hiveName = "pr1ma";`).
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
# The one hive-level option that describes something ABOVE the hive,
|
# The one hive-level option that describes something ABOVE the hive,
|
||||||
# which is why it sits under `swarm` with the swarm-global services
|
# which is why it sits under `swarm` with the swarm-global services
|
||||||
# rather than beside `hiveName`.
|
# rather than beside `hiveName`.
|
||||||
|
|
|
||||||
|
|
@ -919,7 +919,7 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf (hyperhiveCfg.enable && deployCfg.authelia.enable) {
|
config = lib.mkIf deployCfg.authelia.enable {
|
||||||
# The derived half of the client list, declared the same way an
|
# The derived half of the client list, declared the same way an
|
||||||
# operator declares one. Everything downstream then reads a single
|
# operator declares one. Everything downstream then reads a single
|
||||||
# uniformly-typed `cfg.oidc.clients` and cannot tell the parts apart —
|
# uniformly-typed `cfg.oidc.clients` and cannot tell the parts apart —
|
||||||
|
|
|
||||||
|
|
@ -1573,7 +1573,7 @@ in
|
||||||
# they still fire when the cert paths are unset — the arm below is not
|
# they still fire when the cert paths are unset — the arm below is not
|
||||||
# evaluated in that case, and an assertion that disappears exactly when
|
# evaluated in that case, and an assertion that disappears exactly when
|
||||||
# its subject is broken would be worse than none.
|
# its subject is broken would be worse than none.
|
||||||
(lib.mkIf (hyperhiveCfg.enable && deployCfg.bao.enable) {
|
(lib.mkIf deployCfg.bao.enable {
|
||||||
assertions = [
|
assertions = [
|
||||||
{
|
{
|
||||||
assertion = lib.versionOlder baoDeploy.package.version "2.7.0";
|
assertion = lib.versionOlder baoDeploy.package.version "2.7.0";
|
||||||
|
|
@ -1655,7 +1655,7 @@ in
|
||||||
services.hyperhive.network.enable = lib.mkDefault true;
|
services.hyperhive.network.enable = lib.mkDefault true;
|
||||||
})
|
})
|
||||||
|
|
||||||
(lib.mkIf (hyperhiveCfg.enable && deployCfg.bao.enable) {
|
(lib.mkIf deployCfg.bao.enable {
|
||||||
# The wrapper alone, never `baoDeploy.package`: openbao's own module
|
# The wrapper alone, never `baoDeploy.package`: openbao's own module
|
||||||
# installs the CLI *inside* the container, and this host had none at all,
|
# installs the CLI *inside* the container, and this host had none at all,
|
||||||
# so an operator either hopped into the container or re-typed the
|
# so an operator either hopped into the container or re-typed the
|
||||||
|
|
@ -1845,7 +1845,7 @@ in
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
(lib.mkIf (hyperhiveCfg.enable && deployCfg.bao.enable && haveServerTls) {
|
(lib.mkIf (deployCfg.bao.enable && haveServerTls) {
|
||||||
# Provisions the TPM-backed token the seal above names. One-shot and
|
# Provisions the TPM-backed token the seal above names. One-shot and
|
||||||
# idempotent on ABSENCE, never on content: regenerating a PIN would
|
# idempotent on ABSENCE, never on content: regenerating a PIN would
|
||||||
# orphan an already-sealed store, so a rebuild must not rotate it.
|
# orphan an already-sealed store, so a rebuild must not rotate it.
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf (hyperhiveCfg.enable && cfg.autoConfigure) {
|
config = lib.mkIf cfg.autoConfigure {
|
||||||
# A CA that fails to issue is invisible until something makes a TLS call
|
# A CA that fails to issue is invisible until something makes a TLS call
|
||||||
# hours later, so this oneshot is worth more than most services.
|
# hours later, so this oneshot is worth more than most services.
|
||||||
services.hyperhive.swarm.otel.journaldUnits = [ "swarm-ca" ];
|
services.hyperhive.swarm.otel.journaldUnits = [ "swarm-ca" ];
|
||||||
|
|
|
||||||
|
|
@ -349,7 +349,7 @@ in
|
||||||
# `enable` moved to `services.hyperhive.deploy.swarm-controller.enable` — see
|
# `enable` moved to `services.hyperhive.deploy.swarm-controller.enable` — see
|
||||||
# ./deploy.nix. `services.hyperhive.deploy.singleHostSwarm` still
|
# ./deploy.nix. `services.hyperhive.deploy.singleHostSwarm` still
|
||||||
# asserts it, and that was never an exception to "not derived from
|
# asserts it, and that was never an exception to "not derived from
|
||||||
# services.hyperhive.enable": that mode says "this box is the whole
|
# deploy.hive-controller.enable": that mode says "this box is the whole
|
||||||
# deployment", which answers the topology question outright, where
|
# deployment", which answers the topology question outright, where
|
||||||
# `enable` alone never can. Both packages moved to that same
|
# `enable` alone never can. Both packages moved to that same
|
||||||
# `deploy.swarm-controller` block. What stays here is what the daemon IS.
|
# `deploy.swarm-controller` block. What stays here is what the daemon IS.
|
||||||
|
|
|
||||||
|
|
@ -405,7 +405,7 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf (hyperhiveCfg.enable && deployCfg.grafana.enable) {
|
config = lib.mkIf deployCfg.grafana.enable {
|
||||||
# The gateway name and the quick-link, both inside `deploy.grafana` — that
|
# The gateway name and the quick-link, both inside `deploy.grafana` — that
|
||||||
# guard is the load-bearing part. Every hive in a swarm may know this UI
|
# guard is the load-bearing part. Every hive in a swarm may know this UI
|
||||||
# exists, but only the host that RUNS it may claim the name; a client
|
# exists, but only the host that RUNS it may claim the name; a client
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ let
|
||||||
|
|
||||||
# Both halves have to be here: the mint (authelia, for the plaintext) and an
|
# Both halves have to be here: the mint (authelia, for the plaintext) and an
|
||||||
# identity (for the store). Neither implies the other.
|
# identity (for the store). Neither implies the other.
|
||||||
active = hyperhiveCfg.enable && cfg.enable && deployCfg.authelia.enable && haveClientIdentity;
|
active = cfg.enable && deployCfg.authelia.enable && haveClientIdentity;
|
||||||
|
|
||||||
hiveNames = lib.attrNames hyperhiveCfg.swarm.hives;
|
hiveNames = lib.attrNames hyperhiveCfg.swarm.hives;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf (hyperhiveCfg.enable && deployCfg.victorialogs.enable) {
|
config = lib.mkIf deployCfg.victorialogs.enable {
|
||||||
# This store publishes its own health as prometheus metrics on the same
|
# This store publishes its own health as prometheus metrics on the same
|
||||||
# listener it serves queries on, so the swarm's collector scrapes it with
|
# listener it serves queries on, so the swarm's collector scrapes it with
|
||||||
# no exporter and no extra port — same arrangement as the metrics store.
|
# no exporter and no extra port — same arrangement as the metrics store.
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf (hyperhiveCfg.enable && deployCfg.victoriametrics.enable) {
|
config = lib.mkIf deployCfg.victoriametrics.enable {
|
||||||
# The gateway name and the quick-link, both inside `deployCfg.victoriametrics.enable` — that
|
# The gateway name and the quick-link, both inside `deployCfg.victoriametrics.enable` — that
|
||||||
# guard is the load-bearing part. Every hive in a swarm may know this
|
# guard is the load-bearing part. Every hive in a swarm may know this
|
||||||
# store exists, but only the host that RUNS it may claim the name; a
|
# store exists, but only the host that RUNS it may claim the name; a
|
||||||
|
|
|
||||||
|
|
@ -84,13 +84,11 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Gated on the mesh itself, NOT on the c0re daemon. The mesh is host
|
# Gated on the mesh itself, NOT on `deploy.hive-controller.enable`. The
|
||||||
# networking, not a c0re feature: a swarm host that runs no hive —
|
# mesh is host networking, not a c0re feature: a swarm host that runs no
|
||||||
# the snapshot store, for one — still has to join the mesh, and under
|
# hive — the snapshot store, for one — still has to join the mesh.
|
||||||
# the old `c0re.enable` gate it silently got no `wg-hive` interface
|
# Nothing below is c0re-specific; the peer data c0re consumes
|
||||||
# at all. Nothing below is c0re-specific; the peer data c0re consumes
|
# (HIVE_PEER_CA_PATHS) is rendered in ./hive-c0re and stays gated there.
|
||||||
# (HIVE_PEER_CA_PATHS) is rendered in ./hive-c0re and stays gated
|
|
||||||
# there.
|
|
||||||
config = lib.mkIf config.services.hyperhive.deploy.wireguard.enable {
|
config = lib.mkIf config.services.hyperhive.deploy.wireguard.enable {
|
||||||
assertions = [
|
assertions = [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,8 @@ let
|
||||||
|
|
||||||
# The same omission on a hive that does NOT run a homeserver. Separates the
|
# The same omission on a hive that does NOT run a homeserver. Separates the
|
||||||
# matrix refusal's `deploy.matrix.enable` clause from the queue refusal, which
|
# matrix refusal's `deploy.matrix.enable` clause from the queue refusal, which
|
||||||
# has no toggle to check — an arm below reads exactly one refusal off it.
|
# checks only the hive's own toggle — an arm below reads exactly one refusal
|
||||||
|
# off it.
|
||||||
remoteReaderNoMatrixMissingLeaves = hive {
|
remoteReaderNoMatrixMissingLeaves = hive {
|
||||||
deploy.bao.clientCertFile = "/etc/pki/bao-client.pem";
|
deploy.bao.clientCertFile = "/etc/pki/bao-client.pem";
|
||||||
deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem";
|
deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem";
|
||||||
|
|
|
||||||
|
|
@ -30,17 +30,17 @@ let
|
||||||
|
|
||||||
bare = hive { };
|
bare = hive { };
|
||||||
|
|
||||||
# The same stub with the central toggle off. Paired with `bare` below to pin
|
# The same stub with the central toggle (`deploy.hive-controller.enable`)
|
||||||
# the defaults that used to read `services.hyperhive.enable` and no longer
|
# off. Paired with `bare` below to pin the defaults that do not read it:
|
||||||
# do: each is asserted to hold the SAME literal in both, so a future edit
|
# each is asserted to hold the SAME literal in both, so a future edit
|
||||||
# that quietly re-introduces the dependency — or that changes what the
|
# that quietly introduces the dependency — or that changes what the
|
||||||
# default renders for a hive with the toggle on — fails here. Reading an
|
# default renders for a hive with the toggle on — fails here. Reading an
|
||||||
# option off this fixture forces that option only, not the config, so the
|
# option off this fixture forces that option only, not the config, so the
|
||||||
# toggle being off costs nothing. Also the "installs the modules and turns
|
# toggle being off costs nothing. Also the "installs the modules and turns
|
||||||
# nothing on" host the swarm-service absences below read: none of the
|
# nothing on" host the swarm-service absences below read: none of the
|
||||||
# per-service deployment toggles derives from the hive being on, so it
|
# per-service deployment toggles derives from the hive being on, so it
|
||||||
# renders the same absences `bare` does.
|
# renders the same absences `bare` does.
|
||||||
centralToggleOff = hive { enable = false; };
|
centralToggleOff = hive { deploy.hive-controller.enable = false; };
|
||||||
|
|
||||||
# Every hive defaults the agents' queue address to the queue's name, so the
|
# Every hive defaults the agents' queue address to the queue's name, so the
|
||||||
# only hive without one is a hive told to have none.
|
# only hive without one is a hive told to have none.
|
||||||
|
|
@ -104,7 +104,7 @@ let
|
||||||
name = "the swarm controller's forgeTokenFile default does not consult the central toggle";
|
name = "the swarm controller's forgeTokenFile default does not consult the central toggle";
|
||||||
ok =
|
ok =
|
||||||
(hive {
|
(hive {
|
||||||
enable = false;
|
deploy.hive-controller.enable = false;
|
||||||
deploy.forgejo.enable = true;
|
deploy.forgejo.enable = true;
|
||||||
}).services.hyperhive.deploy.swarm-controller.forgeTokenFile
|
}).services.hyperhive.deploy.swarm-controller.forgeTokenFile
|
||||||
== "/var/lib/hyperhive-forge/swarm-controller.token";
|
== "/var/lib/hyperhive-forge/swarm-controller.token";
|
||||||
|
|
@ -453,6 +453,55 @@ let
|
||||||
&& bare.services.dnsmasq.enable
|
&& bare.services.dnsmasq.enable
|
||||||
&& bare.networking.bridges ? hive-br0;
|
&& bare.networking.bridges ? hive-br0;
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
# A host config written against either old spelling still runs its hive.
|
||||||
|
# The builder's own `true` is lowered to `mkDefault false`, so only the
|
||||||
|
# forwarded definition can turn it on.
|
||||||
|
name = "the old hive toggles forward to deploy.hive-controller.enable and warn";
|
||||||
|
ok =
|
||||||
|
let
|
||||||
|
renamedFrom =
|
||||||
|
old: m:
|
||||||
|
lib.any (
|
||||||
|
w: lib.hasInfix old w && lib.hasInfix "services.hyperhive.deploy.hive-controller.enable" w
|
||||||
|
) m.warnings;
|
||||||
|
viaOld =
|
||||||
|
extra:
|
||||||
|
hive (
|
||||||
|
extra
|
||||||
|
// {
|
||||||
|
deploy.hive-controller.enable = lib.mkDefault false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
oldEnable = viaOld { enable = true; };
|
||||||
|
oldC0re = viaOld { c0re.enable = true; };
|
||||||
|
in
|
||||||
|
oldEnable.services.hyperhive.deploy.hive-controller.enable
|
||||||
|
&& oldEnable.systemd.services ? hive-c0re
|
||||||
|
&& renamedFrom "services.hyperhive.enable" oldEnable
|
||||||
|
&& oldC0re.services.hyperhive.deploy.hive-controller.enable
|
||||||
|
&& oldC0re.systemd.services ? hive-c0re
|
||||||
|
&& renamedFrom "services.hyperhive.c0re.enable" oldC0re;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
# Refused wherever something turns the name into an identifier, and only
|
||||||
|
# there. The store host is the control that the refusal is not the hive's
|
||||||
|
# alone; the host running neither is the control that it is gated at all.
|
||||||
|
name = "a missing hiveName is refused on a hive and on a store host, not elsewhere";
|
||||||
|
ok =
|
||||||
|
let
|
||||||
|
refusedHiveName =
|
||||||
|
extra:
|
||||||
|
lib.any (a: !a.assertion && lib.hasInfix "services.hyperhive.hiveName to be set" a.message)
|
||||||
|
(hive ({ hiveName = null; } // extra)).assertions;
|
||||||
|
in
|
||||||
|
refusedHiveName { }
|
||||||
|
&& refusedHiveName {
|
||||||
|
deploy.hive-controller.enable = false;
|
||||||
|
deploy.bao.enable = true;
|
||||||
|
}
|
||||||
|
&& !(refusedHiveName { deploy.hive-controller.enable = false; });
|
||||||
|
}
|
||||||
];
|
];
|
||||||
in
|
in
|
||||||
runGroup "core-toggle" cases
|
runGroup "core-toggle" cases
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ let
|
||||||
# just one level down. `recursiveUpdate` merges nested attrsets
|
# just one level down. `recursiveUpdate` merges nested attrsets
|
||||||
# all the way down instead.
|
# all the way down instead.
|
||||||
services.hyperhive = lib.recursiveUpdate {
|
services.hyperhive = lib.recursiveUpdate {
|
||||||
enable = true;
|
deploy.hive-controller.enable = true;
|
||||||
hiveName = "h1";
|
hiveName = "h1";
|
||||||
swarm.domain = "t.local";
|
swarm.domain = "t.local";
|
||||||
swarm.hives.h1.domain = "h1.t.local";
|
swarm.hives.h1.domain = "h1.t.local";
|
||||||
|
|
|
||||||
|
|
@ -23,20 +23,37 @@ let
|
||||||
swarmServiceEnables
|
swarmServiceEnables
|
||||||
;
|
;
|
||||||
|
|
||||||
# The swarm-services toggle with the central one off, so the only thing
|
# A dedicated services host with hives elsewhere: the swarm-services
|
||||||
# that can enable the gateway/resolver/bridge here is that toggle's own
|
# toggle and no hive, the recipe docs/swarm/services.md gives.
|
||||||
# module — every other module that asserts them is behind `enable`.
|
|
||||||
swarmServicesOnly = hive {
|
swarmServicesOnly = hive {
|
||||||
enable = false;
|
deploy.hive-controller.enable = false;
|
||||||
deploy.allSwarmServices = true;
|
deploy.allSwarmServices = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# The same switch with every service it owns placed elsewhere, so the only
|
||||||
|
# thing that can enable the gateway/resolver/bridge here is that toggle's
|
||||||
|
# own module — each service module asserts them from its own `enable`.
|
||||||
|
swarmServicesSwitchAlone = hive {
|
||||||
|
deploy.hive-controller.enable = false;
|
||||||
|
deploy.allSwarmServices = true;
|
||||||
|
deploy.matrix.enable = false;
|
||||||
|
otel.enable = false;
|
||||||
|
deploy.authelia.enable = false;
|
||||||
|
deploy.nats.enable = false;
|
||||||
|
deploy.swarm-otel.enable = false;
|
||||||
|
deploy.victoriametrics.enable = false;
|
||||||
|
deploy.grafana.enable = false;
|
||||||
|
deploy.victorialogs.enable = false;
|
||||||
|
deploy.bao.enable = false;
|
||||||
|
deploy.forgejo.enable = false;
|
||||||
|
};
|
||||||
|
|
||||||
# Ports asked for on such a host. The request is the whole condition: the
|
# Ports asked for on such a host. The request is the whole condition: the
|
||||||
# firewall hole exists because an operator named a port, not because the
|
# firewall hole exists because an operator named a port, not because the
|
||||||
# hive is running, and an agent reaching a host service is a claim about
|
# hive is running, and an agent reaching a host service is a claim about
|
||||||
# the host's own listeners either way.
|
# the host's own listeners either way.
|
||||||
exposedPortsNoHive = hive {
|
exposedPortsNoHive = hive {
|
||||||
enable = false;
|
deploy.hive-controller.enable = false;
|
||||||
network.exposeHostPorts = [ 5432 ];
|
network.exposeHostPorts = [ 5432 ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -129,9 +146,44 @@ let
|
||||||
# module rather than from any of their defaults.
|
# module rather than from any of their defaults.
|
||||||
name = "the swarm-services toggle turns on the gateway, resolver and bridge by itself";
|
name = "the swarm-services toggle turns on the gateway, resolver and bridge by itself";
|
||||||
ok =
|
ok =
|
||||||
swarmServicesOnly.services.hyperhive.gateway.enable
|
swarmServicesSwitchAlone.services.hyperhive.gateway.enable
|
||||||
&& swarmServicesOnly.services.hyperhive.gateway.dns.enable
|
&& swarmServicesSwitchAlone.services.hyperhive.gateway.dns.enable
|
||||||
&& swarmServicesOnly.services.hyperhive.network.enable;
|
&& swarmServicesSwitchAlone.services.hyperhive.network.enable;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
# Each service is gated on its own deployment toggle alone, so a host
|
||||||
|
# that runs no hive still runs every shared service the switch turns
|
||||||
|
# on, plus the plumbing and the self-signed CA they are served through.
|
||||||
|
# Nothing starts the hive's own daemons. `hive-c0re` is checked by what
|
||||||
|
# starts it rather than by key: ../host-modules/hive-network.nix's bridge
|
||||||
|
# block writes to its environment wherever the bridge is up.
|
||||||
|
name = "a services-only host runs the swarm's shared services and no hive";
|
||||||
|
ok =
|
||||||
|
let
|
||||||
|
c = swarmServicesOnly.containers;
|
||||||
|
s = swarmServicesOnly.systemd.services;
|
||||||
|
h = swarmServicesOnly.services.hyperhive;
|
||||||
|
in
|
||||||
|
lib.all (m: c ? ${m}) [
|
||||||
|
"swarm-authelia"
|
||||||
|
"swarm-bao"
|
||||||
|
"swarm-grafana"
|
||||||
|
"swarm-victorialogs"
|
||||||
|
"swarm-victoriametrics"
|
||||||
|
"swarm-otel"
|
||||||
|
"swarm-nats"
|
||||||
|
"hive-matrix"
|
||||||
|
"hive-forge"
|
||||||
|
]
|
||||||
|
&& h.gateway.enable
|
||||||
|
&& h.gateway.dns.enable
|
||||||
|
&& h.network.enable
|
||||||
|
&& s ? hive-tls-ca
|
||||||
|
&& s ? swarm-bao-pki
|
||||||
|
&& (s.hive-c0re.wantedBy or [ ]) == [ ]
|
||||||
|
&& !(swarmServicesOnly.systemd.sockets ? hive-c0re)
|
||||||
|
&& !(swarmServicesOnly.systemd.sockets ? hive-priv)
|
||||||
|
&& !(s ? swarm-bao-queue-agent);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
# An operator's explicit `false` beats every `mkDefault` assertion,
|
# An operator's explicit `false` beats every `mkDefault` assertion,
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@ owns what is true _across_ hives — so a swarm runs one of them and most hives
|
||||||
leave it off.
|
leave it off.
|
||||||
|
|
||||||
Opt-in per host via `services.hyperhive.deploy.swarm-controller.enable`, which is
|
Opt-in per host via `services.hyperhive.deploy.swarm-controller.enable`, which is
|
||||||
deliberately **not** derived from `services.hyperhive.enable`: turning it on is
|
deliberately **not** derived from
|
||||||
a statement about swarm topology, not about whether hyperhive is installed.
|
`services.hyperhive.deploy.hive-controller.enable`: turning it on is a statement
|
||||||
|
about swarm topology, not about whether this host runs a hive.
|
||||||
|
|
||||||
## What it does today
|
## What it does today
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue