`docs/swarm.md` becomes `docs/swarm/README.md` and the shared-services material moves to `docs/swarm/services.md`, following the shape `docs/turn-loop/` and `docs/web-ui/` already use. The README keeps a pointer so the reading path is unbroken. Every referrer moved with it — five docs pages, two option descriptions in swarm.nix, and CLAUDE.md's reading path. A pointer to a file that moved is worse than one to a file that was deleted: the content still exists, so the reader concludes the note is wrong rather than the path.
197 lines
8.5 KiB
Nix
197 lines
8.5 KiB
Nix
# Swarm peering: who the peer hives are. Serialised into hive-c0re's
|
|
# environment (HYPERHIVE_PEERS / HIVE_PEER_CA_PATHS — see ./hive-c0re)
|
|
# and consumed by identity.rs + the dashboard's P33RS tab.
|
|
#
|
|
# Declaration only — this module has no `config` block. The mesh that
|
|
# uses the `wireguard*` fields below lives in ./swarm-wireguard.nix,
|
|
# because bringing up an interface is host networking rather than swarm
|
|
# bookkeeping, and a host that runs no hive still needs it.
|
|
{
|
|
lib,
|
|
config,
|
|
...
|
|
}:
|
|
{
|
|
# Peer hives in the same swarm. Each entry declares a remote hive
|
|
# reachable from this host.
|
|
options.services.hyperhive.swarm.peers = lib.mkOption {
|
|
type = lib.types.attrsOf (
|
|
lib.types.submodule {
|
|
options = {
|
|
certFingerprint = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
default = null;
|
|
example = "sha256:b1946ac92492d2347c6235b4d2611184a3f5b6cae6c19d6e3c2f0a8e7d4c9f12";
|
|
description = ''
|
|
Expected TLS certificate fingerprint for this peer's HTTPS
|
|
endpoint. Null = trust the system CA bundle (for Let's
|
|
Encrypt peers). Set to pin a self-signed cert.
|
|
|
|
Format: the literal `sha256:` followed by exactly 64
|
|
hex digits (case-insensitive, no colon separators) — the
|
|
SHA-256 digest of the peer's DER-encoded leaf certificate.
|
|
Generate with `openssl x509 -noout -fingerprint -sha256`,
|
|
then strip the colons and prepend `sha256:`. A malformed
|
|
value is ignored with a warning rather than weakening
|
|
trust. See docs/swarm/README.md for the full recipe.
|
|
|
|
Scopes only to hive-c0re's own peer HTTPS checks — it does
|
|
NOT help Matrix federation (tuwunel validates against its
|
|
container trust bundle). For a self-signed peer whose root
|
|
CA you want trusted hive-wide (every agent + Matrix
|
|
federation), set `caCert` below.
|
|
'';
|
|
};
|
|
|
|
caCert = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.path;
|
|
default = null;
|
|
example = "./peers/edge-ca.pem";
|
|
description = ''
|
|
Path to this peer hive's root CA certificate (PEM). When
|
|
set, the CA is embedded (at build time, into the nix store
|
|
— no runtime file on the host) and trusted **everywhere the
|
|
hive's own internal CA is**: it rides alongside `hive-ca.pem`
|
|
in each agent's `security.pki.certificateFiles` (via the
|
|
meta-flake renderer), and is added to the Matrix homeserver
|
|
container's trust bundle so tuwunel validates *federation*
|
|
TLS from a self-signed peer hive whose cert chains to it.
|
|
This is the CA-trust path that `certFingerprint`
|
|
(leaf-pinning, c0re-only) can't cover, and is what unblocks
|
|
Matrix federation with a self-signed peer hive. Trust stays
|
|
inside the hive (agents + the Matrix container), never the
|
|
host system trust store. Mutually complementary with
|
|
`certFingerprint`; set `caCert` for the federation case. See
|
|
docs/swarm/README.md.
|
|
'';
|
|
};
|
|
|
|
wireguardPublicKey = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
default = null;
|
|
example = "base64pubkey=";
|
|
description = ''
|
|
WireGuard public key for this peer host. Required when
|
|
`services.hyperhive.swarm.wireguard.enable = true` and
|
|
you want this peer reachable over the mesh. Null = TLS-
|
|
only peering (public internet, no mesh tunnel).
|
|
'';
|
|
};
|
|
|
|
wireguardEndpoint = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
default = null;
|
|
example = "203.0.113.1:51820";
|
|
description = ''
|
|
WireGuard endpoint for this peer in `host:port` form.
|
|
Required when the peer host is behind a firewall and
|
|
this host needs to initiate the tunnel. Null = this host
|
|
waits for the peer to connect (peer-initiates; peer must
|
|
have an endpoint pointing back at this host).
|
|
'';
|
|
};
|
|
|
|
wireguardAddress = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
default = null;
|
|
example = "10.100.0.2/32";
|
|
description = ''
|
|
IP address (with prefix) of the peer host on the
|
|
WireGuard mesh. Used as the `allowedIPs` for the peer's
|
|
WireGuard config entry and injected into `HYPERHIVE_PEERS`
|
|
so hive-c0re can route intra-swarm traffic to the mesh
|
|
address rather than the public domain. Required to include
|
|
the peer in the WireGuard mesh (peers missing this field
|
|
are silently excluded from `wg-hive`).
|
|
'';
|
|
};
|
|
|
|
};
|
|
}
|
|
);
|
|
default = { };
|
|
example = {
|
|
"lab.example.com" = {
|
|
certFingerprint = "sha256:b1946ac92492d2347c6235b4d2611184a3f5b6cae6c19d6e3c2f0a8e7d4c9f12";
|
|
};
|
|
"edge.corp" = { };
|
|
};
|
|
description = ''
|
|
Peer hives in the same swarm. The attrset key is the peer's DNS
|
|
domain — used for dashboard links and Matrix federation discovery.
|
|
Null `certFingerprint` trusts the system CA bundle; set it to pin
|
|
a self-signed TLS cert. Add `wireguardPublicKey` + `wireguardAddress`
|
|
(and optionally `wireguardEndpoint`) to include the peer in the
|
|
WireGuard mesh when `swarm.wireguard.enable = true`.
|
|
'';
|
|
};
|
|
|
|
# "The swarm-wide services run HERE." A swarm has one forge, one
|
|
# matrix, one SSO — this says this host is where they live. The
|
|
# OPTIONAL ones (matrix, authelia) default their own enable from it,
|
|
# rather than the operator enabling them one at a time; the forge has
|
|
# no enable to derive because it is not optional.
|
|
options.services.hyperhive.swarm.enableRequiredServices = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = config.services.hyperhive.enableAllLocalDefaults;
|
|
defaultText = lib.literalExpression "services.hyperhive.enableAllLocalDefaults";
|
|
example = true;
|
|
description = ''
|
|
Host the swarm's shared services on this hive. The services that
|
|
exist once per swarm rather than once per hive and are *optional*
|
|
— the matrix homeserver, the SSO provider — default their
|
|
`enable` from this, so a swarm's service host is declared in one
|
|
place.
|
|
|
|
The forge is swarm-wide too but has no `enable` to derive: it is
|
|
the canonical store for the meta flake and every agent's config
|
|
repo, so it deploys with hyperhive itself and is not optional.
|
|
|
|
Defaults from `services.hyperhive.enableAllLocalDefaults` (off),
|
|
which is the all-on-one-box switch. Set it directly to run the
|
|
swarm's services on a host that is not otherwise all-local — a
|
|
dedicated services box with hives elsewhere is exactly that shape.
|
|
|
|
With it off, this hive is a *client* of those services: it still
|
|
configures how to reach them, it just doesn't run them.
|
|
'';
|
|
};
|
|
|
|
options.services.hyperhive.swarm.snapshotStore = {
|
|
address = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
default = null;
|
|
example = "10.100.0.1";
|
|
description = ''
|
|
Mesh address of the swarm's snapshot store — the single
|
|
`btrfs receive` endpoint every hive in this swarm pushes agent
|
|
snapshots to. Bare IP, no prefix.
|
|
|
|
There is exactly **one** store per swarm, not one per peer: the
|
|
receiver keys destinations by *agent*, so an agent that migrates
|
|
between hives keeps a single unbroken incremental chain. Per-hive
|
|
stores would split that chain in two, which is the case the store
|
|
exists to serve.
|
|
|
|
Null means this swarm has no store configured, and pushing fails
|
|
saying so rather than guessing an address. Set it on every hive
|
|
that pushes; the receiving host separately sets
|
|
`services.hyperhive.snapshotStore.enable`.
|
|
'';
|
|
};
|
|
|
|
port = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 51821;
|
|
description = ''
|
|
TCP port the swarm's snapshot store listens on. Must match the
|
|
receiving host's `services.hyperhive.snapshotStore.port`.
|
|
|
|
Defaulted (unlike `address`) because it is a shared convention
|
|
both sides read from the same option docs — whereas an address
|
|
is deployment-specific and cannot be guessed.
|
|
'';
|
|
};
|
|
};
|
|
|
|
}
|