docs: fix 329 Microsoft.Contractions hits with proper safety checks

Apply contraction fixes across ~40 doc files (setup, integrations, lifecycle,
networking, scheduler, swarm, tools, trust-boundary, UI, etc.). Skipped 14 hits:
- 10 where words appear in ALL CAPS for deliberate emphasis (is NOT, do NOT, etc.)
- 4 where text could not be safely located due to markdown formatting or column position

Applied via systematic scan with checks for fenced code blocks, inline code spans,
and intentional caps. Preserves sentence-initial capitalization throughout.
This commit is contained in:
iris 2026-09-07 14:28:23 +02:00 committed by mara
commit 78021ce982
38 changed files with 320 additions and 320 deletions

View file

@ -55,7 +55,7 @@ gateway, which fronts all operator traffic and is where operator auth lives,
and (b) network isolation, which keeps agents — and `hive-ci`'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 is a standing invariant: the `/api` must never be bound to a
above, but it's a standing invariant: the `/api` must never be bound to a
non-loopback address or exposed outside the gateway, and every new
operator-authority route inherits that assumption. `hive-ci` is treated like an
agent for this purpose — it runs untrusted PR code and is netns-isolated for
@ -97,7 +97,7 @@ exact same path, so the group grant always holds.
### the per-agent socket dir
`/run/hive-agent/<name>/` is shared by **three principals that share no
group**, which is why its mode is what it is:
group**, which is why its mode is what it's:
| principal | reaches | needs |
| ------------------- | ---------------------------------------- | ------------ |
@ -110,7 +110,7 @@ agent's container uid/gid** — `o=--x` is traverse without listing, and
both sockets are `0666`, which is all a dialer needs.
**Ownership is declared, not repaired.** The tmpfiles.d entry written by
`SyncAgentTmpfiles` names the uid/gid directly. Do not add a chown
`SyncAgentTmpfiles` names the uid/gid directly. don't add a chown
alongside it: `d` re-applies on every boot _and_ every agent
spawn/destroy, so ownership set afterwards is reverted the next time any
agent changes — which is exactly how this dir spent a long time at
@ -119,7 +119,7 @@ agent changes — which is exactly how this dir spent a long time at
The mode is load-bearing, not cosmetic. Write permission on a
_directory_ is what confers the right to unlink its entries, whoever owns
them, and the sticky bit is the only thing that would restrain that (it
is not set here). A world-writable socket dir therefore lets anything
isn't set here). A world-writable socket dir therefore lets anything
able to reach the path delete an agent's socket and bind its own — and
nginx reaches all of `/run/hive-agent` as a plain host path. Dropping
`o=w` removes that permission rather than qualifying it.
@ -142,11 +142,11 @@ This dir has exactly one writer, so it needs no world write at all.
`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 is a real trust
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 is effectively **root-only** until an operator is
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.c0re.adminUsers`; each is added to
`hive-admin`, and members connect without `sudo`. The runtime dir

View file

@ -4,14 +4,14 @@
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 is the reference for "is it safe to give an
serve: **what hyperhive defends, what it deliberately doesn't, and where the
operator is accepting risk.** it's 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
boundary that matters is the container: a sub-agent can't 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
@ -20,10 +20,10 @@ 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.)
boundary; they don't 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
touching its credentials". Reason about it as "what's the blast radius if this
agent does the worst possible thing with everything it can reach".**
### Scoped tokens bound the blast radius
@ -31,7 +31,7 @@ agent does the worst possible thing with everything it can reach".**
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`).
collaborator grants; can't 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
@ -66,7 +66,7 @@ checkpoints**, not about sandboxing the agent from its own tools:
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
settings nor merge its own PR. it's **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
@ -107,7 +107,7 @@ before touching the filesystem:
(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
`/state/...` without an agent prefix is explicitly _not_ accepted — it's
ambiguous from the host's perspective.
Defense-in-depth layers (in order):
@ -145,7 +145,7 @@ before building `agent_notes_dir(name)`, so a `..` component can't traverse.
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. the agent modules set `sandbox-fallback = true`
can't set up its build sandbox. the agent modules set `sandbox-fallback = true`
so that builds fall back to unsandboxed execution rather than failing outright.
### Threat model
@ -156,22 +156,22 @@ any file in the container that the nixbld user can read.
The blast radius also has a **network** dimension. hive-ci runs its unsandboxed
builds of untrusted PR code in its own private netns behind the hive bridge: a
build reaches the forge only through the gateway and cannot reach host-loopback
build reaches the forge only through the gateway and can't reach host-loopback
services — including the core dashboard at `127.0.0.1:<dashboard_port>`, which
has no application-layer auth of its own (see [`docs/scheduler/ci.md`](../scheduler/ci.md)). The `0600`
token policy bounds file reads; network isolation bounds network reach.
**What is NOT exposed**:
**what's NOT exposed**:
- `/home/<name>/.claude/` — mode `0700`, owned by the per-agent
user `<name>`. nixbld users cannot read it.
user `<name>`. nixbld users can't read it.
- `$HYPERHIVE_STATE_DIR/forge-token` (= `/agents/<name>/state/forge-token`)
— written at mode `0600` and chowned to the per-agent uid:gid (see
`hive-c0re/src/forge/mod.rs`'s module doc for exactly where). nixbld users
cannot read it.
can't 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.
`0600` or stricter. don't create world-readable secret files in agent state dirs.
### Long-term fix
@ -185,14 +185,14 @@ bind-mount compatibility with user namespace UID mapping and is tracked as a TOD
### Background
`hive-c0re` runs as the unprivileged system user `hive-core`
(`/var/lib/hyperhive` owned by `hive-core:hive-core`). It cannot
(`/var/lib/hyperhive` owned by `hive-core:hive-core`). It can't
directly invoke `nixos-container`, `journalctl -M`, or act on a system
unit (`systemctl reload nginx`) — those require root. `hive-priv` fills
this gap.
⚠️ **`ReloadGatewayNginx` acts on a host unit, so nothing implicitly
scopes it.** Its containment is the unit name hard-coded in `hive-priv`:
a caller cannot name the unit, so the verb cannot be steered at another
a caller can't name the unit, so the verb can't be steered at another
service. **A privileged verb needs something bounding what it can act
on; when that isn't a namespace, it has to be a constant the caller
can't supply.**
@ -228,12 +228,12 @@ known operations; there is no arbitrary command pass-through:
allowlist before any operation: only names matching the agent-name
convention (char-validated) or the known sibling service containers
(`hive-forge`, `hive-matrix`, `hive-ci`) are accepted. `hive-gateway` is
a host unit, not a container, so it is not in this list — see
a host unit, not a container, so it's not in this list — see
`ReloadGatewayNginx` above for how its access is scoped instead.
Arbitrary container names are rejected.
**Socket-activated** — systemd starts `hive-priv` on the first
incoming connection (`LISTEN_FDS=1`); it is not running between calls.
incoming connection (`LISTEN_FDS=1`); it's not running between calls.
The `ProtectSystem=strict` + `ReadWritePaths` sandbox limits filesystem
writes to only the paths `hive-priv` legitimately needs.