Each agent row on the hive dashboard, and the agent page's link menu, linked to the agent's forge profile and its config repo. Both are gone, with what only they used: - hive-agent no longer builds the two links, the `Forge` link kind, or the `forge_public_url` field of its state, and no longer reads `HIVE_FORGE_PUBLIC_URL`. The agent page's link menu and the dashboard row's icon strip (which skips a forge link an agent not yet rebuilt still sends) drop their forge handling. - hive-c0re's dashboard state no longer carries `forge_public_url`. - `HIVE_FORGE_PUBLIC_URL` is no longer forwarded to agents through the meta flake (the forwarded-variable list, its test fixture, the controller's settings and the VM test), so the next meta flake render differs by that one line. hive-c0re still reads it for the forge URL a hive publishes, so `swarm.forge.publicUrl` stays, with its description corrected. - The docs stop describing the links.
166 lines
7 KiB
Nix
166 lines
7 KiB
Nix
# 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;
|
||
swarmDomain = config.services.hyperhive.swarm.domain;
|
||
|
||
# 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 = "https://${cfg.domain}/";
|
||
effectiveRootUrl = if cfg.rootUrl != null then cfg.rootUrl else defaultRootUrl;
|
||
in
|
||
{
|
||
# 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 = {
|
||
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 (sub-domain routing — see
|
||
`docs/4-implementation/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.
|
||
|
||
⚠️ To use the per-hive name `forge.''${services.hyperhive.domain}`
|
||
instead, pin it here. Certificates follow either way: the
|
||
store's `swarm-services` role issues for the configured names,
|
||
not for 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 = "https://${cfg.domain}";
|
||
defaultText = lib.literalExpression ''"https://''${domain}"'';
|
||
example = "https://forge.example.com";
|
||
description = ''
|
||
Browser-facing forge URL, sourced into hive-c0re as
|
||
`HIVE_FORGE_PUBLIC_URL` and published with the hive's URLs.
|
||
|
||
Defaults to `https://''${cfg.domain}`, the gateway vhost. When
|
||
`null`, the hive publishes no forge URL rather than guessing one.
|
||
'';
|
||
};
|
||
|
||
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 `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, on the default port. A gateway that listens
|
||
on another `gateway.httpsPort` does not change this URL.
|
||
|
||
Set this only 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.
|
||
'';
|
||
};
|
||
# The secret half is fetched from the swarm secret store on the host
|
||
# that runs the forge: `swarm-bao-forge-oidc`, in ./default.nix.
|
||
};
|
||
|
||
otel.clientId = lib.mkOption {
|
||
type = lib.types.str;
|
||
readOnly = true;
|
||
# Not `hive-forge-collector`: `hive-<name>` is a hive's own client id,
|
||
# and `forge-collector` is a name a hive may take.
|
||
default = "swarm-forge-collector";
|
||
description = ''
|
||
OAuth2 client id the journal forwarder inside the forge's container
|
||
authenticates as, and the audience it asks its token for. Its own,
|
||
not {option}`services.hyperhive.swarm.forge.sso.clientId`, which is
|
||
the browser login's.
|
||
|
||
Read-only because three hosts have to agree on it: authelia
|
||
registers the client (`glue-forge-otel-oidc-client.nix`), the
|
||
swarm collector admits it on the container's receiver
|
||
(`swarm.otel.serviceProducers`), and authelia's mint stores
|
||
its secret in the store.
|
||
'';
|
||
};
|
||
};
|
||
}
|