From 2aa924d85a2237cf6555e40aee2e6806ce645dff Mon Sep 17 00:00:00 2001 From: atlas Date: Sun, 30 Aug 2026 14:56:09 +0200 Subject: [PATCH 1/2] deploy: move the matrix homeserver toggle out of swarm swarm.matrix.enable was the last toggle still spelled swarm.*, which is the namespace every host in the swarm agrees on - and "does this host run the homeserver" is exactly what differs between them. It moves to deploy.matrix.enable with the rest; the genuinely swarm-wide matrix settings stay where they are. mkRenamedOptionModule keeps existing configs evaluating with one warning naming both paths. The sweep for readers had to be unanchored and cross-language: three of them were outside nix, including a hive-c0re error message telling the operator to set the old name. A dotted grep also cannot see the rename module's own list form, so that was checked separately. --- docs/tools/hivectl.md | 2 +- hive-c0re/src/matrix.rs | 2 +- hive-c0re/src/server.rs | 2 +- nix/docs/default.nix | 2 +- nix/host-modules/deploy.nix | 18 +++++++++++ nix/host-modules/hive-c0re/environment.nix | 2 +- nix/host-modules/hive-matrix.nix | 32 ++++++++------------ nix/host-modules/swarm-required-services.nix | 14 +++------ 8 files changed, 40 insertions(+), 34 deletions(-) diff --git a/docs/tools/hivectl.md b/docs/tools/hivectl.md index a8d502c2..7f7b5d76 100644 --- a/docs/tools/hivectl.md +++ b/docs/tools/hivectl.md @@ -58,7 +58,7 @@ hivectl forge reconcile-config iris --verbose # include the full diff, not Manual entry to the same idempotent matrix provisioning flow `hive-c0re` runs at boot. Requires the `hive-matrix` container to be -running (`services.hyperhive.swarm.matrix.enable = true`). +running (`services.hyperhive.deploy.matrix.enable = true`). ```bash hivectl matrix create-user iris # provision (or re-provision) matrix account for agent `iris` diff --git a/hive-c0re/src/matrix.rs b/hive-c0re/src/matrix.rs index d524e0ce..dd08459b 100644 --- a/hive-c0re/src/matrix.rs +++ b/hive-c0re/src/matrix.rs @@ -1,7 +1,7 @@ //! Optional matrix-tuwunel wiring: shared registration token (host) + //! per-agent UIAA registration → `/matrix-token`. No-op //! when the `hive-matrix` container isn't running, so operators who -//! haven't flipped `services.hyperhive.swarm.matrix.enable = true` pay +//! haven't flipped `services.hyperhive.deploy.matrix.enable = true` pay //! nothing. //! //! See `docs/matrix.md::Provisioning flow (registration token)` for diff --git a/hive-c0re/src/server.rs b/hive-c0re/src/server.rs index 2a469429..85a80cde 100644 --- a/hive-c0re/src/server.rs +++ b/hive-c0re/src/server.rs @@ -487,7 +487,7 @@ fn require_matrix_present() -> Result<()> { return Ok(()); } anyhow::bail!( - "no matrix homeserver configured — set services.hyperhive.swarm.matrix.enable = true to run one \ + "no matrix homeserver configured — set services.hyperhive.deploy.matrix.enable = true to run one \ here, or services.hyperhive.swarm.matrix.apiUrl to point at an existing one, before \ provisioning matrix users" ) diff --git a/nix/docs/default.nix b/nix/docs/default.nix index ec1836df..3d809600 100644 --- a/nix/docs/default.nix +++ b/nix/docs/default.nix @@ -54,7 +54,7 @@ let boot.loader.grub.enable = false; system.stateVersion = "25.11"; services.hyperhive.enable = lib.mkForce false; - services.hyperhive.swarm.matrix.enable = lib.mkForce false; + services.hyperhive.deploy.matrix.enable = lib.mkForce false; } ) ]; diff --git a/nix/host-modules/deploy.nix b/nix/host-modules/deploy.nix index 8ae9ae71..e4020c0b 100644 --- a/nix/host-modules/deploy.nix +++ b/nix/host-modules/deploy.nix @@ -152,6 +152,24 @@ in ''; }; + 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. + + Derives from + {option}`services.hyperhive.swarm.enableRequiredServices` with + the other once-per-swarm services. Set it here directly to put + the homeserver somewhere other than the host holding the rest. + + 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; diff --git a/nix/host-modules/hive-c0re/environment.nix b/nix/host-modules/hive-c0re/environment.nix index c1df0c6e..11088fc3 100644 --- a/nix/host-modules/hive-c0re/environment.nix +++ b/nix/host-modules/hive-c0re/environment.nix @@ -162,7 +162,7 @@ in // lib.optionalAttrs ( - config.services.hyperhive.swarm.matrix.enable + config.services.hyperhive.deploy.matrix.enable && config.services.hyperhive.swarm.matrix.gatewayHost != null ) { diff --git a/nix/host-modules/hive-matrix.nix b/nix/host-modules/hive-matrix.nix index 8084f380..8d1d33bd 100644 --- a/nix/host-modules/hive-matrix.nix +++ b/nix/host-modules/hive-matrix.nix @@ -164,6 +164,13 @@ in [ "services" "hyperhive" "matrix" ] [ "services" "hyperhive" "swarm" "matrix" ] ) + # "does THIS host run it" is a per-host decision, and `swarm.*` is the + # namespace every host in the swarm agrees on. The rest of the matrix + # settings genuinely are swarm-wide and stay where they are. + (lib.mkRenamedOptionModule + [ "services" "hyperhive" "swarm" "matrix" "enable" ] + [ "services" "hyperhive" "deploy" "matrix" "enable" ] + ) (lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "matrix" "sso" "enable" ] '' SSO is no longer optional: a homeserver that runs at all delegates login to the swarm's authelia. @@ -177,21 +184,6 @@ in ]; options.services.hyperhive.swarm.matrix = { - enable = lib.mkOption { - type = lib.types.bool; - default = false; - description = '' - Run hive-matrix — a private matrix-tuwunel homeserver (in a - nixos-container) for hyperhive agents. - - Matrix is a swarm-wide service — one homeserver, not one per - hive — so `services.hyperhive.swarm.enableRequiredServices` - turns this on as part of saying the swarm's shared services live - on this host. Set it here directly to run the homeserver - somewhere other than the host that holds the rest of them. - ''; - }; - package = lib.mkOption { type = lib.types.package; default = pkgs.matrix-tuwunel; @@ -254,9 +246,9 @@ in apiUrl = lib.mkOption { type = lib.types.nullOr lib.types.str; - default = if cfg.enable then "http://127.0.0.1:${toString cfg.httpPort}" else null; + default = if deployCfg.matrix.enable then "http://127.0.0.1:${toString cfg.httpPort}" else null; defaultText = lib.literalExpression '' - if services.hyperhive.swarm.matrix.enable + if services.hyperhive.deploy.matrix.enable then "http://127.0.0.1:''${toString services.hyperhive.swarm.matrix.httpPort}" else null ''; @@ -411,8 +403,8 @@ in gui = { enable = lib.mkOption { type = lib.types.bool; - default = cfg.enable; - defaultText = lib.literalExpression "config.services.hyperhive.swarm.matrix.enable"; + default = deployCfg.matrix.enable; + defaultText = lib.literalExpression "config.services.hyperhive.deploy.matrix.enable"; description = '' Serve a matrix web client at `matrix.''${services.hyperhive.domain}/`. Requires `matrix.gatewayHost != null` (default `matrix.` @@ -498,7 +490,7 @@ in }; }; - config = lib.mkIf cfg.enable { + config = lib.mkIf deployCfg.matrix.enable { # Matrix's own gateway surface: the sub-domain vhost, the name the # hive resolver answers for, and the Accept-header map that vhost's # SPA fallback reads. All three are matrix knowledge and none of diff --git a/nix/host-modules/swarm-required-services.nix b/nix/host-modules/swarm-required-services.nix index 8e8e5fc1..c69eeaff 100644 --- a/nix/host-modules/swarm-required-services.nix +++ b/nix/host-modules/swarm-required-services.nix @@ -48,15 +48,11 @@ in # Same precedence reasoning as ./local-defaults.nix: fills in for an # operator who hasn't spoken, yields to one who has. - config.services.hyperhive.swarm = { - # The last one still spelled `swarm.*.enable`. Everything else that - # used to derive here — the queue, the SSO provider, the collector, - # the metrics pair, the log store — now derives below under - # `deploy.*`, because "does THIS host run it" is a per-host decision - # and `swarm.*` has to be identical on every host. Same switch, same - # rule, different attribute path. - matrix.enable = lib.mkDefault swarmCfg.enableRequiredServices; - }; + # + # Everything derives under `deploy.*` now, because "does THIS host run + # it" is a per-host decision and `swarm.*` has to be identical on every + # host. Same switch, same rule, one attribute path. + config.services.hyperhive.deploy.matrix.enable = lib.mkDefault swarmCfg.enableRequiredServices; # The collector that feeds the pair above (note: no `swarm.` prefix, # this is ./otel.nix's existing per-hive option). From e8813b86eb31c0a4c5f7060b0345413f25eba46c Mon Sep 17 00:00:00 2001 From: atlas Date: Sun, 30 Aug 2026 15:08:27 +0200 Subject: [PATCH 2/2] matrix: forward the homeserver URL from client hives too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HIVE_MATRIX_URL was conditioned on this host deploying the homeserver, so a hive that is a client of the swarm's matrix forwarded nothing and every agent's hive-matrix-daemon no-opped. hive-c0re already provisions those agents' accounts against a remote homeserver through HIVE_MATRIX_API_URL three lines below, whose gate is `apiUrl != null` — it registered the users and then never told them where to log in. gatewayHost is swarm-scoped and defaults to chat., so it names the swarm's one homeserver from any member hive. Its being set is the real question, and a null swarm domain is already caught by the assertion in hive-network.nix rather than by this expression. --- nix/host-modules/hive-c0re/environment.nix | 42 +++++++++++----------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/nix/host-modules/hive-c0re/environment.nix b/nix/host-modules/hive-c0re/environment.nix index 11088fc3..ffe9ba18 100644 --- a/nix/host-modules/hive-c0re/environment.nix +++ b/nix/host-modules/hive-c0re/environment.nix @@ -159,28 +159,26 @@ in # a reading no correctly-built hive should ever have to make. HIVE_RESERVED_NAMES = lib.concatStringsSep " " (import ../../reserved-names.nix); } -// - lib.optionalAttrs - ( - config.services.hyperhive.deploy.matrix.enable - && config.services.hyperhive.swarm.matrix.gatewayHost != null - ) - { - # In-cluster matrix homeserver URL for each agent's - # hive-matrix-daemon — the gateway vhost (`matrix.`). - # Forwarded to agents by meta.rs alongside HIVE_FORGE_URL; shares the - # same env-forwarding ordering caveat (value baked at - # config-generation time). - # - # A domain-less config forwards nothing rather than falling back to - # loopback. The old fallback read as harmless because hive-c0re shares - # the host netns — but the value it produced was handed to *agents*, - # which do not, so `127.0.0.1` there names the agent itself. An absent - # forward leaves `hyperhive.matrix.url` null and the daemon no-ops; - # that is the honest answer when the hive has no matrix vhost to point - # at. - HIVE_MATRIX_URL = "http://${config.services.hyperhive.swarm.matrix.gatewayHost}"; - } +// lib.optionalAttrs (config.services.hyperhive.swarm.matrix.gatewayHost != null) { + # Matrix homeserver URL for each agent's hive-matrix-daemon — the + # gateway vhost (`chat.`). Forwarded to agents by meta.rs + # alongside HIVE_FORGE_URL; shares the same env-forwarding ordering + # caveat (value baked at config-generation time). + # + # Deliberately NOT conditioned on this host running the homeserver. A + # swarm has one matrix, and every hive's agents talk to it — the hive + # that hosts it is not the only hive whose agents need its address. + # `gatewayHost` is swarm-scoped and names that one homeserver from any + # member hive, so its being set is the whole question. + # + # Null forwards nothing rather than falling back to loopback. That + # fallback reads as harmless because hive-c0re shares the host netns — + # but the value is handed to *agents*, which do not, so `127.0.0.1` + # there names the agent itself. An absent forward leaves + # `hyperhive.matrix.url` null and the daemon no-ops; that is the honest + # answer when there is no matrix vhost to point at. + HIVE_MATRIX_URL = "http://${config.services.hyperhive.swarm.matrix.gatewayHost}"; +} // lib.optionalAttrs (config.services.hyperhive.swarm.matrix.apiUrl != null) { # Client-server API base hive-c0re uses to provision matrix (register # agent users, create the hive space + chat room, invite members).