mara: enableAllLocalDefaults is more of a deployment mode via settings set, less a default setting. That reframing is the change. A mode asserts values; an option declares what it is when nobody asks. Written as default = <flag>, every service option had to name a switch it has no relationship to, and the answer to what does all-local turn on was spread across five files. Two modules now hold the two tiers - local-defaults.nix for the mode and what it asserts directly, swarm-required-services.nix for the services-live-here switch and the per-service enables under it. Each service module keeps its own declaration and loses only the line about when a deployment wants it. mkDefault is the right precedence rather than a compromise: it beats an option default and yields to any explicit definition, so the mode fills in for an operator who has not spoken and never argues with one who has. Evaluated config is unchanged in both directions.
171 lines
7.3 KiB
Nix
171 lines
7.3 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`.
|
|
'';
|
|
};
|
|
|
|
# `enableRequiredServices` is declared in ./swarm-required-services.nix
|
|
# together with the per-service `enable`s it asserts — it is a
|
|
# deployment-shape switch rather than swarm bookkeeping, so it lives
|
|
# with its consequences instead of here.
|
|
|
|
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.
|
|
'';
|
|
};
|
|
};
|
|
|
|
}
|