Per mara's go-ahead on hyperhive#3902 ("getting started is good, but
terminal rendering does not go in there i think"):
Moved 21 top-level docs/*.md files into 7 new topic subdirectories
(existing web-ui/, turn-loop/, swarm/, tools/, crates/ untouched):
getting-started/ setup.md
agent-lifecycle/ agent-hierarchy.md, approvals.md, persistence.md
trust-boundary/ boundary.md, security.md
integrations/ forge.md, matrix.md, github.md, knowledge.md
networking/ gateway.md, network.md, snapshot-store.md
scheduler/ jobq.md, coordinator.md, ci.md, observability.md
process/ conventions.md, gotchas.md, pr-review-gate.md
web-ui/ terminal-rendering.md (moved into the EXISTING dir,
per mara's correction to the original getting-started
guess -- it's UI implementation detail, not onboarding)
The physical layout now matches docs/README.md's own topical headers,
which already amounted to this taxonomy -- see the scoping comment on
the issue for the two findings that motivated this (a genuine
duplication between CLAUDE.md's old "Reading paths" list and
docs/README.md's grouped one, since drifted out of sync with each
other; and the flat layout not matching the grouping we already had).
Fixed every cross-reference this moved across the whole repo (~120
files: docs/ internal links at every depth, Rust doc comments, nix
module option docs, crate READMEs) -- verified two ways: a grep sweep
confirming zero remaining references to any old path, and a script
that resolves every markdown link in docs/**/*.md + CLAUDE.md +
README.md against the filesystem and reports anything that doesn't
exist (zero broken links).
Collapsed CLAUDE.md's "Reading paths" section (the duplicate) down to
a pointer at docs/README.md, now the single index. Rewrote
docs/README.md itself to use the new subdirectory paths and added the
one doc it was missing that CLAUDE.md's old copy had (pr-review-gate.md).
Classified all 22 docs/*.md files first via a haiku subagent (mara's
suggestion) on two axes -- proposed grouping and operator-vs-
implementation focus -- before finalizing the taxonomy; spot-checked
the report and found internal inconsistencies (its classification
table disagreed with its own summary section for a few files), so this
taxonomy is my original proposal + the one correction mara gave
directly, not a blind application of the subagent's table. The
operator-focus data it gathered is still useful for a follow-up
content pass (docs skewing 'mixed' rather than pure operator-facing),
not addressed in this PR -- structure only.
nix fmt clean, both pre-push lints clean.
12 KiB
hyperhive — claude entry point
Hey claude. This is your starting page. The detailed docs live in
docs/ and are written for humans + you both — read them
when you need depth on a subsystem. This file is the index.
- High-level project intro: README.md.
- Open work + backlog: the forge issue tracker at
$HIVE_FORGE_URL/hyperhive/hyperhive/issues. Read the host out of the env var — inside an agent containerlocalhostis the agent, not the forge, so a hardcoded loopback address fails to connect. - Operator/agent trust-boundary design:
docs/trust-boundary/boundary.md (
area/opsissues for the deployment/gateway/privsep work — the separator is a slash, and--label area:opsnow fails withdid you mean "area/ops"?). - Agent trust model (trust boundary, prompt-injection threat model, capability = accepted risk), credential isolation + sandbox threat model: docs/trust-boundary/security.md.
Repo map
One line per crate / top-level dir. Each module's authoritative,
always-current description lives in its own //! doc-comment —
grep/read the module when you need detail. This index is kept
deliberately lean: it auto-loads into every turn's context, and a
hand-maintained per-file tree drifts out of sync with the code.
Rust workspace (Cargo.toml members)
hive-c0re/— host daemon (runs as the unprivilegedhive-coreuser).src/main.rsis thehive-c0rebinary — daemon-only (serve+ the periodic vacuum/sweep loops); the operator CLI lives in the separatehivectlcrate, which talks to the daemon over the host admin socket. Owns the sqlite broker, approval + reminder + schedule queues, the meta flake, lifecycle (nixos-containershellouts), gateway / forge / matrix provisioning, per-container stats, and the axum operator dashboard (dashboard.rs). Largest crate.hivectl/— standalone operator CLI (hivectlbinary). Talks to thehive-c0redaemon over the host admin socket (hive-host-sockwire types) — does NOT linkhive-c0re. Full, always-current verb reference (CI-enforced against the clap tree, seedocs/process/conventions.md):docs/tools/hivectl-cli.md.hive-agent/,hive-agent-mcp/— in-container harness, two sibling crates for every agent (not a singlehive-ag3nt/dir — that's the runtime/binary-family nickname, not a directory).hive-agent/— the serve-loop binary: turn-loop policy layer (turn.rs) over thehive-claudedriver, per-agent web UI (web_ui/module dir), event + turn-stats sqlite sinks, login flow, system-prompt renderer.hive-agent-mcp/— the embedded MCP server (long-lived streamable-http listener,hive-mcp-httpsystemd unit) + its claude launch-config layer (tool-group/capability →--allowedTools,--mcp-configrender).hive-jobq/— job-DAG scheduler, extracted from hive-c0re's in-treejob_queueas a domain-agnostic library. Runtime-only — nothing writes the graph to disk; hive-c0re starts empty each boot and re-derives desired state via the reconcile sweep, so node ids and timestamps are stable within a run, not across restarts. One shared graph for the whole system (not a DAG per job); enqueuing inserts a self-contained sub-DAG and returns the ids of the nodes the job asked for, in the order it named them. Generic over the node payloadNand the resource nameR; resource deps are named counting semaphores acquired all-or-nothing at node start.hive-c0re's remainingjob_queue/module is the c0re-specific layer over this crate, and is being removed in favour of it — new scheduler-shaped code belongs here, not there.hive-jobq-wire/— wire types for serving ahive-jobqgraph to a viewer, plus theWireNode/WireResourcetraits a host implements to say how itsNandRrender. Deliberately not part ofhive-jobq: that crate is logic, this is presentation, and folded together they remix.GraphWire::wire_snapshotis blanket-implemented for anyGraph<N, R>whose parameters implement both — so a payload that has never said how it displays cannot reach a viewer at all.hive-screen-mcp/— stdio MCP bridge for GUI agents (hyperhive.gui.enable):screenshotviagrim,type_text/key_pressviawtype(Wayland virtual-keyboard protocol), andmouse_move/mouse_clickas RFB pointer events to the local neatvnc server. All userspace, no daemon of its own.hive-priv/— minimal root privileged-helper, socket-activated at/run/hive/priv.sock; performs the few root operations (bind-mount edits, nsenter) the unprivilegedhive-c0redelegates to it. Seedocs/trust-boundary/boundary.md.hive-forge/—hive-forgeForgejo CLI wrapper; one module per verb undersrc/verbs/.hive-forge-notify/— per-agent notification poller daemons; turns unread notification threads into todos on the harness's in-agent socket. Two binaries from one crate:hive-forge-notify(the hive's Forgejo) andhive-github-notify(github.com, installed bynix/agent-modules/github.nix). Was a task inside thehive-agentserve loop; own process since it needs nothing else from the harness.hive-matrix-mcp/— per-agent matrix-sdk daemon (hive-matrix-daemon); serves its MCP tools (send_message,read_room, …) directly over streamable-http (no stdio bridge), same shape ashive-bash-mcp.hive-bash-mcp/— per-agent bash-task runner daemon (hive-bash-daemon); serves its MCP tools (run/status/kill) directly over streamable-http (no stdio bridge), writes task files under/harness/bash-tasks/, and records the favorite-toolsbash_commandsstat into turn-stats.sqlite.hive-sh4re/— shared wire types (Agent / Manager request + response,Message,Approval,HelperEvent) used across the unix sockets. Host-admin-socket and hive-priv-socket wire types have been split out into their own crates (below) sohivectlandhive-privdon't need to pull in the rest ofhive-sh4re.hive-host-sock/— wire types for the host admin socket (/run/hyperhive/host.sock), the protocolhivectlspeaks tohive-c0re. Split out ofhive-sh4reso a standalonehivectlonly depends on this protocol crate, not the whole daemon crate.hive-priv-sock/— wire types for thehive-privprivileged-helper socket (/run/hive/priv.sock), shared byhive-priv(server) andhive-c0re(client). Also split out ofhive-sh4re.hive-core-agent-sock/— wire types for the host-served per-agent- manager socket (
/run/hive/mcp.sock), the protocol an agent's harness speaks tohive-c0re. Same split rationale as the two above; the shared payload types it references stay inhive-sh4re.
- manager socket (
hive-agent-sock/— wire types for the in-agent socket, served by the harness to in-container producers — matrix / bash MCP daemons andforge_notifyare the built-in ones, but any user-configured MCP server can push todos here too, nothing restricts thesubsystemset. Carries the loose-ends-v2 todo ops plus harness-local reminders. ⚠️ Distinct fromhive-core-agent-sockabove: this socket never leaves the container andhive-c0reis not in the path at all — no broker round-trip, no long-poll, no marker files.hive-types/— zero-dependency (bar serde) leaf crate holding the foundational newtypes, chieflyIdent(1–63 chars of[a-z0-9-], constructed only via the validating parser). Lets every wire-type crate and both binaries type their agent-name fields as a validated ident and get serde-checked parsing at the socket boundary, without coupling tohive-sh4re.hive-sock-client/— the shared JSON-line-over-unix-socket client every daemon uses to talk to a hyperhive socket. Generic over the request/response types, so the host-served control socket and the harness's in-agent socket both use it with their own wire-type crates. Retry is a policy value (Retry::Nonefor callers already inside a poll loop,Retry::RideOutRestartfor callers with no natural retry), and the response is either decoded (request) or drained (notify). Deliberately separate from the*-sockcrates — those stay dependency-free wire types.hive-metric/— small CLI to push a single labeled metric to the OTEL collector via the OpenTelemetry Rust SDK / OTLP HTTP exporter.swarm-controller/— swarm-level daemon, opt-in per host (services.hyperhive.deploy.swarm-controller.enable). Wherehive-c0reowns the agents on one host, this owns what is true across hives; a swarm runs one of them, so most hives leave it off. Serves HTTP over a unix socket the gateway's nginx proxies to — ⚠️ the socket's directory is its access control; the constraint that governs it is in the crate's README, and a unit test pins the path.swarmctl/— swarm-level operator CLI, installed by the swarm-controller module on the host that runs the daemon. Runs as root and acts directly — no socket, no HTTP route, no priv helper; the crate's README records why the rootless shape was examined and rejected. Does not linkswarm-controller, mirroringhivectl÷hive-c0re. ⚠️ Its user store is authelia's ownusers.yml, read and written in place — one file, shared withswarm-authelia-bridge; see that crate's README for what both writers must uphold. Full, always-current verb reference (CI-enforced against the clap tree, same pattern ashivectl's — seedocs/process/conventions.md):docs/tools/swarmctl-cli.md.
External dependencies with no directory here
hive-claude— reusable, app-agnostic driver for headlessclaude --print: spawns the CLI, streams + classifies stream-json, parses per-turnTelemetry, and drives a durable self-compactingInfiniteSession(name +SessionStore+CompactionPolicy). Usesthiserror(it's a library); thehive-*binaries consume it withanyhow. ⚠️ Its own repo, not a workspace member — consumed as a dependency in the rootCargo.toml, so there is nohive-claude/directory to read andgrephere will not find it.
Other top-level dirs
frontend/— npm workspaces → static dashboard + per-agent UI dist, built hermetically bynix/packages/frontend.nix. Packages:shared(terminal pane + Catppuccin palette),dashboard(the operator SPA),agent(the default per-container UI),swarm-ui(swarm-level UI shell — Preact + wouter + TypeScript + JSX, project- bootstrap scope; packaged separately bynix/packages/swarm-ui.nix, not bundled intopackages.default's closure — see that file).nix/—host-modules/(the host stack: hyperhive core options,hive-{c0re,priv,forge,gateway,matrix,network,tls,ci}, otel, swarm),agent-modules/(the per-agent harness feature modules),templates/{agent,ruth}.nix(container entry points),packages/(flake package outputs),docs/(the options-doc derivation), plussources.nix/rust.nix/checks.nix/devshell.nix/treefmt.nixbehind the thinflake.nix.docs/— subsystem reference docs (see Reading paths below).branding/,scripts/— static assets + helper scripts.
Reading paths
docs/README.md is the single index — grouped by topic
(getting started, agent lifecycle, trust boundary, integrations, networking,
scheduler, process). Read it instead of a second copy here; this file used to
carry its own parallel "Reading paths" list, and the two drifted out of sync
with each other.
Conventions & process
- Never add
#[allow(clippy::…)]— fix the lint instead (extract a helper, add backticks, etc.). Details + worked examples: →docs/process/conventions.md. - Pre-push lint hook (catches tracker-tag and comment-block failures
before CI does — install once per clone):
ln -sf ../../scripts/pre-push .git/hooks/pre-push - Comment/PR-description length: size it to how non-obvious the thing is, not to how much investigation it took to find it — a mechanical fix gets a one-line comment, a genuinely non-obvious invariant or decision earns real prose.