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
This commit is contained in:
parent
a5eb3c15c4
commit
4e8225c058
3 changed files with 226 additions and 192 deletions
|
|
@ -36,7 +36,7 @@ in
|
||||||
imports = [
|
imports = [
|
||||||
# Same type, same meaning, new path — so a rename carries it exactly
|
# Same type, same meaning, new path — so a rename carries it exactly
|
||||||
# and existing configs keep evaluating with one warning naming both
|
# and existing configs keep evaluating with one warning naming both
|
||||||
# paths. Precedent: ./hive-forge/default.nix, ./hive-matrix.nix.
|
# paths. Precedent: ./hive-forge/service.nix, ./hive-matrix.nix.
|
||||||
(lib.mkRenamedOptionModule
|
(lib.mkRenamedOptionModule
|
||||||
[ "services" "hyperhive" "swarm" "grafana" "enable" ]
|
[ "services" "hyperhive" "swarm" "grafana" "enable" ]
|
||||||
[ "services" "hyperhive" "deploy" "grafana" "enable" ]
|
[ "services" "hyperhive" "deploy" "grafana" "enable" ]
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ let
|
||||||
# Forgejo's name for the login source. A constant, not an option: it
|
# Forgejo's name for the login source. A constant, not an option: it
|
||||||
# is the key this module's own idempotency check looks up, so making
|
# is the key this module's own idempotency check looks up, so making
|
||||||
# it configurable would buy nothing and add a way for the lookup and
|
# it configurable would buy nothing and add a way for the lookup and
|
||||||
# the row to disagree.
|
# the row to disagree. Duplicated in ./service.nix, which builds
|
||||||
|
# `sso.redirectUri` from it; keep the two equal.
|
||||||
ssoSourceName = "authelia";
|
ssoSourceName = "authelia";
|
||||||
|
|
||||||
# `url` is the half of the authelia module that exists on EVERY hive —
|
# `url` is the half of the authelia module that exists on EVERY hive —
|
||||||
|
|
@ -55,12 +56,6 @@ let
|
||||||
# `forgeSecretPath` above.
|
# `forgeSecretPath` above.
|
||||||
swarmControllerTokenPath = "/var/lib/forgejo/swarm-controller-token";
|
swarmControllerTokenPath = "/var/lib/forgejo/swarm-controller-token";
|
||||||
|
|
||||||
# Forgejo's OAuth2 callback shape. Built from the SAME `ssoSourceName`
|
|
||||||
# the registration uses, so the redirect URI authelia is told to allow
|
|
||||||
# and the one forgejo will actually send cannot drift apart — a
|
|
||||||
# mismatch there is a rejected login with no error text worth reading.
|
|
||||||
ssoRedirectUri = "${effectiveRootUrl}user/oauth2/${ssoSourceName}/callback";
|
|
||||||
|
|
||||||
caTrust = import ../lib/hive-ca-trust.nix { inherit lib tlsCfg gatewayCfg; };
|
caTrust = import ../lib/hive-ca-trust.nix { inherit lib tlsCfg gatewayCfg; };
|
||||||
|
|
||||||
# ROOT_URL forgejo advertises in clone links + outbound URLs. When
|
# ROOT_URL forgejo advertises in clone links + outbound URLs. When
|
||||||
|
|
@ -73,7 +68,8 @@ let
|
||||||
# the port suffix. When direct (`behindGateway = false`), keep the
|
# the port suffix. When direct (`behindGateway = false`), keep the
|
||||||
# host:httpPort shape so direct browser access still produces correct
|
# host:httpPort shape so direct browser access still produces correct
|
||||||
# links. Operators can still override via `cfg.rootUrl` for bespoke
|
# links. Operators can still override via `cfg.rootUrl` for bespoke
|
||||||
# shapes.
|
# shapes. Both bindings are duplicated in ./service.nix, which builds
|
||||||
|
# `sso.redirectUri` from them; keep the two equal.
|
||||||
defaultRootUrl =
|
defaultRootUrl =
|
||||||
if deployCfg.forgejo.behindGateway then
|
if deployCfg.forgejo.behindGateway then
|
||||||
let
|
let
|
||||||
|
|
@ -125,190 +121,12 @@ in
|
||||||
# (base URL), the same shape as the GitHub PAT / matrix extra-account
|
# (base URL), the same shape as the GitHub PAT / matrix extra-account
|
||||||
# flows. See `hive-c0re/src/dashboard/extra_forges.rs`.
|
# flows. See `hive-c0re/src/dashboard/extra_forges.rs`.
|
||||||
|
|
||||||
# Forge moved under `swarm` when the swarm-global services were
|
imports = [ ./service.nix ];
|
||||||
# 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
|
# What ./service.nix declares is what the forge IS from any hive's point of
|
||||||
OFF fails here, where the line is, instead of silently gaining a
|
# view: the names and ports it answers on, the URLs it advertises, and the
|
||||||
login provider on the next rebuild. Drop the line; if it was
|
# client id it is registered under. What lives here is what the host running
|
||||||
false, set services.hyperhive.deploy.forgejo.sso.clientSecretFile and
|
# it decides — which build it runs, how it is served, what it mirrors, 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` — see the block below.
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# What stays above is what the forge IS from any hive's point of view: the
|
|
||||||
# names and ports it answers on, the URLs it advertises, and the client id
|
|
||||||
# it is registered under. What lives here is what the host running it
|
|
||||||
# decides — which build it runs, how it is served, what it mirrors, and
|
|
||||||
# where its host-local secrets sit. Same rule as ./swarm-victorialogs.nix,
|
# where its host-local secrets sit. Same rule as ./swarm-victorialogs.nix,
|
||||||
# and the renames are in ./deploy.nix with the rest.
|
# and the renames are in ./deploy.nix with the rest.
|
||||||
options.services.hyperhive.deploy.forgejo = {
|
options.services.hyperhive.deploy.forgejo = {
|
||||||
|
|
|
||||||
216
nix/host-modules/hive-forge/service.nix
Normal file
216
nix/host-modules/hive-forge/service.nix
Normal file
|
|
@ -0,0 +1,216 @@
|
||||||
|
# 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.
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue