Second slice of the swarm-global service consolidation, same shape as
the forge move: the operator-facing host options become
services.hyperhive.swarm.matrix.*, and one mkRenamedOptionModule on the
namespace carries the whole subtree (nested gui.* included), so there is
no leaf list to forget an entry from.
The rename lives in hive-matrix.nix, the module that declares the
options, so each service's migration stays independent of its siblings.
The per-agent hyperhive.matrix.{enable,url} and hyperhive.matrixAccounts
are a different namespace -- a client pointer at the service, not the
service -- and deliberately do not move.
227 lines
10 KiB
Nix
227 lines
10 KiB
Nix
# Environment of the hive-c0re daemon unit — a plain function file
|
|
# (not a module) returning the env attrset, imported by ./default.nix.
|
|
# Everything meta.rs forwards into agent containers or reads for the
|
|
# meta-flake render is assembled here.
|
|
{
|
|
lib,
|
|
config,
|
|
pkgs,
|
|
}:
|
|
let
|
|
cfg = config.services.hyperhive.c0re;
|
|
in
|
|
{
|
|
# nix (the prebuild `nix build`, flake-check, and meta eval) writes
|
|
# its fetcher/eval cache under $HOME/.cache. As a system user
|
|
# hive-core has no home, so HOME defaults to the unwritable
|
|
# /var/empty and Lix fails to initialise its cache. Point HOME at
|
|
# the writable StateDirectory.
|
|
HOME = "/var/lib/hyperhive";
|
|
HYPERHIVE_GIT = "${pkgs.git}/bin/git";
|
|
# Never let git block on an interactive credential prompt. hive-core is a
|
|
# TTY-less system user, so a prompt (e.g. the forge credential helper
|
|
# returns nothing because the forge isn't reachable yet on cold boot)
|
|
# would hang forever — this is what froze the whole daemon during startup
|
|
# migration's `nix flake lock` of the forge-hosted config inputs. With
|
|
# this set, git fails fast instead of prompting. Paired with the git http
|
|
# low-speed abort in `safeDirGitconfig` (bounds a stalled transfer) and
|
|
# the 120s migration shellout timeout in migrate.rs.
|
|
GIT_TERMINAL_PROMPT = "0";
|
|
# No HIVE_STATIC_DIR: the gateway static-serves the dashboard dist
|
|
# (see the hive-gateway module); this router is API-only.
|
|
# Path to the base agent frontend dist. hive-c0re's
|
|
# gateway_nginx.rs uses this to generate split location
|
|
# blocks in agents.conf — static HTML/CSS/JS served from the
|
|
# nix store directly; dynamic API paths still proxied to the
|
|
# agent daemon. The nix store is shared across nspawn
|
|
# containers, so this path is reachable from inside the
|
|
# gateway container's nginx.
|
|
HIVE_AGENT_FRONTEND_DIR = "${cfg.servedFrontend}/agent";
|
|
# Path to the static runtime asset tree (branding + claude
|
|
# prompts). `hive_sh4re::assets::*` reads paths underneath.
|
|
# `forge.rs` reads the avatar PNGs from here on startup.
|
|
HIVE_ASSETS_DIR = "${cfg.assets}/share/hyperhive";
|
|
# Whether this hive runs ruthless — no root/manager agent at all
|
|
# (`auto_update::ensure_root_agent`). Default false = root
|
|
# auto-managed; true makes the sweep a no-op.
|
|
HYPERHIVE_RUTHLESS = lib.boolToString config.services.hyperhive.ruthless;
|
|
}
|
|
// {
|
|
# Identity env vars threaded into c0re's own service env and
|
|
# forwarded by meta.rs into every sub-agent's harness env —
|
|
# full chain in docs/conventions.md::Hive identity. `domain` is
|
|
# required (asserted in hive-network.nix), so it's always set.
|
|
HYPERHIVE_HIVE_DOMAIN = config.services.hyperhive.domain;
|
|
}
|
|
// lib.optionalAttrs (config.services.hyperhive.hiveName != null) {
|
|
HYPERHIVE_HIVE_NAME = config.services.hyperhive.hiveName;
|
|
}
|
|
// lib.optionalAttrs (config.services.hyperhive.swarm.name != null) {
|
|
HYPERHIVE_SWARM_NAME = config.services.hyperhive.swarm.name;
|
|
}
|
|
// lib.optionalAttrs (!config.services.hyperhive.github.enable) {
|
|
# GitHub integration is on by default; only signal the OFF override to
|
|
# meta.rs, which then injects `hyperhive.github.enable = false` into
|
|
# every agent. See services.hyperhive.github.enable.
|
|
HYPERHIVE_GITHUB_DISABLED = "1";
|
|
}
|
|
// lib.optionalAttrs config.services.hyperhive.otel.enable (
|
|
# Hive-wide OTEL config -> read by meta.rs::otel_config and
|
|
# injected as build-time `hyperhive.otel.*` into every agent.
|
|
# Endpoint presence is the enable signal on the meta side; the
|
|
# optional fields are only emitted when set so absent values
|
|
# don't render no-op env lines.
|
|
let
|
|
otel = config.services.hyperhive.otel;
|
|
in
|
|
{
|
|
HYPERHIVE_OTEL_ENDPOINT = otel.endpoint;
|
|
HYPERHIVE_OTEL_PROTOCOL = otel.protocol;
|
|
}
|
|
// lib.optionalAttrs (otel.extraResourceAttributes != "") {
|
|
HYPERHIVE_OTEL_EXTRA_RESOURCE_ATTRIBUTES = otel.extraResourceAttributes;
|
|
}
|
|
// lib.optionalAttrs (otel.headersCredential != null) {
|
|
HYPERHIVE_OTEL_HEADERS_CREDENTIAL = otel.headersCredential;
|
|
}
|
|
// lib.optionalAttrs (otel.metricIntervalMs != null) {
|
|
HYPERHIVE_OTEL_METRIC_INTERVAL_MS = toString otel.metricIntervalMs;
|
|
}
|
|
// lib.optionalAttrs otel.debug {
|
|
HYPERHIVE_OTEL_DEBUG = "1";
|
|
}
|
|
)
|
|
// {
|
|
# In-cluster forge URL — the gateway vhost (`forge.<domain>`), which
|
|
# nginx proxies to forgejo. Used both for internal API calls in
|
|
# hive-c0re (forge/mod.rs `forge_http_base()`) and forwarded to
|
|
# agents via meta.rs for their forge-notify client. The forge is
|
|
# mandatory, so this is unconditional (the whole env block is already
|
|
# gated on hyperhive being enabled). See `docs/gateway.md::HIVE_FORGE_URL`.
|
|
HIVE_FORGE_URL = "http://${config.services.hyperhive.swarm.forge.domain}";
|
|
}
|
|
//
|
|
lib.optionalAttrs
|
|
(
|
|
config.services.hyperhive.swarm.matrix.enable
|
|
&& config.services.hyperhive.swarm.matrix.gatewayHost != null
|
|
)
|
|
{
|
|
# In-cluster matrix homeserver URL for each agent's
|
|
# hive-matrix-daemon — the gateway vhost (`matrix.<domain>`).
|
|
# Forwarded to agents by meta.rs alongside HIVE_FORGE_URL; shares the
|
|
# same env-forwarding ordering caveat (value baked at
|
|
# config-generation time).
|
|
#
|
|
# A domain-less config forwards nothing rather than falling back to
|
|
# loopback. The old fallback read as harmless because hive-c0re shares
|
|
# the host netns — but the value it produced was handed to *agents*,
|
|
# which do not, so `127.0.0.1` there names the agent itself. An absent
|
|
# forward leaves `hyperhive.matrix.url` null and the daemon no-ops;
|
|
# that is the honest answer when the hive has no matrix vhost to point
|
|
# at.
|
|
HIVE_MATRIX_URL = "http://${config.services.hyperhive.swarm.matrix.gatewayHost}";
|
|
}
|
|
// lib.optionalAttrs (config.services.hyperhive.swarm.matrix.apiUrl != null) {
|
|
# Client-server API base hive-c0re uses to provision matrix (register
|
|
# agent users, create the hive space + chat room, invite members).
|
|
# Supplied by `services.hyperhive.swarm.matrix.apiUrl`, which the matrix
|
|
# module fills in with its own loopback listener when it is the thing
|
|
# running tuwunel — and which the operator sets by hand when the
|
|
# homeserver lives on another machine.
|
|
#
|
|
# NOT the agent-facing HIVE_MATRIX_URL above: that one is the gateway
|
|
# vhost, and it is absent whenever there is no vhost. Reusing it here
|
|
# would silently stop provisioning on a hive that runs matrix without
|
|
# one.
|
|
HIVE_MATRIX_API_URL = config.services.hyperhive.swarm.matrix.apiUrl;
|
|
}
|
|
// lib.optionalAttrs config.services.hyperhive.swarm.matrix.gui.enable {
|
|
# Availability flags read by the dashboard's `/api/state`.
|
|
# Matrix GUI lives entirely on the gateway nginx (matrix tab
|
|
# only shows when both flags are on). Gateway routing detail:
|
|
# docs/gateway.md::Vhost map.
|
|
HIVE_MATRIX_GUI_ENABLED = "1";
|
|
}
|
|
// {
|
|
# The gateway always runs, so the dashboard always builds
|
|
# same-origin `/agent/<name>/` links (never the direct
|
|
# `<host>:<port>` TCP fallback). Kept as an env flag so the
|
|
# dashboard doesn't need to learn the gateway is unconditional.
|
|
HIVE_GATEWAY_ENABLED = "1";
|
|
}
|
|
// lib.optionalAttrs (config.services.hyperhive.swarm.forge.publicUrl != null) {
|
|
# Public URL of the forge, for the dashboard to build browser-facing
|
|
# forge links from instead of guessing `<hostname>:3000` (which
|
|
# breaks the moment the operator's browser hostname isn't the forge
|
|
# host, e.g. through the gateway or a reverse proxy). Sourced from
|
|
# `services.hyperhive.swarm.forge.publicUrl`, which itself defaults to the
|
|
# gateway vhost URL when `behindGateway = true` and `null` otherwise
|
|
# — see that option's doc for the "hide, don't guess" rationale.
|
|
# Absent here whenever `publicUrl` is `null`; the dashboard hides
|
|
# forge links rather than emitting one it can't justify.
|
|
HIVE_FORGE_PUBLIC_URL = config.services.hyperhive.swarm.forge.publicUrl;
|
|
}
|
|
//
|
|
lib.optionalAttrs
|
|
(
|
|
config.services.hyperhive.swarm.matrix.gui.enable
|
|
&& config.services.hyperhive.swarm.matrix.gatewayHost != null
|
|
)
|
|
{
|
|
# Browser-facing matrix GUI (fluffychat) URL — the gateway
|
|
# vhost (`matrix.<domain>`). Surfaced via the daemon's `Urls`
|
|
# request for `hivectl open matrix`. Absent when the GUI is off
|
|
# or no gatewayHost is set (no browser-reachable matrix vhost).
|
|
HIVE_MATRIX_PUBLIC_URL = "https://${config.services.hyperhive.swarm.matrix.gatewayHost}/";
|
|
}
|
|
// lib.optionalAttrs (config.services.hyperhive.swarm.snapshotStore.address != null) {
|
|
# `host:port` of the swarm's single snapshot store, for pushing agent
|
|
# snapshots (hive-c0re::snapshot_push). One per swarm, not one per
|
|
# peer — the receiver keys destinations by agent so a migrating agent
|
|
# keeps one incremental chain. Absent when no store is configured, and
|
|
# a push then fails naming the option rather than guessing.
|
|
HYPERHIVE_SNAPSHOT_STORE =
|
|
let
|
|
s = config.services.hyperhive.swarm.snapshotStore;
|
|
in
|
|
"${s.address}:${toString s.port}";
|
|
}
|
|
// lib.optionalAttrs (config.services.hyperhive.swarm.peers != { }) {
|
|
# Peer hives serialised as a JSON array of {domain, cert_fingerprint,
|
|
# wireguard_address?} objects. Consumed by hive-agent::identity::peers()
|
|
# + the dashboard's peer_hives StateSnapshot field (P33RS tab). Domain
|
|
# is the attrset key; cert_fingerprint is null for CA-trusted peers;
|
|
# wireguard_address is omitted when not part of the mesh.
|
|
HYPERHIVE_PEERS = builtins.toJSON (
|
|
lib.mapAttrsToList (
|
|
domain: p:
|
|
{
|
|
inherit domain;
|
|
cert_fingerprint = p.certFingerprint;
|
|
}
|
|
// lib.optionalAttrs (p.wireguardAddress != null) {
|
|
wireguard_address = p.wireguardAddress;
|
|
}
|
|
) config.services.hyperhive.swarm.peers
|
|
);
|
|
}
|
|
//
|
|
lib.optionalAttrs
|
|
(lib.any (p: p.caCert != null) (lib.attrValues config.services.hyperhive.swarm.peers))
|
|
{
|
|
# Peer-hive root CA file paths (colon-joined), one per peer that
|
|
# declares `swarm.peers.<domain>.caCert`. hive-c0re's meta-flake
|
|
# renderer (meta.rs) embeds each next to every agent's flake and
|
|
# adds it to `security.pki.certificateFiles`, so a peer CA is
|
|
# trusted everywhere the hive's own internal CA (`hive-ca.pem`)
|
|
# is — i.e. by every agent. The matrix container trusts the same
|
|
# CAs separately for federation TLS. The `caCert` files are
|
|
# copied into the nix store at build, so these are store paths —
|
|
# nothing mutable lives on the host.
|
|
HIVE_PEER_CA_PATHS = lib.concatStringsSep ":" (
|
|
lib.filter (c: c != null) (
|
|
lib.mapAttrsToList (_domain: p: p.caCert) config.services.hyperhive.swarm.peers
|
|
)
|
|
);
|
|
}
|