refactor(nix): a hive's domain comes out of the swarm directory
`services.hyperhive.domain` and `swarm.hives.<hiveName>.domain` were two
homes for one value with nothing asserting they agreed. The failure that
buys is the worst shape a config defect has: it evaluates cleanly, and
the symptom ("the other hives can't reach me") appears on a machine
other than the misconfigured one.
The directory is now the single source. `hives.<name>.domain` gains the
`<name>.<swarm.domain>` default -- a derivation from two values an
operator had to state explicitly, not a guess -- so a conventional swarm
is a list of names and a hive addressed by something else says so in the
one place every other hive reads. `services.hyperhive.domain` reads its
own entry; the direct formula is deleted rather than kept as a fallback,
which would have restored the second path (and, reading `swarm.domain`
itself, a second path that can disagree).
Setting it directly still wins, with a deprecation warning: nothing
breaks today, but a value written only there is invisible to the swarm.
The self-entry assertion now fires on an EMPTY directory too. Since
`swarm.domain` became required, every hive is in a swarm -- a swarm of
one is still a swarm -- and this host's address is read out of the
directory, so the entry is missing either way and the precise message
should be the one that fires.
Upgrading costs one line on hives that never listed themselves:
`services.hyperhive.swarm.hives.<hiveName> = { };`, no value.
This commit is contained in:
parent
30551464c1
commit
3b6576faee
5 changed files with 221 additions and 122 deletions
|
|
@ -87,14 +87,18 @@ listener on `bridgeIp` is on the host's bridge interface.
|
||||||
{
|
{
|
||||||
services.hyperhive = {
|
services.hyperhive = {
|
||||||
enable = true;
|
enable = true;
|
||||||
domain = "darkest.space";
|
hiveName = "pr1ma";
|
||||||
# network.bridgeIp = "10.42.0.1"; # default
|
swarm.domain = "darkest.space";
|
||||||
|
swarm.hives.pr1ma = { }; # -> domain = pr1ma.darkest.space
|
||||||
|
# network.bridgeIp = "10.42.0.1"; # default
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Requires `services.hyperhive.domain` to be set — the dnsmasq resolver
|
Requires `services.hyperhive.domain` to be set — the dnsmasq resolver
|
||||||
is authoritative for `<hive-domain>` and its sub-domains.
|
is authoritative for `<hive-domain>` and its sub-domains. You do not
|
||||||
|
write it: it is read from this hive's entry in the swarm directory
|
||||||
|
(`docs/swarm/README.md` § Hive identity config).
|
||||||
|
|
||||||
## Bridge addressing
|
## Bridge addressing
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,14 @@ the additional config needed when the swarm spans multiple hosts.
|
||||||
services.hyperhive = {
|
services.hyperhive = {
|
||||||
swarm.domain = "example.com"; # required — the swarm's DNS domain
|
swarm.domain = "example.com"; # required — the swarm's DNS domain
|
||||||
hiveName = "pr1ma"; # required — this hive's label in it
|
hiveName = "pr1ma"; # required — this hive's label in it
|
||||||
# domain = "pr1ma.example.com"; # derived from the two above
|
|
||||||
swarm.name = "constellat1on"; # shared swarm display name (optional)
|
swarm.name = "constellat1on"; # shared swarm display name (optional)
|
||||||
|
|
||||||
|
# required — the directory, identical on every host in the swarm.
|
||||||
|
# Names only: each entry's `domain` defaults to <name>.<swarm.domain>.
|
||||||
|
swarm.hives = {
|
||||||
|
pr1ma = { };
|
||||||
|
edge = { };
|
||||||
|
};
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -34,12 +40,22 @@ and deploys — an eval failure asking the operator to write the address
|
||||||
down is the cheaper outcome. **Upgrading past this release means setting
|
down is the cheaper outcome. **Upgrading past this release means setting
|
||||||
both once.**
|
both once.**
|
||||||
|
|
||||||
`domain` is required too, but you no longer have to *write* it: it
|
`domain` is required too, but you no longer *write* it: it is read from
|
||||||
defaults to `<hiveName>.<swarm.domain>`, since every hive in a swarm
|
this hive's own entry in the directory, whose `domain` defaults to
|
||||||
occupies its own sub-domain of it. A hive that pins `domain` explicitly
|
`<name>.<swarm.domain>`. So a conventional swarm states no addresses at
|
||||||
keeps exactly the value it has today — that's why this is a default and
|
all, and a hive addressed by something else states it in the one place
|
||||||
not a rename: re-rooting where a value *comes from* must not reinterpret
|
the other hives read — `swarm.hives.edge.domain = "edge.elsewhere.example";`.
|
||||||
the values already deployed.
|
|
||||||
|
Setting `services.hyperhive.domain` directly still works and still wins,
|
||||||
|
with a **deprecation warning**. The reason it's deprecated is not tidiness:
|
||||||
|
that option is local to one host, the directory is copied to every host,
|
||||||
|
so a value written only there leaves every peer pointing somewhere else
|
||||||
|
with nothing detecting the disagreement.
|
||||||
|
|
||||||
|
⚠️ **Upgrading:** a hive that has been running on `swarm.domain` +
|
||||||
|
`hiveName` alone now needs its own directory entry —
|
||||||
|
`services.hyperhive.swarm.hives.<hiveName> = { };`, one line, no value.
|
||||||
|
Eval fails naming it if you forget.
|
||||||
|
|
||||||
`domain` drives `HYPERHIVE_HIVE_DOMAIN` in every container so agents can
|
`domain` drives `HYPERHIVE_HIVE_DOMAIN` in every container so agents can
|
||||||
form qualified labels (`iris@pr1ma.example.com`).
|
form qualified labels (`iris@pr1ma.example.com`).
|
||||||
|
|
|
||||||
|
|
@ -171,11 +171,16 @@ in
|
||||||
hyperhive requires services.hyperhive.domain to be set — the
|
hyperhive requires services.hyperhive.domain to be set — the
|
||||||
hive resolver is authoritative for `<hive-domain>` and its
|
hive resolver is authoritative for `<hive-domain>` and its
|
||||||
sub-domains, and agents reach the forge/matrix through the
|
sub-domains, and agents reach the forge/matrix through the
|
||||||
gateway by that domain. Pin a hostname
|
gateway by that domain.
|
||||||
(`services.hyperhive.domain = "example.com";`), or set
|
|
||||||
`services.hyperhive.swarm.domain` and
|
It is read from this hive's entry in the swarm directory, so
|
||||||
`services.hyperhive.hiveName` and it is derived for you as
|
what is actually missing is that entry:
|
||||||
`<hiveName>.<swarm.domain>`.
|
|
||||||
|
services.hyperhive.swarm.hives."<hiveName>" = { };
|
||||||
|
|
||||||
|
whose `domain` defaults to `<hiveName>.<swarm.domain>`. The
|
||||||
|
assertion in ./swarm.nix names it precisely; this one is the
|
||||||
|
backstop.
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
{
|
{
|
||||||
lib,
|
lib,
|
||||||
config,
|
config,
|
||||||
|
options,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
|
|
@ -36,19 +37,24 @@ in
|
||||||
# Hive identity (label + domain + display names).
|
# Hive identity (label + domain + display names).
|
||||||
options.services.hyperhive.domain = lib.mkOption {
|
options.services.hyperhive.domain = lib.mkOption {
|
||||||
type = lib.types.nullOr lib.types.str;
|
type = lib.types.nullOr lib.types.str;
|
||||||
# Every hive in a swarm lives at its own sub-domain of the swarm's,
|
# Read out of the swarm directory rather than derived here. The
|
||||||
# so this is derivable rather than something each hive repeats. It
|
# directory is what every OTHER hive reads this hive's address from,
|
||||||
# stays a DEFAULT and not a rename: an alias would reinterpret the
|
# so deriving it separately gave two homes for one value with
|
||||||
# domains hives have already deployed, whereas a default only fills
|
# nothing asserting they agreed — and a disagreement surfaces as
|
||||||
# in the ones that never set it. Null (both parts unset) keeps the
|
# "the other hives can't reach me", on a machine other than the
|
||||||
# existing "required" assertion in hive-network.nix as the single
|
# misconfigured one.
|
||||||
# place this can fail.
|
#
|
||||||
|
# ⚠️ The `<hiveName>.<swarm.domain>` formula did NOT move here from
|
||||||
|
# there and back: it lives once, on `hives.<name>.domain`, which
|
||||||
|
# this reads. Restoring a direct fallback here would recreate the
|
||||||
|
# second path (and, since that default reads `swarm.domain` too, a
|
||||||
|
# value that can differ from the directory's).
|
||||||
default =
|
default =
|
||||||
if hiveCfg.swarm.domain != null && hiveCfg.hiveName != null then
|
if hiveCfg.hiveName != null && hiveCfg.swarm.hives ? ${hiveCfg.hiveName} then
|
||||||
"${hiveCfg.hiveName}.${hiveCfg.swarm.domain}"
|
hiveCfg.swarm.hives.${hiveCfg.hiveName}.domain
|
||||||
else
|
else
|
||||||
null;
|
null;
|
||||||
defaultText = lib.literalExpression ''"''${hiveName}.''${swarm.domain}", or null when either is unset'';
|
defaultText = lib.literalExpression "services.hyperhive.swarm.hives.\${hiveName}.domain, or null when there is no entry for this hive";
|
||||||
example = "darkest.space";
|
example = "darkest.space";
|
||||||
description = ''
|
description = ''
|
||||||
Canonical host domain for hyperhive subsystems that need a
|
Canonical host domain for hyperhive subsystems that need a
|
||||||
|
|
@ -63,12 +69,44 @@ in
|
||||||
`hive-agent::identity::hive_domain()` for `<name>@<domain>`
|
`hive-agent::identity::hive_domain()` for `<name>@<domain>`
|
||||||
qualified labels.
|
qualified labels.
|
||||||
|
|
||||||
Defaults to `<hiveName>.<swarm.domain>` when both of those are
|
**Deprecated as a place to write.** It is read from this hive's
|
||||||
set, so a hive in a swarm does not restate its own address.
|
own entry in `services.hyperhive.swarm.hives`, whose `domain`
|
||||||
Setting this explicitly always wins.
|
defaults to `<hiveName>.<swarm.domain>` — so a conventional hive
|
||||||
|
states nothing at all, and a non-conventional one states its
|
||||||
|
address in the directory every other hive reads. Setting it here
|
||||||
|
still wins and still works, with a warning: the directory is
|
||||||
|
shared, this option is not, so a value written only here is
|
||||||
|
invisible to the rest of the swarm.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Deprecation warning for the shorthand above, fired on PRIORITY.
|
||||||
|
#
|
||||||
|
# ⚠️ Not `isDefined`, and not `files`: the module system injects an
|
||||||
|
# option's own `default` as a definition attributed to the declaring
|
||||||
|
# file, so both say "defined, in hyperhive.nix" for a config that set
|
||||||
|
# nothing — measured, after this warning fired on the conventional
|
||||||
|
# case. `mkOptionDefault` is priority 1500, so anything lower is a
|
||||||
|
# definition someone actually wrote (100 plain, 1000 mkDefault).
|
||||||
|
config.warnings =
|
||||||
|
lib.optional (hiveCfg.enable && options.services.hyperhive.domain.highestPrio < 1500)
|
||||||
|
''
|
||||||
|
services.hyperhive.domain is set explicitly and is deprecated. This
|
||||||
|
hive's address belongs in the swarm directory, which every hive in
|
||||||
|
the swarm shares a copy of:
|
||||||
|
|
||||||
|
services.hyperhive.swarm.hives."${toString hiveCfg.hiveName}".domain = "${toString hiveCfg.domain}";
|
||||||
|
|
||||||
|
(Or drop the value entirely if it is the conventional
|
||||||
|
`<hiveName>.<swarm.domain>` — that is the directory entry's own
|
||||||
|
default.)
|
||||||
|
|
||||||
|
Setting it here still wins, so nothing is broken right now. What it
|
||||||
|
does not do is tell the other hives: they read this hive's address
|
||||||
|
out of their copy of the directory, so a value written only here
|
||||||
|
leaves them pointing somewhere else with nothing detecting it.
|
||||||
|
'';
|
||||||
|
|
||||||
# Where the swarm lives. Declared beside the hive's own identity
|
# Where the swarm lives. Declared beside the hive's own identity
|
||||||
# because it is what that identity is derived FROM — every hive in a
|
# because it is what that identity is derived FROM — every hive in a
|
||||||
# swarm is a sub-domain of it. Unlike the renamed options nearby, this
|
# swarm is a sub-domain of it. Unlike the renamed options nearby, this
|
||||||
|
|
@ -79,11 +117,11 @@ in
|
||||||
example = "darkest.space";
|
example = "darkest.space";
|
||||||
description = ''
|
description = ''
|
||||||
DNS domain of the wider swarm this hive belongs to. Each hive
|
DNS domain of the wider swarm this hive belongs to. Each hive
|
||||||
occupies its own sub-domain of it, which is why
|
occupies its own sub-domain of it, which is why every entry in
|
||||||
`services.hyperhive.domain` defaults to
|
`services.hyperhive.swarm.hives` defaults its `domain` to
|
||||||
`<hiveName>.<swarm.domain>` — set this plus
|
`<name>.<swarm.domain>` — set this plus
|
||||||
`services.hyperhive.hiveName` and a hive needs no domain of its
|
`services.hyperhive.hiveName`, list the hives by name, and no
|
||||||
own.
|
hive in the swarm states an address at all.
|
||||||
|
|
||||||
**Required** when `services.hyperhive.enable`, and deliberately
|
**Required** when `services.hyperhive.enable`, and deliberately
|
||||||
not defaulted: there is no fallback worth having. A guessed
|
not defaulted: there is no fallback worth having. A guessed
|
||||||
|
|
|
||||||
|
|
@ -44,103 +44,121 @@ in
|
||||||
{
|
{
|
||||||
options.services.hyperhive.swarm.hives = lib.mkOption {
|
options.services.hyperhive.swarm.hives = lib.mkOption {
|
||||||
type = lib.types.attrsOf (
|
type = lib.types.attrsOf (
|
||||||
lib.types.submodule {
|
lib.types.submodule (
|
||||||
options = {
|
{ name, ... }:
|
||||||
domain = lib.mkOption {
|
{
|
||||||
type = lib.types.str;
|
options = {
|
||||||
example = "lab.example.com";
|
domain = lib.mkOption {
|
||||||
description = ''
|
type = lib.types.str;
|
||||||
Public DNS domain this hive occupies — used for dashboard
|
# `<name>.<swarm.domain>` is a derivation from two values an
|
||||||
links, peer HTTPS checks and Matrix federation discovery.
|
# operator had to state explicitly (both are required), not a
|
||||||
|
# guess — and it is what makes this directory worth copying:
|
||||||
|
# a conventional swarm is `{ pr1ma = { }; umbra = { }; }`,
|
||||||
|
# names only, with a non-conventional hive saying so and only
|
||||||
|
# that. A shared file is read far more often than written.
|
||||||
|
#
|
||||||
|
# Total rather than a throw when `swarm.domain` is unset, for
|
||||||
|
# the reason ./hive-network.nix:155 gives in full: defaults
|
||||||
|
# that interpolate the domain are forced *while the assertion
|
||||||
|
# list evaluates*, so a throw here would replace the message
|
||||||
|
# naming the missing option with a coercion error naming this
|
||||||
|
# one. `.invalid` is reserved (RFC 2606) and fails loudly at
|
||||||
|
# resolution if it ever escaped — which the required-domain
|
||||||
|
# assertion is there to stop.
|
||||||
|
default = if swarmCfg.domain == null then "${name}.invalid" else "${name}.${swarmCfg.domain}";
|
||||||
|
defaultText = lib.literalExpression ''"''${name}.''${services.hyperhive.swarm.domain}"'';
|
||||||
|
example = "lab.example.com";
|
||||||
|
description = ''
|
||||||
|
Public DNS domain this hive occupies — used for dashboard
|
||||||
|
links, peer HTTPS checks and Matrix federation discovery.
|
||||||
|
|
||||||
Deliberately has no default. It is conventionally
|
Defaults to `<name>.<swarm.domain>`, the convention every
|
||||||
`<name>.<swarm.domain>`, but defaulting to that would let
|
hive in a swarm follows, so a conventional directory is
|
||||||
a typo'd swarm domain produce a name that resolves
|
names only. Set it for a hive that is addressed by
|
||||||
somewhere real; an eval failure naming the hive is the
|
something else.
|
||||||
better outcome.
|
'';
|
||||||
'';
|
};
|
||||||
|
|
||||||
|
certFingerprint = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "sha256:b1946ac92492d2347c6235b4d2611184a3f5b6cae6c19d6e3c2f0a8e7d4c9f12";
|
||||||
|
description = ''
|
||||||
|
Expected TLS certificate fingerprint for this hive's HTTPS
|
||||||
|
endpoint. Null = trust the CA bundle — which for a hive
|
||||||
|
inside the swarm CA hierarchy is the normal case, since
|
||||||
|
every hive under the swarm root already chains to it.
|
||||||
|
Set it to pin a leaf that no CA in the bundle vouches for.
|
||||||
|
|
||||||
|
Format: the literal `sha256:` followed by exactly 64
|
||||||
|
hex digits (case-insensitive, no colon separators) — the
|
||||||
|
SHA-256 digest of the 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, which validates against the
|
||||||
|
container's trust bundle. There is no per-hive CA field to
|
||||||
|
cover that case any more: the swarm root is the trust path
|
||||||
|
(see ./swarm-ca.nix).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
wireguardPublicKey = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "base64pubkey=";
|
||||||
|
description = ''
|
||||||
|
WireGuard public key for this hive's host. Required when
|
||||||
|
`services.hyperhive.swarm.wireguard.enable = true` and
|
||||||
|
you want this hive 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 hive in `host:port` form.
|
||||||
|
Null = this hive has no reachable endpoint, so the tunnel
|
||||||
|
is initiated from the other side.
|
||||||
|
|
||||||
|
Reads like a fact about the relationship and is not: it
|
||||||
|
says whether *this* hive can be dialled, which every other
|
||||||
|
hive in the swarm needs the same answer to.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
wireguardAddress = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "10.100.0.2/32";
|
||||||
|
description = ''
|
||||||
|
IP address (with prefix) of this hive's host on the
|
||||||
|
WireGuard mesh. Used as the `allowedIPs` for its
|
||||||
|
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
|
||||||
|
a hive in the mesh (entries missing this field are
|
||||||
|
silently excluded from `wg-hive`).
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
}
|
||||||
certFingerprint = lib.mkOption {
|
)
|
||||||
type = lib.types.nullOr lib.types.str;
|
|
||||||
default = null;
|
|
||||||
example = "sha256:b1946ac92492d2347c6235b4d2611184a3f5b6cae6c19d6e3c2f0a8e7d4c9f12";
|
|
||||||
description = ''
|
|
||||||
Expected TLS certificate fingerprint for this hive's HTTPS
|
|
||||||
endpoint. Null = trust the CA bundle — which for a hive
|
|
||||||
inside the swarm CA hierarchy is the normal case, since
|
|
||||||
every hive under the swarm root already chains to it.
|
|
||||||
Set it to pin a leaf that no CA in the bundle vouches for.
|
|
||||||
|
|
||||||
Format: the literal `sha256:` followed by exactly 64
|
|
||||||
hex digits (case-insensitive, no colon separators) — the
|
|
||||||
SHA-256 digest of the 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, which validates against the
|
|
||||||
container's trust bundle. There is no per-hive CA field to
|
|
||||||
cover that case any more: the swarm root is the trust path
|
|
||||||
(see ./swarm-ca.nix).
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
wireguardPublicKey = lib.mkOption {
|
|
||||||
type = lib.types.nullOr lib.types.str;
|
|
||||||
default = null;
|
|
||||||
example = "base64pubkey=";
|
|
||||||
description = ''
|
|
||||||
WireGuard public key for this hive's host. Required when
|
|
||||||
`services.hyperhive.swarm.wireguard.enable = true` and
|
|
||||||
you want this hive 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 hive in `host:port` form.
|
|
||||||
Null = this hive has no reachable endpoint, so the tunnel
|
|
||||||
is initiated from the other side.
|
|
||||||
|
|
||||||
Reads like a fact about the relationship and is not: it
|
|
||||||
says whether *this* hive can be dialled, which every other
|
|
||||||
hive in the swarm needs the same answer to.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
wireguardAddress = lib.mkOption {
|
|
||||||
type = lib.types.nullOr lib.types.str;
|
|
||||||
default = null;
|
|
||||||
example = "10.100.0.2/32";
|
|
||||||
description = ''
|
|
||||||
IP address (with prefix) of this hive's host on the
|
|
||||||
WireGuard mesh. Used as the `allowedIPs` for its
|
|
||||||
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
|
|
||||||
a hive in the mesh (entries missing this field are
|
|
||||||
silently excluded from `wg-hive`).
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
default = { };
|
default = { };
|
||||||
example = {
|
example = {
|
||||||
pr1ma = {
|
pr1ma = {
|
||||||
domain = "pr1ma.example.com";
|
|
||||||
wireguardAddress = "10.100.0.1/32";
|
wireguardAddress = "10.100.0.1/32";
|
||||||
wireguardEndpoint = "203.0.113.1:51820";
|
wireguardEndpoint = "203.0.113.1:51820";
|
||||||
};
|
};
|
||||||
edge = {
|
edge = {
|
||||||
domain = "edge.example.com";
|
domain = "edge.elsewhere.example";
|
||||||
wireguardAddress = "10.100.0.2/32";
|
wireguardAddress = "10.100.0.2/32";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -151,10 +169,15 @@ in
|
||||||
`services.hyperhive.hiveName` is what makes a given host read it
|
`services.hyperhive.hiveName` is what makes a given host read it
|
||||||
as "me and four others" rather than "five peers".
|
as "me and four others" rather than "five peers".
|
||||||
|
|
||||||
Empty (the default) means this host is not part of a swarm. Once
|
Each entry needs no fields at all in the conventional case: a
|
||||||
non-empty it must contain an entry for `hiveName`, which is
|
hive's `domain` defaults to `<name>.<swarm.domain>`, so the whole
|
||||||
asserted — a hive that lists everyone but itself would otherwise
|
directory is usually a list of names.
|
||||||
derive its own peer set as *everything* and peer with itself.
|
|
||||||
|
It must contain an entry for `hiveName`, which is asserted — this
|
||||||
|
host's own address is read out of it (it is where
|
||||||
|
`services.hyperhive.domain` derives from), and a hive that lists
|
||||||
|
everyone but itself would otherwise derive its own peer set as
|
||||||
|
*everything* and peer with itself.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -197,20 +220,33 @@ in
|
||||||
|
|
||||||
assertions = [
|
assertions = [
|
||||||
{
|
{
|
||||||
|
# An EMPTY `hives` fires this too, deliberately: since
|
||||||
|
# `swarm.domain` became required, every hive is in a swarm — a
|
||||||
|
# swarm of one is still a swarm — so a directory with no entry
|
||||||
|
# for this host is missing one either way. It also has to fire
|
||||||
|
# here, because this host's own domain is now read out of the
|
||||||
|
# directory: without the entry `services.hyperhive.domain` is
|
||||||
|
# null and the generic required-domain assertion in
|
||||||
|
# ./hive-network.nix would fire instead, naming an option the
|
||||||
|
# operator should no longer be setting.
|
||||||
|
#
|
||||||
# Guarded on `hiveName != null` so the required-hiveName
|
# Guarded on `hiveName != null` so the required-hiveName
|
||||||
# assertion in ./hyperhive.nix is what fires for that case —
|
# assertion in ./hyperhive.nix is what fires for that case —
|
||||||
# two assertions naming the same missing value is noise.
|
# two assertions naming the same missing value is noise.
|
||||||
assertion = swarmCfg.hives == { } || cfg.hiveName == null || swarmCfg.hives ? ${cfg.hiveName};
|
assertion = cfg.hiveName == null || swarmCfg.hives ? ${cfg.hiveName};
|
||||||
message = ''
|
message = ''
|
||||||
services.hyperhive.swarm.hives has no entry for this hive
|
services.hyperhive.swarm.hives has no entry for this hive
|
||||||
(services.hyperhive.hiveName = "${toString cfg.hiveName}").
|
(services.hyperhive.hiveName = "${toString cfg.hiveName}").
|
||||||
|
|
||||||
`hives` describes every hive in the swarm including this one,
|
`hives` describes every hive in the swarm including this one,
|
||||||
so that every host can share one identical attrset. Add:
|
so that every host can share one identical attrset, and this
|
||||||
|
hive's own domain is derived from its entry. Add:
|
||||||
|
|
||||||
services.hyperhive.swarm.hives."${toString cfg.hiveName}" = {
|
services.hyperhive.swarm.hives."${toString cfg.hiveName}" = { };
|
||||||
domain = "${toString cfg.domain}";
|
|
||||||
};
|
No fields are needed: `domain` defaults to
|
||||||
|
`<name>.<swarm.domain>`. Set it in the entry if this hive is
|
||||||
|
addressed by something else.
|
||||||
|
|
||||||
Declared hives: ${lib.concatStringsSep ", " (lib.attrNames swarmCfg.hives)}
|
Declared hives: ${lib.concatStringsSep ", " (lib.attrNames swarmCfg.hives)}
|
||||||
'';
|
'';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue