Watch
0
0
Fork
You've already forked hyperhive
0

Compare commits

..
Author SHA1 Message Date
iris
2aa9f19ccb docs: clear write-good.Passive hits in docs/trust-boundary/security.md
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.
2026-09-20 13:24:24 +02:00
iris
6f0985824c docs: clear write-good.Passive hits in docs/web-ui/shape.md
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.
2026-09-20 13:24:24 +02:00
iris
ad16f3dba5 docs: clear write-good.Passive hits in docs/agent-lifecycle/agent-hierarchy.md
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...').
2026-09-20 13:24:24 +02:00
iris
c75604aa42 docs: clear write-good.Passive hits in docs/swarm/sso.md
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"').
2026-09-20 13:24:24 +02:00
4 changed files with 39 additions and 36 deletions

View file

@ -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 `null` = root-level agent. New agents **default to root** — there is no
structural manager that everything hangs under. Hierarchy is built structural manager that everything hangs under. The operator builds
explicitly: an agent gets a parent edge written before its first spawn, hierarchy explicitly: an agent gets a parent edge written before its
or the operator reparents it afterwards (so `bob` above sits under first spawn, or the operator reparents it afterwards (so `bob` above sits under
`alice`). Any agent is reparentable, the bootstrap container (`ruth`) `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 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, "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 `/applied` (diff against what's deployed) and `/meta` (system-wide
deploy log). Planned: each agent gets RW to `/agents/<descendant>/` deploy log). Planned: each agent gets RW to `/agents/<descendant>/`
for just its own subtree — the manager's full-forest RW becomes the 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` "root's subtree is everything" case of that same rule. hive-c0re will
access will be gated on a "meta read" capability; only gate RO `/meta` access on a "meta read" capability; only
`request_update_meta_inputs` writes `flake.lock`, gated by its own `request_update_meta_inputs` writes `flake.lock`, gated by its own
capability. capability.
- **Prompt/tools** — the system prompt uses `<!-- role:agent -->` / - **Prompt/tools** — the system prompt uses `<!-- role:agent -->` /
@ -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 ancestor/descendant check instead of a manager-name check — see the
module docs for `loose_ends.rs`, `stores/broker.rs`, `actions.rs`, module docs for `loose_ends.rs`, `stores/broker.rs`, `actions.rs`,
and `workers/crash_watch.rs` for the current owner-check logic in and `workers/crash_watch.rs` for the current owner-check logic in
each. (Reminder cancellation is handled fully in-agent — see the note each. (The harness handles reminder cancellation fully in-agent — see
on `CancelLooseEndKind::Reminder` in the note on `CancelLooseEndKind::Reminder` in
`hive-c0re/src/socket_server/mod.rs`.) `hive-c0re/src/socket_server/mod.rs`.)
None of the above is a stable interface — treat the module doc None of the above is a stable interface — treat the module doc

View file

@ -1,9 +1,9 @@
# Swarm SSO # Swarm SSO
The swarm runs one authelia, and it's two things at once: the **session The swarm runs one authelia, and it's two things at once: the **session
provider** every protected vhost checks (`auth_request`), and — once any provider** every protected vhost checks (`auth_request`), and — once an
client is declared — an **OIDC provider** issuing tokens to relying operator declares any client — an **OIDC provider** issuing tokens to
parties: the forge and the matrix homeserver. relying parties: the forge and the matrix homeserver.
The second role derives rather than switches: The second role derives rather than switches:
`services.hyperhive.swarm.authelia.oidc.clients` being non-empty turns it `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 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 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. 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. 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 ## What secrets exist, and where each one lives
Every secret in the swarm, with its generator and its path, is tabulated [`secrets.md`](secrets.md) tabulates every secret in the swarm, with its
in one place: [`secrets.md`](secrets.md), including authelia's own keys generator and its path, in one place — including authelia's own keys
(session, JWT, storage-encryption, OIDC HMAC, OIDC issuer) and the two (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 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 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 service's container, and the service's own module contributes its client
entry — callback URL included — to authelia's client list. entry — callback URL included — to authelia's client list.
The callback is built once and read twice, so the redirect URI authelia Each relying service's own glue module builds its callback once, and
accepts and the one the service actually sends can't drift apart. A both authelia's client list and the service's own OIDC client config
mismatch there is a rejected login with no error text worth reading. 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: ⚠️ The delivery is a copy, not a `bindMounts` entry, and deliberately so:
nixos-container refuses to start a container whose bind source is 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 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 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 ## 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 door, and a dead provider locks everyone out, the built-in admin
included. That's why `swarm.grafana.oidc.role` defaults to `Admin`; see included. That's why `swarm.grafana.oidc.role` defaults to `Admin`; see
[`services.md`](services.md#metrics-victoriametrics--grafana). [`services.md`](services.md#metrics-victoriametrics--grafana).
- **It doesn't provision users.** Agents are created and destroyed - **It doesn't provision users.** `swarmctl` creates and destroys agents
continuously, so the subject set belongs to a program rather than to a continuously today, so the subject set belongs to a program rather
config file; today that program is `swarmctl`. than to a config file.

View file

@ -236,7 +236,7 @@ this gap.
⚠️ **`ReloadGatewayNginx` acts on a host unit, so nothing implicitly ⚠️ **`ReloadGatewayNginx` acts on a host unit, so nothing implicitly
scopes it.** Its containment is the unit name hard-coded in `hive-priv`: 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 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 on; when that isn't a namespace, it has to be a constant the caller
can't supply.** 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`), sibling service containers (`hive-forge`, `hive-matrix`, `hive-ci`),
and rejects arbitrary container names. `hive-gateway` is a host unit, and rejects arbitrary container names. `hive-gateway` is a host unit,
not a container, so it's not in this list — see `ReloadGatewayNginx` 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 **Socket-activated** — systemd starts `hive-priv` on the first
incoming connection (`LISTEN_FDS=1`); it's not running between calls. incoming connection (`LISTEN_FDS=1`); it's not running between calls.

View file

@ -37,10 +37,10 @@
`marked` (npm dep, replacing the vendored UMD bundle) and then `marked` (npm dep, replacing the vendored UMD bundle) and then
`DOMPurify.sanitize()` before hitting `innerHTML` — untrusted row `DOMPurify.sanitize()` before hitting `innerHTML` — untrusted row
text (peer-agent / matrix-relayed message bodies, agent-authored text (peer-agent / matrix-relayed message bodies, agent-authored
state files) can carry arbitrary HTML/script via markdown, so the state files) can carry arbitrary HTML/script via markdown, so
markdown path is sanitized rather than XSS-safe by construction the `DOMPurify.sanitize()` cleans the markdown path rather than it being
way the text-node `linkify` path is. Rendered `<a>`s are rewritten XSS-safe by construction the way the text-node `linkify` path is.
to `target="_blank"`. Rendered `<a>`s are rewritten to `target="_blank"`.
- `GET /api/state` → JSON snapshot the JS app renders into the - `GET /api/state` → JSON snapshot the JS app renders into the
DOM. Includes a top-level `seq` (the dashboard event channel's DOM. Includes a top-level `seq` (the dashboard event channel's
high-water mark at the moment the server assembled the snapshot); 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. trailing-punctuation strip keeps `.,;:` outside the link surface.
Markdown bodies go through `marked` separately, then Markdown bodies go through `marked` separately, then
`DOMPurify.sanitize()` on the resulting HTML before it's assigned to `DOMPurify.sanitize()` on the resulting HTML before it's assigned to
`innerHTML` (see `mdNode` in `app.js` / `common.js`) — this path is `innerHTML` (see `mdNode` in `app.js` / `common.js`) —
sanitized, not text-node-safe like `linkify`, since markdown can `DOMPurify.sanitize()` cleans this path rather than it being
carry raw HTML. Rendered `<a>`s get the same target rewrite. text-node-safe like `linkify`, since markdown can carry raw HTML.
Rendered `<a>`s get the same target rewrite.
The JS app handles all `form[data-async]` submissions via a delegated The JS app handles all `form[data-async]` submissions via a delegated
listener: read `data-confirm`, swap the button to a spinner, POST 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 **Keyed DOM caching:** for sections whose rows hold interactive state
(textarea drafts, checkboxes, focused inputs) `paintAtomic` isn't (textarea drafts, checkboxes, focused inputs) `paintAtomic` isn't
enough — wiping and rebuilding still destroys the state even if the enough — wiping and rebuilding still destroys the state even if
flash is hidden. The keyed pattern keeps a `Map<id, {el, fingerprint}>` `paintAtomic` hides the flash. The keyed pattern keeps a
where the fingerprint is `JSON.stringify({...visible fields...})`. `Map<id, {el, fingerprint}>` where the fingerprint is
On each render: cache-hit rows are reused verbatim (preserving `JSON.stringify({...visible fields...})`. On each render, the pattern
textarea draft, checkbox state, and event listeners); only cache-miss reuses cache-hit rows verbatim (preserving textarea draft, checkbox
rows are rebuilt and inserted. Used for: containers (`containerRowCache`), 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 rebuild-queue entries (`rebuildQueueRowCache`). The spawn-form input+focus and meta-input
checkboxes use a lighter snapshot-then-restore pattern (snapshot checkboxes use a lighter snapshot-then-restore pattern (snapshot
before `replaceChildren`, restore after) since they're single 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. than looping forever.
The per-agent UI optionally binds a `UnixListener` instead of 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/<name>/` bind-mount, mechanics (per-agent `/run/hive-agent/<name>/` bind-mount,
`.bound` marker filtering, `agent-sockets.json` consumer on the `.bound` marker filtering, `agent-sockets.json` consumer on the
gateway side) live in [`docs/networking/gateway.md::Per-agent unix-socket gateway side) live in [`docs/networking/gateway.md::Per-agent unix-socket