Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/docs/2-handbook/hives/boundary.md
iris 0e84fe8bc4 docs: the handbook follows the three layers: swarm, hives, agents
The handbook's sections were agent-lifecycle, integrations, swarm, tools,
trust-boundary and web-ui, which do not follow how the system is built, so a
reader could not tell which layer a page is about. Its pages now sit in
swarm/, hives/ and agents/, with the accounts an agent can hold outside the
system in external-accounts/.

- swarm/: forge, matrix and the knowledge repo join the swarm pages, and so
  does the swarmctl reference.
- hives/: the dashboard, hivectl and its reference, and the operator/agent
  boundary, with a README that takes over the old web-ui and tools pages.
- agents/: the roster, persistence, config changes, the agent's own web page
  (was web-ui/agent) and the security model, with a README.
- external-accounts/: GitHub and GitLab.

Pages are moved whole; none is split yet. Every reference follows: relative
links in the docs, `docs/2-handbook/...` paths in code comments, the nix
checks that regenerate the CLI references and the ignore list, and docs/README.md,
whose handbook section is now by layer.
2026-11-07 12:19:36 +01:00

5.4 KiB

The operator/agent boundary

Design rationale for hyperhive's two-principal trust model. The implementation work — container network isolation, the unifying gateway, core-daemon privsep — is tracked as area:ops issues on the forge.

The operator/agent boundary is technically enforced, not just a convention: containers run in private netns (network isolation is always on), the gateway proxies all operator-facing traffic, and hive-c0re runs as the unprivileged hive-core user.

Two principals, two paths

  • Operator — reaches every UI (the dashboard + every per-agent page) through the gateway, on one origin. The core daemon serves operator-authority actions (answer-as-operator, lifecycle POSTs), reachable only via the gateway.
  • Agent — speaks only for itself, only over its per-agent unix socket. The socket's identity is the agent (see docs/5-project/process/conventions.md, "identity = socket"). An agent must not be able to reach the core daemon's HTTP surface, another agent's socket, or another agent's web UI.

Design rule

Operator-authority actions never get a per-agent-socket entry point. They live in hive-c0re.

Worked example — destroying or rebuilding a container is a POST /api/{destroy,rebuild}/{name} on the core dashboard, never a per-agent-socket Request variant. If it were a per-agent-socket request, a compromised agent could curl its own socket and destroy or rebuild itself (or, if the variant took an arbitrary target, another agent) without ever touching the core's own authenticated surface.

Why network isolation is the load-bearing step

Without network isolation, containers share the host network namespace and can reach localhost:<core-port>, the dashboard, and every other agent's web port — the operator/agent split is on the honour system and every boundary claim above is aspirational. Network isolation is what makes the boundary real; the gateway and privsep are ergonomics and defence-in-depth layered on top.

Network isolation is complete and always on: every agent container runs in a private netns behind the hive bridge, and there is no shared-netns mode. See docs/4-implementation/networking/network.md.

Concretely, the core daemon's dashboard /api carries no application-layer authentication — the core daemon serves operator-authority routes unauthenticated at the HTTP layer. Their protection is entirely (a) the gateway, which fronts all operator traffic and is where operator auth lives, and (b) network isolation, which keeps agents — and the CI runner's untrusted PR builds — off host-loopback so nothing can reach 127.0.0.1:<dashboard_port> directly. This is deliberate given the load-bearing role of network isolation above, but it's a standing invariant: the /api must never bind to a non-loopback address or get exposed outside the gateway, and every new operator-authority route inherits that assumption. hyperhive treats swarm-ci-runner like an agent for this purpose — it runs untrusted PR code and is netns-isolated for the same reason.

The boundary rests on three layers:

  1. Gateway — fronts all surfaces (dashboard + every per-agent UI) on one origin. An nginx nixos-container proxies per-agent UIs under /agent/<name>/, which is what lets each agent page's inbox panel POST mark-all-read to the core dashboard's /api/agent/{name}/mark-all-read go same-origin instead of needing a cross-origin CORS shim. Pure ergonomics — no behavioural risk on its own.
  2. Network isolation — the load-bearing layer: every agent container runs in a private netns behind the hive bridge, always on and unconditional. This is what turns the operator/agent split from an honour-system convention into an enforced boundary.
  3. Privsep — defence in depth on the core process; hive-c0re runs as the unprivileged hive-core user and delegates root operations to hive-priv, a narrow socket-activated helper. See docs/4-implementation/trust-boundary/hive-c0re-privilege.md for the privilege boundary table.

host admin socket access (hivectl)

hivectl drives the whole hive — spawn / kill / destroy / rebuild / deploy — over the host admin socket /run/hyperhive/host.sock, socket-activated by the hive-c0re.socket unit. That socket is the full-control surface, so who can connect to it's a real trust boundary.

By default the socket is 0660 group-owned by hive-admin, an empty group — so it's effectively root-only until an operator is explicitly granted access. Grant sudoless hivectl by listing login users in services.hyperhive.deploy.hive-controller.adminUsers; the option adds each to hive-admin, and members connect without sudo. The runtime dir /run/hyperhive is 0751 (traverse-only, no listing) so the group can reach the socket path; the socket's own 0660 hive-admin mode gates the connection, and the per-agent subdirs under it keep their own restrictive perms. Keep adminUsers to trusted operators — membership is equivalent to root over the hive.

Implementation detail

How hive-priv is socket-activated and how the per-agent socket directory gets its owner and mode: socket mechanics.