refactor(gateway): extract forge + matrix sub-domain vhosts into named bindings

Step 1 of the hive-gateway.nix vhost-builder cleanup. The two
sub-domain vhosts (forge, matrix) were inline `lib.optionalAttrs`
blocks `//`-appended to the virtualHosts attrset, burying the
top-level structure. Lift them into `forgeVhost` / `matrixVhost`
bindings in the config-closure let so the composition reads as
`{ "_" = ...; } // forgeVhost // matrixVhost` — the three vhosts are
now visible at a glance. Also fixes a garbled merge-mangled comment on
the nginx-reload host-trigger.

Pure readability refactor, eval-identical: verified the generated
`services.nginx.virtualHosts` toJSON is byte-identical before/after
(8888 bytes, diff empty) on a host with matrix+forge+gui+auth+tls all
enabled. The deeper `_` vhost location-group extraction is a follow-up.
This commit is contained in:
atlas 2026-06-06 13:08:55 +02:00 committed by mara
commit a03aafb004

View file

@ -565,6 +565,82 @@ in
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
${lib.optionalString cfg.hsts.enable ''add_header Strict-Transport-Security "${hstsDirectives}" always;''}
'';
# Forge sub-domain vhost. `server_name = forge.domain`, proxies
# all `/` → forgejo. Tuned for git: `client_max_body_size 1G`,
# `proxy_read_timeout 1h` (multi-GB clones). SSH stays direct on
# `forge.sshPort`. See `docs/gateway.md`. Empty attrset when the
# forge isn't behind the gateway.
forgeVhost = lib.optionalAttrs (forgeCfg.enable or false && forgeCfg.behindGateway or false) {
"${forgeCfg.domain}" = vhostTls // {
listen = vhostListen;
extraConfig = securityHeaders;
locations."/" = {
proxyPass = "http://127.0.0.1:${toString forgeCfg.httpPort}/";
proxyWebsockets = true;
extraConfig = ''
proxy_buffering off;
client_max_body_size 1G;
proxy_read_timeout 1h;
proxy_send_timeout 1h;
'';
};
};
};
# 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}" = vhostTls // {
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;
'';
};
}
// lib.optionalAttrs (hyperhiveDomain != null) {
# FluffyChat boot-config pre-fill so the client's
# `.well-known/matrix/client` lookup hits the
# right delegation endpoint.
"= /config.json" = {
extraConfig = ''
default_type application/json;
return 200 '{"defaultHomeserver":"${hyperhiveDomain}"}';
'';
};
}
)
// lib.optionalAttrs (!matrixCfg.gui.enable) {
"/" = {
return = "404";
};
};
};
};
in
{
system.stateVersion = "26.05";
@ -657,14 +733,13 @@ in
};
# nginx reload is triggered from the HOST side by hive-c0re
# via `systemctl -M hive-gateway reload nginx` — lets systemd
# resolve the nginx binary path, avoiding exit-203 EXEC failures.
# after each agents.conf write. A path unit watching the
# bind-mounted file inside the container was tried first
# (A path unit inside the container was tried but IN_MOVED_TO from an atomic rename on the host
# does not propagate across the nspawn mount-namespace boundary,
# does not cross the mount-namespace boundary. Host-side trigger is the
# correct approach.
# via `systemctl -M hive-gateway reload nginx` after each
# agents.conf write — letting systemd resolve the nginx binary
# path avoids exit-203 EXEC failures. A path unit watching the
# bind-mounted file inside the container was tried first but
# doesn't work: an IN_MOVED_TO from an atomic rename on the host
# does not propagate across the nspawn mount-namespace boundary.
# The host-side trigger is the correct approach.
services.nginx = {
enable = true;
@ -879,83 +954,8 @@ in
'';
};
}
//
# Forge sub-domain vhost. `server_name =
# forge.domain`, proxies all `/` → forgejo. Tuned for
# git: `client_max_body_size 1G`, `proxy_read_timeout 1h`
# (multi-GB clones). SSH stays direct on `forge.sshPort`.
# See `docs/gateway.md`.
lib.optionalAttrs (forgeCfg.enable or false && forgeCfg.behindGateway or false) {
"${forgeCfg.domain}" = vhostTls // {
listen = vhostListen;
extraConfig = securityHeaders;
locations."/" = {
proxyPass = "http://127.0.0.1:${toString forgeCfg.httpPort}/";
proxyWebsockets = true;
extraConfig = ''
proxy_buffering off;
client_max_body_size 1G;
proxy_read_timeout 1h;
proxy_send_timeout 1h;
'';
};
};
}
//
# 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`.
lib.optionalAttrs (matrixCfg.enable && matrixCfg.gatewayHost != null) {
"${matrixCfg.gatewayHost}" = vhostTls // {
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;
'';
};
}
// lib.optionalAttrs (hyperhiveDomain != null) {
# FluffyChat boot-config pre-fill so the client's
# `.well-known/matrix/client` lookup hits the
# right delegation endpoint.
"= /config.json" = {
extraConfig = ''
default_type application/json;
return 200 '{"defaultHomeserver":"${hyperhiveDomain}"}';
'';
};
}
)
// lib.optionalAttrs (!matrixCfg.gui.enable) {
"/" = {
return = "404";
};
};
};
};
// forgeVhost
// matrixVhost;
};
# Hive-internal DNS resolver, co-located in the