# "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, # CA, 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.forgejo = { enable; ci; }` is then an ordinary addition # rather than a migration. `ci` ("does this host run the runner too") is # exactly that shape, and a bare bool leaves it unrepresentable. # # ⚠️ The renames below are deliberately in this one file rather than # spread across the service modules, so the whole move has a single home # and a single file to delete when the deprecation window closes — the # shape ./swarm-peers-removed.nix already uses. { lib, config, ... }: let deployCfg = config.services.hyperhive.deploy; 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/default.nix, ./hive-matrix.nix. (lib.mkRenamedOptionModule [ "services" "hyperhive" "swarm" "grafana" "enable" ] [ "services" "hyperhive" "deploy" "grafana" "enable" ] ) (lib.mkRenamedOptionModule [ "services" "hyperhive" "swarm" "victoriametrics" "enable" ] [ "services" "hyperhive" "deploy" "victoriametrics" "enable" ] ) (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.mkRenamedOptionModule [ "services" "hyperhive" "swarm" "ui" "enable" ] [ "services" "hyperhive" "deploy" "swarm-ui" "enable" ] ) (lib.mkRenamedOptionModule [ "services" "hyperhive" "swarm" "authelia" "enable" ] [ "services" "hyperhive" "deploy" "authelia" "enable" ] ) (lib.mkRenamedOptionModule [ "services" "hyperhive" "swarm" "nats" "enable" ] [ "services" "hyperhive" "deploy" "nats" "enable" ] ) (lib.mkRenamedOptionModule [ "services" "hyperhive" "swarm" "otel" "enable" ] [ "services" "hyperhive" "deploy" "swarm-otel" "enable" ] ) # The CI runner, and the only entry here that renames more than an # `enable`: every knob under it describes the runner THIS host would run, # so leaving `name`/`concurrency`/`labels`/`package` in the namespace that # must be identical swarm-wide would keep the original defect for four # more options. Renamed one by one because `ci` is a plain attrset of # options rather than a submodule type, so there is no parent path to # rename in a single entry. (lib.mkRenamedOptionModule [ "services" "hyperhive" "swarm" "forge" "ci" "enable" ] [ "services" "hyperhive" "deploy" "forgejo" "ci" "enable" ] ) (lib.mkRenamedOptionModule [ "services" "hyperhive" "swarm" "forge" "ci" "name" ] [ "services" "hyperhive" "deploy" "forgejo" "ci" "name" ] ) (lib.mkRenamedOptionModule [ "services" "hyperhive" "swarm" "forge" "ci" "concurrency" ] [ "services" "hyperhive" "deploy" "forgejo" "ci" "concurrency" ] ) (lib.mkRenamedOptionModule [ "services" "hyperhive" "swarm" "forge" "ci" "labels" ] [ "services" "hyperhive" "deploy" "forgejo" "ci" "labels" ] ) (lib.mkRenamedOptionModule [ "services" "hyperhive" "swarm" "forge" "ci" "package" ] [ "services" "hyperhive" "deploy" "forgejo" "ci" "package" ] ) # 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 everything else — package, domain, port # — in `swarm.*`, because a client hive needs those to reach them. (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, and the name changes with the path # because the old one described the wrong subject: those services are # required of the SWARM, while the option says whether THIS host runs # them. `allSwarmServices` is mara's own phrasing of what it means. (lib.mkRenamedOptionModule [ "services" "hyperhive" "swarm" "enableRequiredServices" ] [ "services" "hyperhive" "deploy" "allSwarmServices" ] ) # The mode above that one. It sat at the TOP of `services.hyperhive`, # which is the same defect one tier up: that namespace is everything # about hyperhive, not the settings of a single hive. The new name says # what the mode asserts — the whole swarm runs on this host — instead of # naming its mechanism. (lib.mkRenamedOptionModule [ "services" "hyperhive" "enableAllLocalDefaults" ] [ "services" "hyperhive" "deploy" "singleHostSwarm" ] ) # The hive CA's own knobs. They sat at the TOP of `services.hyperhive`, # which is meant to be everything about hyperhive rather than the settings # of one hive — and where the CA lives, how long it lasts and how long its # leaves last are decisions of the host that holds the key. `hive-controller` # is hive-c0re's new name (mara, on the issue), so the daemon that owns the # CA is what they hang off. (lib.mkRenamedOptionModule [ "services" "hyperhive" "tls" "stateDir" ] [ "services" "hyperhive" "deploy" "hive-controller" "tls" "stateDir" ] ) (lib.mkRenamedOptionModule [ "services" "hyperhive" "tls" "caValidityDays" ] [ "services" "hyperhive" "deploy" "hive-controller" "tls" "caValidityDays" ] ) (lib.mkRenamedOptionModule [ "services" "hyperhive" "tls" "leafValidityDays" ] [ "services" "hyperhive" "deploy" "hive-controller" "tls" "leafValidityDays" ] ) ]; # ⚠️ `deploy.forgejo` is declared in ./hive-ci.nix, not here, and it is the # one entry with no `enable`: the forge is not optional — it is the canonical # store for the meta flake and every agent's config repo, so it deploys with # hyperhive itself. Running the CI runner is the only *deployment* decision # it has, which is exactly the `{ enable; ci; }` shape the header describes, # minus the half that does not apply. The knobs live with the module that # reads them; this file stays the registry of toggles. options.services.hyperhive.deploy = { 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.enable`: a swarm has one Grafana, so running it is a decision about this host rather than about whether hyperhive is installed. ''; }; 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. ''; }; 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.enable`: a swarm has one controller, so running it is a decision about this host rather than about whether hyperhive is installed. ''; }; swarm-ui.enable = lib.mkOption { type = lib.types.bool; default = deployCfg.swarm-controller.enable; defaultText = lib.literalExpression "services.hyperhive.deploy.swarm-controller.enable"; example = true; description = '' Serve the swarm UI from this host. Derived from {option}`services.hyperhive.deploy.swarm-controller.enable`: 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 host that can serve the UI. A hive that merely *uses* a swarm has nothing to serve here. ''; }; }; }