# hyperhive > a swarm of claude-code agents, each in its own nspawn cage, gossiping > over unix sockets. config changes flow as git commits, the operator > approves them in a browser, every deploy is a tag. cyberpunk-themed > dashboard included. 💜⚡ Claude code is great in one window, *exponentielle* across many — but only if you can keep the agents from stepping on each other, give them durable identity, and stop them from eating production. hyperhive is the substrate. - identity = unix socket - communication = sqlite-backed broker (`send` / `recv` / `ask` / `answer` / `remind`) - config = git (manager proposes, operator approves, deploys land as tagged commits) - blast radius = container ``` host (NixOS, runs hive-c0re.service) │ ├── operator │ ├── browser → :7000 hive-c0re dashboard │ ├── browser → :8100-8999 per-agent web UIs (all agents, FNV-1a hash) │ └── CLI → /run/hyperhive/host.sock admin protocol │ ├── hive-c0re (Rust daemon: lifecycle / broker / approvals / │ auto-update / dashboard / sockets) │ └── nixos-containers ├── hm1nd manager agent (privileged MCP surface) └── h- sub-agent (vanilla MCP surface + per-agent extras) ``` Depth lives in [`docs/`](docs/) — pick the one matching your task: | reading path | doc | | --- | --- | | dashboard layout + endpoints | [`docs/web-ui.md`](docs/web-ui.md) | | claude turn loop + MCP tools | [`docs/turn-loop.md`](docs/turn-loop.md) | | config-edit + approval state machine | [`docs/approvals.md`](docs/approvals.md) | | what survives destroy / purge / restart | [`docs/persistence.md`](docs/persistence.md) | | naming, wire protocol, commit style | [`docs/conventions.md`](docs/conventions.md) | | NixOS / nspawn gotchas | [`docs/gotchas.md`](docs/gotchas.md) | ## Host config Minimal `flake.nix` for a host that runs hive-c0re: ```nix { inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05"; hyperhive.url = "git+https://forge.darkest.space/hyperhive/hyperhive"; }; outputs = { nixpkgs, hyperhive, ... }: { nixosConfigurations.my-host = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; modules = [ hyperhive.nixosModules.default # hive-c0re + hive-forge + hive-gateway in one import ({ ... }: { services.hyperhive.enable = true; # services.hyperhive.c0re.operatorPronouns = "they/them"; # default: "she/her" # ... rest of your host config system.stateVersion = "25.11"; }) ]; }; }; } ``` hive-c0re opens its admin socket + dashboard, auto-creates the manager container, and auto-rebuilds any container whose hyperhive rev goes stale. `claude-code` is unfree — hyperhive scopes the whitelist to itself, nothing for the operator to set. Optional: set `services.hyperhive.gateway.enable = false;` to bypass the default nginx in front. By default (`gateway.enable = true`) every request hits a small nginx in its own nixos-container that proxies to hive-c0re's dashboard on the upstream port; `/matrix/` is served directly from `services.hyperhive.matrix.gui.package` when the matrix GUI is on. v0 is HTTP-only (TLS lives in a follow-up); pair with `services.hyperhive.gateway.localHostsEntry = true;` for local dev so `http://` resolves to the host without setting up real DNS. Optional: set `services.hyperhive.c0re.preBuildAgentTemplates = true;` to pre-fetch the per-container system closures into your host's /nix/store as part of `nixos-rebuild`. First-agent-spawn then completes in seconds instead of minutes (no nixpkgs/claude-code fetch on the critical path), at the cost of a few GB extra in your system closure. Off by default (the toplevels are pinned to `x86_64-linux`, so non-x86 hosts would otherwise force a cross-build). Alternatively warm the store manually: `nix build git+https://forge.darkest.space/hyperhive/hyperhive#agent-base-toplevel`. Optional: set `services.hyperhive.domain = "example.com";` to define the canonical hostname for hyperhive subsystems that need a stable public name. No default — subsystems that require it (currently: `services.hyperhive.matrix`) assert non-null at eval time with a clear error message if it is missing. Optional: set `services.hyperhive.hiveName = "pr1ma";` and / or `services.hyperhive.swarmName = "constellat1on";` to give the hive and the wider swarm human-readable display labels. Distinct from `services.hyperhive.domain` (the DNS address): the names surface in the dashboard chrome and the per-agent system prompt; the domain is how things are addressed on the wire. Federated hives at different domains can share a swarm name. Both default to null — chrome falls back to the domain, the prompt simply doesn't mention them (#701). Optional: set `services.hyperhive.matrix.enable = true;` to spin up a private [matrix-tuwunel](https://github.com/matrix-construct/tuwunel) homeserver in a nixos-container. Requires either `services.hyperhive.domain` or `services.hyperhive.matrix.serverName` to be set (eval fails with a clear error if both are absent). The `server_name` (embedded irrevocably in every user and room ID) defaults to `` (bare hive domain, since #660); the hive-gateway serves `.well-known/matrix/{client,server}` endpoints so clients auto-discover the homeserver without a subdomain. **Existing homeservers** must set `services.hyperhive.matrix.serverName = "matrix.";` explicitly to preserve pre-#660 user/room IDs. Override with `services.hyperhive.matrix.serverName = "chat.example.com";` for a custom server name. State lives at `/var/lib/nixos-containers/hive-matrix/`. Federation is enabled with an empty `trusted_servers` list; e2ee is deferred to a follow-up (#551). The hive-gateway nginx container serves a [fluffychat-web](https://fluffychat.im) matrix client at `/matrix/` when `services.hyperhive.matrix.gui.enable` is on. This defaults to `services.hyperhive.matrix.enable`, so enabling the homeserver enables the web client by default; set `gui.enable = false` explicitly to keep the homeserver without the GUI. The `◆ M4TR1X ◆ →` tab appears in the dashboard chrome when enabled. The web client can connect to any Matrix homeserver; enter the URL at first visit (e.g. the local tuwunel instance if `services.hyperhive.matrix.enable = true`, or any external homeserver). ## Agent configuration Per-agent settings live in each agent's `agent.nix` and are synced to the container as environment variables. Common options: - **`hyperhive.model`** — Claude model for this agent (default: `"haiku"`). Sets `HIVE_DEFAULT_MODEL` in the container; the harness applies it at boot and it takes priority over any persisted runtime override. The operator can still switch the model at runtime via the per-agent web UI, but that choice is reset by any rebuild that changes this option. - **`hyperhive.allowedRecipients`** — List of agent names this agent can message (via `send`). If unset, all agents are allowed. Useful to restrict an agent to talking only to the manager. - **`hyperhive.forge.url`** — Base URL of the hyperhive-managed Forgejo (default: `"http://localhost:3000"`). Used to configure the agent's tea login at boot; no-op if `/state/forge-token` is missing. - **`hyperhive.forge.keepSubscriptions`** — Boolean. If `true`, the agent's forge repo subscriptions are never auto-cleaned during rebuild; useful for agents that want to watch specific repos. Rendered as `HIVE_FORGE_KEEP_SUBSCRIPTIONS`. - **`hyperhive.forge.skipNotifyReasons`** — List of forge notification `reason` values to suppress (e.g. `[ "subscribed" "participating" ]`). Notifications matching these reasons are silently dropped; all others including direct mentions and reviews are delivered. Empty list (default) delivers all notifications. Rendered as `HIVE_FORGE_NOTIFY_SKIP_REASONS` (comma-separated). - **`hyperhive.frontend.dist`** — Override the default frontend package (`pkgs.hyperhive-frontend`, built by `nix/frontend.nix`). Set to a custom derivation to ship a fully custom per-agent SPA. The JSON contract (`/api/state`, `/events/stream`, action endpoints) is the source of truth for any replacement. - **`hyperhive.frontend.extraFiles`** — Attrset of extra files/directories to layer on top of the default agent dist. Each entry has a `source` (nix path) and an optional `target` (URL prefix in the static tree, defaults to the attribute name). Example: `{ bitburner.source = ./bitburner-dist; }` serves that dist at `/bitburner/`. Pure additions only — overwriting an existing default file is a hard eval-time error; use `frontend.dist` to replace the whole dist. Paths with leading `/` or `..` segments are rejected at eval time. - **`hyperhive.matrix.enable`** — Boolean (default `true`). When true, each agent container runs `hive-matrix-daemon` (a long-running matrix-sdk process that holds the per-agent client + sync) and auto-injects `hive-matrix-mcp` as a stdio MCP server so claude can call the matrix tools (`mcp__matrix__send_message`, `send_dm`, `send_reaction`, `send_reply`, `mark_read`, `list_rooms`, `list_room_members`, `read_room`; `room` args accept both `!id:server` and `#alias:server` forms). Silently no-ops when `/matrix-token` is absent (i.e., the host-level `hyperhive.matrix` tuwunel container hasn't provisioned the account yet). Set to `false` to opt a specific agent out of matrix. - **`hyperhive.user.name`** — Unix username the agent runs as inside the container (default: the agent's own label, injected by the meta-flake). Agents no longer run as root. Stand-alone module eval without the meta-flake falls back to `"agent"`. - **`hyperhive.user.passwordlessSudo`** — Boolean (default `true`). Grants the agent user `NOPASSWD: ALL` sudo so claude's `Bash` tool can still escalate when needed — same surface as the old root-user shape, just explicit. Set `false` for strictly unprivileged agents. See `nix/templates/harness-base.nix` for the full list of options and their descriptions. ## Operator CLI `hivectl` is the operator-facing host CLI for ad-hoc administration that doesn't go through the broker (built alongside `hive-c0re` when the host module is enabled): ```sh sudo hivectl forge create-user mara # provisions a forge user sudo hivectl forge create-user mara --password 'hunter2' # … with a fixed password sudo hivectl matrix create-user mara # provisions a matrix user sudo hivectl matrix create-user mara --password-stdin # … reading one line from stdin ``` For agent names (i.e., a `Coordinator::agent_state_root(name)` exists), `hivectl` persists the resulting token to the agent's state dir like the boot sweep does. For non-agent names (e.g. the operator's own forge/matrix account), it prints the token to stdout and writes nothing. ## Build / deploy ```sh nix develop -c cargo check nix flake check # rust + nix + toml fmt + clippy # deploy from a host config that imports hyperhive.nixosModules.hive-c0re nix flake update --update-input hyperhive sudo nixos-rebuild switch --flake .# ```