216 lines
8.4 KiB
Nix
216 lines
8.4 KiB
Nix
{
|
|
hyperhivePackage,
|
|
hyperhiveFrontend,
|
|
hyperhiveFlake,
|
|
agentBaseToplevel,
|
|
managerToplevel,
|
|
}:
|
|
{
|
|
pkgs,
|
|
lib,
|
|
config,
|
|
...
|
|
}:
|
|
let
|
|
cfg = config.services.hive-c0re;
|
|
in
|
|
{
|
|
# The forge is part of the standard install — hive-c0re mirrors
|
|
# every agent's applied config repo into it. On by default; opt out
|
|
# with `hyperhive.forge.enable = false`. hive-matrix is opt-in (off
|
|
# by default) and asserts that `hyperhive.domain` is set before it
|
|
# can be enabled.
|
|
imports = [
|
|
./hive-forge.nix
|
|
./hive-matrix.nix
|
|
];
|
|
|
|
# Top-level option shared by any hyperhive subsystem that needs a
|
|
# stable hostname (matrix server_name today, forge ROOT_URL likely
|
|
# next). Type is nullable + default null so existing operator
|
|
# configs that don't set it still evaluate; subsystems that
|
|
# actually need it (matrix) assert non-null in their own config
|
|
# block with a helpful message.
|
|
options.hyperhive.domain = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
default = null;
|
|
example = "darkest.space";
|
|
description = ''
|
|
Canonical host domain for hyperhive subsystems that need a
|
|
stable name (currently: `hyperhive.matrix.serverName` derives
|
|
from this, defaulting to `matrix.''${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.
|
|
'';
|
|
};
|
|
|
|
options.services.hive-c0re = {
|
|
enable = lib.mkEnableOption "hive-c0re — hyperhive coordinator daemon";
|
|
package = lib.mkOption {
|
|
type = lib.types.package;
|
|
default = hyperhivePackage pkgs.stdenv.hostPlatform.system;
|
|
defaultText = lib.literalExpression "hyperhive.packages.\${system}.default";
|
|
description = "Package that provides /bin/hive-c0re.";
|
|
};
|
|
frontend = lib.mkOption {
|
|
type = lib.types.package;
|
|
default = hyperhiveFrontend pkgs.stdenv.hostPlatform.system;
|
|
defaultText = lib.literalExpression "hyperhive.packages.\${system}.frontend";
|
|
description = ''
|
|
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.
|
|
'';
|
|
};
|
|
hyperhiveFlake = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = hyperhiveFlake;
|
|
defaultText = lib.literalMD "the flake's own store path";
|
|
description = ''
|
|
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.
|
|
'';
|
|
};
|
|
dashboardPort = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 7000;
|
|
description = "TCP port the hive-c0re dashboard listens on.";
|
|
};
|
|
operatorPronouns = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "she/her";
|
|
example = "they/them";
|
|
description = ''
|
|
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.
|
|
'';
|
|
};
|
|
preBuildAgentTemplates = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
example = true;
|
|
description = ''
|
|
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. Closes #97.
|
|
|
|
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 ${hyperhiveFlake}#agent-base-toplevel` once
|
|
manually to warm the store.
|
|
'';
|
|
};
|
|
contextWindowTokens = lib.mkOption {
|
|
type = lib.types.attrsOf lib.types.int;
|
|
default = {
|
|
haiku = 200000;
|
|
sonnet = 1000000;
|
|
opus = 1000000;
|
|
};
|
|
example = {
|
|
haiku = 150000;
|
|
sonnet = 900000;
|
|
};
|
|
description = ''
|
|
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.
|
|
'';
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
environment.systemPackages = [
|
|
cfg.package
|
|
pkgs.git
|
|
];
|
|
|
|
# Pull the per-container toplevels into the host system closure
|
|
# (#97). `system.extraDependencies` adds paths to the system build
|
|
# without referencing them at runtime — nixos-rebuild fetches /
|
|
# builds them, they end up in /nix/store, and the first
|
|
# nixos-container update + start for an agent has nothing left to
|
|
# do. Gated because the closure is sizeable and pinned to x86_64.
|
|
system.extraDependencies = lib.optionals cfg.preBuildAgentTemplates [
|
|
agentBaseToplevel
|
|
managerToplevel
|
|
];
|
|
|
|
# Dashboard + per-container web UIs share the host's network namespace and
|
|
# need their ports reachable. Dashboard: `cfg.dashboardPort` (default 7000).
|
|
# Manager: 8000. Sub-agents: 8100..8999 (deterministic hash; see
|
|
# `lifecycle::agent_web_port`).
|
|
networking.firewall.allowedTCPPorts = [
|
|
cfg.dashboardPort
|
|
8000
|
|
];
|
|
networking.firewall.allowedTCPPortRanges = [
|
|
{
|
|
from = 8100;
|
|
to = 8999;
|
|
}
|
|
];
|
|
|
|
systemd.services.hive-c0re = {
|
|
description = "hyperhive coordinator daemon";
|
|
wantedBy = [ "multi-user.target" ];
|
|
path = [
|
|
pkgs.git
|
|
"/run/current-system/sw"
|
|
];
|
|
environment = {
|
|
HYPERHIVE_GIT = "${pkgs.git}/bin/git";
|
|
# Path to the dashboard static dist. The hive-c0re axum router
|
|
# serves this via `tower_http::ServeDir` for any path it doesn't
|
|
# match against an API/action route.
|
|
HIVE_STATIC_DIR = "${cfg.frontend}/dashboard";
|
|
}
|
|
// lib.optionalAttrs config.hyperhive.forge.enable {
|
|
# Agents poll this URL for Forgejo notifications. Derived from
|
|
# hyperhive.forge.{domain,httpPort} so it tracks forge config changes.
|
|
HIVE_FORGE_URL = "http://${config.hyperhive.forge.domain}:${toString config.hyperhive.forge.httpPort}";
|
|
};
|
|
serviceConfig = {
|
|
ExecStart = "${cfg.package}/bin/hive-c0re --socket /run/hyperhive/host.sock serve --hyperhive-flake ${cfg.hyperhiveFlake} --dashboard-port ${toString cfg.dashboardPort} --operator-pronouns ${lib.escapeShellArg cfg.operatorPronouns} --context-window-tokens ${lib.escapeShellArg (builtins.toJSON cfg.contextWindowTokens)}";
|
|
Restart = "on-failure";
|
|
RestartSec = 2;
|
|
RuntimeDirectory = "hyperhive";
|
|
RuntimeDirectoryMode = "0750";
|
|
RuntimeDirectoryPreserve = "yes";
|
|
StateDirectory = "hyperhive";
|
|
};
|
|
};
|
|
};
|
|
}
|