a swarm o 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. 💜
  • Rust 68.5%
  • Nix 15.9%
  • JavaScript 6.8%
  • CSS 3.7%
  • TypeScript 3.6%
  • Other 1.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
müde 7d33da3727 retry hive socket up to 5x over 60s, surface retry count to claude
socket client now retries connect/IO failures with 2-4-8-16-30s
backoffs (60s total budget). transparent for non-tool callers via
request(); tool handlers go through request_retried() which also
returns the retry count, then annotate_retries() appends a one-line
note to the tool result so claude knows the slow round-trip was a
c0re flicker, not a content failure — avoids burning tokens on an
LLM-level retry.
2026-05-16 15:28:18 +02:00
docs recv: None = peek, positive value = opt-in long-poll 2026-05-16 03:22:42 +02:00
hive-ag3nt retry hive socket up to 5x over 60s, surface retry count to claude 2026-05-16 15:28:18 +02:00
hive-c0re feat: add optional description to request_apply_commit and request_spawn 2026-05-16 15:18:32 +02:00
hive-sh4re feat: add optional description to request_apply_commit and request_spawn 2026-05-16 15:18:32 +02:00
nix auto-install claude plugins at harness boot 2026-05-16 15:17:34 +02:00
.gitignore gitignore .claude/settings.local.json 2026-05-15 14:44:58 +02:00
Cargo.lock dashboard: diff against applied/proposal/<id>, prefer fetched_sha 2026-05-15 23:18:17 +02:00
Cargo.toml turn loop: tool whitelist (no web/task), no skip-permissions 2026-05-15 14:41:38 +02:00
CLAUDE.md docs: sync to current state of the world 2026-05-16 02:49:48 +02:00
flake.lock fmt 2026-05-14 22:27:03 +02:00
flake.nix docs sync + revert auto-unfree removal 2026-05-15 21:26:13 +02:00
README.md docs: sync to current state of the world 2026-05-16 02:49:48 +02:00
TODO.md todo: add bug - pending message wake-up issue 2026-05-16 13:43:41 +02:00

hyperhive

Multi-Claude-Code-agent orchestration on nixos-containers.

A host-side Rust daemon (hive-c0re) spawns nspawn-isolated agent containers and brokers messages between them. A manager agent (hm1nd) coordinates the swarm and gates lifecycle changes on user approval via git commits, surfaced through a vibec0re-styled HTTP dashboard.

host (NixOS, runs hive-c0re.service)
│
├── operator
│   ├── browser → :7000               hive-c0re dashboard (containers, approvals)
│   ├── browser → :8000 / :8100-8999  per-agent web UIs (live SSE, send, login)
│   └── CLI     → /run/hyperhive/host.sock         JSON-line admin protocol
│
├── hive-c0re  (Rust daemon)
│   ├── lifecycle    nixos-container CRUD + per-agent flake generation
│   ├── broker       sqlite messages + tokio broadcast (powers SSE + wake-ups)
│   ├── approvals    sqlite queue, two kinds: ApplyCommit (config) + Spawn
│   ├── auto_update  rebuilds any container whose recorded flake rev is stale
│   ├── dashboard    axum HTTP + async-form actions + SSE message flow
│   └── sockets      /run/hyperhive/{host,manager,agents/<n>}/mcp.sock
│
└── nixos-containers  (each bind-mounts its socket dir → /run/hive,
   │                   credentials dir → /root/.claude,
   │                   durable notes dir → /state;
   │                   manager additionally gets /agents RW,
   │                   /applied RO (deployed-tag mirror),
   │                   /meta RO (swarm-wide deploy flake))
   │
   ├── hm1nd      hive-m1nd serve : claude turn loop +
   │              MCP (send / recv / request_spawn / kill / start /
   │                   restart / update / request_apply_commit /
   │                   ask_operator) + web UI on :8000
   │
   └── h-<name>   hive-ag3nt serve : claude turn loop +
                  MCP (send / recv / ask_operator + agent-declared extras
                       via hyperhive.extraMcpServers) + web UI on a
                  hashed :8100-8999

Each turn: harness pops one inbox message (Recv long-polls server-side and wakes on a broker Sent event) → builds a wake prompt → spawns claude --print --continue --output-format stream-json --mcp-config … → streams JSON events into the per-agent SSE bus + a sqlite history db → claude drives any further recv/send itself via the embedded MCP server.

