Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b106bb8c87 | ||
|
|
01cc664d76 |
2 changed files with 180 additions and 24 deletions
|
|
@ -326,21 +326,48 @@ in
|
|||
# (https://spec.matrix.org/v1.15/client-server-api/#getwell-knownmatrixclient).
|
||||
# No-op until the operator turns matrix on; until then
|
||||
# there's no homeserver to advertise.
|
||||
lib.optionalAttrs (matrixCfg.enable && hyperhiveDomain != null) {
|
||||
"= /.well-known/matrix/client" = {
|
||||
extraConfig = ''
|
||||
default_type application/json;
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
return 200 '{"m.homeserver":{"base_url":"http://${hyperhiveDomain}:${toString matrixCfg.httpPort}"}}';
|
||||
'';
|
||||
};
|
||||
"= /.well-known/matrix/server" = {
|
||||
extraConfig = ''
|
||||
default_type application/json;
|
||||
return 200 '{"m.server":"${hyperhiveDomain}:${toString matrixCfg.httpPort}"}';
|
||||
'';
|
||||
};
|
||||
}
|
||||
lib.optionalAttrs (matrixCfg.enable && hyperhiveDomain != null) (
|
||||
let
|
||||
# `.well-known/matrix/{client,server}` advertise where
|
||||
# the actual matrix API lives. When `matrixCfg.gatewayHost`
|
||||
# is set (default `matrix.<hive-domain>`, #747), point
|
||||
# at the sub-domain — no port suffix when the gateway
|
||||
# is on the canonical port 80, transparent to clients
|
||||
# (mara on #749:9609 sub-domain verdict, "not user-
|
||||
# visible because the .well-known redirect routes
|
||||
# clients through automatically"). When `gatewayHost`
|
||||
# is unset (no hive-domain, or operator nulled it),
|
||||
# fall back to the direct `host:port` shape — clients
|
||||
# reach tuwunel without going through the gateway,
|
||||
# no sub-domain delegation.
|
||||
portSuffix = if cfg.port == 80 then "" else ":${toString cfg.port}";
|
||||
clientBaseUrl =
|
||||
if matrixCfg.gatewayHost != null then
|
||||
"http://${matrixCfg.gatewayHost}${portSuffix}"
|
||||
else
|
||||
"http://${hyperhiveDomain}:${toString matrixCfg.httpPort}";
|
||||
serverHostPort =
|
||||
if matrixCfg.gatewayHost != null then
|
||||
"${matrixCfg.gatewayHost}${portSuffix}"
|
||||
else
|
||||
"${hyperhiveDomain}:${toString matrixCfg.httpPort}";
|
||||
in
|
||||
{
|
||||
"= /.well-known/matrix/client" = {
|
||||
extraConfig = ''
|
||||
default_type application/json;
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
return 200 '{"m.homeserver":{"base_url":"${clientBaseUrl}"}}';
|
||||
'';
|
||||
};
|
||||
"= /.well-known/matrix/server" = {
|
||||
extraConfig = ''
|
||||
default_type application/json;
|
||||
return 200 '{"m.server":"${serverHostPort}"}';
|
||||
'';
|
||||
};
|
||||
}
|
||||
)
|
||||
//
|
||||
# Per-agent UIs (#15 v0). One `/agent/<name>/`
|
||||
# block per `<name>: <port>` entry in
|
||||
|
|
@ -442,6 +469,61 @@ in
|
|||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
//
|
||||
# Matrix homeserver vhost (#747, mara verdict on #749:9609 —
|
||||
# sub-domain over sub-path for matrix; "not user-visible"
|
||||
# because clients discover the sub-domain via the
|
||||
# `.well-known/matrix/{client,server}` delegation served
|
||||
# above on the bare hive-domain).
|
||||
#
|
||||
# `server { server_name = matrixCfg.gatewayHost; }` proxies
|
||||
# `/_matrix/...` → `http://127.0.0.1:''${matrixCfg.httpPort}/_matrix/...`.
|
||||
# Tuwunel listens on `:''${httpPort}` (default 8008); the
|
||||
# gateway terminates on `:''${cfg.port}` (80) so external
|
||||
# clients speak matrix over the canonical web port without
|
||||
# operators having to open the tuwunel port through firewalls.
|
||||
#
|
||||
# `/` returns 404 — nothing else lives at the matrix vhost;
|
||||
# the matrix client-server API is entirely under `/_matrix/`,
|
||||
# and federation under `/_matrix/federation/...`.
|
||||
#
|
||||
# CORS `*` on the matrix vhost per the matrix spec —
|
||||
# federation + client requests come from any origin.
|
||||
#
|
||||
# `client_max_body_size 50M` covers typical media uploads
|
||||
# (matrix-spec media size cap default); operators with bigger
|
||||
# uploads override via the matrix module's own cap when that
|
||||
# lands.
|
||||
#
|
||||
# `proxy_read_timeout 1h` for long-poll `/sync`; the default
|
||||
# 60s would abort `/sync?timeout=30000` legitimately when
|
||||
# tuwunel's keepalive exceeds that.
|
||||
lib.optionalAttrs (matrixCfg.enable && matrixCfg.gatewayHost != null) {
|
||||
"${matrixCfg.gatewayHost}" = {
|
||||
listen = [
|
||||
{
|
||||
addr = "0.0.0.0";
|
||||
port = cfg.port;
|
||||
}
|
||||
];
|
||||
locations = {
|
||||
"/_matrix/" = {
|
||||
proxyPass = "http://127.0.0.1:${toString matrixCfg.httpPort}";
|
||||
proxyWebsockets = true;
|
||||
extraConfig = ''
|
||||
proxy_buffering off;
|
||||
client_max_body_size 50M;
|
||||
proxy_read_timeout 1h;
|
||||
proxy_send_timeout 1h;
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
'';
|
||||
};
|
||||
"/" = {
|
||||
return = "404";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -452,16 +534,16 @@ in
|
|||
};
|
||||
|
||||
# `/etc/hosts` entries for local dev: the bare hive domain plus
|
||||
# any sub-domain modules (forge, matrix-via-#751) that are on.
|
||||
# All map to `127.0.0.1` since the gateway shares host netns.
|
||||
# Operators with real DNS leave `localHostsEntry = false`; this
|
||||
# is the dev-loop shortcut for `http://<hive-domain>/` +
|
||||
# `http://forge.<hive-domain>/` resolving locally.
|
||||
# any sub-domain modules (forge via #749/#754, matrix via #747)
|
||||
# that are on. All map to `127.0.0.1` since the gateway shares
|
||||
# host netns. Operators with real DNS leave `localHostsEntry =
|
||||
# false`; this is the dev-loop shortcut for `http://<hive-domain>/`
|
||||
# + `http://forge.<hive-domain>/` + `http://matrix.<hive-domain>/`
|
||||
# resolving locally.
|
||||
#
|
||||
# Forge's `cfg.domain` may equal `hyperhiveDomain` (e.g. operator
|
||||
# set `forge.domain = "darkest.space"` matching the hive domain)
|
||||
# — `lib.unique` collapses the duplicate so `/etc/hosts` doesn't
|
||||
# carry the same entry twice.
|
||||
# `lib.unique` collapses any duplicate (e.g. if forge.domain
|
||||
# happens to equal hyperhiveDomain or matrixCfg.gatewayHost) so
|
||||
# `/etc/hosts` doesn't carry the same entry twice.
|
||||
networking.hosts = lib.mkIf (cfg.localHostsEntry && hyperhiveDomain != null) {
|
||||
"127.0.0.1" = lib.unique (
|
||||
[ hyperhiveDomain ]
|
||||
|
|
@ -469,6 +551,7 @@ in
|
|||
(config.services.hyperhive.forge.enable or false)
|
||||
&& (config.services.hyperhive.forge.behindGateway or false)
|
||||
) config.services.hyperhive.forge.domain
|
||||
++ lib.optional (matrixCfg.enable && matrixCfg.gatewayHost != null) matrixCfg.gatewayHost
|
||||
);
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -252,6 +252,63 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
gatewayHost = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = if hyperhiveDomain != null then "matrix.${hyperhiveDomain}" else null;
|
||||
defaultText = lib.literalExpression ''
|
||||
if services.hyperhive.domain != null then
|
||||
"matrix.''${services.hyperhive.domain}"
|
||||
else
|
||||
null
|
||||
'';
|
||||
example = "matrix.example.com";
|
||||
description = ''
|
||||
Public hostname for the matrix homeserver behind the
|
||||
hive-gateway nginx (#747, mara verdict on #749:9609 — sub-domain
|
||||
over sub-path for matrix, but **not user-visible** because the
|
||||
`.well-known/matrix/{client,server}` redirect routes clients
|
||||
through automatically).
|
||||
|
||||
When set + gateway is on, the gateway adds a `server { server_name
|
||||
= gatewayHost; }` block that proxies `/_matrix/...` →
|
||||
`http://127.0.0.1:''${httpPort}/_matrix/...`. The
|
||||
`.well-known/matrix/{client,server}` endpoints (served by the
|
||||
gateway at the bare hive-domain) then point at
|
||||
`http(s)://''${gatewayHost}/` — matrix clients automatically
|
||||
discover + follow that delegation.
|
||||
|
||||
Defaults to `matrix.''${services.hyperhive.domain}` when the
|
||||
hive-domain is set (idiomatic matrix-spec shape — `matrix`
|
||||
labelled under the hive's bare server_name domain). Defaults to
|
||||
`null` when the hive-domain is unset (gateway vhost not added;
|
||||
clients reach tuwunel directly on `httpPort`).
|
||||
|
||||
Set to a full hostname (`matrix.example.com`,
|
||||
`homeserver.internal.lan`) for a bespoke vhost shape. Set to
|
||||
`null` to disable the gateway vhost entirely (tuwunel stays
|
||||
direct on `httpPort`).
|
||||
|
||||
**server_name vs gatewayHost**: `serverName` is the matrix
|
||||
identifier domain embedded in user/room IDs irrevocably (per
|
||||
#660 default = bare hive-domain). `gatewayHost` is just where
|
||||
the API listens behind nginx. The two are different — see the
|
||||
matrix-spec server-discovery flow.
|
||||
|
||||
**Federation port caveat**: the `.well-known/matrix/server`
|
||||
delegation advertises `''${gatewayHost}` with no port suffix
|
||||
when the gateway listens on 80. Per the matrix federation
|
||||
spec, peers fall back to port 8448 when no explicit port is
|
||||
present — but the gateway only listens on the configured
|
||||
`services.hyperhive.gateway.port`. Cross-hive federation
|
||||
therefore needs either:
|
||||
- a DNS SRV record (`_matrix._tcp.''${gatewayHost}` → port 80),
|
||||
- or `services.hyperhive.matrix.openFirewall = true` so peers
|
||||
can reach tuwunel's federation port directly.
|
||||
Hyperhive is mostly closed/internal, so this rarely bites in
|
||||
practice — but flagging for the federation-curious operator.
|
||||
'';
|
||||
};
|
||||
|
||||
openFirewall = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
|
|
@ -395,6 +452,22 @@ in
|
|||
stable hostname before enabling.
|
||||
'';
|
||||
}
|
||||
{
|
||||
# Same footgun as forge.domain (#754): empty string renders
|
||||
# `.<hive>` shaped garbage in both nginx server_name (treated
|
||||
# as wildcard catch-all, surprising) and /etc/hosts (invalid
|
||||
# entry). Argus 🟡 on #764 — fail loud here rather than ship
|
||||
# the surprising behaviour.
|
||||
assertion = cfg.gatewayHost == null || cfg.gatewayHost != "";
|
||||
message = ''
|
||||
services.hyperhive.matrix.gatewayHost = "" is rejected. The
|
||||
rendered URLs would be invalid (nginx wildcard catch-all for
|
||||
an empty server_name, /etc/hosts rejects empty entries).
|
||||
Use `null` to disable the gateway vhost entirely (tuwunel
|
||||
stays direct on httpPort), or set a non-empty hostname like
|
||||
"matrix.example.com" or "homeserver.internal".
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
# Generate the registration token at system activation time, BEFORE
|
||||
|
|
|
|||
Loading…
Reference in a new issue