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.7%
  • Nix 15.7%
  • JavaScript 8.4%
  • CSS 3.7%
  • TypeScript 1.9%
  • Other 1.6%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
atlas 7a826f9ee2 refactor(sock): one socket client, retry as a policy value
Six places in the tree hand-rolled the same connect / write one JSON
line / read one JSON line back. Two of them — the harness serve loop's
client and the MCP server's — were byte-identical apart from a six-line
wrapper, ~145 lines of literal copy-paste. The other four each
reimplemented a subset, and the subsets had drifted: some named the
socket path in their errors and some did not, one classified transient
against fatal failures and the rest retried nothing at all, two drained
the response and two decoded it.

That duplication was defended when the daemons were split out, on the
grounds that a daemon's socket etiquette should stay visible in the
crate that depends on it. The etiquette genuinely does differ. The code
does not, and five copies is where "each daemon documents its own
etiquette" stops paying for itself.

`hive-sock-client` now owns the transport once, generic over the
request and response types so it is protocol-agnostic: the host-served
control socket and the harness's in-agent socket both use it with their
own wire-type crates. The two real differences become values instead of
forks. Retry is `Retry::RideOutRestart` (2/4/8/16/30s, sized to ride out
a service restart) for callers with no natural retry of their own, or
`Retry::None` for callers already inside a poll loop where the poll
interval is the retry — and the reason each caller picked one is a
comment at the call site rather than a reimplementation. The response is
either decoded (`request`) or half-closed and drained (`notify`, where
the drain exists so the server's write-back doesn't land on a closed
socket). Whether a failure propagates or is logged and swallowed stays
at the call site, because that is the caller's choice and not a property
of the transport.

Errors always name the socket path now, everywhere. That detail is
load-bearing: a permission problem on a socket that reads as "is the
daemon running?" sends the operator to fix the wrong thing.

The transient-against-fatal enum is gone rather than moved. Serialising
happens before the retry loop and deserialising after it, so only
connect, I/O and short-read failures can reach the loop at all — a
deterministic failure is now unretryable by construction instead of by
classification.

It is deliberately a new crate and not part of `hive-agent-sock`. The
`*-sock` crates are pure wire types by convention — `hive-agent-sock`
depends on serde and nothing else — and the two largest copies talk to
the host socket, whose types live in a different crate entirely. A
transport in either wire-type crate would drag tokio into it and point
the wrong way besides.

