Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/nix/host-modules/hive-forge/service.nix
atlas 4e8225c058 nix: split hive-forge into service and deploy-mode files
`swarm.forge` (what the forge is to every hive: ports, domain, public and
root URLs, OIDC client id and callback) moves to
nix/host-modules/hive-forge/service.nix, together with the rename of the
old `services.hyperhive.forge` tree and the removed `swarm.forge.sso.enable`,
both of which name `swarm.forge` paths. Everything else -- the
`deploy.forgejo` options, the whole `config` block including
`containers.hive-forge`, and the helpers only they read -- stays in
nix/host-modules/hive-forge/default.nix, which now imports ./service.nix.
Importing it from the directory's own default.nix, as hive-c0re/ and
hive-gateway/ do with their option files, keeps the flake's standalone
`nixosModules.hive-forge` export whole.

Both halves read `cfg`, `gatewayCfg`, `swarmDomain` and `deployCfg`. They
are option reads, so each file binds them from `config`. `ssoSourceName`,
`defaultRootUrl` and `effectiveRootUrl` are not options and both halves
need them (the service half builds `sso.redirectUri` from them, the deploy
half registers the login source and sets ROOT_URL), so they are duplicated,
with a note at each copy. `ssoRedirectUri` is read only by the service
half and moves.

`swarm.forge.publicUrl` and `swarm.forge.sso.redirectUri` default from
`deploy.forgejo.behindGateway`; both move as they are.

A pure move: option paths, option definitions and config are unchanged
apart from comments: the two on either side of the cut, the
`ssoRedirectUri` comment and the duplication notes, and the rename
precedent in ./deploy.nix, which now names ./hive-forge/service.nix.

Refs #3742
2026-10-01 13:00:52 +02:00

