From fdf05c1673cf9c719e004960381681f199270cd7 Mon Sep 17 00:00:00 2001 From: atlas Date: Mon, 8 Jun 2026 23:44:58 +0200 Subject: [PATCH] =?UTF-8?q?refactor(gateway):=20make=20the=20gateway=20unc?= =?UTF-8?q?onditional=20=E2=80=94=20remove=20gateway.enable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gateway container starts alongside every hyperhive deployment, so gating it behind a separate enable flag was a footgun: an operator who set it false lost the only thing exposed to the outside while the agent containers kept running. Re-gate the gateway config on the top-level services.hyperhive.enable instead. - hive-gateway.nix: drop the gateway.enable mkOption; gate the config block on config.services.hyperhive.enable. - hive-forge.nix: behindGateway now defaults to services.hyperhive.enable; remove the behindGateway-requires-gateway assertion (now vacuous). - hive-network.nix: remove both gateway.enable assertions (vacuous). - hive-c0re.nix: drop the firewall.allowedTCPPortRanges 8100-8999 fallback that opened agent ports when the gateway was off (the gateway is now the sole entry point); HIVE_GATEWAY_ENABLED is always set since the gateway always runs. - nix/docs/default.nix: remove the gateway.enable = mkForce false stub (would be an eval error against the removed option; the gateway is already re-gated on hyperhive.enable, which docs force false). - hive-matrix.nix, dashboard.rs: comment/prose updates only. BREAKING: operators relying on services.hyperhive.gateway.enable = false to suppress the gateway must instead point their own reverse proxy at the gateway's port. NixOS errors clearly on the now-unknown option. --- hive-c0re/src/dashboard.rs | 20 ++++++++++---------- nix/docs/default.nix | 1 - nix/modules/hive-c0re.nix | 20 ++++++++------------ nix/modules/hive-forge.nix | 25 +++++-------------------- nix/modules/hive-gateway.nix | 23 ++++++----------------- nix/modules/hive-matrix.nix | 10 +++++----- nix/modules/hive-network.nix | 21 --------------------- 7 files changed, 34 insertions(+), 86 deletions(-) diff --git a/hive-c0re/src/dashboard.rs b/hive-c0re/src/dashboard.rs index f03897de..0a243645 100644 --- a/hive-c0re/src/dashboard.rs +++ b/hive-c0re/src/dashboard.rs @@ -229,15 +229,15 @@ struct StateSnapshot { /// chrome so the `M4TR1X →` tab doesn't flash when the GUI is off. matrix_gui_enabled: bool, /// Whether `hive-gateway` is in front of this dashboard. Sourced - /// from `HIVE_GATEWAY_ENABLED` env var (set by the c0re NixOS - /// module when `services.hyperhive.gateway.enable` is on). When - /// true the dashboard frontend builds same-origin - /// `/agent//` links to the per-agent web UI (the gateway - /// routes them via the runtime-generated `agents.conf` include - /// file — see `gateway_nginx.rs`); when false it falls back to - /// direct `http://:/` TCP links so gateway-off / - /// local-dev deploys keep working. See `docs/gateway.md::Vhost - /// map`. + /// from the `HIVE_GATEWAY_ENABLED` env var, which the c0re NixOS + /// module now always sets (the gateway runs unconditionally + /// alongside hyperhive), so this is effectively always true: the + /// dashboard frontend builds same-origin `/agent//` links to + /// the per-agent web UI (the gateway routes them via the + /// runtime-generated `agents.conf` include file — see + /// `gateway_nginx.rs`). The `false` branch (direct + /// `http://:/` TCP links) is retained as a defensive + /// fallback for the env being unset. See `docs/gateway.md::Vhost map`. gateway_enabled: bool, /// Public URL of the forge vhost served by hive-gateway (e.g. /// `"https://forge.pr1ma.darkest.space"`). Sourced from the @@ -480,7 +480,7 @@ async fn api_state(headers: HeaderMap, State(state): State) -> axum::J gateway_enabled: std::env::var_os("HIVE_GATEWAY_ENABLED").is_some_and(|v| { // Same truthy-string parse as `matrix_gui_enabled`; the // env var is set by the c0re NixOS module to the literal - // "1" when `services.hyperhive.gateway.enable` is on. + // "1" — the gateway always runs alongside hyperhive. let s = v.to_string_lossy().to_ascii_lowercase(); matches!(s.as_str(), "1" | "true" | "yes") }), diff --git a/nix/docs/default.nix b/nix/docs/default.nix index 0595e603..90edb099 100644 --- a/nix/docs/default.nix +++ b/nix/docs/default.nix @@ -31,7 +31,6 @@ let services.hyperhive.enable = lib.mkForce false; services.hyperhive.forge.enable = lib.mkForce false; services.hyperhive.matrix.enable = lib.mkForce false; - services.hyperhive.gateway.enable = lib.mkForce false; } ) ]; diff --git a/nix/modules/hive-c0re.nix b/nix/modules/hive-c0re.nix index 7a1fc782..b5d3356a 100644 --- a/nix/modules/hive-c0re.nix +++ b/nix/modules/hive-c0re.nix @@ -634,15 +634,9 @@ in }; users.groups.hive-core = { }; - # Open the per-agent web-port range when the gateway is *off* — - # otherwise the gateway nginx is the sole external entry point. - # See `docs/gateway.md::Firewall posture (host-level)`. - networking.firewall.allowedTCPPortRanges = lib.mkIf (!config.services.hyperhive.gateway.enable) [ - { - from = 8100; - to = 8999; - } - ]; + # The gateway nginx is always the sole external entry point (it runs + # alongside hyperhive), so the per-agent web-port range stays closed on + # the host firewall. See `docs/gateway.md::Firewall posture (host-level)`. # WireGuard inter-hive mesh. Enabled when # `services.hyperhive.swarm.wireguard.enable = true`. Brings up a @@ -777,9 +771,11 @@ in # docs/gateway.md::Vhost map. HIVE_MATRIX_GUI_ENABLED = "1"; } - // lib.optionalAttrs config.services.hyperhive.gateway.enable { - # When true the dashboard builds same-origin `/agent//` - # links; when false it falls back to direct `:` TCP. + // { + # The gateway always runs, so the dashboard always builds + # same-origin `/agent//` links (never the direct + # `:` TCP fallback). Kept as an env flag so the + # dashboard doesn't need to learn the gateway is unconditional. HIVE_GATEWAY_ENABLED = "1"; } // diff --git a/nix/modules/hive-forge.nix b/nix/modules/hive-forge.nix index da52a894..ce420e58 100644 --- a/nix/modules/hive-forge.nix +++ b/nix/modules/hive-forge.nix @@ -112,8 +112,8 @@ in behindGateway = lib.mkOption { type = lib.types.bool; - default = gatewayCfg.enable or false; - defaultText = lib.literalExpression "config.services.hyperhive.gateway.enable"; + default = config.services.hyperhive.enable; + defaultText = lib.literalExpression "config.services.hyperhive.enable"; description = '' Serve forgejo through the hive-gateway nginx as a sub-domain vhost (`server_name = cfg.domain`) instead of directly on @@ -127,9 +127,9 @@ in - `gateway.localHostsEntry = true` extends `/etc/hosts` to include `cfg.domain → 127.0.0.1` for local dev. - Defaults to `services.hyperhive.gateway.enable` — flipping - the gateway on/off auto-routes forge through it. Set `false` - explicitly to keep forge on the direct port even when the + Defaults to `services.hyperhive.enable` (the gateway always runs + alongside hyperhive, so forge auto-routes through it). Set `false` + explicitly to keep forge on the direct port even though the gateway is running (e.g. an external git client that doesn't traverse the gateway). @@ -211,21 +211,6 @@ in or "git.internal". ''; } - { - # behindGateway requires the gateway module to actually be on. - # Otherwise the configured `ROOT_URL` flips to a sub-domain - # shape that has no nginx vhost backing it → broken on the - # rebuild. - assertion = !cfg.behindGateway || (gatewayCfg.enable or false); - message = '' - services.hyperhive.forge.behindGateway = true requires - services.hyperhive.gateway.enable = true (the gateway vhost - serving forge needs the gateway container to actually be - running). Either turn the gateway on, or set - services.hyperhive.forge.behindGateway = false to keep forge - on its direct port. - ''; - } ]; containers.hive-forge = { diff --git a/nix/modules/hive-gateway.nix b/nix/modules/hive-gateway.nix index 219785e7..efa3e776 100644 --- a/nix/modules/hive-gateway.nix +++ b/nix/modules/hive-gateway.nix @@ -69,22 +69,11 @@ in # `docs/gateway.md`. options.services.hyperhive.gateway = { - enable = lib.mkOption { - type = lib.types.bool; - default = true; - description = '' - Run hive-gateway — a single nginx in front of every hyperhive - surface. On by default: the gateway hosts the matrix GUI static - dist (when `services.hyperhive.matrix.gui.enable` is true) and - proxies everything else to hive-c0re's dashboard upstream. Set - `services.hyperhive.gateway.enable = false` to bypass nginx - entirely and reach hive-c0re directly on its dashboard port - (7000 by default). - - v0 is HTTP-only; TLS / public-domain shape is tracked - separately. - ''; - }; + # The gateway is always run alongside hyperhive (it's the single nginx + # in front of every surface and the only thing exposed to the outside); + # there is no enable flag. An operator who wants their own reverse proxy + # in front points it at the gateway's `port`. The gateway config below + # is gated on the top-level `services.hyperhive.enable`. port = lib.mkOption { type = lib.types.port; @@ -375,7 +364,7 @@ in }; - config = lib.mkIf cfg.enable { + config = lib.mkIf config.services.hyperhive.enable { assertions = [ { assertion = !cfg.localHostsEntry || hyperhiveDomain != null; diff --git a/nix/modules/hive-matrix.nix b/nix/modules/hive-matrix.nix index e510de50..5c8b5ba7 100644 --- a/nix/modules/hive-matrix.nix +++ b/nix/modules/hive-matrix.nix @@ -248,8 +248,8 @@ in defaultText = lib.literalExpression "config.services.hyperhive.matrix.enable"; description = '' Serve a matrix web client at `matrix.''${services.hyperhive.domain}/`. - Requires `gateway.enable` + `matrix.gatewayHost != null` - (default true / `matrix.` when hive-domain set). When + Requires `matrix.gatewayHost != null` (default `matrix.` + when hive-domain set); the gateway itself always runs. When off, the dashboard's `M4TR1X →` tab is hidden. See `docs/gateway.md` for the discovery flow that lets clients auto-find the sub-domain. @@ -435,9 +435,9 @@ in # boot failure this module fixes was an *empty* resolv.conf, a parse # error, not a connectivity one) — so this is robustness, not a boot # requirement. Soft `after` ordering (not `requires`) keeps the matrix - # container's lifecycle decoupled from the gateway's. `network.enable` - # asserts `gateway.enable`, so the gateway container unit always exists - # here. (Declarative `containers.` → `container@.service` — the + # container's lifecycle decoupled from the gateway's. The gateway + # always runs alongside hyperhive, so the gateway container unit always + # exists here. (Declarative `containers.` → `container@.service` — the # nspawn template NixOS generates, confirmed from the live # `container@hive-matrix.service` host unit.) systemd.services."container@hive-matrix".after = lib.mkIf networkCfg.enable [ diff --git a/nix/modules/hive-network.nix b/nix/modules/hive-network.nix index 9d86f770..9c495b43 100644 --- a/nix/modules/hive-network.nix +++ b/nix/modules/hive-network.nix @@ -154,16 +154,6 @@ in `services.hyperhive.network.enable = false` explicitly. ''; } - { - assertion = config.services.hyperhive.gateway.enable; - message = '' - services.hyperhive.network.enable = true requires - services.hyperhive.gateway.enable = true — the dnsmasq - resolver runs inside the hive-gateway container (single - front-door for both DNS and HTTP). Enable the gateway or - set `services.hyperhive.network.enable = false` explicitly. - ''; - } ]; # Virtual bridge — veth pairs attach when isolateContainers flips on. @@ -196,17 +186,6 @@ in resolver must be running before isolation is flipped on). ''; } - { - assertion = !config.services.hyperhive.forge.enable || config.services.hyperhive.gateway.enable; - message = '' - services.hyperhive.network.isolateContainers = true with - services.hyperhive.forge.enable = true requires - services.hyperhive.gateway.enable = true — isolated agents - reach the forge via `http://forge.` which nginx (in - the gateway container) proxies to forgejo. Without the gateway - there is nothing listening on port 80 to serve that hostname. - ''; - } ]; })