Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/hive-priv/README.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

69 lines
3.6 KiB
Markdown

# hive-priv
The minimal **root privileged-helper** for hive-c0re. It runs as root and
exposes a narrow unix socket at `/run/hive/priv.sock` that accepts `PrivRequest`
JSON lines and performs only the handful of operations that genuinely require
root — bind-mount edits, `nsenter` into a container, btrfs subvolume ops. All
coordination logic (broker, HTTP, scheduling) stays in the _unprivileged_
`hive-c0re` process, which delegates here.
## Why it exists
Privsep. `hive-c0re` runs as the unprivileged `hive-core` user so a bug or a
prompt-injection in the large daemon can't directly wield root. The few root
operations it needs are funnelled through this small, auditable helper instead.
See `docs/2-handbook/hives/boundary.md` and `docs/4-implementation/trust-boundary/hive-c0re-privilege.md` for the privilege boundary.
## Security model
- **Strict allowlist.** hive-priv checks every request against a container-name
allowlist before any filesystem or process operation, and accepts only names
matching the hive convention (`h-*`, the manager container included).
- **No pass-through.** Every `PrivRequest` variant maps to a single known
operation; there is no arbitrary-command escape hatch.
- **Socket-activated, always.** systemd binds `/run/hive/priv.sock`
(`SocketGroup=hive-core`, `0660`) and passes the listener as fd 3
(`LISTEN_FDS`); the helper requires this and has no self-bind fallback, so dev
and prod take the identical path and the group grant always holds.
The wire contract (`PrivRequest` / response types) lives in the separate
`hive-priv-sock` crate so this root binary depends on just the protocol shapes,
not the whole daemon-shared crate.
## Layout
`src/main.rs` holds the socket loop and the dispatch table, one arm per
`PrivRequest` variant. Each op family, with its validation, has its own module:
`container` (lifecycle), `container_config` (nspawn flags, systemd drop-ins),
`storage` (btrfs subvolumes, quotas, snapshots, disk measurement), `files`
(pause marker, socket dir, legacy tmpfiles) and `infra` (gateway nginx, forge
admin, infra containers). Helpers more than one family calls live in `shared`.
Each module's `//!` header lists its ops.
## Implementation notes
### Container toplevels (create/update)
`container_toplevel_action` (in `src/container.rs`) applies a closure that is
already in the store; hive-priv builds nothing. hive-c0re names the closure
(`toplevel` on `CreateContainer` / `UpdateContainer`), downloaded beforehand
with `RealiseClosure`. hive-priv remembers, per agent, the path its last
successful `RealiseClosure` returned (in memory), and applies only that path to
that agent. It refuses a path never realised for the agent, another agent's
and the host's own, and logs an error naming the agent and both paths. A
restart between the download and the apply empties the record, so the apply
fails and the job retries. hive-priv does not re-check the signature at apply
time: `RealiseClosure` enforces it, and the binding limits an apply to what that
call stored.
`create` passes the path to `nixos-container create --system-path`, so
`nixos-container`'s own `buildFlake()` never runs. Without the flag, it builds
to a hardcoded relative `.tmp` out-link: `$systemPath` stays `undef` and
`"$systemPath.tmp"` interpolates to the bare string `.tmp` in the caller's cwd.
With `services.hyperhive.deploy.hive-controller.buildSlots > 1`, two concurrent
calls could share that one `.tmp`, and one agent's container got another
agent's closure.
`update` points the container's profile at the path with `nix-env --set` and
reloads the unit if it runs, because `nixos-container update` dies on its
version probe before it honors `--system-path`.