hyperhive/nix/host-modules/hyperhive.nix
atlas ec565120af feat(nix): derive the hive domain from a new swarm.domain
Every hive in a swarm occupies its own sub-domain of the swarm's, so
the hive domain is derivable rather than something each hive restates.
`services.hyperhive.swarm.domain` is new and nullable; the hive's own
`domain` keeps its existing required-ness and its existing assertion,
and gains a default of `<hiveName>.<swarm.domain>`.

Deliberately a default and not a rename: an alias would reinterpret the
domains hives have already deployed, while a default only fills in the
ones that never set one. Same reason there is no new assertion — the
existing message just names the derivation, so this adds a way to stop
failing rather than a way to fail.
2026-08-05 15:56:49 +02:00

167 lines
7.4 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,
...
}:
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;
# Every hive in a swarm lives at its own sub-domain of the swarm's,
# so this is derivable rather than something each hive repeats. It
# stays a DEFAULT and not a rename: an alias would reinterpret the
# domains hives have already deployed, whereas a default only fills
# in the ones that never set it. Null (both parts unset) keeps the
# existing "required" assertion in hive-network.nix as the single
# place this can fail.
default =
if hiveCfg.swarm.domain != null && hiveCfg.hiveName != null then
"${hiveCfg.hiveName}.${hiveCfg.swarm.domain}"
else
null;
defaultText = lib.literalExpression ''"''${hiveName}.''${swarm.domain}", or null when either is unset'';
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.
Defaults to `<hiveName>.<swarm.domain>` when both of those are
set, so a hive in a swarm does not restate its own address.
Setting this explicitly always wins.
'';
};
# 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
`services.hyperhive.domain` defaults to
`<hiveName>.<swarm.domain>` set this plus
`services.hyperhive.hiveName` and a hive needs no domain of its
own.
A swarm needs this set somewhere to address its hives uniformly;
it is left nullable so an existing single-hive deployment that
pins `services.hyperhive.domain` directly keeps evaluating
untouched. The only thing eval insists on is that the hive ends
up with a domain, by either route.
'';
};
# 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.
Distinct from `services.hyperhive.domain` (the machine-
addressable DNS name): the domain may carry the hive name as
its leftmost label by convention, but this option is the
canonical readable identity. Exposed to agents as
`HYPERHIVE_HIVE_NAME`; surfaced in the dashboard chrome and
per-agent system prompt when set. Null falls back to the
default behaviour (chrome shows the domain, prompt doesn't
mention a hive name).
'';
};
# 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.
'';
};
# 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).
'';
};
}