`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.
209 lines
9.7 KiB
Nix
209 lines
9.7 KiB
Nix
# Top-level, cross-cutting hyperhive options: the master enable
|
|
# switch, the hive's identity (domain + display names), and hive-wide
|
|
# feature toggles read by several subsystem modules. Imported by the
|
|
# ./default.nix aggregator.
|
|
{
|
|
lib,
|
|
config,
|
|
options,
|
|
...
|
|
}:
|
|
let
|
|
hiveCfg = config.services.hyperhive;
|
|
in
|
|
{
|
|
# The swarm's display name moved under `swarm` when the swarm-global
|
|
# settings were consolidated; the hive's own name and domain stayed put,
|
|
# because they describe this hive rather than the swarm it joins.
|
|
imports = [
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "hyperhive" "swarmName" ]
|
|
[ "services" "hyperhive" "swarm" "name" ]
|
|
)
|
|
];
|
|
|
|
# Top-level hyperhive enable flag. When true, automatically enables
|
|
# hive-c0re and the on-by-default hyperhive subsystems.
|
|
options.services.hyperhive.enable = lib.mkEnableOption "hyperhive — the agent swarm coordinator";
|
|
|
|
# Canonical hive DNS domain shared by every subsystem that needs a
|
|
# stable hostname. Typed nullOr (default null) so the option always
|
|
# exists, but it's REQUIRED whenever hyperhive is enabled — an
|
|
# assertion in hive-network.nix fails eval when it's unset, since
|
|
# matrix bakes it in on first boot and the gateway/forge/agent URLs all
|
|
# derive from it (no safe default). Full identity-surface
|
|
# context (HYPERHIVE_HIVE_DOMAIN / HIVE_NAME / SWARM_NAME env-var
|
|
# chain → identity.rs → claude prompt): docs/conventions.md::
|
|
# Hive identity (label + domain + display names).
|
|
options.services.hyperhive.domain = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
# Read out of the swarm directory rather than derived here. The
|
|
# directory is what every OTHER hive reads this hive's address from,
|
|
# so deriving it separately gave two homes for one value with
|
|
# nothing asserting they agreed — and a disagreement surfaces as
|
|
# "the other hives can't reach me", on a machine other than the
|
|
# misconfigured one.
|
|
#
|
|
# ⚠️ 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 =
|
|
if hiveCfg.hiveName != null && hiveCfg.swarm.hives ? ${hiveCfg.hiveName} then
|
|
hiveCfg.swarm.hives.${hiveCfg.hiveName}.domain
|
|
else
|
|
null;
|
|
defaultText = lib.literalExpression "services.hyperhive.swarm.hives.\${hiveName}.domain, or null when there is no entry for this hive";
|
|
example = "darkest.space";
|
|
description = ''
|
|
Canonical host domain for hyperhive subsystems that need a
|
|
stable name (currently: `services.hyperhive.swarm.matrix.serverName`
|
|
derives from this, defaulting to
|
|
`matrix.''${services.hyperhive.domain}` when `serverName` is
|
|
null). **Required** when `services.hyperhive.enable` — eval fails
|
|
with a helpful message if it's unset (it's baked into matrix on
|
|
first boot and drives the gateway/forge/agent URLs, with no safe
|
|
default; changing it later is destructive). Exposed to agents as
|
|
`HYPERHIVE_HIVE_DOMAIN`; consumed by
|
|
`hive-agent::identity::hive_domain()` for `<name>@<domain>`
|
|
qualified labels.
|
|
|
|
**Deprecated as a place to write.** It is read from this hive's
|
|
own entry in `services.hyperhive.swarm.hives`, whose `domain`
|
|
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
|
|
# 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
|
|
# is genuinely new: nothing moved here, so there is no alias.
|
|
options.services.hyperhive.swarm.domain = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
default = null;
|
|
example = "darkest.space";
|
|
description = ''
|
|
DNS domain of the wider swarm this hive belongs to. Each hive
|
|
occupies its own sub-domain of it, which is why every entry in
|
|
`services.hyperhive.swarm.hives` defaults its `domain` to
|
|
`<name>.<swarm.domain>` — set this plus
|
|
`services.hyperhive.hiveName`, list the hives by name, and no
|
|
hive in the swarm states an address at all.
|
|
|
|
**Required** when `services.hyperhive.enable`, and deliberately
|
|
not defaulted: there is no fallback worth having. A guessed
|
|
swarm domain is a wrong hostname that evaluates cleanly and
|
|
deploys, which is worse than an eval failure telling an
|
|
operator to write down the one address their swarm answers to.
|
|
Upgrading past this costs one line, once.
|
|
'';
|
|
};
|
|
|
|
# Human display name for this hive. Distinct from the DNS domain
|
|
# above (machine-readable) — see docs/conventions.md::Hive identity
|
|
# for the domain-vs-name-vs-swarm distinction + the env-var
|
|
# propagation chain. The swarm's display name is
|
|
# `services.hyperhive.swarm.name`, one level out: this hive is named
|
|
# here, the swarm it belongs to is named there.
|
|
options.services.hyperhive.hiveName = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
default = null;
|
|
example = "pr1ma";
|
|
description = ''
|
|
Human-readable name of this single-host hive instance.
|
|
**Required** when `services.hyperhive.enable`. Distinct from
|
|
`services.hyperhive.domain` (the machine-addressable DNS name)
|
|
but no longer merely cosmetic: a hive occupies
|
|
`<hiveName>.<swarm.domain>`, so this is the label the hive is
|
|
*addressed* by as well as the one it is called. Exposed to
|
|
agents as `HYPERHIVE_HIVE_NAME`; surfaced in the dashboard
|
|
chrome and per-agent system prompt.
|
|
'';
|
|
};
|
|
|
|
# The one hive-level option that describes something ABOVE the hive,
|
|
# which is why it sits under `swarm` with the swarm-global services
|
|
# rather than beside `hiveName`.
|
|
options.services.hyperhive.swarm.name = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
default = null;
|
|
example = "constellat1on";
|
|
description = ''
|
|
Human-readable name of the wider swarm this hive belongs to.
|
|
Hives at different DNS domains can share a swarm name when
|
|
they federate together. Exposed to agents as
|
|
`HYPERHIVE_SWARM_NAME`; surfaced in the dashboard chrome and
|
|
per-agent system prompt when set.
|
|
'';
|
|
};
|
|
|
|
# `enableAllLocalDefaults` is declared in ./local-defaults.nix, with
|
|
# the values it asserts. It is a deployment mode rather than a setting
|
|
# this module's options read, so it lives with its consequences.
|
|
|
|
# Whether this hive runs "ruthless" — with no root/manager agent at
|
|
# all. Some hives don't want a root agent — see issue tracker
|
|
# "scope concept: special agents".
|
|
options.services.hyperhive.ruthless = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
example = true;
|
|
description = ''
|
|
Run this hive "ruthless" — with no root (manager) agent at all (no
|
|
ruth). When `true`, hive-c0re skips the root-agent auto-management
|
|
sweep entirely (it otherwise creates the root agent's container when
|
|
missing and restarts it when present but stopped). Defaults to
|
|
`false` (the root agent is auto-managed as required
|
|
infrastructure). Exposed to hive-c0re as `HYPERHIVE_RUTHLESS`.
|
|
'';
|
|
};
|
|
|
|
options.services.hyperhive.github.enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
example = false;
|
|
description = ''
|
|
Hive-wide switch for the per-agent GitHub integration (the `gh` CLI
|
|
wrapper + git credential helper, per `hyperhive.github.enable`). On by
|
|
default: every agent gets the integration, inert until a PAT is
|
|
provisioned via the dashboard credentials tab or `hivectl github
|
|
set-token`. Set `false` to turn it off for the whole hive --- the
|
|
meta-flake renderer (`hive-c0re/src/meta.rs`) then injects
|
|
`hyperhive.github.enable = false` into every agent. Exposed to hive-c0re
|
|
as `HYPERHIVE_GITHUB_DISABLED` (set only when the integration is off).
|
|
'';
|
|
};
|
|
}
|