diff --git a/docs/tools/hivectl.md b/docs/tools/hivectl.md index 7f7b5d76..a8d502c2 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.deploy.matrix.enable = true`). +running (`services.hyperhive.swarm.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 dd08459b..d524e0ce 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.deploy.matrix.enable = true` pay +//! haven't flipped `services.hyperhive.swarm.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 85a80cde..2a469429 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.deploy.matrix.enable = true to run one \ + "no matrix homeserver configured — set services.hyperhive.swarm.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 3d809600..ec1836df 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.deploy.matrix.enable = lib.mkForce false; + services.hyperhive.swarm.matrix.enable = lib.mkForce false; } ) ]; diff --git a/nix/host-modules/deploy.nix b/nix/host-modules/deploy.nix index e4020c0b..8ae9ae71 100644 --- a/nix/host-modules/deploy.nix +++ b/nix/host-modules/deploy.nix @@ -152,24 +152,6 @@ 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 ffe9ba18..c1df0c6e 100644 --- a/nix/host-modules/hive-c0re/environment.nix +++ b/nix/host-modules/hive-c0re/environment.nix @@ -159,26 +159,28 @@ 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.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.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.apiUrl != null) { # Client-server API base hive-c0re uses to provision matrix (register # agent users, create the hive space + chat room, invite members). diff --git a/nix/host-modules/hive-matrix.nix b/nix/host-modules/hive-matrix.nix index 8d1d33bd..8084f380 100644 --- a/nix/host-modules/hive-matrix.nix +++ b/nix/host-modules/hive-matrix.nix @@ -164,13 +164,6 @@ 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. @@ -184,6 +177,21 @@ 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; @@ -246,9 +254,9 @@ in apiUrl = lib.mkOption { type = lib.types.nullOr lib.types.str; - default = if deployCfg.matrix.enable then "http://127.0.0.1:${toString cfg.httpPort}" else null; + default = if cfg.enable then "http://127.0.0.1:${toString cfg.httpPort}" else null; defaultText = lib.literalExpression '' - if services.hyperhive.deploy.matrix.enable + if services.hyperhive.swarm.matrix.enable then "http://127.0.0.1:''${toString services.hyperhive.swarm.matrix.httpPort}" else null ''; @@ -403,8 +411,8 @@ in gui = { enable = lib.mkOption { type = lib.types.bool; - default = deployCfg.matrix.enable; - defaultText = lib.literalExpression "config.services.hyperhive.deploy.matrix.enable"; + default = cfg.enable; + defaultText = lib.literalExpression "config.services.hyperhive.swarm.matrix.enable"; description = '' Serve a matrix web client at `matrix.''${services.hyperhive.domain}/`. Requires `matrix.gatewayHost != null` (default `matrix.` @@ -490,7 +498,7 @@ in }; }; - config = lib.mkIf deployCfg.matrix.enable { + config = lib.mkIf cfg.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 c69eeaff..8e8e5fc1 100644 --- a/nix/host-modules/swarm-required-services.nix +++ b/nix/host-modules/swarm-required-services.nix @@ -48,11 +48,15 @@ in # Same precedence reasoning as ./local-defaults.nix: fills in for an # operator who hasn't spoken, yields to one who has. - # - # 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; + 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; + }; # The collector that feeds the pair above (note: no `swarm.` prefix, # this is ./otel.nix's existing per-hive option).