refactor(3202): matrix declares its own vhost, dns name and SPA map

Moves the matrix sub-domain vhost out of the gateway's vhosts.nix, its
`address=` rule out of dnsmasq.nix, and the Accept-header
`$matrix_spa_target` map out of the gateway's appendHttpConfig — all
three into hive-matrix.nix.

The map is the one that had no business being where it was: it exists
solely for the SPA fallback in the vhost's `/` location, and
`appendHttpConfig` is a `lines` option, so a module can contribute to
it without the gateway assembling it.

The `.well-known/matrix/*` delegation deliberately stays on the hive's
own vhost. The spec requires it at the SERVER NAME, which is the hive
domain: that is the hive answering "where is my homeserver", not the
homeserver answering for itself. Moving it would have been the obvious
symmetric thing and it would have been wrong.
This commit is contained in:
atlas 2026-08-13 14:36:17 +02:00
commit 6caf177416
4 changed files with 82 additions and 82 deletions

View file

@ -347,7 +347,7 @@ in
recommendedTlsSettings = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
inherit (nginxTree) appendHttpConfig virtualHosts;
inherit (nginxTree) virtualHosts;
};
# ⚠️ NO `SupplementaryGroups = [ "hive-core" ]` on nginx, and its
@ -379,7 +379,6 @@ in
lib
cfg
networkCfg
matrixCfg
uiCfg
hyperhiveDomain
;

View file

@ -9,7 +9,6 @@
lib,
cfg, # services.hyperhive.gateway
networkCfg,
matrixCfg,
uiCfg,
hyperhiveDomain,
}:
@ -54,20 +53,9 @@
# Hive authoritative records — answer queries for the hive domain
# + its sub-domains with the bridge IP, where nginx is reachable
# from every container netns.
#
# The matrix entry is redundant in the common case where
# `matrix.gatewayHost` is a sub-domain of `hyperhive.domain` —
# dnsmasq's `/<domain>/` rule already matches sub-domains. Kept
# explicit because an operator can override it to a cross-domain
# hostname (e.g. `git.example.com` for the forge); listing such a
# name explicitly keeps that case routed without an extra config
# block.
address = [
"/${hyperhiveDomain}/${networkCfg.bridgeIp}"
]
++ lib.optional (
matrixCfg.enable && matrixCfg.gatewayHost != null
) "/${matrixCfg.gatewayHost}/${networkCfg.bridgeIp}"
# The swarm UI's name is the swarm APEX by default — a sibling of
# the three above, not a child of anything this resolver already
# answers for, so the `/<hive domain>/` rule does not cover it.

View file

@ -3,7 +3,7 @@
# and authelia sub-domain vhosts, and the Accept-header SPA map for the
# matrix GUI. Pure function — called from ./default.nix with the
# outer-scope config values as arguments; returns
# `{ virtualHosts, appendHttpConfig }`.
# `{ virtualHosts }`.
{
lib,
cfg, # services.hyperhive.gateway
@ -145,61 +145,6 @@ let
};
};
# Matrix sub-domain vhost. `server_name = matrixCfg.gatewayHost`.
# `/_matrix/*` → tuwunel (CORS *, 50M body cap, 1h long-poll
# timeout). `/` serves fluffychat or 404 if GUI off. nginx
# longer-prefix-wins puts `/_matrix/` ahead of `/`. See
# `docs/gateway.md`. Empty attrset when matrix has no gateway host.
matrixVhost = lib.optionalAttrs (matrixCfg.enable && matrixCfg.gatewayHost != null) {
"${matrixCfg.gatewayHost}" = (vhostTlsFor matrixCfg.gatewayHost) // {
listen = vhostListen;
extraConfig = securityHeaders;
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;
${securityHeaders}
add_header Access-Control-Allow-Origin *;
'';
};
}
// lib.optionalAttrs (matrixCfg.gui.enable) (
{
# fluffychat at sub-domain root, SPA-fallback via
# the Accept-header `$matrix_spa_target` map.
"/" = {
alias = "${matrixCfg.gui.package}/";
extraConfig = ''
try_files $uri $uri/ $matrix_spa_target =404;
'';
};
}
// {
# FluffyChat boot-config pre-fill so the client's
# `.well-known/matrix/client` lookup hits the
# right delegation endpoint. `domain` is required, so
# this is always present.
"= /config.json" = {
extraConfig = ''
default_type application/json;
return 200 '{"defaultHomeserver":"${hyperhiveDomain}"}';
'';
};
}
)
// lib.optionalAttrs (!matrixCfg.gui.enable) {
"/" = {
return = "404";
};
};
};
};
# `<hive>/matrix/*` → 301 → `matrix.<hive>/$1` (legacy deep-link
# shim during the fluffychat sub-domain move). See `docs/gateway.md`.
matrixRedirectLocations =
@ -385,17 +330,6 @@ let
};
in
{
# Accept-header SPA map for the matrix GUI only (see docs/gateway.md
# "SPA fallback"): text/html → index.html, else a sentinel so
# try_files falls through to 404. The dashboard doesn't use an
# Accept-header map — it routes by path (see dashboardProxyLocation).
appendHttpConfig = lib.optionalString (matrixCfg.enable && matrixCfg.gui.enable) ''
map $http_accept $matrix_spa_target {
default "/__matrix_spa_no_html_fallback";
"~*text/html" "/index.html";
}
'';
virtualHosts = {
# `tlsFor "_"`, not a separate binding: the default server is a
# vhost named `_`, and a name that is not a swarm service domain
@ -435,6 +369,5 @@ in
};
}
// matrixVhost
// swarmUiVhost;
}