hyperhive/README.md
müde 1278f880da docs: sync to current state of the world
claude.md scratchpad rewritten — folds in pronouns option,
extra MCP servers + flakeInputs forwarding, ask_operator
on sub-agents, dashboard compose box with @-mentions, new-
session button, cwd=/state for claude turns, meta-mutex +
stale-lock cleanup.

readme picks up the operator pronouns option example,
the dashboard compose box description, the new slash
commands list, the deployed-sha chip, the per-agent UI
gains new-session.

docs/web-ui.md gains:
- a fuller MESS4GE FL0W description that calls out the
  compose box, sticky @-mention recipient, /op-send, and
  the manager-name swap
- /op-send in the dashboard endpoint table
- new-session button + /new-session slash command in the
  per-agent surface
- compact endpoint now notes 'same session shape as a normal
  turn'

docs/turn-loop.md:
- new-session one-shot, cwd=/state with CLAUDE.md auto-load
  walking upward, operator-pronouns substitution
- sub-agent tool list grows ask_operator
- new 'Extra MCP servers (per-agent)' section documenting
  hyperhive.extraMcpServers + the flakeInputs forwarding
  pattern
2026-05-16 02:49:48 +02:00

160 lines
7.1 KiB
Markdown

# 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 restore`s 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`](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:
```nix
{
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
```sh
# 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.