diff --git a/nix/modules/hive-forge.nix b/nix/modules/hive-forge.nix index ce29e344..b7e4e3c7 100644 --- a/nix/modules/hive-forge.nix +++ b/nix/modules/hive-forge.nix @@ -6,28 +6,6 @@ }: let cfg = config.services.hyperhive.forge; - gatewayCfg = config.services.hyperhive.gateway; - hyperhiveDomain = config.services.hyperhive.domain; - - # ROOT_URL forgejo advertises in clone links + outbound URLs. When - # served behind the gateway (#749 — mara verdict at issue:9609, - # sub-domain over sub-path), `cfg.domain` doubles as both the - # forgejo `DOMAIN` setting AND the gateway vhost server-name, so - # ROOT_URL just uses it directly (drops the port suffix when the - # gateway is on the canonical port 80). When direct (gateway off - # or `behindGateway = false`), keep the host:port shape so direct - # browser access on `:httpPort` still produces correct links. - # Operators can override via `cfg.rootUrl` for TLS / non-default - # gateway ports / bespoke shapes. - defaultRootUrl = - if cfg.behindGateway then - let - portSuffix = if gatewayCfg.port == 80 then "" else ":${toString gatewayCfg.port}"; - in - "http://${cfg.domain}${portSuffix}/" - else - "http://${cfg.domain}:${toString cfg.httpPort}/"; - effectiveRootUrl = if cfg.rootUrl != null then cfg.rootUrl else defaultRootUrl; in { # Private Forgejo for hyperhive agents, wrapped in a nixos-container @@ -82,30 +60,13 @@ in domain = lib.mkOption { type = lib.types.str; - default = if hyperhiveDomain != null then "forge.${hyperhiveDomain}" else "localhost"; - defaultText = lib.literalExpression '' - if services.hyperhive.domain != null then - "forge.''${services.hyperhive.domain}" - else - "localhost" - ''; - example = "git.example.com"; + default = "localhost"; + example = "forge.internal"; description = '' - Public hostname for the forge. Doubles as both the forgejo - `DOMAIN` setting (clone URLs forgejo advertises) AND the - gateway vhost server-name when `behindGateway = true` - (#749, mara verdict at issue:9609 — sub-domain over sub-path). - - Defaults to `forge.''${services.hyperhive.domain}` when the - hive-domain is set (idiomatic sub-domain shape — `forge` - labelled under the hive's bare domain), falling back to - `localhost` otherwise (pre-#749 direct-on-port behaviour). - - Set to a full hostname (`git.example.com`, - `forge.internal.lan`, etc.) for a bespoke vhost shape — the - full domain goes here, no separate sub-domain-label option - (mara on #754:9684 — "specify full forge domain in options - instead"). + Hostname used in repo clone URLs the forge advertises. The + container shares host netns so `localhost` works for any + agent on the same host; set a real hostname when you want + clones from outside the host to look canonical. ''; }; @@ -124,58 +85,6 @@ in ''; }; - behindGateway = lib.mkOption { - type = lib.types.bool; - default = gatewayCfg.enable or false; - defaultText = lib.literalExpression "config.services.hyperhive.gateway.enable"; - description = '' - Serve forgejo through the hive-gateway nginx as a sub-domain - vhost (`server_name = cfg.domain`) instead of directly on - `httpPort` (#749, mara verdict at issue:9609 — sub-domain - over sub-path). - - When `true`: - - The gateway adds a `server { server_name = ''${cfg.domain}; }` - block that proxies all `/` → `http://127.0.0.1:''${httpPort}/`. - - Forgejo's `ROOT_URL` flips to `http(s)://''${cfg.domain}/` - (sub-domain root, no port suffix when gateway is on 80). - - `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 - gateway is running (e.g. an external git client that doesn't - traverse the gateway). - - The mara-call on #749:9609 picks sub-domain over sub-path for - forge + matrix (both are external standard apps with sub-domain- - native config defaults). Per-agent UIs stay on sub-path - (`/agent//`) because they're hyperhive-internal + - already base-path-aware via #731. - ''; - }; - - rootUrl = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - example = "https://forge.example.com/"; - description = '' - Override the auto-derived forgejo `ROOT_URL`. When `null` - (default), `ROOT_URL` is derived from `cfg.domain` + gateway - state: - - - `behindGateway = true` → `http://''${cfg.domain}/` (uses - `services.hyperhive.gateway.port` when non-80) - - `behindGateway = false` → `http://''${cfg.domain}:''${cfg.httpPort}/` - - Set this to a fully-qualified URL when running behind TLS - termination (`https://...`), a non-default gateway port, or - a bespoke shape. Must end with `/` per forgejo's `ROOT_URL` - contract. - ''; - }; - openFirewall = lib.mkOption { type = lib.types.bool; default = false; @@ -199,51 +108,6 @@ in }; config = lib.mkIf cfg.enable { - assertions = [ - { - assertion = cfg.rootUrl == null || lib.hasSuffix "/" cfg.rootUrl; - message = '' - services.hyperhive.forge.rootUrl must end with "/". forgejo's - ROOT_URL contract requires a trailing slash for correct - relative-link generation; without it forgejo emits URLs like - `https://forge.example.com.user.id` instead of - `https://forge.example.com/user.id`. Got: ${toString cfg.rootUrl} - ''; - } - { - # `cfg.domain` can't be empty — would render `.` shaped - # garbage as both server_name (nginx wildcard catch-all) and - # /etc/hosts entry (invalid). Default rejects this case (lands - # `"localhost"` when hive-domain is unset), but operator-set - # empty strings should fail loud. - assertion = cfg.domain != ""; - message = '' - services.hyperhive.forge.domain = "" is rejected. The - rendered URLs would be invalid (nginx wildcard catch-all - for an empty server_name, /etc/hosts rejects empty entries). - Either leave at default (auto-derives to - "forge." when set, else - "localhost"), or set a non-empty hostname like "forge.example.com" - 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 = { autoStart = true; ephemeral = false; @@ -286,7 +150,7 @@ in DEFAULT.APP_NAME = "HyperHive"; server = { DOMAIN = cfg.domain; - ROOT_URL = effectiveRootUrl; + ROOT_URL = "http://${cfg.domain}:${toString cfg.httpPort}/"; HTTP_PORT = cfg.httpPort; START_SSH_SERVER = true; SSH_PORT = cfg.sshPort; diff --git a/nix/modules/hive-gateway.nix b/nix/modules/hive-gateway.nix index d38e05de..b497426b 100644 --- a/nix/modules/hive-gateway.nix +++ b/nix/modules/hive-gateway.nix @@ -8,7 +8,6 @@ let cfg = config.services.hyperhive.gateway; hyperhiveDomain = config.services.hyperhive.domain; matrixCfg = config.services.hyperhive.matrix; - forgeCfg = config.services.hyperhive.forge; # Per-agent port table for `/agent//` routing (#15 v0). Single- # sourced from `cfg.agentPortsFile` (default @@ -238,8 +237,7 @@ in "~*text/html" "/matrix/index.html"; } ''; - virtualHosts = { - "_" = { + virtualHosts."_" = { listen = [ { addr = "0.0.0.0"; @@ -395,54 +393,7 @@ in ''; }; }; - }; - } - // - # Forge vhost (#749, mara verdict at issue:9609 — - # sub-domain over sub-path). When forgejo runs behind the - # gateway (`forge.behindGateway = true`), it gets its own - # `server { server_name = forge.domain; }` block. The - # block proxies all `/` → `http://127.0.0.1:/` - # so forgejo handles requests at root (default deploy shape - # — no `ROOT_URL`-prefix translation needed). - # - # `forge.domain` is the full hostname (e.g. - # `forge.darkest.space`, `git.example.com`) — single source - # of truth for both the forgejo `DOMAIN` setting and the - # gateway vhost name (mara on #754:9684 — "specify full - # forge domain in options instead"). - # - # `client_max_body_size 1G` — git pushes + LFS uploads can - # be large; nginx's default 1M would 413 most real commits. - # - # Long timeouts for big repo operations: a fresh clone of a - # multi-GB repo can take minutes; the default 60s - # `proxy_read_timeout` would abort mid-stream. - # - # `proxyWebsockets = true` keeps forgejo's live-update - # endpoints (`/api/v1/events`) + any future websocket - # endpoints working transparently. SSH stays direct on - # `forge.sshPort` (separate listener protocol, not HTTP). - lib.optionalAttrs (forgeCfg.enable or false && forgeCfg.behindGateway or false) { - "${forgeCfg.domain}" = { - listen = [ - { - addr = "0.0.0.0"; - port = cfg.port; - } - ]; - 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; - ''; - }; - }; - }; + }; }; }; }; @@ -451,25 +402,8 @@ in allowedTCPPorts = [ cfg.port ]; }; - # `/etc/hosts` entries for local dev: the bare hive domain plus - # any sub-domain modules (forge, matrix-via-#751) that are on. - # All map to `127.0.0.1` since the gateway shares host netns. - # Operators with real DNS leave `localHostsEntry = false`; this - # is the dev-loop shortcut for `http:///` + - # `http://forge./` resolving locally. - # - # Forge's `cfg.domain` may equal `hyperhiveDomain` (e.g. operator - # set `forge.domain = "darkest.space"` matching the hive domain) - # — `lib.unique` collapses the duplicate so `/etc/hosts` doesn't - # carry the same entry twice. networking.hosts = lib.mkIf (cfg.localHostsEntry && hyperhiveDomain != null) { - "127.0.0.1" = lib.unique ( - [ hyperhiveDomain ] - ++ lib.optional ( - (config.services.hyperhive.forge.enable or false) - && (config.services.hyperhive.forge.behindGateway or false) - ) config.services.hyperhive.forge.domain - ); + "127.0.0.1" = [ hyperhiveDomain ]; }; }; }