docs: suppress reviewed write-good.Passive false positives

133 hits across 38 files, all previously classified during #4548's sweep
and deliberately left un-rewritten (predicate-adjective state/necessity
description, design-intent idiom, structural/type-description idiom,
no-single-actor topology claim, parallel-triple exception, vale
substring-match artifact — see hyperhive#4548's per-PR bodies for the
per-hit reasoning).

Wraps each one in a scoped <!-- vale write-good.Passive = NO/YES -->
pair (the supported mechanism — TokenIgnores has a known offset-drift
bug) rather than a blanket per-file or per-rule silence, so a *new*
passive-voice hit anywhere in these files still fails once the rule
gates CI (next commit). Table/list false positives (docs/swarm/credentials.md's
renewal-table cells) wrap the whole block, not each cell.

Part of #4546.
This commit is contained in:
iris 2026-09-20 15:07:46 +02:00
commit 04e27c4fb6
38 changed files with 368 additions and 71 deletions

View file

@ -40,6 +40,8 @@ services.hyperhive = {
};
```
<!-- vale write-good.Passive = NO -->
`swarm.domain` and `hiveName` are **required** whenever hyperhive is
enabled; eval fails with a hint naming each. Neither defaults,
because a guessed value here is a wrong hostname that evaluates cleanly
@ -47,7 +49,9 @@ and deploys — an eval failure asking the operator to write the address
down is the cheaper outcome. **Upgrading past this release means setting
both once.**
`domain` is required too, but you no longer _write_ it: it's read from
<!-- vale write-good.Passive = YES -->
You must still set `domain` too, but you no longer _write_ it: it's read from
this hive's own entry in the directory, whose `domain` defaults to
`<name>.<swarm.domain>`. A conventional swarm states no addresses at
all, and a hive addressed by something else states it in the one place
@ -276,8 +280,8 @@ If one host is behind NAT and can't accept incoming connections, only
that host needs a null `wireguardEndpoint` on the peer config — the
other side initiates. With keepalive on, the NAT hole stays open.
If both hosts are behind NAT, a STUN relay or a third host (exit node)
is required. Out of scope for v0.
If both hosts are behind NAT, you need a STUN relay or a third host
(exit node). Out of scope for v0.
## Snapshot store
@ -458,12 +462,16 @@ leftover at startup. A webhook has exactly one target URL, so per-hive
registration never added a recipient — it took delivery away from
whichever hive registered before it.
<!-- vale write-good.Passive = NO -->
**The `agent-configs` org isn't yet.** Each hive still registers its own
`pull_request` hook there, so that repo has two — the hive's and the
controller's — and **both are expected; don't delete either.** Removing
a hive's stops it acting on config PRs; removing the controller's just
gets recreated on its next start.
<!-- vale write-good.Passive = YES -->
Nothing to configure. The controller registers the hooks only when this
host also serves the swarm UI vhost — that's what publishes the
endpoint, and a hook the forge can't reach would collect failed

View file

@ -71,9 +71,13 @@ its own subdomain — enforced by every verifier rather than by
convention. The constraint excludes both IP families as well, since a
permitted-DNS-only constraint says nothing about IP SANs.
<!-- vale write-good.Passive = NO -->
The root is issued with `pathlen:1`: it may sign hive CAs, and those may
sign leaves, and the chain stops there.
<!-- vale write-good.Passive = YES -->
## What to hand a peer
`hivectl peer-config` prints the `services.hyperhive.swarm.hives."<name>"`
@ -103,6 +107,8 @@ per-hive file.
## Adopting the hierarchy on an existing hive
<!-- vale write-good.Passive = NO -->
A hive that predates the swarm root carries a self-signed `ca.pem`, and
adopting the hierarchy means replacing it. That invalidates an anchor
consumers already trust, and they refresh on their own schedule — agents
@ -110,6 +116,8 @@ only pick up new trust when their container restarts, peers only on
their own rebuild. **Who is allowed to decide that's what splits the
two cases.**
<!-- vale write-good.Passive = YES -->
### Where this host owns the root (`autoConfigure`)
Adoption happens by itself, once. `hive-tls-ca.service` notices that
@ -141,10 +149,14 @@ rm <deploy.hive-controller.tls.stateDir>/ca.pem <deploy.hive-controller.tls.stat
systemctl restart hive-tls-ca.service
```
<!-- vale write-good.Passive = NO -->
Failing rather than warning is deliberate: a hive whose CA doesn't
chain to the root it received is misconfigured, and a warning in a
build log isn't something anyone reads twice.
<!-- vale write-good.Passive = YES -->
To keep the current CA on purpose — a hive that deliberately stays
outside the hierarchy, or one mid-migration — `touch` the marker file
named in the message. That's a decision, and it's recorded as one.

View file

@ -1,11 +1,15 @@
# Credentials: the target shape
<!-- vale write-good.Passive = NO -->
The swarm's credential store is bao. This page describes the shape every
credential is meant to have — who mints it, who reads it, and how it renews
— not what's on disk today. [`secrets.md`](secrets.md) remains the map of
the files that exist right now; this page replaces it, and `secrets.md` gets
deleted, once the swarm's credential path matches what's described below.
<!-- vale write-good.Passive = YES -->
**Public material is a value.** The store hands a certificate or a public
nkey to every client that connects, so it's a fine place for that material.
Nothing below is about those.
@ -48,6 +52,8 @@ path at runtime — not a path on disk, and not a unit whose job is to turn a
store value into a file. A renewal cell may never read `NONE`: state the
strategy for every credential, including the mTLS leaf.
<!-- vale write-good.Passive = NO -->
| store path | minter | reader — pulls at runtime, holds in memory | renewal |
| -------------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- | -------------- |
| `swarm/agents/<agent>/matrix/<account>` | `swarm-controller` | the agent container itself, under the certificate its hive passed in | must be stated |
@ -57,6 +63,8 @@ strategy for every credential, including the mTLS leaf.
| `swarm/services/<clientId>/oidc/client` | authelia | the service process that presents the client secret, under the certificate of the host it runs on | must be stated |
| _(not in the store)_ a hive's mTLS leaf | the store's own PKI, or an operator placing it by hand | its own client, off disk — the exception above, because it's what makes every other row's pull possible | must be stated |
<!-- vale write-good.Passive = YES -->
**An agent's mTLS leaf is in the store; a hive's isn't, and the difference
isn't an inconsistency.** The rule the exception protects is that nothing
can fetch from the store the credential it would need in order to fetch. A

View file