216 lines
9.5 KiB
Nix
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# The swarm's forge as every hive sees it: the names and ports it answers on,
# the URLs it advertises, and the OIDC client it is registered as. What the
# host running it decides, and the container itself, are in ./default.nix.
{
lib,
config,
...
}:
let
cfg = config.services.hyperhive.swarm.forge;
gatewayCfg = config.services.hyperhive.gateway;
swarmDomain = config.services.hyperhive.swarm.domain;
deployCfg = config.services.hyperhive.deploy;
# Forgejo's name for the login source. Duplicated in ./default.nix, which
# registers the source under it.
ssoSourceName = "authelia";
# Forgejo's OAuth2 callback shape. `ssoSourceName` and `effectiveRootUrl`
# must equal their copies in ./default.nix: a mismatch is a rejected login
# with no error text worth reading.
ssoRedirectUri = "${effectiveRootUrl}user/oauth2/${ssoSourceName}/callback";
# Forgejo's `ROOT_URL`, duplicated from ./default.nix, which documents its
# shape.
defaultRootUrl =
if deployCfg.forgejo.behindGateway then
let
portSuffix = if gatewayCfg.httpsPort == 443 then "" else ":${toString gatewayCfg.httpsPort}";
in
"https://${cfg.domain}${portSuffix}/"
else
"http://${cfg.domain}:${toString cfg.httpPort}/";
effectiveRootUrl = if cfg.rootUrl != null then cfg.rootUrl else defaultRootUrl;
in
{
# Forge moved under `swarm` when the swarm-global services were
# consolidated. One rename for the namespace: the subtree comes with it,
# so existing hives keep evaluating and get one warning naming both paths.
imports = [
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "forge" ]
[ "services" "hyperhive" "swarm" "forge" ]
)
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "forge" "sso" "enable" ] ''
SSO is no longer optional: the forge always registers the swarm's
authelia as a login source.
Removed rather than defaulted to true so a config that turned it
OFF fails here, where the line is, instead of silently gaining a
login provider on the next rebuild. Drop the line; if it was
false, set services.hyperhive.deploy.forgejo.sso.clientSecretFile and
services.hyperhive.swarm.authelia.url as the assertions describe.
'')
];
# Every swarm needs this forge — it's the canonical store for the meta
# flake + every agent's config repo (and the `internal/*` repos) — but
# only one host runs it: `deploy.forgejo.enable`, in ../deploy.nix. What
# follows is what every hive needs to reach it, wherever it runs.
options.services.hyperhive.swarm.forge = {
httpPort = lib.mkOption {
type = lib.types.port;
default = 3000;
description = ''
TCP port the forge serves HTTP on. Default 3000 sits outside
hyperhive's claimed ranges (dashboard 7000, every agent in
8100..8999 via FNV-1a hash). Change this if you already have
another forgejo bound to 3000.
'';
};
sshPort = lib.mkOption {
type = lib.types.port;
default = 2222;
description = ''
TCP port the forge's built-in SSH server listens on. Kept off
22 so it doesn't clash with the host's openssh. Agents push
with `ssh -p <sshPort> git@<domain>:<owner>/<repo>.git`.
'';
};
domain = lib.mkOption {
type = lib.types.str;
# 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 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
`DOMAIN` setting (clone URLs forgejo advertises) AND the
gateway vhost server-name when `deploy.forgejo.behindGateway = true`
(sub-domain routing — see `docs/networking/gateway.md`).
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
full domain goes here, no separate sub-domain-label option.
'';
};
publicUrl = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = if deployCfg.forgejo.behindGateway then "https://${cfg.domain}" else null;
defaultText = lib.literalExpression ''
if behindGateway then "https://''${domain}" else null
'';
example = "https://forge.example.com";
description = ''
Browser-facing forge URL the dashboard uses to build clickable
forge links (the H0M3 Forge tile, per-agent-row forge links,
the approval-queue's "review PR on forge" link) — sourced into
every agent container + hive-c0re as `HIVE_FORGE_PUBLIC_URL`.
Defaults to `https://''${cfg.domain}` when `deploy.forgejo.behindGateway =
true` (the gateway vhost is genuinely reachable at that URL)
and `null` otherwise. When `null`, the dashboard **hides**
forge links rather than guessing one — see
`docs/web-ui/dashboard.md::H0M3 page` for the rationale (a
link built from the operator's own browser hostname + a
container port is only an accident away from wrong on any
deployment that isn't plain localhost).
**Set this explicitly if `deploy.forgejo.behindGateway = false`** and the
forge is still reachable at a stable URL you want linked from
the dashboard (e.g. `http://<lan-host>:''${toString cfg.httpPort}`
for an all-LAN deployment) — leaving it unset there means the
dashboard's forge links are simply absent, not broken.
'';
};
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, including the scheme:
- `deploy.forgejo.behindGateway = true` → `https://''${cfg.domain}/`. The gateway
always terminates TLS (self-signed is the implicit floor when no
`gateway.tls.certDir` / ACME is set), so the forge is always
advertised over https. A non-canonical `gateway.httpsPort` is
appended as `:<port>`.
- `deploy.forgejo.behindGateway = false` → `http://''${cfg.domain}:''${cfg.httpPort}/`
The TLS scheme is derived automatically now, so you only need to
set this for a genuinely bespoke shape (e.g. an external reverse
proxy on a different host/path). Must end with `/` per forgejo's
`ROOT_URL` contract.
'';
};
# The swarm's authelia is always registered as an OpenID Connect
# login source here — there is no toggle: a forge without it has no
# way to log a person in through the swarm's SSO.
#
# **Additive, never exclusive.** Forgejo keeps its local password
# database and gains an extra "sign in with" button; this does not
# disable local login. Deliberate: an identity provider that can take
# the forge offline when it hiccups is a worse forge than one with
# two ways in — which is also what makes always-on safe.
sso = {
clientId = lib.mkOption {
type = lib.types.str;
default = "forgejo";
description = ''
OAuth2 client id this forge identifies itself with. Must match
the `id` of the corresponding entry in
`services.hyperhive.swarm.authelia.oidc.clients`.
'';
};
redirectUri = lib.mkOption {
type = lib.types.str;
readOnly = true;
default = ssoRedirectUri;
defaultText = lib.literalExpression ''"''${ROOT_URL}user/oauth2/authelia/callback"'';
description = ''
OAuth2 callback authelia sends the browser back to, and the URI
it matches **exactly**.
Read-only: forgejo derives it from its own `ROOT_URL` and the
login source's name, so it is a fact other modules read rather
than a knob. The glue that registers this client wherever
authelia runs reads it from here instead of restating the
format.
⚠️ With {option}`services.hyperhive.swarm.forge.rootUrl` unset,
`ROOT_URL` follows
{option}`services.hyperhive.deploy.forgejo.behindGateway` and
the gateway's `httpsPort`, which are per-host. An authelia host
that is not the forge's host renders the forge's callback only
if the two agree on them; set `rootUrl` if they do not.
'';
};
# The secret half is a path on the host that runs the forge, so it
# lives under `deploy.forgejo.sso`, in ./default.nix.
};
};
}