Operator surface per agent: terminal-themed live tail with a textarea prompt; slash commands /help /clear /cancel /compact /model <name> /new-session; granular state badge (idle / thinking / compacting / offline) with age timer + last-turn duration chip + model chip; cancel-turn + new-session buttons in the state row; sticky-bottom auto-scroll with "↓ N new" pill; event history backfilled on page load; collapsible inbox + collapsible journald viewer + collapsible agent.nix viewer per agent on the dashboard; deployed-sha chip per container (read from meta's flake.lock).

Operator surface on the dashboard itself: a terminal compose box under the message-flow stream — @name picks the recipient with auto-complete from the live container list, sticky across sends, POSTs /op-send which drops the message into the broker as {from:"operator", to:<name>, body}. Same shape any sub-agent sees as a regular inbox message.

Config changes flow the other way: manager edits files under /agents/<name>/config/agent.nix is a plain NixOS module function { config, pkgs, lib, ... }: { ... }, and arbitrary sibling files in the commit are preserved → commits → submits the sha via request_apply_commit. Hive-c0re immediately fetches that commit from the proposed repo into the applied repo and pins it as proposal/<id> — immutable from the manager's side from then on. Operator clicks ◆ APPR0VE → hive-c0re fast-forwards applied/<n>/main to the proposal, runs nix flake lock --update-input agent-<n> against the host-wide meta flake at /var/lib/hyperhive/meta/, builds via nixos-container update <c> --flake meta#<name>, and either commits the lock + tags deployed/<id> on success or git restores the lock + annotates failed/<id> with the build error + rolls back applied/<n>/main on failure. Denials leave a denied/<id> annotated tag carrying the operator's note.

Meta's git log is the swarm-wide deploy audit trail (one commit per successful deploy). Per-agent applied repos carry the tag-rich state machine for inside-baseball decisions. The manager sees both — proposed repos ship with an applied remote pre-wired, and /meta/ is RO-bound inside the container — so git fetch applied, git show applied/refs/tags/deployed/<id>, git log /meta, cat /meta/flake.lock all just work without constructing paths by hand. See docs/approvals.md for the full state machine + lock-flow walkthrough. For decisions the manager needs human signal on, ask_operator(question, options?, multi?) queues a free-text/checkbox/radio form on the dashboard; the answer arrives later as a HelperEvent::OperatorAnswered in the manager's inbox.

Host config

Minimal flake.nix for a host that runs hive-c0re:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
    hyperhive.url = "git+https://git.berlin.ccc.de/vinzenz/hyperhive";
  };

  outputs = { nixpkgs, hyperhive, ... }: {
    nixosConfigurations.my-host = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        hyperhive.nixosModules.hive-c0re
        ({ ... }: {
          services.hive-c0re.enable = true;
          # Free-text operator pronouns — defaults to "she/her", threaded
          # through to every agent's system prompt as HIVE_OPERATOR_PRONOUNS
          # so claude refers to you naturally in third person.
          # services.hive-c0re.operatorPronouns = "they/them";

          # ... rest of your host config (hardware, networking, users, …)
          system.stateVersion = "25.11";
        })
      ];
    };
  };
}

hive-c0re will then:

  • open its admin socket at /run/hyperhive/host.sock + dashboard on :7000,
  • auto-create the manager container (hm1nd) if missing,
  • auto-rebuild any managed container whose hyperhive rev is stale.

claude-code is unfree; hyperhive whitelists it for itself (scoped: only claude-code, nothing else) inside the claude-unstable overlay and harness-base.nix. Per-agent containers evaluate their own nixpkgs instance so the operator's host-level allowUnfree doesn't propagate in — the predicate has to live inline. Nothing to set on the operator side.

Build / deploy

# inside the repo (devshell first; no global cargo)
nix develop -c cargo check
nix develop -c cargo clippy --workspace --all-targets -- -D warnings

# evaluate everything (rust+nix+toml fmt + clippy)
nix flake check

# deploy to a host that imports `hyperhive.nixosModules.hive-c0re`
cd ~/Repos/<nixos-config-repo>
nix flake update --update-input hyperhive
sudo nixos-rebuild switch --flake .#<host>

No overlays on the host's pkgs — the module pulls hive-c0re's package straight from hyperhive.packages.<system>.default. Just import the module and the service is wired up.