No wire-format change: same JSON line in, same line out.
2026-07-26 22:44:48 +02:00
.forgejo/workflows docs: stop claiming tracker-tag/comment-block lint are non-required 2026-07-23 00:10:59 +02:00
branding docs(#1182): remove component-diagram.svg; trim README; link to website + options 2026-06-03 19:06:06 +02:00
docs refactor(hive-agent): split the forge notification poller into its own crate 2026-07-26 21:30:29 +02:00
frontend fix(core): add card styling for K3PT ST4T3 and 1NFR4 entries 2026-07-26 17:23:52 +02:00
hive-agent refactor(sock): one socket client, retry as a policy value 2026-07-26 22:44:48 +02:00
hive-agent-mcp refactor(sock): one socket client, retry as a policy value 2026-07-26 22:44:48 +02:00
hive-agent-sock feat(#2635): wire harness-local questions mirror (inc2 pt2) 2026-07-26 02:07:33 +02:00
hive-bash-mcp refactor(sock): one socket client, retry as a policy value 2026-07-26 22:44:48 +02:00
hive-c0re feat: derive BUN_JSC_forceRAMSize from effective per-agent MemoryMax= 2026-07-26 21:56:25 +02:00
hive-claude refactor(#2359): bare Deserialize derive + clarify error to_string scan 2026-07-10 20:43:58 +02:00
hive-core-agent-sock docs(#2627): add READMEs for the remaining infra crates 2026-07-23 13:16:29 +02:00
hive-forge docs: trim readmes down, stop restating impl detail the module docs already own 2026-07-26 21:01:00 +02:00
hive-forge-notify refactor(sock): one socket client, retry as a policy value 2026-07-26 22:44:48 +02:00
hive-host-sock refactor(hivectl): move quota + subvol under agents 2026-07-26 18:26:27 +02:00
hive-jobq docs(#2627): add READMEs for hive-jobq + the socket wire crates 2026-07-23 12:34:22 +02:00
hive-matrix-mcp refactor(sock): one socket client, retry as a policy value 2026-07-26 22:44:48 +02:00
hive-metric docs(#2627): add READMEs for the remaining infra crates 2026-07-23 13:16:29 +02:00
hive-priv fix(hive-priv): a stop that succeeds must actually release the machine name 2026-07-26 20:17:45 +02:00
hive-priv-sock docs(#2627): add READMEs for hive-jobq + the socket wire crates 2026-07-23 12:34:22 +02:00
hive-screen-mcp docs(#2627): add README for hive-screen-mcp 2026-07-23 14:17:47 +02:00
hive-sh4re feat(dashboard): show jobq node tree in build queue 2026-07-26 03:14:52 +02:00
hive-sock-client refactor(sock): one socket client, retry as a policy value 2026-07-26 22:44:48 +02:00
hive-types docs(#2627): add READMEs for the remaining infra crates 2026-07-23 13:16:29 +02:00
hivectl docs: trim readmes down, stop restating impl detail the module docs already own 2026-07-26 21:01:00 +02:00
nix feat: derive BUN_JSC_forceRAMSize from effective per-agent MemoryMax= 2026-07-26 21:56:25 +02:00
scripts docs: stop claiming tracker-tag/comment-block lint are non-required 2026-07-23 00:10:59 +02:00
.gitignore fix(review): drop libnull.rlib artifact + add Errors doc to ensure_config_pr_webhook 2026-07-11 12:19:52 +02:00
.mailmap chore(#2165): add damocles@pr1ma + lexis@pr1ma mailmap entries 2026-07-04 13:50:16 +02:00
.prettierignore fix(#1997): exclude docs/tools/forge.md from prettier (list-item continuations) 2026-07-02 23:33:11 +02:00
.prettierrc temp: add prettier configs 2026-07-02 23:33:11 +02:00
Cargo.lock refactor(sock): one socket client, retry as a policy value 2026-07-26 22:44:48 +02:00
Cargo.toml refactor(sock): one socket client, retry as a policy value 2026-07-26 22:44:48 +02:00
CLAUDE.md refactor(sock): one socket client, retry as a policy value 2026-07-26 22:44:48 +02:00
clippy.toml hivectl: wireguard mesh setup verbs (#1756) 2026-06-19 14:37:50 +02:00
flake.lock nix flake update 2026-07-13 13:58:53 +02:00
flake.nix fix(nix): wire hive-forge-notify into the agent package set 2026-07-26 21:30:29 +02:00
README.md docs: shorten Overriding nixpkgs section 2026-07-16 00:06:17 +02:00

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 → :80 (hive-gateway)    dashboard + per-agent UIs
│   │                                   /agent/<name>/ → per-agent unix socket
│   └── CLI     → /run/hyperhive/host.sock   admin protocol
│
├── hive-c0re  (Rust daemon: lifecycle / broker / approvals /
│               auto-update / dashboard / sockets)
│
├── optional containers
│   ├── hive-gateway   nginx — proxies :80 → c0re dashboard + per-agent sockets
│   ├── hive-forge     Forgejo — per-agent accounts, config mirror (agent-configs/)
│   └── hive-matrix    tuwunel — Matrix homeserver + per-agent accounts
│
└── agent containers
    ├── h-ruth     manager (privileged MCP surface, approval gating)
    └── h-<name>   sub-agent (claude + MCP tools + per-agent web UI + unix socket)

→ website · → options reference

Depth lives in docs/ — pick the one matching your task:

reading path doc
dashboard layout + endpoints docs/web-ui.md (shape · dashboard · agent)
claude turn loop + MCP tools docs/turn-loop.md
config-edit + approval state machine docs/approvals.md
what survives destroy / purge / restart docs/persistence.md
naming, wire protocol, commit style docs/conventions.md
nginx vhost map + sub-domain routing docs/gateway.md
NixOS / nspawn gotchas docs/gotchas.md

Quick start

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

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
    hyperhive.url = "git+https://forge.darkest.space/hyperhive/hyperhive";
    # Pin hyperhive to your own nixpkgs instead of the one it ships with
    # (see "Overriding nixpkgs" below) — recommended for most hosts:
    hyperhive.inputs.nixpkgs.follows = "nixpkgs";
  };

  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.

Overriding nixpkgs

hyperhive pins its own nixpkgs so it builds standalone in CI. Add hyperhive.inputs.nixpkgs.follows = "nixpkgs" (as in the quick-start above) to build it against your host's nixpkgs instead — one less nixpkgs evaluation, no version drift from the rest of your system. Standard flake follows pattern; works as long as your channel is reasonably close to the nixos-26.05 hyperhive develops against. Drop it again if a much older/newer channel hits breakage hyperhive's CI doesn't catch.

For the full list of host and agent NixOS options see the options reference.

Agent configuration

Per-agent config lives in each agent's agent.nix (proposed, operator-approved, deployed as git commits). Key options:

Multi-account Matrix support

hyperhive.matrixAccounts declares additional matrix accounts for an agent, beyond the hive-internal one. Each entry is keyed by account name and specifies:

  • tokenFile — path to the matrix bearer token (provisioned out-of-band)
  • sessionDir — path to the per-account matrix-sdk sqlite state (crypto keys + cache)
  • homeserver — optional homeserver URL (defaults to hyperhive.matrix.url)

Example:

hyperhive.matrixAccounts = {
  external-public = {
    tokenFile = "/agents/myagent/state/matrix-token-external";
    sessionDir = "/agents/myagent/state/matrix-sdk-state-external";
    homeserver = "https://matrix.org";
  };
};

The hive-internal account is always named main (synthesized from hyperhive.matrix.url + agent state). This option only declares extras; the main name is reserved and cannot be used here. Requires hyperhive.matrix.enable = true.

For more details see docs/matrix.md.

GitHub account

Every agent gets a managed GitHub identity — a gh CLI wrapper and git push over HTTPS — on by default (hyperhive.github.enable), inert until a PAT is provisioned. There is nothing per-agent to declare: paste an operator-supplied personal access token into the agent's dashboard credentials tab (or hivectl github set-token <agent> --token-stdin) and it works. The gh wrapper + git credential helper read the token live (git auths as x-access-token + the PAT; github.com only), so a rotated PAT takes effect with no rebuild.

Turn the integration off for the whole hive with the host option:

services.hyperhive.github.enable = false;

The PAT value is never in nix — only the enable flag. For more details see docs/github.md.

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):

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

nix develop -c cargo check
nix flake check        # rust + nix + toml fmt + clippy

# deploy from a host config that imports hyperhive.nixosModules.default
nix flake update --update-input hyperhive
sudo nixos-rebuild switch --flake .#<host>