Account minting, knowledge sync, the archetype refresh, the matrix appservice flow and the SSO secret delivery move to pages under 4-implementation/swarm. Each handbook page keeps what an operator does or sees, with a pointer.
9.7 KiB
Swarm Matrix homeserver
Private Matrix homeserver (matrix-tuwunel — the conduwuit
successor) wrapped in a nixos-container (hive-matrix), plus optional fluffychat-web
client at chat.<swarm-domain>/ (the gatewayHost vhost). A swarm runs one
homeserver, on one host. Two namespaces configure it:
services.hyperhive.swarm.matrix.*— what the homeserver is, as every hive sees it:serverName,gatewayHost.services.hyperhive.deploy.matrix.*— what the host running it decides:enable,gui.package,trustedServers,maxRequestSize,sso.clientSecretFile.
Vhost routing lives in gateway.md.
Container shape
Same shape as gateway.md::hive-forge container shape:
- Container name
hive-matrix(noth-*) so c0re's lifecycle scanner ignores it; operator manages via the standardnixos-containerCLI. - Keeps hive-matrix from fighting any
services.matrix-*the operator already runs on the host — separate systemd namespace, separate state dir. - Container runs in a network namespace of its own
(
privateNetwork = true) on the hive bridge, and binds no TCP port. Agents reach the homeserver atchat.<swarm-domain>via the gateway, which dials its unix socket (see Reach). - Persistent state at
/var/lib/nixos-containers/hive-matrix/var/lib/matrix-tuwunel/survives container restart / host reboot. To wipe, destroy the container.
Identity vs API listener: serverName vs gatewayHost
Two distinct hostnames:
serverName— matrix-specserver_name, embedded irrevocably in every@user:<server_name>and!room:<server_name>identifier minted on this homeserver. You can't change it later without abandoning every account and chat history. Defaults to the bareservices.hyperhive.swarm.domain.gatewayHost— the API listener hostname, where the gateway's matrix vhost proxies/_matrix/*to tuwunel. Defaults tochat.<services.hyperhive.swarm.domain>. Set tonullto skip the gateway vhost, which leaves nothing able to reach the homeserver.
Both default under the 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.
⚠️ they're still not interchangeable, and the difference is the
cost of changing one. gatewayHost is a routing detail clients
rediscover through .well-known, so it's safe to move on a running
deployment. The controller's host serves that .well-known on the swarm
domain, and a homeserver pinned to a hive domain gets it on that hive's
vhost. The matrix id format bakes serverName into every user and
room id, so adopting a new one does not rename the existing users and rooms —
it strands them, because their ids still name a homeserver that no
longer answers.
Pinning serverName on a homeserver with existing ids
serverName defaults to the bare services.hyperhive.swarm.domain. A
homeserver that changes serverName after minting ids strands its
existing users and rooms — keep the value it minted them under:
services.hyperhive.swarm.matrix = {
# whichever this deployment already uses
serverName = config.services.hyperhive.domain;
gatewayHost = "matrix.${config.services.hyperhive.domain}";
};
A rebuild on a host that already has a homeserver prints a
hive-matrix: WARNING — … serverName is unset line when this is missing,
naming the value it's about to default to. It never fails the rebuild, so
pin the value before the homeserver mints any ids under the default.
A pinned hive domain answers .well-known/matrix/* itself, built from
gatewayHost, on any host that runs that hive's gateway. It doesn't need
the homeserver on the same host.
Reach
tuwunel binds no TCP port. It listens on the unix socket
/run/hive-matrix/tuwunel.sock, in a host directory (root:nginx,
0770) bind-mounted into the container, and the gateway's
/_matrix/ location proxies to that socket. Clients, agents, hive-c0re
and federation peers all reach the homeserver at chat.<swarm-domain>
through the gateway; from outside the host that takes
gateway.openFirewall.
hive-c0re doesn't talk to the homeserver: matrix is swarm-level, and the hive has no matrix API URL to set.
Federation peers find the homeserver through .well-known/matrix/server
at the server name, which names <gatewayHost>:443
(gateway.md::Discovery flow).
Port 8448 isn't used.
The web chat client at chat.<swarm-domain> signs in the same way: its
login dialog preselects the server name and looks the homeserver up through
that discovery answer. That's the swarm domain by default, and the hive
domain on a homeserver that pinned serverName to it. Typing the server name
into any other Matrix client uses the same value.
Outbound, the container sits on the hive bridge like the CI runner: it leases an address from the gateway's DHCP, resolves through its dnsmasq, and leaves through the bridge's NAT. It reaches the secret store, authelia and federation peers by name.
Accounts
The homeserver refuses registration. The swarm's appservice creates every account:
agents never see an appservice token, and an agent only ever receives its own
access_token. swarm-controller creates each agent's accounts and one sender
account per hive, @hive-<hive>:<server_name>, an ordinary account that
nothing promotes.
How the registration gets rendered, where its tokens live and how the
controller keeps an agent's token: matrix-provisioning.md.
Operators are homeserver admins
Every member of authelia's admins group who has a matrix account is a
homeserver admin. swarm-controller reads the group through
swarm-authelia-bridge at start and every five minutes after, and for
each member not yet in #admins:<server_name> it sends
!admin users make-user-admin @<name>:<server_name> there as @swarm.
<name> is the authelia username lowercased, the account an SSO login
lands on.
- Leaving
adminsrevokes what the sync granted. The controller records each account it sends a grant for in/var/lib/swarm-controller/operator-matrix-admins.json. When one of them is no longer inadminsbut still in#admins, it sends!admin users force-leave-room @<name>:<server_name> <admin room id>there as@swarm; leaving that room is what ends homeserver admin. - Admins the sync didn't make keep admin. An admin missing from that
file,
@swarm, the server user@conduit:<server_name>, every agent's account, and every user id inservices.hyperhive.deploy.swarm-controller.matrixAdminSync.ignoreUsersare never revoked. - The last admin stays. A revoke that would leave no admin in
#adminsbesides@swarmand the server user isn't sent; the controller logs a warning on each pass instead. - No account, no grant. A member who has never logged in to matrix has no account to promote. The first pass after their first SSO login promotes them.
- One command per account per controller run. A member still missing
from
#adminsafter the controller sends its grant gets amake-user-admin … failed to takewarning in the controller log on each later pass, and no second command until the controller restarts; a revoke that leaves its account in#adminsgetsforce-leave-room … failed to takethe same way. - Runs only on a controller with both
services.hyperhive.deploy.swarm-controller.authBridgeUrlandservices.hyperhive.deploy.swarm-controller.matrixHomeserverUrlset.
Settings
- Federation is on at the protocol level, but
trusted_servers = []keeps it closed until you list peers intrustedServers(see Configuration tuning). - Registration is off. The swarm's appservice still creates accounts, and the homeserver refuses everyone else.
- Encryption:
services.hyperhive.deploy.matrix.allowEncryption(defaultfalse) lets rooms on this homeserver use encryption. Turn it on for encrypted rooms with external homeservers, or to keep contents opaque to the homeserver admin. The agents' matrix client reads encrypted rooms either way. - Key backup. Every matrix account an agent has gets a recovery passphrase
in the swarm secret store, so a crypto store lost to
--purgeor a token reset comes back with its history.
The details, and the retired hive-side code, are in
matrix-provisioning.md
and the implementation notes.
Configuration tuning
services.hyperhive.deploy.matrix = {
trustedServers = [ "matrix.org" "example.com" ]; # default: []
maxRequestSize = 20000000; # default: 20 MB
};
trustedServers (default []) — list of peer homeserver names
whose signing keys tuwunel fetches and trusts. tuwunel enables
federation at the protocol level from first boot (allow_federation = true) but trusts no remote homeserver until you list it here. For a closed
single-hive deployment the default empty list is correct — add peer
hive domains here when connecting hives into a swarm (see
docs/2-handbook/swarm/).
maxRequestSize (default 20_000_000 bytes = 20 MB) — maximum
size of a single matrix client request body. Matches the matrix-spec
recommendation for media uploads and the upstream tuwunel default.
Raise for deployments that need large file transfers; lower for
resource-constrained hosts where a 20 MB request is unexpectedly large.