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.
207 lines
9.7 KiB
Markdown
207 lines
9.7 KiB
Markdown
# 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`](../../4-implementation/networking/gateway.md).
|
|
|
|
## Container shape
|
|
|
|
Same shape as [`gateway.md::hive-forge container shape`](../../4-implementation/networking/gateway.md):
|
|
|
|
- Container name `hive-matrix` (not `h-*`) so c0re's lifecycle
|
|
scanner ignores it; operator manages via the standard
|
|
`nixos-container` CLI.
|
|
- 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 at `chat.<swarm-domain>` via the gateway, which
|
|
dials its unix socket (see [Reach](#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-spec `server_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
|
|
bare `services.hyperhive.swarm.domain`.
|
|
- **`gatewayHost`** — the API listener hostname, where the gateway's
|
|
matrix vhost proxies `/_matrix/*` to tuwunel. Defaults to
|
|
`chat.<services.hyperhive.swarm.domain>`. Set to `null` to 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.
|
|
|
|
<details><summary>Pinning <code>serverName</code> on a homeserver with existing ids</summary>
|
|
|
|
`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:
|
|
|
|
```nix
|
|
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.
|
|
|
|
</details>
|
|
|
|
## 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`](../../4-implementation/networking/gateway.md#discovery-flow-matrix)).
|
|
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`](../../4-implementation/swarm/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 `admins` revokes 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 in `admins` but 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 in
|
|
`services.hyperhive.deploy.swarm-controller.matrixAdminSync.ignoreUsers` are never
|
|
revoked.
|
|
- **The last admin stays.** A revoke that would leave no admin in
|
|
`#admins` besides `@swarm` and 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 `#admins` after the controller sends its grant gets a
|
|
`make-user-admin … failed to take` warning in the controller log on
|
|
each later pass, and no second command until the controller restarts;
|
|
a revoke that leaves its account in `#admins` gets
|
|
`force-leave-room … failed to take` the same way.
|
|
- Runs only on a controller with both
|
|
`services.hyperhive.deploy.swarm-controller.authBridgeUrl` and
|
|
`services.hyperhive.deploy.swarm-controller.matrixHomeserverUrl` set.
|
|
|
|
|
|
### Settings
|
|
|
|
- **Federation** is on at the protocol level, but `trusted_servers = []` keeps
|
|
it closed until you list peers in `trustedServers` (see
|
|
[Configuration tuning](#configuration-tuning)).
|
|
- **Registration** is off. The swarm's appservice still creates accounts, and
|
|
the homeserver refuses everyone else.
|
|
- **Encryption**: `services.hyperhive.deploy.matrix.allowEncryption` (default
|
|
`false`) 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 `--purge` or a token
|
|
reset comes back with its history.
|
|
|
|
The details, and the retired hive-side code, are in
|
|
[`matrix-provisioning.md`](../../4-implementation/swarm/matrix-provisioning.md)
|
|
and the [implementation notes](../../4-implementation/networking/matrix-homeserver.md).
|
|
|
|
## Configuration tuning
|
|
|
|
```nix
|
|
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/`](README.md)).
|
|
|
|
**`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.
|