diff --git a/nix/modules/hive-c0re.nix b/nix/modules/hive-c0re.nix index 19863253..c5aeacce 100644 --- a/nix/modules/hive-c0re.nix +++ b/nix/modules/hive-c0re.nix @@ -195,19 +195,29 @@ in ]; # Dashboard + per-container web UIs share the host's network namespace and - # need their ports reachable. Dashboard: `cfg.dashboardPort` (default 7000). - # Manager: 8000. Sub-agents: 8100..8999 (deterministic hash; see - # `lifecycle::agent_web_port`). - networking.firewall.allowedTCPPorts = [ - cfg.dashboardPort - 8000 - ]; - networking.firewall.allowedTCPPortRanges = [ - { - from = 8100; - to = 8999; - } - ]; + # need their ports reachable when there's no gateway in front. Dashboard: + # `cfg.dashboardPort` (default 7000). Manager: 8000. Sub-agents: 8100..8999 + # (deterministic hash; see `lifecycle::agent_web_port`). + # + # When `services.hyperhive.gateway.enable = true` (the default), the + # gateway nginx is the sole external entry point and proxies to + # `127.0.0.1:7000` etc. internally — leaving the direct ports open + # in the host firewall would defeat the gateway's "single front + # door" story (closes #621). Operators who opt out of the gateway + # still get the direct ports opened so the legacy + # `http://:7000/` flow works. + networking.firewall = lib.mkIf (!config.services.hyperhive.gateway.enable) { + allowedTCPPorts = [ + cfg.dashboardPort + 8000 + ]; + allowedTCPPortRanges = [ + { + from = 8100; + to = 8999; + } + ]; + }; systemd.services.hive-c0re = { description = "hyperhive coordinator daemon";