Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/nix/host-modules/hive-matrix-service.nix

183 lines
8.2 KiB
Nix
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# The swarm's matrix homeserver as every hive sees it: the name it mints ids
# under, the 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;
in
{
imports = [
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "matrix" "apiUrl" ] ''
hive-c0re no longer talks to matrix, so it has no API URL to set: matrix is swarm-level.
'')
];
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:<server_name>`) 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 controller's host 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).
**Changing this cannot be undone by rebuilding.** The default
is the swarm domain, so a homeserver that minted ids under
another value (`matrix.''${services.hyperhive.domain}` or the
bare hive domain) must pin that value, 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.
'';
};
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, which leaves nothing able to
reach the homeserver. 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 sit under the swarm domain, but only this
one is safe to change on a running deployment.
To use the per-hive name `matrix.''${services.hyperhive.domain}`
instead, pin it here.
'';
};
adminSync.ignoreUsers = lib.mkOption {
type = lib.types.listOf (lib.types.strMatching "@[^:[:space:]]+:[^[:space:]]+");
default = [ ];
example = [ "@mara:example.org" ];
description = ''
Full matrix user ids `swarm-controller`'s operator admin sync never
revokes homeserver admin from. The sync revokes admin only from
accounts it made admin itself, once their owner leaves authelia's
`admins` group; an account listed here keeps admin regardless.
'';
};
# 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. The host
running authelia registers it, whichever host runs the homeserver.
The homeserver's host reads the secret it pairs with out of the
swarm secret store, into
`services.hyperhive.deploy.matrix.sso.clientSecretFile`.
'';
};
redirectUri = lib.mkOption {
type = lib.types.nullOr lib.types.str;
readOnly = true;
default =
if cfg.gatewayHost == null then
null
else
"https://${cfg.gatewayHost}/_matrix/client/unstable/login/sso/callback/${cfg.sso.clientId}";
defaultText = lib.literalExpression ''"https://''${services.hyperhive.swarm.matrix.gatewayHost}/_matrix/client/unstable/login/sso/callback/''${services.hyperhive.swarm.matrix.sso.clientId}"'';
description = ''
SSO callback authelia sends the browser back to, and the URI it
matches **exactly**. Format-locked by tuwunel: the callback host
must point directly at the homeserver, and the path is fixed.
Read-only: the homeserver's config and its authelia client entry
both read it from here. `null` when `gatewayHost` is, because the
homeserver then has no name a browser can reach.
'';
};
};
otel.clientId = lib.mkOption {
type = lib.types.str;
readOnly = true;
# Not `hive-matrix-collector`: `hive-<name>` is a hive's own client id,
# and `matrix-collector` is a name a hive may take.
default = "swarm-matrix-collector";
description = ''
OAuth2 client id the journal forwarder inside the homeserver's
container authenticates as, and the audience it asks its token for.
Its own, not {option}`services.hyperhive.swarm.matrix.sso.clientId`,
which is the browser login's.
Read-only because three hosts have to agree on it: authelia
registers the client (`glue-matrix-otel-oidc-client.nix`), the
swarm collector admits it on the container's receiver
(`swarm.otel.serviceProducers`), and the secret publisher carries
its secret to the store.
'';
};
};
}