nix/hive-forge: unify on cfg.domain as full hostname, drop subdomain label (mara #754:9684)

mara on PR #754: "would it be better to specify full forge domain in
options instead?"

Drops the awkward `cfg.subdomain` label option. Now `cfg.domain` is
the single source of truth for both the forgejo `DOMAIN` setting
(existing semantics) AND the gateway vhost server-name (new).

## Before / after

```nix
# before: separate label + cfg.domain juggling
services.hyperhive.forge.subdomain = "forge";       # → forge.<hive>
services.hyperhive.forge.domain = "localhost";      # unused for vhost

# after: full domain, single option
services.hyperhive.forge.domain = "forge.darkest.space";  # ← used for ROOT_URL + vhost
```

## Default

`cfg.domain` default auto-derives:
- `forge.<services.hyperhive.domain>` when hive-domain is set
- `"localhost"` otherwise (pre-#749 direct-on-port shape)

So the common case (hive-domain set) gets `forge.<hive>` for free,
operators with a bespoke shape (`git.example.com`) set the full
hostname directly.

## Assertions

- `cfg.domain != ""` — empty would render `.<hive>` shaped garbage
  in both server_name + /etc/hosts.
- `cfg.behindGateway → gateway.enable` — can't route through a
  gateway that isn't running.

(The previous "subdomain = empty" assertion is dropped — that
edge case is gone with the rename.)

## Verified

- default with `hyperhive.domain = "test.local"` → `forge.test.local`,
  `ROOT_URL = http://forge.test.local/`, vhost present
- `forge.domain = "git.example.com"` → `git.example.com`,
  `ROOT_URL = http://git.example.com/`, vhost = `["_", "git.example.com"]`
- `gateway.enable = false` → `forge.domain` falls back to `localhost`,
  `ROOT_URL = http://localhost:3000/`, no gateway vhost
  (`behindGateway = false`)
- `/etc/hosts` (when `localHostsEntry = true`) → unique entries for
  hive-domain + forge.domain (de-duped via `lib.unique` for the
  edge case where forge.domain = hive-domain)
- full container toplevel builds clean

## PR title

(Will fix the PR title separately — still says "/forge/" which is
wrong since the rewrite to sub-domain shape.)
This commit is contained in:
atlas 2026-05-31 13:35:24 +02:00 committed by mara
commit 9c27c4076f
2 changed files with 107 additions and 82 deletions

View file

@ -398,14 +398,20 @@ in
};
}
//
# Forge sub-domain vhost (#749, mara verdict at issue:9609 —
# Forge vhost (#749, mara verdict at issue:9609 —
# sub-domain over sub-path). When forgejo runs behind the
# gateway, it gets its own `server { server_name ...; }`
# block keyed on `<forge.subdomain>.<hive-domain>`. The
# gateway (`forge.behindGateway = true`), it gets its own
# `server { server_name = forge.domain; }` block. The
# block proxies all `/` → `http://127.0.0.1:<forge.httpPort>/`
# 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.
#
@ -416,13 +422,9 @@ in
# `proxyWebsockets = true` keeps forgejo's live-update
# endpoints (`/api/v1/events`) + any future websocket
# endpoints working transparently. SSH stays direct on
# `cfg.sshPort` (separate listener protocol, not HTTP).
lib.optionalAttrs (
forgeCfg.enable or false
&& (forgeCfg.subdomain or null) != null
&& hyperhiveDomain != null
) {
"${forgeCfg.subdomain}.${hyperhiveDomain}" = {
# `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";
@ -455,12 +457,19 @@ in
# Operators with real DNS leave `localHostsEntry = false`; this
# is the dev-loop shortcut for `http://<hive-domain>/` +
# `http://forge.<hive-domain>/` 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" = [ hyperhiveDomain ]
"127.0.0.1" = lib.unique (
[ hyperhiveDomain ]
++ lib.optional (
(config.services.hyperhive.forge.enable or false)
&& (config.services.hyperhive.forge.subdomain or null) != null
) "${config.services.hyperhive.forge.subdomain}.${hyperhiveDomain}";
&& (config.services.hyperhive.forge.behindGateway or false)
) config.services.hyperhive.forge.domain
);
};
};
}