Compare commits
2 changed files with 24 additions and 180 deletions
|
|
@ -326,48 +326,21 @@ in
|
||||||
# (https://spec.matrix.org/v1.15/client-server-api/#getwell-knownmatrixclient).
|
# (https://spec.matrix.org/v1.15/client-server-api/#getwell-knownmatrixclient).
|
||||||
# No-op until the operator turns matrix on; until then
|
# No-op until the operator turns matrix on; until then
|
||||||
# there's no homeserver to advertise.
|
# there's no homeserver to advertise.
|
||||||
lib.optionalAttrs (matrixCfg.enable && hyperhiveDomain != null) (
|
lib.optionalAttrs (matrixCfg.enable && hyperhiveDomain != null) {
|
||||||
let
|
"= /.well-known/matrix/client" = {
|
||||||
# `.well-known/matrix/{client,server}` advertise where
|
extraConfig = ''
|
||||||
# the actual matrix API lives. When `matrixCfg.gatewayHost`
|
default_type application/json;
|
||||||
# is set (default `matrix.<hive-domain>`, #747), point
|
add_header Access-Control-Allow-Origin *;
|
||||||
# at the sub-domain — no port suffix when the gateway
|
return 200 '{"m.homeserver":{"base_url":"http://${hyperhiveDomain}:${toString matrixCfg.httpPort}"}}';
|
||||||
# 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
|
"= /.well-known/matrix/server" = {
|
||||||
# clients through automatically"). When `gatewayHost`
|
extraConfig = ''
|
||||||
# is unset (no hive-domain, or operator nulled it),
|
default_type application/json;
|
||||||
# fall back to the direct `host:port` shape — clients
|
return 200 '{"m.server":"${hyperhiveDomain}:${toString matrixCfg.httpPort}"}';
|
||||||
# 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>/`
|
# Per-agent UIs (#15 v0). One `/agent/<name>/`
|
||||||
# block per `<name>: <port>` entry in
|
# block per `<name>: <port>` entry in
|
||||||
|
|
@ -469,61 +442,6 @@ 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";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -534,16 +452,16 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
# `/etc/hosts` entries for local dev: the bare hive domain plus
|
# `/etc/hosts` entries for local dev: the bare hive domain plus
|
||||||
# any sub-domain modules (forge via #749/#754, matrix via #747)
|
# any sub-domain modules (forge, matrix-via-#751) that are on.
|
||||||
# that are on. All map to `127.0.0.1` since the gateway shares
|
# All map to `127.0.0.1` since the gateway shares host netns.
|
||||||
# host netns. Operators with real DNS leave `localHostsEntry =
|
# Operators with real DNS leave `localHostsEntry = false`; this
|
||||||
# false`; this is the dev-loop shortcut for `http://<hive-domain>/`
|
# is the dev-loop shortcut for `http://<hive-domain>/` +
|
||||||
# + `http://forge.<hive-domain>/` + `http://matrix.<hive-domain>/`
|
# `http://forge.<hive-domain>/` resolving locally.
|
||||||
# resolving locally.
|
|
||||||
#
|
#
|
||||||
# `lib.unique` collapses any duplicate (e.g. if forge.domain
|
# Forge's `cfg.domain` may equal `hyperhiveDomain` (e.g. operator
|
||||||
# happens to equal hyperhiveDomain or matrixCfg.gatewayHost) so
|
# set `forge.domain = "darkest.space"` matching the hive domain)
|
||||||
# `/etc/hosts` doesn't carry the same entry twice.
|
# — `lib.unique` collapses the duplicate so `/etc/hosts` doesn't
|
||||||
|
# carry the same entry twice.
|
||||||
networking.hosts = lib.mkIf (cfg.localHostsEntry && hyperhiveDomain != null) {
|
networking.hosts = lib.mkIf (cfg.localHostsEntry && hyperhiveDomain != null) {
|
||||||
"127.0.0.1" = lib.unique (
|
"127.0.0.1" = lib.unique (
|
||||||
[ hyperhiveDomain ]
|
[ hyperhiveDomain ]
|
||||||
|
|
@ -551,7 +469,6 @@ in
|
||||||
(config.services.hyperhive.forge.enable or false)
|
(config.services.hyperhive.forge.enable or false)
|
||||||
&& (config.services.hyperhive.forge.behindGateway or false)
|
&& (config.services.hyperhive.forge.behindGateway or false)
|
||||||
) config.services.hyperhive.forge.domain
|
) config.services.hyperhive.forge.domain
|
||||||
++ lib.optional (matrixCfg.enable && matrixCfg.gatewayHost != null) matrixCfg.gatewayHost
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -252,63 +252,6 @@ 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 {
|
openFirewall = lib.mkOption {
|
||||||
type = lib.types.bool;
|
type = lib.types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
|
|
@ -452,22 +395,6 @@ in
|
||||||
stable hostname before enabling.
|
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
|
# Generate the registration token at system activation time, BEFORE
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue