# The swarm's matrix homeserver as every hive sees it: the name it mints ids # under, the ports and URLs it is reached on, its encryption policy and the # OIDC client id it is registered under, identical on every host. What the # host running it decides, and the container itself, are in ./hive-matrix.nix. { lib, config, ... }: let cfg = config.services.hyperhive.swarm.matrix; swarmDomain = config.services.hyperhive.swarm.domain; deployCfg = config.services.hyperhive.deploy; in { options.services.hyperhive.swarm.matrix = { serverName = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; example = "chat.example.org"; description = '' Matrix `server_name` — the host part of every user ID (`@argus:`) and room ID minted on this homeserver. CRITICAL: must be stable from day one because it's embedded irrevocably in the identifiers. Defaults to `services.hyperhive.swarm.domain` (the bare swarm domain), because **a swarm runs one homeserver** — tying its identity to a single hive's domain would make relocating the container between hives look like a different homeserver. Combined with the `.well-known/matrix/{client,server}` routes the gateway serves at that domain, clients auto-discover the actual matrix endpoint without needing a subdomain. Override here only if you need a different server_name shape (e.g. `chat.example.org` for a bespoke hostname). **Breaking change, and the one on this page that cannot be undone by rebuilding.** This default has now moved twice — from `matrix.''${services.hyperhive.domain}`, then to the bare hive domain, and now to the swarm domain. Every existing homeserver must pin whichever value it already minted ids under, e.g. ```nix services.hyperhive.swarm.matrix.serverName = config.services.hyperhive.domain; # or "matrix.''${…domain}" ``` before rebuilding. Adopting a new `server_name` does not rename the old users and rooms — it strands them, because their ids still name a homeserver that no longer answers. ''; }; httpPort = lib.mkOption { type = lib.types.port; default = 8008; description = '' TCP port tuwunel serves the matrix client-server API on. Default 8008 is the matrix-spec well-known port. Sits outside hyperhive's claimed ranges (dashboard 7000, every agent in 8100..8999 via FNV-1a hash). Federation listens on `federationPort` separately. ''; }; 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; defaultText = lib.literalExpression '' if services.hyperhive.deploy.matrix.enable then "http://127.0.0.1:''${toString services.hyperhive.swarm.matrix.httpPort}" else null ''; example = "https://matrix.example.com"; description = '' Client-server API base URL **hive-c0re itself** uses to provision matrix (register agent users, create the hive space and chat room, invite members). Distinct from the agent-facing `hyperhive.matrix.url`, which is the gateway vhost handed to each agent's `hive-matrix-daemon`. Defaults to the loopback listener **only when this module is the thing running tuwunel** — in that case the address is not a guess, it is where this module just put the container. Set it explicitly (with `enable = false`) when the homeserver runs on another machine; "everything on one host" is a special case of the full deployment, not the assumption. `null` means hive-c0re has no homeserver to provision against and matrix provisioning no-ops. There is deliberately no fallback compiled into the daemon: an address baked into the binary is one that builds fine and then talks to the wrong machine. ''; }; gatewayHost = lib.mkOption { type = lib.types.nullOr lib.types.str; # `chat.` under the SWARM domain — both halves change: a swarm runs # one homeserver, and the label follows the service rather than the # protocol. # # Total on a null swarm domain so the required-domain assertion in # hive-network.nix is the thing that fires; see the comment there. default = if swarmDomain == null then "chat.invalid" else "chat.${swarmDomain}"; defaultText = lib.literalExpression ''"chat.''${services.hyperhive.swarm.domain}"''; example = "matrix.example.com"; description = '' Public hostname for the matrix homeserver behind the gateway. Defaults to `chat.''${services.hyperhive.swarm.domain}` — the swarm's domain, because a swarm runs **one** homeserver. Set to `null` to skip the gateway vhost (tuwunel stays direct on `httpPort`). See `docs/networking/gateway.md` for the vhost map + matrix discovery flow, and the federation port-8448 caveat at the bottom of that doc. ⚠️ **`gatewayHost` and `serverName` are different things, and they carry very different costs.** `gatewayHost` is the API listener hostname (where nginx proxies `/_matrix/*`) and is free to change: it is a routing detail clients rediscover through `.well-known`. `serverName` is the matrix-identifier domain embedded **irrevocably** in every user and room id — adopting a new one is a different homeserver, not a rename. Both defaults now sit under the swarm domain, but only this one is safe to move on a running deployment. A deployment that was running before this moved keeps its current name by pinning `matrix.''${services.hyperhive.domain}` here — exactly what the old default rendered. ''; }; allowEncryption = lib.mkOption { type = lib.types.bool; default = false; description = '' Server-side switch for matrix end-to-end encryption — sets tuwunel's `allow_encryption`. Off by default: on the hive-internal homeserver the operator already controls the transport, so server E2EE adds key-management overhead (cross-signing, device verification, undecryptable-message recovery) without a clear threat-model win for the common single-hive case. Turn on when agents join encrypted rooms on external / federated homeservers, or when the operator wants message contents opaque to the homeserver admin. Independent of the agent matrix client, which always supports decryption so it can read encrypted rooms it is invited to regardless of this flag; this option only governs whether THIS homeserver permits room encryption. ''; }; # This homeserver always delegates login to the swarm's authelia, as # an OIDC relying party — matrix SSO (`m.login.sso`), offered # alongside password login. No toggle: a homeserver in a swarm is a # client of that swarm's identity provider. # # ⚠️ Not to be confused with tuwunel's `oidc_*` settings, which point # the other way: those make this homeserver an *authorization server* # for matrix clients. This family makes it a *client* of an external # identity provider. The two share the protocol's name and answer # opposite questions. # # This **adds** a way in. Password login keeps working: an identity # provider that can take the homeserver offline when it hiccups is a # worse homeserver than one with two ways in — which is also what # makes always-on safe. Making authelia the *only* path is a # separate, reversible switch (tuwunel's `login_with_password`), # deliberately not folded in here. # # ⚠️ Matrix SSO lives **inside** the homeserver, never behind a # forward-auth proxy: the client-server API is spoken by non-browser # clients holding matrix access tokens — every agent's own # `hive-matrix-daemon` — plus federation, and a proxy in front of # `/_matrix/` breaks all of it. sso = { clientId = lib.mkOption { type = lib.types.str; default = "tuwunel"; description = '' OAuth2 client id this homeserver identifies itself with. Must match the `id` of the corresponding entry in `services.hyperhive.swarm.authelia.oidc.clients`. The secret it pairs with is a host path, so it lives at `services.hyperhive.deploy.matrix.sso.clientSecretFile`. ''; }; }; }; }