From 2ec5c9433f376621ea8dbe7c8a900ad22f926c1d Mon Sep 17 00:00:00 2001 From: atlas Date: Fri, 11 Sep 2026 17:31:13 +0200 Subject: [PATCH] docs/security: name all 34 PrivRequest variants, not 17 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `security.md` introduces its hive-priv table as "Narrow interface — `PrivRequest` variants map 1:1 to specific known operations". The table named 17 of 34, so the claim it was making was only half-checkable by the reader it was written for. Two whole subsystems were absent rather than stragglers: btrfs subvolume + snapshot management (9 verbs) and per-agent external forge accounts (2), plus `SendAgentSnapshotToFd`, which passes a file descriptor across the boundary via SCM_RIGHTS — the kind of verb someone reads a security page to find. The table had already been resynced by hand once, in June, and drifted again within three months. mara's call was to keep it exhaustive rather than summarise by subsystem: the swarm-level operations are expected to go away, so the row count is at its peak now and shrinks from here. Rows for the 17 missing variants, each naming what the helper actually runs. Three of them carry the constraint that makes them safe rather than just the command, because that is what the surrounding section is for: `ControlInfraContainer`'s allowlist is the `InfraContainer` enum (serde rejects unknown names at the wire boundary, and `hive-c0re` has no variant); `WriteAgentExtraForgeAccount`'s `label` reaches a filename and is validated as a plain identifier first; `SendAgentSnapshotToFd` requires exactly one descriptor and refuses one arriving alongside any other operation. Prose is active voice throughout the new rows — "hive-priv validates `label`" rather than "`label` is validated" — since the question a reader brings to this table is which component enforces what. Verified with a variant-vs-page diff carrying its own controls (extractor count, page-readable, a present variant resolves, an invented one does not): 34 named, 0 absent. vale is unchanged from main's baseline for this file, 0 errors and 13 warnings on both sides. Closes #4222. --- docs/trust-boundary/security.md | 48 ++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/docs/trust-boundary/security.md b/docs/trust-boundary/security.md index c65c7b72..cc4751c2 100644 --- a/docs/trust-boundary/security.md +++ b/docs/trust-boundary/security.md @@ -236,22 +236,38 @@ for every operation that genuinely requires root. **Narrow interface** — `PrivRequest` variants map 1:1 to specific known operations; there is no arbitrary command pass-through: -| Operation | What it runs | -| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | -| `StartContainer` / `StopContainer` | `nixos-container start/stop ` | -| `KillContainer` | `machinectl kill --signal=SIGKILL` (`nixos-container` has no kill verb) | -| `CreateContainer` / `UpdateContainer` | `nixos-container create/update --flake ` | -| `DestroyContainer` | `nixos-container destroy ` | -| `ListContainers` | `nixos-container list` | -| `ReadContainerJournal` | `journalctl -M -n [filters...]` | -| `ReloadGatewayNginx` | `systemctl reload/start/reset-failed nginx` (host unit; the unit name is hard-coded, not a parameter) | -| `WriteNspawnFlags` | write `/etc/nixos-containers/.conf` (bind-mount list + network isolation vars) | -| `WriteResourceLimits` | write `CPUQuota=`/`MemoryMax=`/`CPUWeight=`/`IOWeight=` systemd drop-in for agent container | -| `RemoveServiceDropin` | remove `container@.service.d/` drop-in on destroy | -| `DaemonReload` | `systemctl daemon-reload` | -| `RunForgeAdmin` | `nixos-container run hive-forge -- runuser -u forgejo -- forgejo admin ` | -| `WriteAgentForgeToken` / `WriteAgentMatrixToken` | write `0600` credential file into agent state dir | -| `RestartMatrixDaemon` | `systemctl --machine=h- restart hive-matrix-daemon.service` | +| Operation | What it runs | +| -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `StartContainer` / `StopContainer` | `nixos-container start/stop ` | +| `KillContainer` | `machinectl kill --signal=SIGKILL` (`nixos-container` has no kill verb) | +| `CreateContainer` / `UpdateContainer` | `nixos-container create/update --flake ` | +| `DestroyContainer` | `nixos-container destroy ` | +| `ListContainers` | `nixos-container list` | +| `ReadContainerJournal` | `journalctl -M -n [filters...]` | +| `ReloadGatewayNginx` | `systemctl reload/start/reset-failed nginx` (host unit; the unit name is hard-coded, not a parameter) | +| `WriteNspawnFlags` | write `/etc/nixos-containers/.conf` (bind-mount list + network isolation vars) | +| `WriteResourceLimits` | write `CPUQuota=`/`MemoryMax=`/`CPUWeight=`/`IOWeight=` systemd drop-in for agent container | +| `RemoveServiceDropin` | remove `container@.service.d/` drop-in on destroy | +| `DaemonReload` | `systemctl daemon-reload` | +| `RunForgeAdmin` | `nixos-container run hive-forge -- runuser -u forgejo -- forgejo admin ` | +| `WriteAgentForgeToken` / `WriteAgentMatrixToken` | write `0600` credential file into agent state dir | +| `RestartMatrixDaemon` | `systemctl --machine=h- restart hive-matrix-daemon.service` | +| `ControlInfraContainer` | `systemctl container@.service` — the `InfraContainer` enum is the allowlist, and serde rejects unknown names at the wire boundary (`hive-c0re` has no variant, so no request can name it) | +| `SyncAgentTmpfiles` | write `/etc/tmpfiles.d/hyperhive-agents.conf` for the agent set, then `systemd-tmpfiles --create` | +| `SetAgentPaused` | create / remove the `//harness/paused` marker that parks an agent's turn loop | +| `WriteAgentGithubToken` | write `0600` `github-token` into agent state dir (same semantics as the forge/matrix token writes) | +| `WriteAgentExtraForgeAccount` / `DeleteAgentExtraForgeAccount` | write / remove `forge-