`forgejo-swarm-controller-account`, in the forge's container, now puts the swarm-controller's forge token into the swarm secret store itself, under the forge publisher's leaf. The container already receives that leaf as nspawn system credentials for its journal forwarder; the mint loads the same three. The token reaches bao on stdin (`value=-`) and the forge's API through a header file, never as an argument, and never touches disk. The mint's guard is a marker holding the published token's name, at /var/lib/forgejo/swarm-controller-token-name, written last: after the put, and after every other `swarm-controller-boot-*` token of the account is revoked through forgejo's admin API, authenticated as the new token. So a put that failed, or a marker lost while the store kept an older token, leaves no live token behind once a mint completes. The mint logs in to the store and checks that the account is a site admin before it mints, so a store that is down or an account the admin API would refuse mints nothing on each retry. Failures retry under store-retry; forgejo does not wait for the mint. No bao read and no grant change: the forge publisher keeps create/update on the token. Removed: the host's `swarm-bao-forge-publisher` service and path unit, which read the token file through the container's root, and their entries in the store and policy ordering glue. The forge publisher's policy unit stays; `swarm-bao-forge-oidc` still uses the leaf. On deploy, an existing forge host has no marker, so the mint runs once: it mints a new token, publishes it, revokes the old one, and deletes the old /var/lib/forgejo/swarm-controller-token. The controller's next forge call gets a 401, re-reads the store and carries on.
781 lines
35 KiB
Nix
781 lines
35 KiB
Nix
# "What does THIS host deploy?"
|
|
#
|
|
# Separated from `services.hyperhive.swarm.*` because those are two
|
|
# different kinds of fact and only one of them varies per machine:
|
|
#
|
|
# swarm.* — swarm-wide truth. The swarm's name, domain, hives, peers,
|
|
# and where each service lives. **Identical on every
|
|
# host**, byte for byte; a hive needs all of it to be a
|
|
# *client* of the swarm.
|
|
# deploy.* — this machine's deployment decisions. Necessarily different
|
|
# on every host, because that is what a deployment is.
|
|
#
|
|
# The `enable` toggles live here rather than under `swarm.*` so the
|
|
# namespace that is identical everywhere does not carry the one thing
|
|
# that must differ per host.
|
|
#
|
|
# Flat and named for the thing deployed — `deploy.forgejo`, not
|
|
# `deploy.swarmServices.forgejo`: grouping by "swarm service" re-encodes
|
|
# the service-side taxonomy into a layer that does not care about it.
|
|
#
|
|
# ⚠️ Each entry is an attrset with an `enable`, not a bare bool, so a
|
|
# service that grows a second *deployment* decision has somewhere to put
|
|
# it — `deploy.nats = { enable; authPackage; }` is then an ordinary
|
|
# addition rather than a migration, where a bare bool leaves it
|
|
# unrepresentable.
|
|
#
|
|
# ⚠️ The renames below are deliberately in this one file rather than
|
|
# spread across the service modules, so all of them have a single home
|
|
# and a single file to delete when the deprecation window closes — the
|
|
# shape ./swarm-peers-removed.nix already uses.
|
|
{ config, lib, ... }:
|
|
let
|
|
# The swarm UI's own switch, in both spellings. Accepted and ignored: a
|
|
# definition only produces a warning, so a host that still sets one keeps
|
|
# evaluating. `null` is "not set".
|
|
swarmUiSwitchIgnored = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.bool;
|
|
default = null;
|
|
visible = false;
|
|
description = ''
|
|
No effect. swarm-ui runs wherever swarm-controller runs.
|
|
'';
|
|
};
|
|
swarmUiSwitchWarning =
|
|
path: value:
|
|
lib.optional (value != null)
|
|
"services.hyperhive.${path} no longer has any effect: swarm-ui now runs wherever swarm-controller runs. Remove this setting; services.hyperhive.deploy.swarm-controller.enable decides it.";
|
|
|
|
# Shared by the two removed spellings of the status-publishing secret below.
|
|
statusSecretRemoved = ''
|
|
hive-c0re reads this hive's status-publishing client secret from the
|
|
swarm's secret store, under the hive's own certificate
|
|
(deploy.bao.clientCertFile), and swarm-secret-publish on the host that runs
|
|
authelia puts it there. Remove this definition; the file it named is read
|
|
by nothing now and can be deleted.
|
|
'';
|
|
in
|
|
{
|
|
imports = [
|
|
# Same type, same meaning, new path — so a rename carries it exactly
|
|
# and existing configs keep evaluating with one warning naming both
|
|
# paths. Precedent: ./hive-forge/service.nix, ./hive-matrix.nix.
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "grafana" "enable" ]
|
|
[ "services" "hyperhive" "deploy" "grafana" "enable" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "grafana" "datasourceUrl" ]
|
|
[ "services" "hyperhive" "deploy" "grafana" "datasourceUrl" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "grafana" "logsDatasourceUrl" ]
|
|
[ "services" "hyperhive" "deploy" "grafana" "logsDatasourceUrl" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "grafana" "plugins" ]
|
|
[ "services" "hyperhive" "deploy" "grafana" "plugins" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "victoriametrics" "enable" ]
|
|
[ "services" "hyperhive" "deploy" "victoriametrics" "enable" ]
|
|
)
|
|
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "victoriametrics" "port" ] ''
|
|
VictoriaMetrics listens on a unix socket in a network namespace of its
|
|
own, and the gateway reaches it there, so it binds no host port. Remove
|
|
this definition.
|
|
'')
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "victorialogs" "enable" ]
|
|
[ "services" "hyperhive" "deploy" "victorialogs" "enable" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "controller" "enable" ]
|
|
[ "services" "hyperhive" "deploy" "swarm-controller" "enable" ]
|
|
)
|
|
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "controller" "forgeTokenFile" ] ''
|
|
The controller reads its forge token from the swarm's secret store, under
|
|
its own certificate, and forgejo-swarm-controller-account in the forge's
|
|
container puts it there. Remove this definition; the file it named is
|
|
read by nothing now and can be deleted.
|
|
'')
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "controller" "authBridgeUrl" ]
|
|
[ "services" "hyperhive" "deploy" "swarm-controller" "authBridgeUrl" ]
|
|
)
|
|
(lib.mkRemovedOptionModule
|
|
[ "services" "hyperhive" "swarm" "controller" "queue" "clientSecretFile" ]
|
|
''
|
|
The controller reads its queue client secret from the swarm's secret
|
|
store, under its own certificate (deploy.swarm-controller.baoClientCertFile),
|
|
and swarm-secret-publish on the host that runs authelia puts it there.
|
|
Remove this definition; the file it named is read by nothing now and can
|
|
be deleted.
|
|
''
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "authelia" "enable" ]
|
|
[ "services" "hyperhive" "deploy" "authelia" "enable" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "authelia" "usersFile" ]
|
|
[ "services" "hyperhive" "deploy" "authelia" "usersFile" ]
|
|
)
|
|
# `hostClientSecretDir`, `hostUsersFile` and `grafana.socketDir`
|
|
# deliberately have NO entry here. A rename module contributes a
|
|
# *definition* to its target, and all three are `readOnly`, which refuses a
|
|
# second one — so a shim for any of them makes every evaluation fail with
|
|
# "set multiple times", naming the option's own default as the rival
|
|
# definition.
|
|
#
|
|
# The first two are derived, so nothing could ever have set them and a
|
|
# shim's only customer is a reader of the old path. `grafana.socketDir` was
|
|
# a plain settable option; only a test fixture set it in
|
|
# this tree, but a deployment outside it can have. That setter now fails
|
|
# evaluation with "option does not exist" at the old path, rather than
|
|
# being carried to the new one.
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "nats" "enable" ]
|
|
[ "services" "hyperhive" "deploy" "nats" "enable" ]
|
|
)
|
|
# `tokenEndpoint` stays under `swarm.*`: it is the swarm IdP's one
|
|
# address, identical on every hive, where this is "the queue as seen from
|
|
# here".
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "statusPublish" "natsUrl" ]
|
|
[ "services" "hyperhive" "deploy" "hive-controller" "statusPublish" "natsUrl" ]
|
|
)
|
|
(lib.mkRemovedOptionModule [
|
|
"services"
|
|
"hyperhive"
|
|
"swarm"
|
|
"statusPublish"
|
|
"clientSecretFile"
|
|
] statusSecretRemoved)
|
|
(lib.mkRemovedOptionModule [
|
|
"services"
|
|
"hyperhive"
|
|
"deploy"
|
|
"hive-controller"
|
|
"statusPublish"
|
|
"clientSecretFile"
|
|
] statusSecretRemoved)
|
|
# 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
|
|
[ "services" "hyperhive" "swarm" "otel" "enable" ]
|
|
[ "services" "hyperhive" "deploy" "swarm-otel" "enable" ]
|
|
)
|
|
# ⚠️ Not to be confused with `services.hyperhive.otel.clientSecretFile`,
|
|
# a different option on the hive tier that is not part of this table. A
|
|
# bare `clientSecretFile` matches five distinct options across the tree.
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "otel" "clientSecretFile" ]
|
|
[ "services" "hyperhive" "deploy" "swarm-otel" "clientSecretFile" ]
|
|
)
|
|
|
|
# The runner moved from `deploy.forgejo.ci` to `deploy.ci-runner` with
|
|
# every option keeping its type and meaning, so the old paths forward.
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "deploy" "forgejo" "ci" "enable" ]
|
|
[ "services" "hyperhive" "deploy" "ci-runner" "enable" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "deploy" "forgejo" "ci" "name" ]
|
|
[ "services" "hyperhive" "deploy" "ci-runner" "name" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "deploy" "forgejo" "ci" "concurrency" ]
|
|
[ "services" "hyperhive" "deploy" "ci-runner" "concurrency" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "deploy" "forgejo" "ci" "labels" ]
|
|
[ "services" "hyperhive" "deploy" "ci-runner" "labels" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "deploy" "forgejo" "ci" "package" ]
|
|
[ "services" "hyperhive" "deploy" "ci-runner" "package" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "deploy" "forgejo" "ci" "jobTimeout" ]
|
|
[ "services" "hyperhive" "deploy" "ci-runner" "jobTimeout" ]
|
|
)
|
|
|
|
# The gateway's services-issuer pair is a swarm-service identity, not a
|
|
# hive's, so it sits with the store's other host-unit readers.
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "deploy" "hive-controller" "tls" "baoClientCertFile" ]
|
|
[ "services" "hyperhive" "deploy" "bao" "servicesIssuerClientCertFile" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "deploy" "hive-controller" "tls" "baoClientKeyFile" ]
|
|
[ "services" "hyperhive" "deploy" "bao" "servicesIssuerClientKeyFile" ]
|
|
)
|
|
|
|
# The runner toggle sat under `swarm.forge.ci` before `deploy.forgejo.ci`.
|
|
# Both were per-host, so each forwards straight to its final path.
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "forge" "ci" "enable" ]
|
|
[ "services" "hyperhive" "deploy" "ci-runner" "enable" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "forge" "ci" "name" ]
|
|
[ "services" "hyperhive" "deploy" "ci-runner" "name" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "forge" "ci" "concurrency" ]
|
|
[ "services" "hyperhive" "deploy" "ci-runner" "concurrency" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "forge" "ci" "labels" ]
|
|
[ "services" "hyperhive" "deploy" "ci-runner" "labels" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "forge" "ci" "package" ]
|
|
[ "services" "hyperhive" "deploy" "ci-runner" "package" ]
|
|
)
|
|
|
|
# The rest of the forge split. What stays under `swarm.forge` is what the
|
|
# forge IS from any hive's point of view — the names and ports it answers
|
|
# on, the URLs it advertises, the client id it is registered under; the
|
|
# two renamed here are what the host running it decides. Its package is
|
|
# renamed further down with the other `*.package` entries. The two removed
|
|
# here named host files that the swarm secret store now replaces.
|
|
#
|
|
# `mirrors` renames in one entry rather than one per field: it is a single
|
|
# option of a list-of-submodule type, so the rename carries its whole
|
|
# value.
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "forge" "openFirewall" ]
|
|
[ "services" "hyperhive" "deploy" "forgejo" "openFirewall" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "forge" "mirrors" ]
|
|
[ "services" "hyperhive" "deploy" "forgejo" "mirrors" ]
|
|
)
|
|
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "forge" "sso" "clientSecretFile" ] ''
|
|
The forge's OIDC client secret is fetched from the swarm secret store
|
|
(swarm-bao-forge-oidc) into a fixed runtime path. Remove this
|
|
definition.
|
|
'')
|
|
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "forge" "hostSwarmControllerTokenFile" ]
|
|
''
|
|
The forge publishes the swarm-controller's forge token to the swarm
|
|
secret store (forgejo-swarm-controller-account), and the controller
|
|
reads it there. Remove this definition; the file it named is read by
|
|
nothing now and can be deleted.
|
|
''
|
|
)
|
|
|
|
# The homeserver, split the same way. `swarm.matrix` keeps what it IS to
|
|
# every hive — the name it answers to, the URLs it is reached on, the
|
|
# client id it is registered under. These six are what the host
|
|
# running it decides; its two packages are renamed further down with
|
|
# the other `*.package` entries. ⚠️ `enable` is NOT here: its shim lives in
|
|
# ./hive-matrix.nix, so that file is the one
|
|
# to check before assuming this table is the whole matrix story.
|
|
#
|
|
# The homeserver's port and its firewall switch, removed rather than
|
|
# renamed: tuwunel listens on a unix socket only the gateway reaches, so
|
|
# there is no host port to choose or open.
|
|
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "matrix" "httpPort" ] ''
|
|
The homeserver no longer listens on a TCP port: it runs in a network
|
|
namespace of its own and the gateway reaches it over a unix socket.
|
|
Remove this definition. Clients and federation peers reach it at
|
|
services.hyperhive.swarm.matrix.gatewayHost.
|
|
'')
|
|
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "matrix" "openFirewall" ] ''
|
|
The homeserver no longer listens on a host port, so there is no port
|
|
to open. Remove this definition. Reach from outside the host goes
|
|
through the gateway: services.hyperhive.gateway.openFirewall.
|
|
'')
|
|
(lib.mkRemovedOptionModule [ "services" "hyperhive" "deploy" "matrix" "openFirewall" ] ''
|
|
The homeserver no longer listens on a host port, so there is no port
|
|
to open. Remove this definition. Reach from outside the host goes
|
|
through the gateway: services.hyperhive.gateway.openFirewall.
|
|
'')
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "matrix" "trustedServers" ]
|
|
[ "services" "hyperhive" "deploy" "matrix" "trustedServers" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "matrix" "maxRequestSize" ]
|
|
[ "services" "hyperhive" "deploy" "matrix" "maxRequestSize" ]
|
|
)
|
|
# Both spellings of the registration token, removed rather than renamed:
|
|
# the homeserver does not accept a shared registration secret at all,
|
|
# so there is no path to point a definition at. Accounts are created by
|
|
# the swarm's appservice.
|
|
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "matrix" "registrationTokenFile" ] ''
|
|
The matrix registration token is gone: swarm-controller creates accounts
|
|
as the swarm's appservice instead, so there is no shared secret for
|
|
tuwunel to check. Remove this definition — nothing needs replacing.
|
|
The old token file at /var/lib/hyperhive/matrix-register-token is read by
|
|
nothing now and can be deleted. See docs/integrations/matrix.md.
|
|
'')
|
|
(lib.mkRemovedOptionModule [ "services" "hyperhive" "deploy" "matrix" "registrationTokenFile" ] ''
|
|
The matrix registration token is gone: swarm-controller creates accounts
|
|
as the swarm's appservice instead, so there is no shared secret for
|
|
tuwunel to check. Remove this definition — nothing needs replacing.
|
|
The old token file at /var/lib/hyperhive/matrix-register-token is read by
|
|
nothing now and can be deleted. See docs/integrations/matrix.md.
|
|
'')
|
|
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "matrix" "gui" "enable" ] ''
|
|
The matrix web client has no on/off switch: every host that runs the
|
|
homeserver serves it at services.hyperhive.swarm.matrix.gatewayHost,
|
|
and the swarm UI links to it. Remove this definition.
|
|
'')
|
|
(lib.mkRemovedOptionModule [ "services" "hyperhive" "deploy" "matrix" "gui" "enable" ] ''
|
|
The matrix web client has no on/off switch: every host that runs the
|
|
homeserver serves it at services.hyperhive.swarm.matrix.gatewayHost,
|
|
and the swarm UI links to it. Remove this definition.
|
|
'')
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "matrix" "sso" "clientSecretFile" ]
|
|
[ "services" "hyperhive" "deploy" "matrix" "sso" "clientSecretFile" ]
|
|
)
|
|
|
|
# Retention is read only where the container is defined, so it is a
|
|
# decision of the host running the store rather than something the swarm
|
|
# agrees on. The two stores keep their domain and port in `swarm.*`,
|
|
# because a client hive needs those to reach them; `package` followed
|
|
# `enable` and `retentionPeriod` across in a later slice.
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "victoriametrics" "retentionPeriod" ]
|
|
[ "services" "hyperhive" "deploy" "victoriametrics" "retentionPeriod" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "victorialogs" "retentionPeriod" ]
|
|
[ "services" "hyperhive" "deploy" "victorialogs" "retentionPeriod" ]
|
|
)
|
|
|
|
# The switch over all of the above. `allSwarmServices` says whether THIS
|
|
# host runs every service the swarm requires; those services are
|
|
# required of the SWARM, so the name is about the swarm rather than this
|
|
# host's wiring.
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "enableRequiredServices" ]
|
|
[ "services" "hyperhive" "deploy" "allSwarmServices" ]
|
|
)
|
|
|
|
# The mode above that one lives under `deploy`, not at the TOP of
|
|
# `services.hyperhive`: that namespace is everything about hyperhive, not
|
|
# the settings of a single hive. `singleHostSwarm` names what the mode
|
|
# asserts — the whole swarm runs on this host — instead of its mechanism.
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "enableAllLocalDefaults" ]
|
|
[ "services" "hyperhive" "deploy" "singleHostSwarm" ]
|
|
)
|
|
|
|
# The hive TLS state dir lives under `deploy.hive-controller` (the
|
|
# hive-c0re daemon's namespace), not at the TOP of `services.hyperhive`,
|
|
# which is everything about hyperhive rather than the settings of one hive.
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "tls" "stateDir" ]
|
|
[ "services" "hyperhive" "deploy" "hive-controller" "tls" "stateDir" ]
|
|
)
|
|
# The hive CA's lifetime and its leaves', both spellings, removed rather
|
|
# than renamed: no hive CA is generated, and the store's per-hive role
|
|
# sets the gateway leaf's lifetime.
|
|
(lib.mkRemovedOptionModule [ "services" "hyperhive" "tls" "caValidityDays" ] ''
|
|
No hive CA is generated any more: the gateway leaf is issued by the
|
|
secret store's pki mount, whose per-hive role sets its lifetime
|
|
(services.hyperhive.deploy.bao.servicesPkiLeafTtlHours). Remove this
|
|
definition. See docs/swarm/ca.md.
|
|
'')
|
|
(lib.mkRemovedOptionModule [ "services" "hyperhive" "tls" "leafValidityDays" ] ''
|
|
No hive CA is generated any more: the gateway leaf is issued by the
|
|
secret store's pki mount, whose per-hive role sets its lifetime
|
|
(services.hyperhive.deploy.bao.servicesPkiLeafTtlHours). Remove this
|
|
definition. See docs/swarm/ca.md.
|
|
'')
|
|
(lib.mkRemovedOptionModule
|
|
[ "services" "hyperhive" "deploy" "hive-controller" "tls" "caValidityDays" ]
|
|
''
|
|
No hive CA is generated any more: the gateway leaf is issued by the
|
|
secret store's pki mount, whose per-hive role sets its lifetime
|
|
(services.hyperhive.deploy.bao.servicesPkiLeafTtlHours). Remove this
|
|
definition. See docs/swarm/ca.md.
|
|
''
|
|
)
|
|
(lib.mkRemovedOptionModule
|
|
[ "services" "hyperhive" "deploy" "hive-controller" "tls" "leafValidityDays" ]
|
|
''
|
|
No hive CA is generated any more: the gateway leaf is issued by the
|
|
secret store's pki mount, whose per-hive role sets its lifetime
|
|
(services.hyperhive.deploy.bao.servicesPkiLeafTtlHours). Remove this
|
|
definition. See docs/swarm/ca.md.
|
|
''
|
|
)
|
|
|
|
# The WireGuard mesh, whole. Unlike every rename above this one covers a
|
|
# namespace rather than a toggle: nothing under it is a fact another hive
|
|
# reads. A peer needs this host's `wireguardEndpoint` — the roster entry
|
|
# in ./swarm.nix, which stays — and nothing about the interface this host
|
|
# brings up. `listenPort` follows for the same reason and is easy to read
|
|
# the other way: it is what this host *binds*, while the port a peer
|
|
# *dials* is the one inside `wireguardEndpoint`.
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "wireguard" "enable" ]
|
|
[ "services" "hyperhive" "deploy" "wireguard" "enable" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "wireguard" "privateKeyFile" ]
|
|
[ "services" "hyperhive" "deploy" "wireguard" "privateKeyFile" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "wireguard" "address" ]
|
|
[ "services" "hyperhive" "deploy" "wireguard" "address" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "wireguard" "listenPort" ]
|
|
[ "services" "hyperhive" "deploy" "wireguard" "listenPort" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "wireguard" "persistentKeepalive" ]
|
|
[ "services" "hyperhive" "deploy" "wireguard" "persistentKeepalive" ]
|
|
)
|
|
|
|
# Which build a service runs is a decision of the host that runs it, so
|
|
# every `*.package` follows its `enable` across. Left in separate
|
|
# namespaces they split one client's two halves, as `gui.enable` and
|
|
# `gui.package` would.
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "matrix" "package" ]
|
|
[ "services" "hyperhive" "deploy" "matrix" "package" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "matrix" "gui" "package" ]
|
|
[ "services" "hyperhive" "deploy" "matrix" "gui" "package" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "grafana" "package" ]
|
|
[ "services" "hyperhive" "deploy" "grafana" "package" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "victorialogs" "package" ]
|
|
[ "services" "hyperhive" "deploy" "victorialogs" "package" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "victoriametrics" "package" ]
|
|
[ "services" "hyperhive" "deploy" "victoriametrics" "package" ]
|
|
)
|
|
# With these two `services.hyperhive.swarm.ui` holds nothing at all: its
|
|
# `enable` is an ignored switch (declared below) and its `package` is
|
|
# renamed here. `deploy.swarm-ui` keeps only its ignored `enable`.
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "ui" "package" ]
|
|
[ "services" "hyperhive" "deploy" "swarm-controller" "ui" "package" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "deploy" "swarm-ui" "package" ]
|
|
[ "services" "hyperhive" "deploy" "swarm-controller" "ui" "package" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "nats" "authPackage" ]
|
|
[ "services" "hyperhive" "deploy" "nats" "authPackage" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "authelia" "package" ]
|
|
[ "services" "hyperhive" "deploy" "authelia" "package" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "authelia" "bridgePackage" ]
|
|
[ "services" "hyperhive" "deploy" "authelia" "bridgePackage" ]
|
|
)
|
|
# ⚠️ `swarm-controller`, not `hive-controller` — see the note in
|
|
# ./swarm-controller.nix. Both namespaces exist, so the wrong target here
|
|
# lands on a live option in hive-c0re's tree instead of failing.
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "controller" "package" ]
|
|
[ "services" "hyperhive" "deploy" "swarm-controller" "package" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "controller" "swarmctlPackage" ]
|
|
[ "services" "hyperhive" "deploy" "swarm-controller" "swarmctlPackage" ]
|
|
)
|
|
# ⚠️ `deploy.forgejo`, not `deploy.forge` — the deploy-side name differs
|
|
# from the swarm-side one here, as it does for the controller and the UI.
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarm" "forge" "package" ]
|
|
[ "services" "hyperhive" "deploy" "forgejo" "package" ]
|
|
)
|
|
];
|
|
|
|
# ⚠️ Only `deploy.forgejo.enable` is declared here. The rest of
|
|
# `deploy.forgejo` is in ./hive-forge/default.nix: the knobs live with the
|
|
# module that reads them; this file stays the registry of toggles.
|
|
options.services.hyperhive.swarm.ui.enable = swarmUiSwitchIgnored;
|
|
|
|
config.warnings =
|
|
swarmUiSwitchWarning "deploy.swarm-ui.enable" config.services.hyperhive.deploy.swarm-ui.enable
|
|
++ swarmUiSwitchWarning "swarm.ui.enable" config.services.hyperhive.swarm.ui.enable;
|
|
|
|
options.services.hyperhive.deploy = {
|
|
swarm-ui.enable = swarmUiSwitchIgnored;
|
|
forgejo.enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
example = true;
|
|
description = ''
|
|
Run the swarm's forge in a `hive-forge` container on this host. A
|
|
swarm has one forge, so one host turns this on. It is the
|
|
canonical store for the meta flake and every agent's config repo,
|
|
so the swarm needs it, but *this* host running it is a decision
|
|
like any other shared service's.
|
|
|
|
With it off, this hive is a *client*: it reaches the swarm's
|
|
forge at {option}`services.hyperhive.swarm.forge.domain`, which
|
|
the operator's DNS must resolve to the forge's host. No container
|
|
is created, and an existing one's state stays on disk under
|
|
`/var/lib/nixos-containers/hive-forge/`.
|
|
|
|
{option}`services.hyperhive.deploy.allSwarmServices` turns it on,
|
|
and `singleHostSwarm` through that.
|
|
'';
|
|
};
|
|
|
|
grafana.enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = ''
|
|
Run the swarm's metrics UI on this host.
|
|
|
|
Off by default and not derived from
|
|
{option}`services.hyperhive.deploy.hive-controller.enable`: a swarm
|
|
has one Grafana, so running it is a decision about this host rather
|
|
than about whether it runs a hive.
|
|
'';
|
|
};
|
|
|
|
victoriametrics.enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = ''
|
|
Run the swarm's metrics store on this host.
|
|
|
|
Paired with
|
|
{option}`services.hyperhive.deploy.grafana.enable`: a store
|
|
with no UI is unreadable and a UI with no store is empty, so there
|
|
is no sensible deployment that takes one and not the other from
|
|
that switch. Set either directly to run exactly one.
|
|
'';
|
|
};
|
|
|
|
victorialogs.enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = ''
|
|
Run the swarm's log store on this host.
|
|
|
|
A hive that is not the service host is a *client* of this store,
|
|
not a second one.
|
|
'';
|
|
};
|
|
|
|
bao.enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
example = true;
|
|
description = ''
|
|
Run the swarm's secret store in a `swarm-bao` container on this
|
|
host. A swarm has one store and it has to exist somewhere.
|
|
|
|
*Where* it runs is a separate question from *that* it runs: set
|
|
this directly to put the store on a host of its own, and clients
|
|
still reach it by name at
|
|
{option}`services.hyperhive.swarm.bao.domain` rather than at a
|
|
local address.
|
|
|
|
With it off, this hive is a *client*: it still reads its own
|
|
secrets from whoever runs the store, authenticating with its own
|
|
client certificate. Every hive needs the client half; only one
|
|
runs the server half, which is why the two live in different
|
|
namespaces.
|
|
'';
|
|
};
|
|
|
|
authelia.enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
example = true;
|
|
description = ''
|
|
Run the swarm's authelia in a `swarm-authelia` container on this
|
|
host. A swarm has one SSO provider, and this says it lives here.
|
|
|
|
With it off, this hive is a *client*:
|
|
{option}`services.hyperhive.swarm.authelia.url` still points at
|
|
whoever runs it, and no container is created. That asymmetry is
|
|
why the two live in different namespaces — every hive needs the
|
|
client half, only one runs the server half.
|
|
'';
|
|
};
|
|
|
|
swarm-otel.enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = ''
|
|
Run the **swarm's** telemetry collector on this host.
|
|
|
|
A swarm has one of these, and it belongs wherever the shared
|
|
services live rather than on every hive.
|
|
|
|
Named `swarm-otel` rather than `otel` because there are two
|
|
collectors and the tier is the whole distinction:
|
|
{option}`services.hyperhive.otel.enable` is the **hive-tier** one,
|
|
which every hive runs. A bare `deploy.otel` would not say which
|
|
it meant. A hive that does not run the swarm collector still runs
|
|
its own, and reaches this one by name at
|
|
{option}`services.hyperhive.swarm.otel.domain`.
|
|
'';
|
|
};
|
|
|
|
matrix.enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = ''
|
|
Run the swarm's matrix homeserver — matrix-tuwunel, in a
|
|
`hive-matrix` container — on this host.
|
|
|
|
Set it directly to put the homeserver somewhere other than the
|
|
host holding the rest of the swarm's services.
|
|
|
|
Client-side settings stay in
|
|
{option}`services.hyperhive.swarm.matrix.*`, which every hive
|
|
agrees on; this is only the decision to run it here.
|
|
'';
|
|
};
|
|
|
|
nats.enable = 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 at most one host turns this on —
|
|
but *which* host is its own decision, not necessarily the one
|
|
running the swarm's other shared services.
|
|
|
|
Off by default, and off means *absent*: no container is created
|
|
and nothing else in the evaluated config changes.
|
|
'';
|
|
};
|
|
|
|
ci-runner.enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
example = true;
|
|
description = ''
|
|
Run the swarm's Forgejo Actions runner in a `swarm-ci-runner`
|
|
container on this host. It reaches the forge at
|
|
{option}`services.hyperhive.swarm.forge.domain` and registers with
|
|
the token swarm-controller publishes to the secret store, so it
|
|
needs neither the forge nor a hive on this host.
|
|
|
|
Not derived from
|
|
{option}`services.hyperhive.deploy.allSwarmServices`: a runner
|
|
executes pull-request code, so running one is a decision of its own.
|
|
'';
|
|
};
|
|
|
|
review-classifier.enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
example = true;
|
|
description = ''
|
|
Run the review classifier in a `swarm-review-classifier` container on
|
|
this host. It answers the swarm UI's review try-outs with `claude` on
|
|
the Anthropic API key the operator writes to the secret store at
|
|
`secret/swarm/services/review-classifier/anthropic-api-key`, field
|
|
`api_key`.
|
|
|
|
On by default where
|
|
{option}`services.hyperhive.deploy.allSwarmServices` is set and this
|
|
host has the classifier's store identity (the pair
|
|
{option}`services.hyperhive.deploy.bao.reviewClassifierClientCertFile`
|
|
and `reviewClassifierClientKeyFile`, minted on the store's host). The
|
|
key is never written for you: until the operator writes it the service
|
|
runs and every run fails with "no API key…".
|
|
'';
|
|
};
|
|
|
|
claude-login.enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
example = true;
|
|
description = ''
|
|
Run the claude login minter in a `swarm-claude-login` container on
|
|
this host. It runs `claude auth login` for the agent an operator logs
|
|
in from its card in the swarm UI, and writes the credentials to that
|
|
agent's `secret/swarm/agents/<agent>/claude-credentials`, which the
|
|
agent's harness installs. Run it on one host: a login in progress
|
|
lives in the service's memory.
|
|
|
|
On by default where
|
|
{option}`services.hyperhive.deploy.allSwarmServices` is set and this
|
|
host has the minter's store identity (the pair
|
|
{option}`services.hyperhive.deploy.bao.claudeLoginClientCertFile`
|
|
and `claudeLoginClientKeyFile`, minted on the store's host).
|
|
'';
|
|
};
|
|
|
|
builder.enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
example = true;
|
|
description = ''
|
|
Run the swarm builder in a `swarm-builder` container on this host. It
|
|
builds each agent config PR's head on this host's agent base, through
|
|
this host's nix daemon, and swarm-controller writes the result as a
|
|
`swarm-builder` commit status on the PR.
|
|
|
|
On by default where
|
|
{option}`services.hyperhive.deploy.allSwarmServices` is set and this
|
|
host has the builder's store identity (the pair
|
|
{option}`services.hyperhive.deploy.bao.builderClientCertFile` and
|
|
`builderClientKeyFile`, minted on the store's host). It evaluates
|
|
unreviewed config PRs and builds them through this host's nix daemon,
|
|
from a container with its own network namespace.
|
|
'';
|
|
};
|
|
|
|
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 {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = ''
|
|
Run the swarm-controller daemon on this host.
|
|
|
|
Off by default and deliberately not derived from
|
|
{option}`services.hyperhive.deploy.hive-controller.enable`: a swarm
|
|
has one controller, so running it is a decision about this host
|
|
rather than about whether it runs a hive.
|
|
|
|
Also serves the swarm UI from this host. The UI is a view onto the
|
|
controller's state and reaches it over that daemon's unix socket,
|
|
so the host that runs the controller is the only host that can
|
|
serve it.
|
|
'';
|
|
};
|
|
};
|
|
}
|