Closes #363 (frontend half of milestone #361). Consumes the `ContainerView.parent: Option<String>` field landing in damocles' backend slice — when present, the container list renders depth-first with sibling-position tree glyphs (├─ / └─ joints + │ continuation columns). When absent (pre-#361 state — every container's `parent` is None) the tree collapses to a flat list with no glyphs and no indent — bit-identical to the legacy render. ## Tree shape - Roots: containers whose `parent` is None OR whose parent name isn't in the current container set (orphan tolerance). - Sibling ordering: alphabetical by name within each level (matches damocles' wire spec at #363#issuecomment-3356). - Cycle safety: any container not reached via the root-walk gets emitted as a root at the end — no agent ever silently disappears from the list when the topology is malformed. - Tree glyphs: ancestor at depth d contributes a │ continuation column when that ancestor has more siblings below; otherwise a 3-space gap. The joint is ├─ for non-last siblings, └─ for the last child. - Depth-0 ancestor column is suppressed: roots already separate visually as top-level rows, no need for a column 0 vertical line. ## DOM / CSS - New `buildAgentTree(containers)` + `treePrefix(node)` helpers in app.js. The render loop walks the tree-ordered list instead of the legacy alphabetical containers array. - Each container row gets `data-depth=N` (only when N > 0) and a `<span class="tree-prefix">…</span>` prepended (absolute-positioned into the row's left margin so the existing flex icon/body layout isn't disrupted). - CSS: per-depth `margin-left` step rules for depths 1-6 (hardcoded rather than typed-attr() because CSS Values 5 is Chromium-only as of 2026). 6 depths cover any reasonable hive topology with headroom; deeper agents render at depth 6 indent without further step — visually clamps gracefully. - `.tree-prefix` rendered with `var(--purple-dim)` so the structural lines read as supporting chrome, not as content. ## Validation - `npm run build` clean. Bundle deltas: dashboard app.js +1.8kb (tree builder + treePrefix + render-loop tweak), dashboard.css +0.4kb (tree-prefix + per-depth indent rules). - The render is a no-op until `ContainerView.parent` is populated — validation in production deferred to once damocles' meta-topology field lands. The pre-#361 path (every parent=None) is exercised by every existing dashboard load. - Forward-compatible with damocles' design pivot at #364 (topology source moved from agent.nix to meta/topology.json). The wire shape on ContainerView is unchanged from the frontend's perspective — the field is just sourced from a different backend store. |
||
|---|---|---|
| branding | ||
| docs | ||
| frontend | ||
| hive-ag3nt | ||
| hive-c0re | ||
| hive-sh4re | ||
| nix | ||
| scripts | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CLAUDE.md | ||
| flake.lock | ||
| flake.nix | ||
| README.md | ||
| TODO.md | ||
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 → :8000 / :8100-8999 per-agent web UIs
│ └── 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-<name> sub-agent (vanilla MCP surface + per-agent extras)
Depth lives in docs/ — pick the one matching your task:
| reading path | doc |
|---|---|
| dashboard layout + endpoints | docs/web-ui.md |
| 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 |
| NixOS / nspawn gotchas | docs/gotchas.md |
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.default # hive-c0re + hive-forge in one import
({ ... }: {
services.hive-c0re.enable = true;
# services.hive-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.
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"). SetsHIVE_DEFAULT_MODELin 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 (viasend). 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-tokenis missing.hyperhive.forge.keepSubscriptions— Boolean. Iftrue, the agent's forge repo subscriptions are never auto-cleaned during rebuild; useful for agents that want to watch specific repos. Rendered asHIVE_FORGE_KEEP_SUBSCRIPTIONS.hyperhive.forge.skipNotifyReasons— List of forge notificationreasonvalues 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 asHIVE_FORGE_NOTIFY_SKIP_REASONS(comma-separated).hyperhive.frontend.dist— Override the default frontend package (pkgs.hyperhive-frontend, built bynix/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 asource(nix path) and an optionaltarget(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; usefrontend.distto replace the whole dist. Paths with leading/or..segments are rejected at eval time.
See nix/templates/harness-base.nix for the full list of options and
their descriptions.
Build / deploy
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 .#<host>