@ -118,7 +118,7 @@ eval, naming the option it wants. That's deliberate: a queue that started
without them would accept `CONNECT {"user":"auth"}` from anyone sharing the
host's network namespace, and nothing would look wrong until somebody connected.
**All four or none** — the seed paths are required too, not just the public
**All four or none** — you must supply the seed paths too, not just the public
keys. They're two halves of the same pair: the server verifies with the public
half, the responder signs with the private one. Supplying only the public keys
used to pass eval and leave the queue with an auth-callout nobody answers, which
@ -195,7 +195,7 @@ the client id it authenticates, because that's how a queue client reads them —
the secret by path, the id by value. No local fallback exists, and none is
possible, so absent files mean this hive's agents don't connect, which is what
a swarm looks like before the publisher on the authelia host has run. The
reader runs before `hive-c0re.service` and is wanted (not required) by it, so
reader runs before `hive-c0re.service`, which wants it (doesn't require it), so
an agent container never renders ahead of the credential; an unreachable store
delays the daemon's start rather than failing it.
@ -231,9 +231,9 @@ exists so a host holds **one** out-of-band secret, its client certificate, and
reads everything else with it.
Two things follow, and `swarm-grafana.nix` asserts both rather than degrading:
running Grafana requires `swarm.authelia.url` (its local login form is disabled
unconditionally, so SSO isn't a feature of some topologies — it's the only way
in), and it requires this host's `deploy.bao.clientCertFile` /
running Grafana requires `swarm.authelia.url` (this module disables its local
login form unconditionally, so SSO isn't a feature of some topologies — it's the
only way in), and it requires this host's `deploy.bao.clientCertFile` /
`clientKeyFile`. Each refusal names the option to set. Both used to be silent:
a null URL dropped the OIDC block, a missing leaf produced a warning, and
either one left a Grafana with no SSO and no password box — nothing failed, so

View file

@ -118,12 +118,16 @@ See `retentionPeriod` below before leaving it at its default.
| `swarm.grafana.oidc.role` | Default `Admin` for everyone who logs in. Lower to `Viewer`/`Editor` if the swarm grows operators who shouldn't be able to reconfigure Grafana. |
| `deploy.grafana.datasourceUrl` | Only if you front VictoriaMetrics with something else. It defaults to the store on this host, which is the only thing it can reach. |
<!-- vale write-good.Passive = NO -->
**Logging in.** Grafana is behind swarm SSO, so the accounts are the
authelia ones — there is no separate Grafana password, and this module
switches off the local login form whenever SSO is configured. If you enable
Grafana on a host with no authelia, the form stays on and Grafana's
default `admin`/`admin` applies; change it before exposing that host.
<!-- vale write-good.Passive = YES -->
**Where the data comes from.** The swarm's OTEL collector, below.
Neither container is reachable except through the gateway: both bind

View file

@ -121,6 +121,8 @@ 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.
<!-- vale write-good.Passive = NO -->
⚠️ The delivery is a copy, not a `bindMounts` entry, and deliberately so:
nixos-container refuses to start a container whose bind source is
missing, and this secret doesn't exist until authelia's first boot has
@ -128,6 +130,8 @@ run. Binding it would make the service wait on a file that waits on a
container that starts after it — on a fresh hive, a permanent stall
presenting as "the forge is broken," many layers from its cause.
<!-- vale write-good.Passive = YES -->
### 2. Swarm-managed services
The controller side owns provisioning: `swarmctl` writes both halves, the

View file

@ -38,6 +38,8 @@ fine and still gets bounced.
swarmctl user add <you> --group admins
```
<!-- vale write-good.Passive = NO -->
`admins` deliberately, not a new word: [`../getting-started/setup.md`](../getting-started/setup.md) has
told every operator to create exactly that group since the bootstrap step
existed, so an account made by following the guide already passes. This
@ -45,6 +47,8 @@ is the first rule that _consumes_ a group name — inventing a second one
would have meant those accounts silently failing a check they were
supposed to pass.
<!-- vale write-good.Passive = YES -->
An account created without any group needs re-adding with the flag —
`swarmctl` reads the existing entry out of `users.yml,` so the group is
what changes.
@ -73,6 +77,8 @@ a container":
| vhost + `gateway.localNames` | the service's own module (for example `nix/host-modules/swarm-ui.nix`) |
| **certificate name** | `nix/host-modules/swarm.nix` (`serviceDomains`) |
<!-- vale write-good.Passive = NO -->
The DNS record and the local-dev `/etc/hosts` entry need no separate
edit: both derive from `services.hyperhive.gateway.localNames`,
which a service's own module already has to push its domain into to be
@ -82,6 +88,8 @@ the surface the hive's own domain serves (dashboard, per-agent routing,
matrix discovery); a swarm service declares its own vhost next to its
own options, the way `swarm-ui.nix` and `swarm-authelia.nix` do.
<!-- vale write-good.Passive = YES -->
⚠️ The certificate one is the hardest to predict and the most visible when
missed. `serviceDomains` is _both_ the services sub-CA's
`nameConstraints` set and the leaf's SAN list, and the apex is a