Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e366c8a10 | ||
|
|
524b1de98c | ||
|
|
9daed8acc1 | ||
|
|
cf581d14b8 |
2 changed files with 92 additions and 1 deletions
|
|
@ -10,7 +10,8 @@ when you need depth on a subsystem. This file is the index.
|
||||||
- Operator/agent trust-boundary design:
|
- Operator/agent trust-boundary design:
|
||||||
**[docs/boundary.md](docs/boundary.md)** (`area:ops` issues
|
**[docs/boundary.md](docs/boundary.md)** (`area:ops` issues
|
||||||
for the deployment/gateway/privsep work).
|
for the deployment/gateway/privsep work).
|
||||||
- Credential isolation + sandbox threat model:
|
- Agent trust model (trust boundary, prompt-injection threat model,
|
||||||
|
capability = accepted risk), credential isolation + sandbox threat model:
|
||||||
**[docs/security.md](docs/security.md)**.
|
**[docs/security.md](docs/security.md)**.
|
||||||
|
|
||||||
## Repo map
|
## Repo map
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,95 @@
|
||||||
# Security model
|
# Security model
|
||||||
|
|
||||||
|
## Agent trust model
|
||||||
|
|
||||||
|
The sections below document specific mechanisms (the state-file endpoint,
|
||||||
|
nixbld isolation, privilege separation). This section frames the model they
|
||||||
|
serve: **what hyperhive defends, what it deliberately does not, and where the
|
||||||
|
operator is accepting risk.** It emerged from a security discussion on
|
||||||
|
2026-06-24 (prompted by the `gh` CLI helper work) and is the reference for
|
||||||
|
"is it safe to give an agent capability X?".
|
||||||
|
|
||||||
|
### The trust boundary is the container, not credential storage
|
||||||
|
|
||||||
|
An agent is **trusted code running inside its own nspawn container**. The
|
||||||
|
boundary that matters is the container: a sub-agent cannot see the host
|
||||||
|
netns, another agent's container, or another agent's state dir. Within its
|
||||||
|
own container the agent is privileged — it has **passwordless `sudo` by
|
||||||
|
default**. Isolating credentials *from the agent itself* is therefore **not a
|
||||||
|
goal**: an agent can read its own tokens, its own `/home/<name>/.claude`, and
|
||||||
|
run arbitrary commands as root inside its container. (The narrow exception is
|
||||||
|
*cross-tenant* leakage — e.g. the unsandboxed-nix-build `0600` token policy
|
||||||
|
below stops a build's nixbld user reading the agent's own forge token, and the
|
||||||
|
state-file endpoint stops one agent proxying another's files. Those harden the
|
||||||
|
boundary; they do not sandbox the agent from itself.)
|
||||||
|
|
||||||
|
The corollary: **don't reason about security as "can the agent be stopped from
|
||||||
|
touching its credentials". Reason about it as "what is the blast radius if this
|
||||||
|
agent does the worst possible thing with everything it can reach".**
|
||||||
|
|
||||||
|
### Scoped tokens bound the blast radius
|
||||||
|
|
||||||
|
Each agent gets its own scoped credentials, never shared:
|
||||||
|
|
||||||
|
- **forge token** → that agent's Forgejo account only (its own repos +
|
||||||
|
collaborator grants; cannot act as another agent or as `core`).
|
||||||
|
- **matrix token** → that agent's matrix account only.
|
||||||
|
|
||||||
|
So a compromised/confused agent's reach on the forge or matrix is bounded by
|
||||||
|
*its own* account's scope, not the swarm's. This is the main thing standing
|
||||||
|
between "one agent does something dumb" and "the whole hive is affected".
|
||||||
|
|
||||||
|
### Threat model: prompt injection → confused deputy
|
||||||
|
|
||||||
|
The realistic adversary **never needs to breach the container**. They supply
|
||||||
|
**untrusted input the agent reads and acts on**: a poisoned issue or PR
|
||||||
|
comment, a cloned repo's README/CI, a scraped webpage, a crafted matrix
|
||||||
|
message. The agent is the trusted, capable party; the *input* is the
|
||||||
|
untrusted part. A successful injection turns the agent into a **confused
|
||||||
|
deputy** — it uses its legitimate capabilities (push, comment, deploy, run
|
||||||
|
shell) on the attacker's behalf.
|
||||||
|
|
||||||
|
Mitigations are therefore about **bounding capability and inserting human
|
||||||
|
checkpoints**, not about sandboxing the agent from its own tools:
|
||||||
|
|
||||||
|
- **Operator merges, not the agent** — an agent may *push* branches, but a
|
||||||
|
**human (the operator) merges the PR**, keeping a person in the loop on the
|
||||||
|
highest-value action. On the **internal forge this is technically enforced,
|
||||||
|
not just convention**: agents can't create repos (`max_repo_creation = 0`),
|
||||||
|
so every repo is `core`-created with branch protection **on by default** —
|
||||||
|
merges restricted to the operators team + a required operators-team approval
|
||||||
|
(`apply_operator_branch_protection` / the config-repo equivalent) — and an
|
||||||
|
agent (a write collaborator, not a repo admin) can neither change those
|
||||||
|
settings nor merge its own PR. It is **not** set up for external VCS (GitHub
|
||||||
|
etc.), though — there, operator-merge is process + accepted risk, not a
|
||||||
|
technical control.
|
||||||
|
- **Approvals** — config changes, schedule additions, and other
|
||||||
|
blast-radius-y operations route through the operator approval queue
|
||||||
|
(see [`approvals.md`](approvals.md)).
|
||||||
|
|
||||||
|
### Capability = accepted risk
|
||||||
|
|
||||||
|
Every capability granted to an agent is a risk the operator is **explicitly
|
||||||
|
accepting**. The rule of thumb:
|
||||||
|
|
||||||
|
> **Don't give an agent access to something you can't afford to lose.**
|
||||||
|
|
||||||
|
If an agent can deploy to prod, you are accepting the risk of a dropped
|
||||||
|
production database (via injection or plain error). If that's unacceptable,
|
||||||
|
the answer is *don't grant the capability* — not "grant it and hope the
|
||||||
|
sandbox holds", because there is no sandbox between an agent and the tools you
|
||||||
|
handed it.
|
||||||
|
|
||||||
|
### No auto-sandboxing of external tokens
|
||||||
|
|
||||||
|
hyperhive provisions and scopes its **own** per-agent forge + matrix tokens.
|
||||||
|
It does **not** automatically sandbox or scope **external** credentials
|
||||||
|
(GitHub PATs, cloud keys, third-party API tokens). The scope of an external
|
||||||
|
token is **operator-accepted risk**: if you drop a broadly-scoped GitHub token
|
||||||
|
into an agent's config, that agent has exactly that reach, with no hyperhive
|
||||||
|
layer narrowing it. Scope external tokens tightly at the source (the external
|
||||||
|
provider) before handing them over.
|
||||||
|
|
||||||
## State-file endpoint security model
|
## State-file endpoint security model
|
||||||
|
|
||||||
`GET /api/state-file?path=<p>` serves files from agent state dirs and
|
`GET /api/state-file?path=<p>` serves files from agent state dirs and
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue