From c75604aa426ddb294a882233292874db76e301ee Mon Sep 17 00:00:00 2001 From: iris Date: Sun, 20 Sep 2026 05:20:57 +0200 Subject: [PATCH 1/4] docs: clear write-good.Passive hits in docs/swarm/sso.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 6 genuine passive-voice rewrites (actor named — the operator, secrets.md, swarm-authelia.nix, the person who introduced the split, swarmctl); 1 false positive left untouched — a quoted phrase + predicate adjective ('presenting as "the forge is broken"'). --- docs/swarm/sso.md | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/swarm/sso.md b/docs/swarm/sso.md index 4054d120..991471ad 100644 --- a/docs/swarm/sso.md +++ b/docs/swarm/sso.md @@ -1,9 +1,9 @@ # Swarm SSO The swarm runs one authelia, and it's two things at once: the **session -provider** every protected vhost checks (`auth_request`), and — once any -client is declared — an **OIDC provider** issuing tokens to relying -parties: the forge and the matrix homeserver. +provider** every protected vhost checks (`auth_request`), and — once an +operator declares any client — an **OIDC provider** issuing tokens to +relying parties: the forge and the matrix homeserver. The second role derives rather than switches: `services.hyperhive.swarm.authelia.oidc.clients` being non-empty turns it @@ -28,7 +28,7 @@ error reading the authentication database: could not validate the schema: users: non zero value required ``` -It then exits 1 and systemd restarts it, so a swarm that has been +It then exits 1 and systemd restarts it, so a swarm the operator has enabled but not bootstrapped shows a **crash-looping unit** and `502 Bad Gateway` from the vhost — not a login page with nobody able to use it. The gateway is working in that state; the upstream isn't up. @@ -95,8 +95,8 @@ an attribute edit can invalidate a login by accident. ## What secrets exist, and where each one lives -Every secret in the swarm, with its generator and its path, is tabulated -in one place: [`secrets.md`](secrets.md), including authelia's own keys +[`secrets.md`](secrets.md) tabulates every secret in the swarm, with its +generator and its path, in one place — including authelia's own keys (session, JWT, storage-encryption, OIDC HMAC, OIDC issuer) and the two halves of each client secret. That page's two rules — a secret is always a path, never a value, and the generator and the reader typically live in @@ -115,9 +115,11 @@ authelia's first boot to mint that client's secret and copies it into the service's container, and the service's own module contributes its client entry — callback URL included — to authelia's client list. -The callback is built once and read twice, so the redirect URI authelia -accepts and the one the service actually sends can't drift apart. A -mismatch there is a rejected login with no error text worth reading. +Each relying service's own glue module builds its callback once, and +both authelia's client list and the service's own OIDC client config +read that same value, so the redirect URI authelia accepts and the one +the service actually sends can't drift apart. A mismatch there is a +rejected login with no error text worth reading. ⚠️ The delivery is a copy, not a `bindMounts` entry, and deliberately so: nixos-container refuses to start a container whose bind source is @@ -238,7 +240,7 @@ upstream and compare three requests, not one: All three matter. A change that silently deleted the browser page would pass a deny-only check, and one that quietly stopped denying would pass a -page-only check. This was verified that way when the split was introduced. +page-only check. The person who introduced the split verified it this way. ## What this doesn't do @@ -254,6 +256,6 @@ page-only check. This was verified that way when the split was introduced. door, and a dead provider locks everyone out, the built-in admin included. That's why `swarm.grafana.oidc.role` defaults to `Admin`; see [`services.md`](services.md#metrics-victoriametrics--grafana). -- **It doesn't provision users.** Agents are created and destroyed - continuously, so the subject set belongs to a program rather than to a - config file; today that program is `swarmctl`. +- **It doesn't provision users.** `swarmctl` creates and destroys agents + continuously today, so the subject set belongs to a program rather + than to a config file. From ad16f3dba5282ef1589c1b2179758bb694734057 Mon Sep 17 00:00:00 2001 From: iris Date: Sun, 20 Sep 2026 05:22:46 +0200 Subject: [PATCH 2/4] docs: clear write-good.Passive hits in docs/agent-lifecycle/agent-hierarchy.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3 genuine passive-voice rewrites (actor named — the operator, hive-c0re, the harness); 5 false positives left untouched — a design-intent idiom ('are meant to run'), a predicate-adjective state description ('is finished'), a predicate-adjective necessity description ('is required', the original established example for this category), a project-planning-state description ('is planned'), and a quoted literal error string ('must be owned by uid 0...'). --- docs/agent-lifecycle/agent-hierarchy.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/agent-lifecycle/agent-hierarchy.md b/docs/agent-lifecycle/agent-hierarchy.md index 90276284..6fcafe2e 100644 --- a/docs/agent-lifecycle/agent-hierarchy.md +++ b/docs/agent-lifecycle/agent-hierarchy.md @@ -20,9 +20,9 @@ Topology lives in the hive-c0re-owned **meta repo**, alongside ``` `null` = root-level agent. New agents **default to root** — there is no -structural manager that everything hangs under. Hierarchy is built -explicitly: an agent gets a parent edge written before its first spawn, -or the operator reparents it afterwards (so `bob` above sits under +structural manager that everything hangs under. The operator builds +hierarchy explicitly: an agent gets a parent edge written before its +first spawn, or the operator reparents it afterwards (so `bob` above sits under `alice`). Any agent is reparentable, the bootstrap container (`ruth`) included — it's just another root. The manager is reparentable like any other agent; there's no "structurally root" carve-out. Its privileges live on its MCP socket, @@ -128,8 +128,8 @@ other agents don't: `/applied` (diff against what's deployed) and `/meta` (system-wide deploy log). Planned: each agent gets RW to `/agents//` for just its own subtree — the manager's full-forest RW becomes the - "root's subtree is everything" case of that same rule. RO `/meta` - access will be gated on a "meta read" capability; only + "root's subtree is everything" case of that same rule. hive-c0re will + gate RO `/meta` access on a "meta read" capability; only `request_update_meta_inputs` writes `flake.lock`, gated by its own capability. - **Prompt/tools** — the system prompt uses `` / @@ -151,8 +151,8 @@ Manager}` switch picks the MCP tool allow-list claude sees. Both are ancestor/descendant check instead of a manager-name check — see the module docs for `loose_ends.rs`, `stores/broker.rs`, `actions.rs`, and `workers/crash_watch.rs` for the current owner-check logic in - each. (Reminder cancellation is handled fully in-agent — see the note - on `CancelLooseEndKind::Reminder` in + each. (The harness handles reminder cancellation fully in-agent — see + the note on `CancelLooseEndKind::Reminder` in `hive-c0re/src/socket_server/mod.rs`.) None of the above is a stable interface — treat the module doc From 6f0985824c00e459a5b041fb39ab4b6de659eca7 Mon Sep 17 00:00:00 2001 From: iris Date: Sun, 20 Sep 2026 05:25:04 +0200 Subject: [PATCH 3/4] docs: clear write-good.Passive hits in docs/web-ui/shape.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 5 genuine passive-voice rewrites (actor named — DOMPurify.sanitize() x2, paintAtomic, the keyed pattern, the operator); 2 false positives left untouched — predicate-adjective state descriptions ('are unaffected', 'is gone'), same category as several already-established in this sweep. --- docs/web-ui/shape.md | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/docs/web-ui/shape.md b/docs/web-ui/shape.md index 38ad869b..c16d8030 100644 --- a/docs/web-ui/shape.md +++ b/docs/web-ui/shape.md @@ -37,10 +37,10 @@ `marked` (npm dep, replacing the vendored UMD bundle) and then `DOMPurify.sanitize()` before hitting `innerHTML` — untrusted row text (peer-agent / matrix-relayed message bodies, agent-authored - state files) can carry arbitrary HTML/script via markdown, so the - markdown path is sanitized rather than XSS-safe by construction the - way the text-node `linkify` path is. Rendered ``s are rewritten - to `target="_blank"`. + state files) can carry arbitrary HTML/script via markdown, so + `DOMPurify.sanitize()` cleans the markdown path rather than it being + XSS-safe by construction the way the text-node `linkify` path is. + Rendered ``s are rewritten to `target="_blank"`. - `GET /api/state` → JSON snapshot the JS app renders into the DOM. Includes a top-level `seq` (the dashboard event channel's high-water mark at the moment the server assembled the snapshot); @@ -192,9 +192,10 @@ inside a fresh text node, so the autolinker never touches trailing-punctuation strip keeps `.,;:` outside the link surface. Markdown bodies go through `marked` separately, then `DOMPurify.sanitize()` on the resulting HTML before it's assigned to -`innerHTML` (see `mdNode` in `app.js` / `common.js`) — this path is -sanitized, not text-node-safe like `linkify`, since markdown can -carry raw HTML. Rendered ``s get the same target rewrite. +`innerHTML` (see `mdNode` in `app.js` / `common.js`) — +`DOMPurify.sanitize()` cleans this path rather than it being +text-node-safe like `linkify`, since markdown can carry raw HTML. +Rendered ``s get the same target rewrite. The JS app handles all `form[data-async]` submissions via a delegated listener: read `data-confirm`, swap the button to a spinner, POST @@ -226,12 +227,12 @@ before returning. **Keyed DOM caching:** for sections whose rows hold interactive state (textarea drafts, checkboxes, focused inputs) `paintAtomic` isn't -enough — wiping and rebuilding still destroys the state even if the -flash is hidden. The keyed pattern keeps a `Map` -where the fingerprint is `JSON.stringify({...visible fields...})`. -On each render: cache-hit rows are reused verbatim (preserving -textarea draft, checkbox state, and event listeners); only cache-miss -rows are rebuilt and inserted. Used for: containers (`containerRowCache`), +enough — wiping and rebuilding still destroys the state even if +`paintAtomic` hides the flash. The keyed pattern keeps a +`Map` where the fingerprint is +`JSON.stringify({...visible fields...})`. On each render, the pattern +reuses cache-hit rows verbatim (preserving textarea draft, checkbox +state, and event listeners) and rebuilds only cache-miss rows. Used for: containers (`containerRowCache`), rebuild-queue entries (`rebuildQueueRowCache`). The spawn-form input+focus and meta-input checkboxes use a lighter snapshot-then-restore pattern (snapshot before `replaceChildren`, restore after) since they're single @@ -298,7 +299,7 @@ flood the journal. The per-agent UI's `bind_with_retry` than looping forever. The per-agent UI optionally binds a `UnixListener` instead of -TCP when `HIVE_WEB_SOCKET` is set — the unix-socket transition +TCP when the nix module sets `HIVE_WEB_SOCKET` — the unix-socket transition mechanics (per-agent `/run/hive-agent//` bind-mount, `.bound` marker filtering, `agent-sockets.json` consumer on the gateway side) live in [`docs/networking/gateway.md::Per-agent unix-socket From 2aa9f19ccb64b57854f6071f19123717c2582760 Mon Sep 17 00:00:00 2001 From: iris Date: Sun, 20 Sep 2026 05:27:26 +0200 Subject: [PATCH 4/4] docs: clear write-good.Passive hits in docs/trust-boundary/security.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2 genuine passive-voice rewrites (actor named — a caller, the hard-coded unit name); 5 false positives left untouched — a definitional/classificatory statement ('an agent is trusted code'), a predicate-adjective state description ('is privileged'), two quoted-literal framing phrases ('can the agent be stopped...', 'the whole hive is affected'), and the already-established 'is tracked as a TODO' pattern. --- docs/trust-boundary/security.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/trust-boundary/security.md b/docs/trust-boundary/security.md index 14007901..907726c8 100644 --- a/docs/trust-boundary/security.md +++ b/docs/trust-boundary/security.md @@ -236,7 +236,7 @@ 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 can't name the unit, so the verb can't be steered at another +a caller can't name the unit, so it can't steer the verb 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.** @@ -290,7 +290,7 @@ matching the agent-name convention (char-validated) or the known sibling service containers (`hive-forge`, `hive-matrix`, `hive-ci`), and rejects arbitrary container names. `hive-gateway` is a host unit, not a container, so it's not in this list — see `ReloadGatewayNginx` -above for how its access is scoped instead. +above for how the hard-coded unit name scopes its access instead. **Socket-activated** — systemd starts `hive-priv` on the first incoming connection (`LISTEN_FDS=1`); it's not running between calls.