feat(nix): the forge's default hostname moves under the swarm domain

A swarm runs one forge and every hive in it reaches the same host, so
the name belongs to the swarm rather than to whichever hive happens to
run it: `forge.<swarm.domain>` instead of `forge.<hive domain>`.

A deployment that was running before this keeps its current name with
one line -- `swarm.forge.domain = "forge.<hive domain>"` -- which is
exactly what the old default rendered, so pinning it is a no-op today
and freezes it against the new default.

Certificates follow either way: the swarm-services sub-CA is
name-constrained to the CONFIGURED names rather than to a fixed tree, so
a pinned legacy name is as issuable as the new default. DNS follows too
-- dnsmasq already lists `forge.domain` explicitly, precisely so a
cross-domain override stays routed.

The default stays total on a null swarm domain (`forge.invalid`) so the
required-domain assertion in hive-network.nix is what an operator sees,
rather than a coercion error naming this option.
This commit is contained in:
atlas 2026-08-05 22:45:09 +02:00 committed by mara
commit 727743507c

View file

@ -8,6 +8,7 @@ let
cfg = config.services.hyperhive.swarm.forge;
gatewayCfg = config.services.hyperhive.gateway;
hyperhiveDomain = config.services.hyperhive.domain;
swarmDomain = config.services.hyperhive.swarm.domain;
tlsCfg = config.services.hyperhive.tls;
# Self-signed gateway TLS: forgejo (Go) validates outbound webhook
@ -120,10 +121,14 @@ in
domain = lib.mkOption {
type = lib.types.str;
# Total on a null hive domain so the required-domain assertion in
# Under the SWARM domain, not this hive's: a swarm runs one forge
# and every hive in it reaches the same host, so the name belongs
# to the swarm rather than to whichever hive happens to run it.
#
# Total on a null swarm domain so the required-domain assertion in
# hive-network.nix is the thing that fires; see the comment there.
default = if hyperhiveDomain == null then "forge.invalid" else "forge.${hyperhiveDomain}";
defaultText = lib.literalExpression ''"forge.''${services.hyperhive.domain}"'';
default = if swarmDomain == null then "forge.invalid" else "forge.${swarmDomain}";
defaultText = lib.literalExpression ''"forge.''${services.hyperhive.swarm.domain}"'';
example = "git.example.com";
description = ''
Public hostname for the forge. Doubles as both the forgejo
@ -131,10 +136,16 @@ in
gateway vhost server-name when `behindGateway = true`
(sub-domain routing see `docs/gateway.md`).
Defaults to `forge.''${services.hyperhive.domain}` (idiomatic
sub-domain shape `forge` labelled under the hive's bare
domain). `services.hyperhive.domain` is required, so there's
always a domain to derive from.
Defaults to `forge.''${services.hyperhive.swarm.domain}` the
swarm's domain, not this hive's, because a swarm runs **one**
forge that every hive in it talks to.
A deployment that was running before this moved keeps its
current name by pinning it here:
`forge.''${services.hyperhive.domain}`, which is exactly what
the old default rendered. Certificates follow either way: the
swarm-services sub-CA is name-constrained to the configured
names (see `./swarm-ca.nix`), not to a fixed tree.
Set to a full hostname (`git.example.com`,
`forge.internal.lan`, etc.) for a bespoke vhost shape the