hyperhive — host options
services.hyperhive.enable
Whether to enable hyperhive — the agent swarm coordinator.
Type: boolean
Default:
false
Example:
true
Declared by:
services.hyperhive.c0re.enable
Enable hive-c0re coordinator daemon (auto-enabled by services.hyperhive.enable).
Type: boolean
Default:
config.services.hyperhive.enable
Declared by:
services.hyperhive.c0re.package
hyperhive workspace package. Provides /bin/hive-c0re
(coordinator daemon + admin-socket CLI) and /bin/hivectl
(operator-facing host CLI for ad-hoc administration).
Type: package
Default:
hyperhive.packages.${system}.default
Declared by:
services.hyperhive.c0re.agentCpuQuota
systemd CPUQuota= applied to every agent container via a
container@h-<name>.service.d/ drop-in written on each
spawn/rebuild. Expressed as a percentage of one CPU core —
"200%" allows each agent to use up to 2 cores. The old
hard-coded value was "50%"; bump this if agents are hitting
CPU limits during builds or heavy tool use.
For a hive-wide cap across all containers, set
systemd.slices.machine.serviceConfig.CPUQuota in your NixOS
config (all nspawn containers live in machine.slice).
Type: string
Default:
"200%"
Example:
"400%"
Declared by:
services.hyperhive.c0re.agentMemoryMax
systemd MemoryMax= applied to every agent container via the
same drop-in as agentCpuQuota. The old hard-coded value was
"2G".
Type: string
Default:
"4G"
Example:
"8G"
Declared by:
services.hyperhive.c0re.assets
Bundled static runtime assets (see ./nix/assets.nix): the
project’s branding family + the claude system-prompt template +
claude-settings JSON. Output has share/hyperhive/{branding,prompts}/;
passed to hive-c0re’s systemd unit via HIVE_ASSETS_DIR
(hive_sh4re::assets::* resolve paths underneath). Override to
ship customised branding or prompts without rebuilding the
rust derivation.
Type: package
Default:
hyperhive.packages.${system}.assets
Declared by:
services.hyperhive.c0re.contextWindowTokens
Per-model context-window sizes in tokens. Each key is a
model-family short name matched case-insensitively as a
substring of the active model name at runtime (e.g. "sonnet"
matches "claude-sonnet-4-5"). The defaults cover the known
Anthropic families; add entries for new models or override
existing ones here to change the window for all agents at once.
Passed to hive-c0re serve as JSON and injected into every
container’s harness service environment as
HIVE_CONTEXT_WINDOW_TOKENS_<KEY_UPPER>. Changes propagate
on the next ↻ R3BU1LD — no per-agent approval needed.
Type: attribute set of signed integer
Default:
{
haiku = 200000;
opus = 1000000;
sonnet = 1000000;
}
Example:
{
haiku = 150000;
sonnet = 900000;
}
Declared by:
services.hyperhive.c0re.dashboardPort
TCP port the hive-c0re dashboard listens on.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
7000
Declared by:
services.hyperhive.c0re.frontend
Bundled frontend dist (see ./nix/frontend.nix). Output has
dashboard/ and agent/ subdirectories — hive-c0re serves
dashboard/ via tower_http::ServeDir from the path passed
in HIVE_STATIC_DIR. Override to ship a custom dashboard SPA;
the JSON contract (/api/state, the SSE streams, the action
endpoints) is the source of truth for any replacement.
Type: package
Default:
hyperhive.packages.${system}.frontend
Declared by:
services.hyperhive.c0re.hyperhiveFlake
URL of the hyperhive flake (no fragment). Inlined into each
per-agent flake.nix at inputs.hyperhive.url. The per-agent
flake then pulls hyperhive.nixosConfigurations.agent-base to
build the container. Defaults to this flake’s own store path —
only override if you want agents tracking a different ref.
Type: string
Default: the flake’s own store path
Declared by:
services.hyperhive.c0re.modelPrices
Per-model USD prices (per million tokens) used for the
hive-wide cost estimate on the dashboard’s ST4TS tab. Each key
is a model-family short name matched case-insensitively as a
substring of the active model id at runtime (e.g. "sonnet"
matches "claude-sonnet-4-5"); the longest matching key wins, so
a specific entry beats a generic family name. Any model not
covered by this table falls back to hive-c0re’s built-in
estimate.
The defaults track Anthropic list pricing at the time of
writing — override them here to keep the estimate current
without a code change. Passed to hive-c0re serve as JSON via
--model-prices; read only by hive-c0re itself (not injected
into containers). Changes apply on the next host rebuild.
Type: attribute set of (submodule)
Default:
{
haiku = {
cache_read = 0.1;
cache_write = 2.0;
input = 1.0;
output = 5.0;
};
opus = {
cache_read = 0.5;
cache_write = 10.0;
input = 5.0;
output = 25.0;
};
sonnet = {
cache_read = 0.3;
cache_write = 6.0;
input = 3.0;
output = 15.0;
};
}
Example:
{
sonnet = {
cache_read = 0.3;
cache_write = 6.0;
input = 3.0;
output = 15.0;
};
}
Declared by:
services.hyperhive.c0re.modelPrices.<name>.cache_read
USD per million cache-read tokens.
Type: nonnegative integer or floating point number, meaning >=0
Declared by:
services.hyperhive.c0re.modelPrices.<name>.cache_write
USD per million cache-creation (write) tokens.
Type: nonnegative integer or floating point number, meaning >=0
Declared by:
services.hyperhive.c0re.modelPrices.<name>.input
USD per million input tokens.
Type: nonnegative integer or floating point number, meaning >=0
Declared by:
services.hyperhive.c0re.modelPrices.<name>.output
USD per million output tokens.
Type: nonnegative integer or floating point number, meaning >=0
Declared by:
services.hyperhive.c0re.nixpkgsFlake
Store-path URL for the nixpkgs input in the generated meta
flake. The meta flake declares this as a top-level input and
wires inputs.hyperhive.inputs.nixpkgs.follows = "nixpkgs" so
every agent container evaluates with this exact nixpkgs.
Defaults to "path:${pkgs.path}" — the store path of the
nixpkgs the host NixOS module was evaluated with. When the
operator sets inputs.hyperhive.inputs.nixpkgs.follows = "nixpkgs" in their host flake, pkgs.path resolves to the
host’s own nixpkgs, so agents transparently track the same
channel as the host.
Override to pin agents to a specific nixpkgs version regardless of the host’s channel.
Type: string
Default:
"path:${pkgs.path}"
Declared by:
services.hyperhive.c0re.nixpkgsUnstableFlake
Store-path URL for the nixpkgs-unstable input in the generated
meta flake. The meta flake declares this as a top-level input and
wires inputs.hyperhive.inputs.nixpkgs-unstable.follows = "nixpkgs-unstable" so agents use this exact unstable nixpkgs.
Defaults to the store path of the nixpkgs-unstable input
hyperhive’s own flake.nix was evaluated with (the channel that
carries claude-code). Override when you want to track a newer
unstable snapshot or a custom claude-code package.
Type: string
Default:
hyperhive’s own nixpkgs-unstable store path
Declared by:
services.hyperhive.c0re.operatorPronouns
Operator pronouns, free text. Threaded into every agent
container as the HIVE_OPERATOR_PRONOUNS env var; the
harness substitutes it into the agent / manager system
prompt at boot so claude refers to the operator naturally
in third person (“ask her”, “tell them”, etc.). Changes
propagate to running agents on the next ↻ R3BU1LD —
forwards as a meta flake env-var bump, no per-agent
approval needed.
Type: string
Default:
"she/her"
Example:
"they/them"
Declared by:
services.hyperhive.c0re.preBuildAgentTemplates
Pre-fetch the per-container system closures (agent-base + manager toplevels) into the host’s /nix/store as part of this host’s NixOS build, instead of letting the first agent spawn do all the work.
Enabling this adds roughly the full nixpkgs runtime closure +
claude-code + the harness binary to your system closure size
(low single-digit GB), but the first nixos-container start
for any agent then completes in seconds instead of minutes
because nothing’s left to fetch.
Off by default because the toplevels are pinned to
x86_64-linux (nixos-containers run native arch). Enabling
on an aarch64 host would force nix to build the x86 closure
via cross or a remote builder, which is rarely what you want.
Flip to true on an x86_64 host when you care more about
first-spawn latency than host store size — or just
nix build /nix/store/1gzmvkrrbxc2zsx217ja9z27px57vdmw-source#agent-base-toplevel once
manually to warm the store.
Type: boolean
Default:
false
Example:
true
Declared by:
services.hyperhive.domain
Canonical host domain for hyperhive subsystems that need a
stable name (currently: services.hyperhive.matrix.serverName
derives from this, defaulting to
matrix.${services.hyperhive.domain} when serverName is
null). No default — subsystems that opt to require it assert
non-null in their own config and fail eval with a helpful
message if it’s missing. Exposed to agents as
HYPERHIVE_HIVE_DOMAIN; consumed by
hive-ag3nt::identity::hive_domain() for <name>@<domain>
qualified labels.
Type: null or string
Default:
null
Example:
"darkest.space"
Declared by:
services.hyperhive.forge.enable
Run hive-forge — a private Forgejo (in a nixos-container) for
hyperhive agents. On by default: hive-c0re mirrors every
agent’s applied config repo into the forge’s agent-configs
org, so the forge is part of the standard install. Set
services.hyperhive.forge.enable = false to opt out.
Type: boolean
Default:
true
Declared by:
services.hyperhive.forge.package
Forgejo package to run inside the container. Defaults to
pkgs.forgejo (the latest release line) rather than the
nixpkgs-module default of pkgs.forgejo-lts, because LTS
lags far behind on schema and the DB easily ends up “newer
than the binary” if the operator ever ran a non-LTS forgejo
against the same state dir. Override to pkgs.forgejo-lts
if you actively want the slower release train.
Type: package
Default:
pkgs.forgejo
Declared by:
services.hyperhive.forge.behindGateway
Serve forgejo through the hive-gateway nginx as a sub-domain
vhost (server_name = cfg.domain) instead of directly on
httpPort (sub-domain routing — see docs/gateway.md).
When true:
- The gateway adds a
server { server_name = ${cfg.domain}; }block that proxies all/→http://127.0.0.1:${httpPort}/. - Forgejo’s
ROOT_URLflips tohttp(s)://${cfg.domain}/(sub-domain root, no port suffix when gateway is on 80). gateway.localHostsEntry = trueextends/etc/hoststo includecfg.domain → 127.0.0.1for local dev.
Defaults to services.hyperhive.enable (the gateway always runs
alongside hyperhive, so forge auto-routes through it). Set false
explicitly to keep forge on the direct port even though the
gateway is running (e.g. an external git client that doesn’t
traverse the gateway).
Sub-domain routing is the preferred shape for forge + matrix
(both are external standard apps with sub-domain-native config
defaults). Per-agent UIs stay on sub-path (/agent/<name>/)
because they’re hyperhive-internal + already base-path-aware.
Type: boolean
Default:
config.services.hyperhive.enable
Declared by:
services.hyperhive.forge.ci.enable
Run a Forgejo Actions runner in a hive-ci nixos-container.
Grouped under services.hyperhive.forge because the runner is
tightly coupled to the forge instance it registers against.
Disabled by default; services.hyperhive.forge.enable = true is
a prerequisite (enforced by assertion).
On first start the container auto-registers against hive-forge using hive-c0re’s admin token — no manual token provisioning needed. Runner credentials are persisted in the container’s state dir and reused on every subsequent boot.
Type: boolean
Default:
false
Example:
true
Declared by:
services.hyperhive.forge.ci.package
gitea-actions-runner package.
Type: package
Default:
pkgs.gitea-actions-runner
Declared by:
services.hyperhive.forge.ci.concurrency
Maximum number of workflow jobs the runner executes in parallel. Each job gets its own temporary working directory; multiple parallel jobs share the container’s nix store and cargo registry cache. Higher values trade memory + CPU headroom for throughput.
Type: positive integer, meaning >0
Default:
1
Example:
4
Declared by:
services.hyperhive.forge.ci.jobTimeout
Per-job wall-clock timeout the runner enforces (act_runner’s
runner.timeout). A job that exceeds it is killed, so a hung or
runaway build is bounded instead of holding the runner’s single
slot indefinitely. Default 1h comfortably covers a cold-cache
nix build while still bounding a stuck job; raise it (e.g.
"3h") if you legitimately run jobs longer than that. Accepts a
Go duration string (30m, 1h, 2h30m). Note: this is
enforced by the runner process, so it only fires while that
process is itself healthy.
Type: string
Default:
"1h"
Example:
"3h"
Declared by:
services.hyperhive.forge.ci.labels
Runner labels in <name>:<scheme> format. The host scheme runs
commands directly in the container (no docker/podman). Workflow
files target this runner with runs-on: [hive-ci].
Type: list of string
Default:
[
"hive-ci:host"
]
Example:
[
"hive-ci:host"
"nix:host"
]
Declared by:
services.hyperhive.forge.ci.name
Runner name as shown in the Forgejo admin panel. Defaults to “hive-ci”; override when multiple hives share a Forgejo instance.
Type: string
Default:
"hive-ci"
Example:
"prod-hive"
Declared by:
services.hyperhive.forge.domain
Public hostname for the forge. Doubles as both the forgejo
DOMAIN setting (clone URLs forgejo advertises) AND the
gateway vhost server-name when behindGateway = true
(sub-domain routing — see docs/gateway.md).
Defaults to forge.${services.hyperhive.domain} when the
hive-domain is set (idiomatic sub-domain shape — forge
labelled under the hive’s bare domain), falling back to
localhost otherwise (direct-on-port behaviour).
Set to a full hostname (git.example.com,
forge.internal.lan, etc.) for a bespoke vhost shape — the
full domain goes here, no separate sub-domain-label option.
Type: string
Default:
if services.hyperhive.domain != null then
"forge.${services.hyperhive.domain}"
else
"localhost"
Example:
"git.example.com"
Declared by:
services.hyperhive.forge.httpPort
TCP port the forge serves HTTP on. Default 3000 sits outside hyperhive’s claimed ranges (dashboard 7000, every agent in 8100…8999 via FNV-1a hash). Change this if you already have another forgejo bound to 3000.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
3000
Declared by:
services.hyperhive.forge.openFirewall
Open httpPort + sshPort in the host firewall. Off by
default (secure-by-default): the forge is reachable from the
host + every agent container via localhost either way
(shared netns), so the firewall opens only matter for access
from outside the host. Flip to true when you want the
operator’s browser / external git clients to hit the forge
directly. (The container shares host netns, so this is the
only firewall layer that matters.)
Breaking change: this used to default to true. If you
relied on the old default for external reach, add
services.hyperhive.forge.openFirewall = true; to your host
config before rebuilding.
Type: boolean
Default:
false
Example:
true
Declared by:
services.hyperhive.forge.rootUrl
Override the auto-derived forgejo ROOT_URL. When null
(default), ROOT_URL is derived from cfg.domain + gateway
state:
behindGateway = true→http://${cfg.domain}/(usesservices.hyperhive.gateway.portwhen non-80)behindGateway = false→http://${cfg.domain}:${cfg.httpPort}/
Set this to a fully-qualified URL when running behind TLS
termination (https://...), a non-default gateway port, or
a bespoke shape. Must end with / per forgejo’s ROOT_URL
contract.
Type: null or string
Default:
null
Example:
"https://forge.example.com/"
Declared by:
services.hyperhive.forge.sshPort
TCP port the forge’s built-in SSH server listens on. Kept off
22 so it doesn’t clash with the host’s openssh. Agents push
with ssh -p <sshPort> git@<domain>:<owner>/<repo>.git.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
2222
Declared by:
services.hyperhive.gateway.auth.enable
Whether to enable HTTP basic auth on the gateway using an htpasswd file. When
enabled, every request to the gateway’s main vhost requires a
valid username and password. nginx’s built-in auth_basic
module validates credentials against
/var/lib/hyperhive/gateway/gateway.htpasswd on the host
(exposed as /run/hive-state/gateway.htpasswd inside the
container via the existing gateway state bind-mount). Off by default.
Manage users with hivectl gateway create-user, delete-user,
and list-users — see hivectl gateway --help for usage.
The htpasswd file is created automatically when auth is enabled;
add at least one user before enabling to avoid locking everyone out.
.
Type: boolean
Default:
false
Example:
true
Declared by:
services.hyperhive.gateway.auth.realm
HTTP Basic auth realm value sent in the WWW-Authenticate
header when credentials are absent or rejected. Must not
contain " or $ (nginx string metacharacters).
Type: string matching the pattern [^"$]*
Default:
"hyperhive"
Example:
"my-hive"
Declared by:
services.hyperhive.gateway.hsts.enable
Add Strict-Transport-Security to all gateway vhosts.
Disabled by default: HSTS pins HTTPS in the browser’s HSTS preload list; enabling it on a deployment that later loses TLS will lock browsers out until the max-age expires. Only enable this when you are certain TLS is permanent.
Requires TLS to be active (selfSignedTls = true, a tls.certDir,
or tls.acme.enable = true). Enabling HSTS without TLS is
technically harmless (browsers ignore the header over plain HTTP)
but is almost certainly a misconfiguration.
Type: boolean
Default:
false
Declared by:
services.hyperhive.gateway.hsts.includeSubDomains
Whether to include includeSubDomains in the HSTS header.
Only disable this if the gateway host has sub-domains that
intentionally serve plain HTTP.
Type: boolean
Default:
true
Declared by:
services.hyperhive.gateway.hsts.maxAge
Value for the max-age directive in seconds.
Default: 31536000 (1 year), which is the value required for
HSTS preload list submission. Use a shorter value (e.g. 86400)
while testing so browsers forget the pin quickly.
Type: positive integer, meaning >0
Default:
31536000
Example:
86400
Declared by:
services.hyperhive.gateway.httpsPort
TCP port for the TLS-terminated vhosts. Active when
selfSignedTls = true OR tls.certDir is set. Default 443.
Setting selfSignedTls = false and leaving tls.certDir = null
renders this inert (the gateway listens on port only).
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
443
Example:
8443
Declared by:
services.hyperhive.gateway.localHostsEntry
Add an /etc/hosts entry mapping services.hyperhive.domain
to 127.0.0.1 on the host. Useful for local deployments +
tests where there’s no real DNS for services.hyperhive.domain
but the operator (or browser-based tests) want to hit
http://${services.hyperhive.domain} to exercise the
gateway shape. Off by default — operators running with real
DNS shouldn’t have a stale /etc/hosts entry sticking
around. Requires services.hyperhive.domain to be set.
Type: boolean
Default:
false
Example:
true
Declared by:
services.hyperhive.gateway.openFirewall
Open port in the host firewall. Off by default (secure-by-default).
Flip to true to expose the gateway to
the operator’s browser / external clients — required for any
out-of-host reach, since the agents themselves talk to
hive-c0re via the per-agent unix sockets and don’t need the
nginx vhost. Leave off when running behind another reverse
proxy (e.g. caddy / traefik on the host) that handles TLS
termination + forwards to port.
Note: this used to default to true. Add
services.hyperhive.gateway.openFirewall = true; to your host
config if external reach stopped working after a recent upgrade.
Type: boolean
Default:
false
Example:
true
Declared by:
services.hyperhive.gateway.port
TCP port the gateway listens on. Default 80 (canonical web port). nginx inside the container binds <1024 because the container’s init runs as root; if 80 is already taken on the host (existing nginx, traefik, etc.) override to an unused port like 8080 or move the conflicting service.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
80
Example:
8080
Declared by:
services.hyperhive.gateway.selfSignedTls
Generate a self-signed TLS cert at first gateway boot and
listen on httpsPort (default 443) with it on every vhost.
On by default because matrix-dart-sdk (the SDK behind
FluffyChat + several other Matrix clients) hardcodes
https://<host>/.well-known/matrix/client for homeserver
discovery and refuses to fall back to plain http — without
TLS the browser client just won’t connect.
Self-signed means browsers will show a “not secure” warning
on first visit; the operator clicks through once per
browser. For production deployments, set this to false
and front the gateway with a reverse proxy (caddy, traefik,
or nginx with ACME) that does proper TLS termination.
The cert is regenerated on demand if the file is missing
but never rotated automatically; delete
/var/lib/hive-gateway/tls/cert.pem inside the gateway
container to force a fresh one.
See docs/gateway.md (“Self-signed TLS”).
Type: boolean
Default:
true
Example:
false
Declared by:
services.hyperhive.gateway.tls.acme.enable
Let nginx inside the gateway container obtain and renew TLS
certificates automatically via ACME (Let’s Encrypt). When
enabled, each vhost calls out to Let’s Encrypt using the
HTTP-01 challenge on port (default 80) and stores certs
inside the gateway container’s persistent state dir.
Requirements:
services.hyperhive.domainmust be set and publicly DNS-resolvable to this host.services.hyperhive.gateway.openFirewall = trueso Let’s Encrypt can reach/.well-known/acme-challenge/.tls.acme.emailmust be set (ACME account contact).
Mutual exclusion: selfSignedTls = true or tls.certDir
set together with tls.acme.enable = true fails at eval.
Typical setup:
services.hyperhive.gateway = {
selfSignedTls = false;
openFirewall = true;
tls.acme = {
enable = true;
email = "admin@example.com";
};
};
After enabling, peer hives can omit certFingerprint in
swarm.peers — Let’s Encrypt certs are CA-trusted
by default.
Type: boolean
Default:
false
Example:
true
Declared by:
services.hyperhive.gateway.tls.acme.email
Email address for the ACME account registration with
Let’s Encrypt. Required when tls.acme.enable = true.
Let’s Encrypt sends expiry warnings to this address.
Type: null or string
Default:
null
Example:
"admin@example.com"
Declared by:
services.hyperhive.gateway.tls.certDir
Path to a host directory containing a TLS certificate and
private key for nginx. When set, nginx listens on httpsPort
and uses this cert, making selfSignedTls unnecessary —
the auto-generated self-signed cert is skipped entirely.
The directory is bind-mounted read-only into the gateway
container at /run/hive-tls/. nginx reads
<certDir>/<tls.certName> and <certDir>/<tls.keyName>.
Default filenames (cert.pem / key.pem) match the output
layout of nixpkgs’s security.acme module.
Typical ACME setup:
security.acme.certs."example.com" = { ... };
services.hyperhive.gateway.tls.certDir =
config.security.acme.certs."example.com".directory;
services.hyperhive.gateway.selfSignedTls = false;
When using an external CA cert, peer hives can declare this
hive in services.hyperhive.swarm.peers without
certFingerprint — the standard CA bundle validates.
Mutual exclusion: selfSignedTls = true and tls.certDir
set together fails an assertion at eval time.
Type: null or absolute path
Default:
null
Example:
"/var/lib/acme/example.com"
Declared by:
services.hyperhive.gateway.tls.certName
Filename of the TLS certificate within tls.certDir. Defaults
to cert.pem which matches nixpkgs’s security.acme output.
Type: string
Default:
"cert.pem"
Declared by:
services.hyperhive.gateway.tls.keyName
Filename of the TLS private key within tls.certDir. Defaults
to key.pem which matches nixpkgs’s security.acme output.
Type: string
Default:
"key.pem"
Declared by:
services.hyperhive.gateway.upstreamHost
Host the gateway proxies non-static requests to. Defaults to
127.0.0.1 because the gateway container shares the host
netns, so loopback resolves directly to hive-c0re.
Type: string
Default:
"127.0.0.1"
Declared by:
services.hyperhive.gateway.upstreamPort
TCP port the gateway proxies non-static requests to. Defaults
to 7000 (hive-c0re’s out-of-the-box dashboard port). Operators
who change services.hyperhive.c0re.dashboardPort should set
upstreamPort to match — kept as a hardcoded default rather
than a cross-reference to keep this module’s options eval
independent of c0re’s option tree shape.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
7000
Declared by:
services.hyperhive.hiveName
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).
Type: null or string
Default:
null
Example:
"pr1ma"
Declared by:
services.hyperhive.matrix.enable
Run hive-matrix — a private matrix-tuwunel homeserver (in a
nixos-container) for hyperhive agents. Off by default while
the integration phases in; flip to true once the operator
has set services.hyperhive.domain and is ready to onboard agents.
Type: boolean
Default:
false
Declared by:
services.hyperhive.matrix.package
matrix-tuwunel package to run inside the container. Defaults
to nixpkgs’s pkgs.matrix-tuwunel. Override to pin a
specific upstream if you need an unreleased feature.
Type: package
Default:
pkgs.matrix-tuwunel
Declared by:
services.hyperhive.matrix.gatewayHost
Public hostname for the matrix homeserver behind the gateway.
Defaults to matrix.${services.hyperhive.domain} (sub-domain
shape — see docs/gateway.md). Set to null to skip the
gateway vhost (tuwunel stays direct on httpPort). See
docs/gateway.md for the vhost map + matrix discovery flow,
and the federation port-8448 caveat at the bottom of that doc.
Note: gatewayHost is the API listener hostname (where nginx
proxies /_matrix/*); serverName is the matrix-identifier
domain embedded irrevocably in user/room IDs (default = bare
hive-domain). The two are distinct.
Type: null or string
Default:
if services.hyperhive.domain != null then
"matrix.${services.hyperhive.domain}"
else
null
Example:
"matrix.example.com"
Declared by:
services.hyperhive.matrix.gui.enable
Serve a matrix web client at matrix.${services.hyperhive.domain}/.
Requires matrix.gatewayHost != null (default matrix.<hive>
when hive-domain set); the gateway itself always runs. When
off, the dashboard’s M4TR1X → tab is hidden. See
docs/gateway.md for the discovery flow that lets clients
auto-find the sub-domain.
Type: boolean
Default:
config.services.hyperhive.matrix.enable
Declared by:
services.hyperhive.matrix.gui.package
Static web client dist served at matrix.<hive>/. Override
to swap fluffychat for hydrogen-web, cinny, element-web, or
an out-of-tree dist — any replacement is mounted at the
sub-domain root with the upstream-default <base href "/">,
no sub-path gymnastics needed.
Type: package
Default:
pkgs.fluffychat-web with a postInstall patch that adds
the three files flutter341.buildFlutterApplication skips.
Declared by:
services.hyperhive.matrix.httpPort
TCP port tuwunel serves the matrix client-server API on.
Default 8008 is the matrix-spec well-known port. Sits
outside hyperhive’s claimed ranges (dashboard 7000, every
agent in 8100…8999 via FNV-1a hash). Federation listens on
federationPort separately.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
8008
Declared by:
services.hyperhive.matrix.maxRequestSize
Maximum size in bytes of a single matrix client request body. Default 20 MB matches the matrix-spec recommendation for media uploads + the upstream tuwunel default.
Type: positive integer, meaning >0
Default:
20000000
Declared by:
services.hyperhive.matrix.openFirewall
Open httpPort in the host firewall. Off by default
(secure-by-default): the homeserver is reachable from the
host + every agent container via localhost either way
(shared netns), so the firewall open only matters for access
from outside the host. Flip to true when announcing the
homeserver to other hives or when an external matrix client
needs to reach the client-server API directly.
Breaking change: this used to default to true. If you
relied on the old default for external reach, add
services.hyperhive.matrix.openFirewall = true; to your host
config before rebuilding.
Note: federation (the matrix-spec well-known port 8448) is
intentionally not opened here. tuwunel serves the federation
API on the same httpPort as the client-server API by
default; reaching it on 8448 requires either binding tuwunel
to that port explicitly OR a reverse-proxy + .well-known/ matrix/server delegation, neither of which lives in this
module. Add that proxy config alongside whatever serves your
dashboard or forge on 443.
Type: boolean
Default:
false
Example:
true
Declared by:
services.hyperhive.matrix.registrationTokenFile
Host path to a file containing the matrix registration token
tuwunel reads to authorise new-account creation. The token is
generated automatically by hive-c0re on first boot (32-byte
random hex, mode 0600) and is bind-mounted read-only into the
tuwunel container at the same path. Agents never see this
token — hive-c0re uses it to provision per-agent accounts
and the agent only receives the resulting access_token.
Override only when integrating with externally-managed
registration tokens.
Type: absolute path
Default:
"/var/lib/hyperhive/matrix-register-token"
Declared by:
services.hyperhive.matrix.serverName
Matrix server_name — the host part of every user ID
(@argus:<server_name>) and room ID minted on this
homeserver. CRITICAL: must be stable from day one because
it’s embedded irrevocably in the identifiers. Defaults to
services.hyperhive.domain (the bare hive domain). Combined
with the .well-known/matrix/{client,server} routes the
hive-gateway serves at that domain, clients auto-discover the
actual matrix endpoint without needing a subdomain. Override
here only if you need a different server_name shape (e.g.
matrix.<domain> if you want the subdomain split, or
chat.example.org for a bespoke hostname).
Breaking change: this used to default to
matrix.${services.hyperhive.domain}. matrix IDs embed
the server_name irrevocably, so existing homeservers must
set services.hyperhive.matrix.serverName = "matrix.${services.hyperhive.domain}";
explicitly to preserve their existing user / room IDs
before rebuilding.
Type: null or string
Default:
null
Example:
"chat.example.org"
Declared by:
services.hyperhive.matrix.trustedServers
List of trusted matrix servers (homeservers whose signing keys this server will fetch identity-server-style). Empty by default — federation is enabled at the protocol level but no peer is trusted until listed here, so the homeserver is effectively closed until the operator declares hive peers explicitly.
Type: list of string
Default:
[ ]
Example:
[
"matrix.org"
]
Declared by:
services.hyperhive.network.enable
Stand up the hive-internal bridge + dnsmasq resolver.
Defaults to config.services.hyperhive.enable so it comes
on automatically with the rest of hyperhive. Requires
services.hyperhive.domain to be set — the dnsmasq resolver
is authoritative for <hive-domain> and its sub-domains.
When enabled: a bridge interface (bridgeName) appears on the
host with bridgeIp assigned, and the hive-gateway container
runs a dnsmasq listening on that IP for <hive-domain> +
sub-domains. Agent containers still default to shared host
netns — the endpoint is up but only used once
isolateContainers = true flips containers to private netns
- veth peers on this bridge.
Type: boolean
Default:
config.services.hyperhive.enable
Example:
false
Declared by:
services.hyperhive.network.bridgeIp
IPv4 address assigned to the bridge interface on the host
side. Becomes the DNS server address agents point at (and
the upstream the gateway proxies to once netns isolation
lands). Default 10.42.0.1 is in RFC 1918 space and
unlikely to clash with operator’s existing setup; override
if a different range is already in use.
Type: string
Default:
"10.42.0.1"
Example:
"172.30.0.1"
Declared by:
services.hyperhive.network.bridgeName
Name of the host-side bridge interface the hive uses for
inter-container traffic. Kept short so it survives the
IFNAMSIZ (15-char) cap, and prefixed so it’s obviously
hive-managed in ip link output.
Type: string
Default:
"hive-br0"
Example:
"h0"
Declared by:
services.hyperhive.network.bridgePrefixLength
Netmask prefix length for the bridge subnet. Default /24
gives 254 usable per-agent addresses, enough for any
single-host hive. Operator with a larger swarm or a tighter
addressing scheme overrides.
Type: signed integer
Default:
24
Example:
16
Declared by:
services.hyperhive.network.isolateContainers
Flip agent containers from shared host netns to private netns.
When true, each agent container gets a dedicated veth
pair attached to bridgeName and a deterministic IP from
the bridge subnet. The bridge (already up when enable = true)
becomes the sole routed path between the host and agent
containers.
The host-side nix effect (this option) is:
- Sets
HIVE_NETWORK_ISOLATION=1in the c0re service env so the Rust lifecycle knows to pass--private-network+ bridge settings when creating/updating containers. - Enables IP forwarding + NAT so agents can reach the internet through the host.
- Adds a firewall rule DROP’ing traffic from the bridge subnet to the host’s loopback addresses — defence-in-depth so a compromised agent can’t reach the c0re dashboard (already bound to 127.0.0.1) or other host-loopback services even if the routing table somehow leaks.
- Allows HTTP/HTTPS (80/443) traffic from the bridge subnet to the host so agents can reach the gateway container (shared host netns, proxies the operator’s per-agent UI).
Prerequisite: all agents must have
hyperhive.web.useUnixSocket = true before enabling isolation.
Agents that still bind TCP on 0.0.0.0:<port> will be
reachable at their bridge IP from other agents on the same
subnet — defeating the isolation goal. The gateway routes via
unix sockets so gateway reach still works regardless.
Migration: containers are destroyed and re-created when
the network isolation flag flips. Operator state under
/agents/<name>/state/ is bind-mounted and survives; the
container rootfs (nix store paths) is recreated cleanly.
Rust counterpart: hive-c0re reads HIVE_NETWORK_ISOLATION
and HIVE_NETWORK_BRIDGE from its service env and uses them in
lifecycle::set_nspawn_flags to configure PRIVATE_NETWORK,
LOCAL_ADDRESS, and HOST_BRIDGE in each container’s
nixos-containers/<name>.conf. See docs/network.md for the
full design.
Type: boolean
Default:
false
Example:
true
Declared by:
services.hyperhive.network.upstreamDns
Upstream DNS servers dnsmasq forwards non-hive queries to.
Defaults to Cloudflare + Quad9. Override for operators on
private networks who need a specific resolver (corporate
DNS, pi-hole, etc.). The hive resolver itself stays
authoritative for <hive-domain> and its sub-domains
regardless of upstream choice.
Type: list of string
Default:
[
"1.1.1.1"
"9.9.9.9"
]
Example:
[
"192.168.1.1"
"8.8.8.8"
]
Declared by:
services.hyperhive.swarm.peers
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.
Type: attribute set of (submodule)
Default:
{ }
Example:
{
"edge.corp" = { };
"lab.example.com" = {
certFingerprint = "sha256:b1946ac92492d2347c6235b4d2611184a3f5b6cae6c19d6e3c2f0a8e7d4c9f12";
};
}
Declared by:
services.hyperhive.swarm.peers.<name>.certFingerprint
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.md for the full recipe.
Type: null or string
Default:
null
Example:
"sha256:b1946ac92492d2347c6235b4d2611184a3f5b6cae6c19d6e3c2f0a8e7d4c9f12"
Declared by:
services.hyperhive.swarm.peers.<name>.wireguardAddress
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).
Type: null or string
Default:
null
Example:
"10.100.0.2/32"
Declared by:
services.hyperhive.swarm.peers.<name>.wireguardEndpoint
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).
Type: null or string
Default:
null
Example:
"203.0.113.1:51820"
Declared by:
services.hyperhive.swarm.peers.<name>.wireguardPublicKey
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).
Type: null or string
Default:
null
Example:
"base64pubkey="
Declared by:
services.hyperhive.swarm.wireguard.enable
Enable the WireGuard inter-hive mesh. When true, a wg-hive
interface is brought up connecting to all swarm peers that
declare a wireguardPublicKey. Requires
privateKeyFile to be set.
Type: boolean
Default:
false
Declared by:
services.hyperhive.swarm.wireguard.address
IP address (with prefix) of this host on the WireGuard mesh.
Use a /24 (or broader) prefix so the routing table covers all
peer /32 routes. Example: "10.100.0.1/24" for a 256-host mesh.
Type: string
Default:
""
Example:
"10.100.0.1/24"
Declared by:
services.hyperhive.swarm.wireguard.listenPort
UDP port the local WireGuard interface listens on. Must be reachable from peer hosts when they initiate the tunnel. Default: 51820 (standard WireGuard port).
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
51820
Declared by:
services.hyperhive.swarm.wireguard.persistentKeepalive
Seconds between keepalive packets sent to each peer. Useful when this host (or a peer) is behind NAT — keeps the UDP hole open. Set to null to disable. Default: 25 seconds.
Type: null or signed integer
Default:
25
Example:
25
Declared by:
services.hyperhive.swarm.wireguard.privateKeyFile
Path to the host’s WireGuard private key file. The file must
be readable by root and should have mode 0400. Generate with
wg genkey > /etc/wireguard/hive.key. Required when
swarm.wireguard.enable = true.
Type: null or absolute path
Default:
null
Example:
"/etc/wireguard/hive.key"
Declared by:
services.hyperhive.swarmName
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.
Type: null or string
Default:
null
Example:
"constellat1on"
Declared by: