hyperhive/docs/security.md

3.3 KiB

Security model

State-file endpoint security model

GET /api/state-file?path=<p> serves files from agent state dirs and the shared space to authenticated dashboard users (browser, operator). Two allow-listed root prefixes are accepted; all other paths are rejected before touching the filesystem:

  • /var/lib/hyperhive/agents/<n>/state/ — per-agent durable notes (canonical host form or the in-container view /agents/<n>/state/)
  • /var/lib/hyperhive/shared/ — shared docs (/shared/ in-container)

/state/... without an agent prefix is explicitly not accepted — it is ambiguous from the host's perspective.

Defense-in-depth layers (in order):

  1. Allow-list prefix check — rejects without touching the filesystem if the path doesn't match either root.
  2. No symlinks below the matched root — each path component is checked with symlink_metadata before canonicalize. A sub-agent that plants ln -s /other/secret /agents/me/state/peek can't proxy another agent's file through this endpoint (canonicalize would happily resolve the symlink to a still-within-allow-list path).
  3. Canonicalize as belt-and-braces — resolves ../. traversal and rejects if the result escapes the roots.
  4. state/ subdir constraint — under AGENTS_ROOT, the second path component must be state/. Applied, proposed git repos and config dirs are off-limits.
  5. World-readable check — file must have mode & 0o004 set. A 0600 file inside state/ would otherwise be accessible to any operator with dashboard access.

scan_validated_paths (broker-message ingest, linkifier) uses the same resolve_state_path helper so security rules stay in sync — the dashboard renders anchors only for tokens that passed the same checks the read endpoint enforces.

Nix builds and credential isolation

Background

Agent containers bind-mount the host's nix-daemon socket. The host daemon may have sandbox-fallback = false (strict NixOS defaults), which causes nix build inside nspawn containers to fail — containers lack kernel user namespaces, so nix cannot set up its build sandbox. harness-base.nix sets sandbox-fallback = true so that builds fall back to unsandboxed execution rather than failing outright.

Threat model

Unsandboxed nix builds run as nixbld users (non-root, typically UIDs 30001-30010). Without sandbox isolation, a build derivation's builder script has read access to any file in the container that the nixbld user can read.

What is NOT exposed:

  • /home/<name>/.claude/ — mode 0700, owned by the per-agent user <name>. nixbld users cannot read it.
  • $HYPERHIVE_STATE_DIR/forge-token (= /agents/<name>/state/forge-token) — written at mode 0600 by hive-c0re/src/forge.rs and chowned to the per-agent uid:gid by lifecycle::chown_to_agent. nixbld users cannot read it.

Policy: all credential files written to agent state directories MUST be mode 0600 or stricter. Do not create world-readable secret files in agent state dirs.

Long-term fix

The proper fix is to enable user namespaces inside nspawn containers (--private-users=inherit in EXTRA_NSPAWN_FLAGS) so nix can set up its real sandbox and sandbox-fallback becomes a true last resort. This requires verifying bind-mount compatibility with user namespace UID mapping and is tracked as a TODO.