nix: split hive-matrix into service and deploy-mode files
`swarm.matrix` (what the homeserver is to every hive: server name, ports, API URL, gateway host, encryption policy, OIDC client id) moves to nix/host-modules/hive-matrix-service.nix. Everything else -- the `imports` block with its two renames and the removed `sso.enable`, the `deploy.matrix` options, the whole `config` block including `containers.hive-matrix`, and every other let binding -- stays in nix/host-modules/hive-matrix.nix, which default.nix now imports alongside the new file. The `swarm.matrix` block reads three let bindings, and the `config` block reads all three too: `cfg` (`apiUrl` defaults from `cfg.httpPort`), `swarmDomain` (`gatewayHost`'s default) and `deployCfg` (`apiUrl` reads `deployCfg.matrix.enable`). All three are option reads, so each file binds them from `config.services.hyperhive.*`. Nothing is duplicated. A pure move: option paths, option definitions and config are unchanged apart from the comment above `deploy.matrix`, which now names the file `swarm.matrix` lives in. Refs #3742
This commit is contained in:
parent
4181d33cad
commit
a539dceab1
3 changed files with 195 additions and 179 deletions
|
|
@ -20,6 +20,7 @@
|
||||||
./hive-ci.nix
|
./hive-ci.nix
|
||||||
./hive-forge
|
./hive-forge
|
||||||
./hive-gateway
|
./hive-gateway
|
||||||
|
./hive-matrix-service.nix
|
||||||
./hive-matrix.nix
|
./hive-matrix.nix
|
||||||
./hive-network.nix
|
./hive-network.nix
|
||||||
./hive-priv.nix
|
./hive-priv.nix
|
||||||
|
|
|
||||||
191
nix/host-modules/hive-matrix-service.nix
Normal file
191
nix/host-modules/hive-matrix-service.nix
Normal file
|
|
@ -0,0 +1,191 @@
|
||||||
|
# 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:<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 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`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -406,185 +406,9 @@ in
|
||||||
'')
|
'')
|
||||||
];
|
];
|
||||||
|
|
||||||
options.services.hyperhive.swarm.matrix = {
|
# `swarm.matrix`, in ./hive-matrix-service.nix, is what the homeserver IS from
|
||||||
serverName = lib.mkOption {
|
# any hive's point of view: the name it answers to, the ports and URLs it is
|
||||||
type = lib.types.nullOr lib.types.str;
|
# reached on, and the client id it is registered under. What lives here is what the host running it
|
||||||
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 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`.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# What stays above is what the homeserver IS from any hive's point of view:
|
|
||||||
# the name it answers to, the ports and URLs it is reached on, and the client
|
|
||||||
# id it is registered under. What lives here is what the host running it
|
|
||||||
# decides — which build it runs, whether it is exposed, which peers it trusts,
|
# decides — which build it runs, whether it is exposed, which peers it trusts,
|
||||||
# how large a request it accepts, and where its host-local secrets sit. Same rule as
|
# how large a request it accepts, and where its host-local secrets sit. Same rule as
|
||||||
# ./swarm-victorialogs.nix; `enable` already lives in ./deploy.nix, which also
|
# ./swarm-victorialogs.nix; `enable` already lives in ./deploy.nix, which also
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue