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:
parent
77296aff35
commit
78021ce982
38 changed files with 320 additions and 320 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue