From d60a0585d680f3df11933e04a4b78c918772faa8 Mon Sep 17 00:00:00 2001 From: atlas Date: Thu, 13 Aug 2026 12:45:47 +0200 Subject: [PATCH] refactor(3202): the forge declares its own vhost and dns name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moves `forgeVhost` out of the gateway's vhosts.nix and the forge's `address=` rule out of dnsmasq.nix, into nix/host-modules/hive-forge — the module that already owns everything else about the forge. The gateway keeps what is gateway knowledge (the listen set, which issuer covers a name, the header block) and loses the last reason it had to read `swarm.forge` at all: `forgeCfg` is gone from both files and from the module's `let`. Both halves stay gated on `behindGateway` — with it off the operator fronts forgejo themselves, so this hive must neither claim the vhost nor answer DNS for the name. --- nix/host-modules/hive-forge/default.nix | 33 +++++++++++++++++++++++ nix/host-modules/hive-gateway/default.nix | 3 --- nix/host-modules/hive-gateway/dnsmasq.nix | 16 +++++------ nix/host-modules/hive-gateway/vhosts.nix | 24 ----------------- 4 files changed, 40 insertions(+), 36 deletions(-) diff --git a/nix/host-modules/hive-forge/default.nix b/nix/host-modules/hive-forge/default.nix index 2cc01ce6..2527f453 100644 --- a/nix/host-modules/hive-forge/default.nix +++ b/nix/host-modules/hive-forge/default.nix @@ -397,6 +397,39 @@ in }; config = lib.mkIf config.services.hyperhive.enable { + # This service's own gateway surface: the vhost that fronts it and + # the name the hive resolver answers for. Declared here rather than + # in the gateway so the forge's public face lives with the forge — + # the gateway supplies the primitives (`lib.listen`, `lib.tlsFor`, + # `lib.securityHeaders`) and never needs to know this service by + # name. + # + # Both halves are gated on `behindGateway`: with it off the operator + # fronts forgejo themselves, so this hive must neither claim the + # vhost nor answer DNS for it. + services.hyperhive.gateway.localNames = lib.optional cfg.behindGateway cfg.domain; + + # `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`. + services.nginx.virtualHosts = lib.optionalAttrs cfg.behindGateway { + "${cfg.domain}" = (gatewayCfg.lib.tlsFor cfg.domain) // { + listen = gatewayCfg.lib.listen; + extraConfig = gatewayCfg.lib.securityHeaders; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString cfg.httpPort}/"; + proxyWebsockets = true; + extraConfig = '' + proxy_buffering off; + client_max_body_size 1G; + proxy_read_timeout 1h; + proxy_send_timeout 1h; + ''; + }; + }; + }; + assertions = [ { # Fail at EVAL, not at boot. The alternative failure is a login diff --git a/nix/host-modules/hive-gateway/default.nix b/nix/host-modules/hive-gateway/default.nix index db4188b9..a2cdc95e 100644 --- a/nix/host-modules/hive-gateway/default.nix +++ b/nix/host-modules/hive-gateway/default.nix @@ -25,7 +25,6 @@ let autheliaCfg = config.services.hyperhive.swarm.authelia; uiCfg = config.services.hyperhive.swarm.ui; controllerCfg = config.services.hyperhive.swarm.controller; - forgeCfg = config.services.hyperhive.swarm.forge; networkCfg = config.services.hyperhive.network; # Dashboard SPA dist, static-served by nginx. @@ -87,7 +86,6 @@ let inherit lib cfg - forgeCfg matrixCfg autheliaCfg uiCfg @@ -374,7 +372,6 @@ in lib cfg networkCfg - forgeCfg matrixCfg autheliaCfg uiCfg diff --git a/nix/host-modules/hive-gateway/dnsmasq.nix b/nix/host-modules/hive-gateway/dnsmasq.nix index 2cfba396..89e6f952 100644 --- a/nix/host-modules/hive-gateway/dnsmasq.nix +++ b/nix/host-modules/hive-gateway/dnsmasq.nix @@ -9,7 +9,6 @@ lib, cfg, # services.hyperhive.gateway networkCfg, - forgeCfg, matrixCfg, autheliaCfg, uiCfg, @@ -57,17 +56,16 @@ # + its sub-domains with the bridge IP, where nginx is reachable # from every container netns. # - # The forge / matrix entries are redundant in the common case - # where `forge.domain` / `matrix.gatewayHost` are sub-domains of - # `hyperhive.domain` — dnsmasq's `//` rule already matches - # sub-domains. Kept explicit because operators can override either - # to a cross-domain hostname (e.g. `forge.domain = - # "git.example.com"`); listing them explicitly keeps that case - # routed without needing an extra config block. + # The matrix entry is redundant in the common case where + # `matrix.gatewayHost` is a sub-domain of `hyperhive.domain` — + # dnsmasq's `//` 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 ((forgeCfg.behindGateway or false)) "/${forgeCfg.domain}/${networkCfg.bridgeIp}" ++ lib.optional ( matrixCfg.enable && matrixCfg.gatewayHost != null ) "/${matrixCfg.gatewayHost}/${networkCfg.bridgeIp}" diff --git a/nix/host-modules/hive-gateway/vhosts.nix b/nix/host-modules/hive-gateway/vhosts.nix index b35e75f7..e2a5a1e9 100644 --- a/nix/host-modules/hive-gateway/vhosts.nix +++ b/nix/host-modules/hive-gateway/vhosts.nix @@ -7,7 +7,6 @@ { lib, cfg, # services.hyperhive.gateway - forgeCfg, matrixCfg, autheliaCfg, # services.hyperhive.swarm.authelia uiCfg, # services.hyperhive.swarm.ui @@ -37,28 +36,6 @@ let publicPort = cfg.httpsPort; publicPortSuffix = if publicPort == 443 then "" else ":${toString publicPort}"; - # 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.behindGateway or false) { - "${forgeCfg.domain}" = (vhostTlsFor forgeCfg.domain) // { - 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; - ''; - }; - }; - }; - # Authelia sub-domain vhost. `server_name = authelia.domain`, all of # `/` → authelia. Empty attrset unless THIS host runs the container: # every hive knows the swarm's `authelia.url`, but only the one @@ -504,7 +481,6 @@ in ''; }; } - // forgeVhost // autheliaVhost // matrixVhost // swarmUiVhost;