hyperhive/README.md
atlas b37c353f0e nix: hive-gateway v0 — nginx in front of c0re (#609)
Per mara's directive on #609: stand up a single nginx in its own
nixos-container, serve the matrix GUI static dist there, proxy
everything else to hive-c0re. v0 is HTTP-only; TLS / public-domain
shape lands in follow-ups.

New `nix/modules/hive-gateway.nix` declaring `containers.hive-gateway`
modelled on `hive-forge`:

- nixos-container running nginx, shares host netns
- `location /matrix/` → static-serves `hyperhive.matrix.gui.package`
  (fluffychat-web by default) when `matrix.gui.enable` is true
- `location /` → proxy_pass to `127.0.0.1:${dashboardPort}` with
  websocket + SSE upgrade headers + 1d read timeout

Options (`hyperhive.gateway.*`):
- `enable` (default `true`) — gateway on by default, opt out to bypass
- `port` (default `80`) — nginx listen port on the host
- `upstreamHost` / `upstreamPort` — c0re target, defaults to
  `127.0.0.1:${services.hive-c0re.dashboardPort}`
- `openFirewall` (default `true`) — open the listen port
- `localHostsEntry` (default `false`) — when true, adds an
  `/etc/hosts` entry mapping `hyperhive.domain` → `127.0.0.1` for
  local-dev / test loops without real DNS (per mara's spec)

`hive-c0re.nix` updates: when gateway is enabled, skip wiring
`HIVE_MATRIX_GUI_DIR` (gateway owns `/matrix/` now). When gateway is
off, c0re's pre-existing matrix mount stays as the fallback.

README: short "Optional" block introducing the gateway + the
`localHostsEntry` knob.

```sh
nix flake check --no-build

nix build .#docs-host
```

End-to-end eval matrix:

| gateway.enable | matrix.gui.enable | c0re HIVE_MATRIX_GUI_DIR | gateway container |
| --- | --- | --- | --- |
| true (default)  | true  | unset (gateway serves) | present |
| true            | false | unset                  | present, no /matrix |
| false           | true  | set (c0re serves)      | absent  |
| false           | false | unset                  | absent  |

- TLS termination — separate follow-up once mara picks a story
  (self-signed-mkcert vs operator-provided certs)
- Per-agent UI routing (`/agent/<name>/`) — depends on agent base-path
  support which is a frontend lift
- Subdomain routing for `matrix.${hyperhive.domain}` — same-origin
  `/matrix/` is the v0 shape per mara ("leave everything else as is")

Closes part of #609 (matrix GUI re-rooting onto nginx); leaves the
issue open for the subdomain re-root + `.well-known/matrix/client`
piece once the multi-host story matures.
2026-05-30 12:01:01 +02:00

183 lines
8.3 KiB
Markdown

# <img src="branding/hyperhive.svg" alt="" width="38" align="top"> 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/`](docs/) — pick the one matching your task:
| reading path | doc |
| --- | --- |
| dashboard layout + endpoints | [`docs/web-ui.md`](docs/web-ui.md) |
| claude turn loop + MCP tools | [`docs/turn-loop.md`](docs/turn-loop.md) |
| config-edit + approval state machine | [`docs/approvals.md`](docs/approvals.md) |
| what survives destroy / purge / restart | [`docs/persistence.md`](docs/persistence.md) |
| naming, wire protocol, commit style | [`docs/conventions.md`](docs/conventions.md) |
| NixOS / nspawn gotchas | [`docs/gotchas.md`](docs/gotchas.md) |
## Host config
Minimal `flake.nix` for a host that runs hive-c0re:
```nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
hyperhive.url = "git+https://forge.darkest.space/hyperhive/hyperhive";
};
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.
Optional: set `services.hyperhive.gateway.enable = false;` to bypass
the default nginx in front. By default (`gateway.enable = true`) every
request hits a small nginx in its own nixos-container that proxies
to hive-c0re's dashboard on the upstream port; `/matrix/` is served
directly from `services.hyperhive.matrix.gui.package` when the matrix
GUI is on. v0 is HTTP-only (TLS lives in a follow-up); pair with
`services.hyperhive.gateway.localHostsEntry = true;` for local dev so
`http://<services.hyperhive.domain>` resolves to the host without
setting up real DNS.
Optional: set `services.hyperhive.c0re.preBuildAgentTemplates = true;`
to pre-fetch the per-container system closures into your host's
/nix/store as part of `nixos-rebuild`. First-agent-spawn then
completes in seconds instead of minutes (no nixpkgs/claude-code
fetch on the critical path), at the cost of a few GB extra in your
system closure. Off by default (the toplevels are pinned to
`x86_64-linux`, so non-x86 hosts would otherwise force a cross-build).
Alternatively warm the store manually:
`nix build git+https://forge.darkest.space/hyperhive/hyperhive#agent-base-toplevel`.
Optional: set `services.hyperhive.domain = "example.com";` to define the
canonical hostname for hyperhive subsystems that need a stable public
name. No default — subsystems that require it (currently:
`services.hyperhive.matrix`) assert non-null at eval time with a clear
error message if it is missing.
Optional: set `services.hyperhive.matrix.enable = true;` to spin up a
private [matrix-tuwunel](https://github.com/matrix-construct/tuwunel)
homeserver in a nixos-container. Requires either
`services.hyperhive.domain` or `services.hyperhive.matrix.serverName`
to be set (eval fails with a clear error if both are absent). The
`server_name` (embedded irrevocably in every user and room ID)
defaults to `matrix.<domain>`; override with
`services.hyperhive.matrix.serverName = "chat.example.com";` if needed. State
lives at `/var/lib/nixos-containers/hive-matrix/`. Federation is
enabled with an empty `trusted_servers` list; e2ee is deferred to
a follow-up (#551).
## 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"`).
Sets `HIVE_DEFAULT_MODEL` in 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 (via `send`). 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-token` is missing.
- **`hyperhive.forge.keepSubscriptions`** — Boolean. If `true`, the agent's
forge repo subscriptions are never auto-cleaned during rebuild; useful
for agents that want to watch specific repos. Rendered as
`HIVE_FORGE_KEEP_SUBSCRIPTIONS`.
- **`hyperhive.forge.skipNotifyReasons`** — List of forge notification
`reason` values 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 as `HIVE_FORGE_NOTIFY_SKIP_REASONS`
(comma-separated).
- **`hyperhive.frontend.dist`** — Override the default frontend package
(`pkgs.hyperhive-frontend`, built by `nix/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 a `source` (nix
path) and an optional `target` (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; use `frontend.dist` to
replace the whole dist. Paths with leading `/` or `..` segments are
rejected at eval time.
- **`hyperhive.matrix.enable`** — Boolean (default `true`). When true,
each agent container runs `hive-matrix-daemon` (a long-running
matrix-sdk process that holds the per-agent client + sync) and
auto-injects `hive-matrix-mcp` as a stdio MCP server so claude can
call the matrix tools (`mcp__matrix__send_message`, `send_dm`,
`send_reaction`, `send_reply`, `mark_read`, `list_rooms`,
`list_room_members`, `read_room`; `room` args accept both
`!id:server` and `#alias:server` forms). Silently no-ops when
`<state>/matrix-token` is absent (i.e., the host-level
`hyperhive.matrix` tuwunel container hasn't provisioned the account
yet). Set to `false` to opt a specific agent out of matrix.
See `nix/templates/harness-base.nix` for the full list of options and
their descriptions.
## Build / deploy
```sh
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>
```