From a03aafb004a52d9571fec3c3c1551320c60aebc2 Mon Sep 17 00:00:00 2001 From: atlas Date: Sat, 6 Jun 2026 13:08:55 +0200 Subject: [PATCH] refactor(gateway): extract forge + matrix sub-domain vhosts into named bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- nix/modules/hive-gateway.nix | 170 +++++++++++++++++------------------ 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/nix/modules/hive-gateway.nix b/nix/modules/hive-gateway.nix index 2a0e1531..b838cbc1 100644 --- a/nix/modules/hive-gateway.nix +++ b/nix/modules/hive-gateway.nix @@ -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