Three leftovers from the rename, plus the gating prose job.
`nix/packages/default.nix` still described the minter as the "matrix
admin credential's minter", and shipped that claim in the package's
`meta.description` — a PR-visible string.
`promote_user_to_admin`'s doc comment pointed at the `admin_execute`
entry in `hive-matrix.nix` as the thing that bootstraps the first
admin. That entry is gone, so the comment referenced nothing. It now
records that the account is ordinary, that the call therefore has no
working sender, and that rehoming at swarm level is the fix rather than
re-granting. `reset_user_password` gained the matching warning; it had
none.
The prose fixes clear all 8 `CI / prose lint (vale, errors)` failures,
all of which were in docs this branch touches. No vale config change,
no exception, no carve-out: contractions, one recast sentence, one
de-hyphenation and one dropped "simply".
Tense: four docs described system behaviour in the future ("will
refuse", "will fetch and trust"). Reference docs get read from the
other side of the change, so they say what the system does.
`swarm/services/matrix/sender-token` has a real minter
(`swarm-matrix-minter`, in the `hive-matrix` container) and two
readers now: the minter's own pre-flight read-back before it mints,
and hive-c0re's `stored_sender_token()` under the hive's own
certificate. The credentials table catalogs minter/reader/renewal for
every credential path; this one was missing a row.
Refs #4345
A swarm runs one homeserver and a homeserver has one appservice sender
account, so "mint it once" is a property of the thing being minted
rather than something a lock has to enforce. That is what makes this
account the one to move first: no trigger route, no controller change
and no agent list — a boot-time oneshot beside tuwunel is the whole
mechanism.
`swarm-matrix-minter` runs inside `containers.hive-matrix`, which
already holds the appservice token: the rendered registration is bound
in read-only because that is how tuwunel is handed it. What the
container lacked was an identity of its own, so this adds one — a leaf
from the store's CA with a grant of exactly one path, not the hive's
leaf, which reads every secret in the store.
Both ends of the credential ship here. The minter reads the path it
publishes to before it touches the homeserver, and returning on a
non-empty read IS the "only once"; `hive-c0re`'s `ensure_hive_user`
reads the same path, authenticating with the hive name already in
`HYPERHIVE_HIVE_NAME`. The existing mint-then-`M_USER_IN_USE`-login
ladder stays as the fallback for a store that is empty, unconfigured or
unreachable, which is every swarm deployed before this — so nothing
needs backfilling and nothing breaks if the rest of the sequence never
lands.
The credential is not an admin credential, and is not named like one.
It is the access token of the appservice registration's own
`sender_localpart` — `@hive:<server_name>`, an account the homeserver
creates for itself when it loads the registration. The store path is
`swarm/services/matrix/sender-token`, the host path is
`matrix/access-token`, and the homeserver no longer runs an
`admin_execute` promotion for that account at boot. Everything the hive
provisions with it — the Space, the chat room, their hierarchy and join
rules, the invites — rides on being the creator of those rooms at power
level 100, not on homeserver admin; there is no Synapse admin API here
to need, tuwunel has none.
Two operations do need an admin *sender* and therefore stop working:
`hivectl matrix promote-user` and `hivectl matrix reset-password`, both
`!admin …` messages into `#admins:<server>`, plus the password-reset
recovery path that an agent with a lost password file falls back to.
They are swarm-level operations and are left failing loudly rather than
served by an over-privileged token every other call site would also
carry. The sweep's own admin-rights check and self-repair go with them:
an account that is deliberately not an admin has nothing to check.
`ephemeral = false` stays, and hive root can still read the container's
filesystem. Accepted: what this buys is identity separation — no hive
*process* holds or reads the appservice token — not physical isolation.
Refs #4345
2 real active-voice rewrites (per mara: main picked these up via a
rebase+merge that landed after write-good.Passive went error-gate,
never checked against the current rule):
- "The datasource is told instead" -> nix/host-modules/swarm-grafana.nix
tells the datasource instead.
- "`value` is compared with `===`" -> the builder compares `value`
with `===`.
No suppressions, no content dropped.
The comment moved from swarm-grafana.nix into observability.md by the
previous commit became visible to vale (it doesn't lint nix comments).
Reword six flagged spots (contractions, a sentence-initial 'So', a
period outside quotes) without dropping any fact.
The 41-line comment on logLevelRules tripped the comment-block lint
(30-line max). Move the detailed walkthrough (why each rule shape is
what it is, the three silent-failure modes, the query a level button
emits) into docs/scheduler/observability.md's existing 'Log severity'
section, which already covered the mapping at a higher level. The nix
comment now carries the short why/contract and points at the doc for
the full detail — no information dropped, just relocated.
Grafana's log-level buttons filter on a field called `level`; no row in
the store has one. The store's name for it is `severity_text`, chosen by
VictoriaLogs' OTLP ingester rather than by us — v1.52.0's
`app/vlinsert/opentelemetry/pb.go` writes it unconditionally and the
ingest parameters have no `_level_field` to rename it with. So the
mapping is made on the reader: the VictoriaLogs datasource, which was
provisioned with no `jsonData` at all, now carries a `logLevelRules`
entry per severity the journald parser can emit.
`logLevelRules` is the datasource plugin's only level-related jsonData
key — there is no field-name setting and no OpenTelemetry preset to
switch on. It is read off `instanceSettings.jsonData` in the plugin's
`datasource.ts` and typed in its `configuration/LogLevelRules/types.ts`,
both recovered from the sourcemap shipped in the pinned artifact
(`grafanaPlugins.victoriametrics-logs-datasource` 0.26.3). Each enabled
rule appends an `OR severity_text:="INFO"`-shaped term to the query a
level button emits, next to the `level:…` term that matches nothing.
A wrong rule here fails silently: Grafana provisions unknown jsonData
without complaint and the buttons go on returning zero rows. The three
ways to get one wrong — a non-literal `enabled`, a non-canonical `level`
spelling, a value that is not the stored text — are recorded at the
binding, and a module-eval arm pins them along with the real failure
mode, a severity added to `nix/journald-severity.nix` and not here.
`Unspecified` is left unmapped on purpose: it is the store's own
rendering of an absent severity and the thing the logs dashboard's "no
severity" panel counts.
Refs #4560
Both blockers are done — #4548 (353 hits in hand-written docs) and
#4549 (46 hits in generated CLI reference docs, hivectl/swarmctl/hive-forge's
own clap help text) — and the previous commit suppresses the reviewed
false-positive remainder. vale --minAlertLevel=error docs now returns
0 errors, matching CI's existing prose-lint-errors job.
Closes#4546.
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.
4 genuine passive-voice rewrites (actor named — you, the swarm, nix);
3 false positives left untouched — a design-constraint description
with no single correct actor across both provisioning modes ('is
issued with pathlen:1'), a governance/definitional statement ('who is
allowed to decide'), and the already-established predicate-adjective
classification ('is misconfigured').
5 genuine passive-voice rewrites (actor named — create-user, hivectl,
the operator, claude, you the CLI user); 2 false positives left
untouched — predicate-adjective necessity descriptions ('is needed',
'is required'), same category already established across this sweep.
Rewrites every write-good.Passive hit in the hive-forge clap help text
into terse, imperative, active voice (meaning unchanged) and drops
clap-markdown's own fixed footer ('This document was generated
automatically by...') via MarkdownOptions::show_footer(false), since
that string isn't ours to reword and vale flagged it too.
docs/tools/{hivectl,swarmctl,forge}-cli.md are generated from each
crate's clap tree (see hive-forge/src/main.rs's MarkdownDocs verb) —
regenerated here from the fixed source, not hand-edited.
Refs #4549
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.
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.
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...').
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"').
get_loose_ends now always returns the caller's own loose ends, for every
caller including the manager (ruth) — there is no separate manager
surface, ruth is a normal agent with different default capabilities.
- AgentGetLooseEndsArgs removed; get_loose_ends takes no args.
- Wire Request::GetLooseEnds collapses from an Option<String> target to
a unit variant.
- hive-c0re's handle_get_loose_ends drops the "*" hive-wide branch and
the subtree/capability resolver (resolve_agent_state_target); both
are gone since there is no longer a target to resolve.
- loose_ends::hive_wide and Capability::QueryAgentState removed as
dead code — their only callers were the two functions above.
- is_descendant_of is untouched (still used by lifecycle_handlers.rs
and schedules.rs independently of this change).
- Docs updated: docs/turn-loop/mcp.md, docs/web-ui/dashboard.md,
docs/process/conventions.md (Loose-ends wire shape + capabilities
table), plus the doc comments in hive-core-agent-sock, mcp_config.rs
and capabilities.rs that described the old shape.
Refs #4480
6 genuine passive-voice rewrites (actor named — the operator,
hive-c0re, the daemon; two converted to active/elliptical-imperative
phrasing matching the bullet list's own established voice); 2 false
positives left untouched — 'been invited' (an agent-perspective
description of its own pending invites, no single inviter is the
point) and 'is keyed' (a structural/type-description idiom, same
category as coordinator.md's 'is typed').
6 genuine passive-voice rewrites (actor named — the core daemon,
hyperhive, the adminUsers option; one flipped subject to match the
table it introduces; two converted to active/imperative phrasing);
2 false positives left untouched — the already-established 'is
tracked as [area:ops] issues' pattern, and a design-intent
contrastive idiom ('ownership is declared, not repaired') matching
the unflagged sibling phrase 'a DAG is declared, not described' in
coordinator.md.
5 genuine passive-voice rewrites (actor named — nixos-container
create; the passive construction replaced with an active
intransitive verb or 'nothing can X' phrasing matching the pattern
already established on credentials.md); 4 false positives left
untouched — a type-description idiom ('is typed'), a predicate-
adjective in a parallel triple ('is satisfied' alongside two
untouched siblings), and two already-established categories
('is unaffected', 'is tracked' as a follow-up).
5 genuine passive-voice rewrites (actor named — pkcs11, the operator,
the minting policy; one converted to a runbook imperative; one
converted to an active 'gate on' verb); 5 false positives left
untouched — predicate-adjective necessity/security-property
descriptions ('is needed', 'is stored', 'is authenticated', same
category as the already-established 'is required'), a
project-tracking-state description ('is tracked'), and a topology
claim with no single correct actor to name ('is generated where it's
read' — which component mints a given credential varies per row in
the credentials table).
4 genuine passive-voice rewrites (actor named, or the passive
construction replaced with an active imperative); 7 false positives
left untouched — predicate-adjective state/necessity descriptions
('is unchanged'/'is needed'/'is enabled' x2/'is unauthenticated'/
'is unaffected', same category as the already-established 'is
required') and one quoted-literal example string ('this hive is
gone').
7 genuine passive-voice rewrites (actor named, or the passive
construction replaced with a natural active verb/imperative); 5 false
positives left untouched — a temporal 'when it was written' reference,
two predicate-adjective necessity descriptions ('is needed'/'is
expected', same category as 'is required'), one predicate-adjective
state description ('are gone'), and one quoted literal + predicate
adjective ('these inputs are unused by rust').
6 genuine passive-voice rewrites (actor named or replaced with an
active construction); 7 false positives left untouched — the
design-intent 'is meant to have' idiom, and the six identical
'must be stated' renewal-column table cells (a deliberate terse
placeholder directive, not a describable action with an actor).
Part of #4548 — rewriting the 353 hand-written-docs write-good.Passive
hits so #4546 can flip the rule to error. This batch: docs/networking/gateway.md
(27 hits), docs/tools/subagent.md (19), docs/web-ui/dashboard.md (18).
Minimal, meaning-preserving rewrites to active voice only — no
restructuring beyond what each flagged sentence needed. Genuine false
positives (predicate-adjective state descriptions like "is required",
"is broken", "is unchanged", adjectival past participles like "named
buckets", and one quoted literal UI string) are left in place; the
per-line reasoning is on the tracking issue.
Note: docs/tools/forge-cli.md (37 hits, would have been the single
biggest file) turned out to be generated from hive-forge's own clap
help text (see hive-forge/src/main.rs's `MarkdownDocs` verb) — same
category as the already-excluded hivectl-cli.md/swarmctl-cli.md, so
it's untouched here. Flagged on #4548.
argus caught it on PR #4557 review: 'certificateFiles reads at system
build time' hands the option itself a verb it can't perform. The
system reads the option's value at build time, not the other way
around.
Part of #4548 — third batch. This PR: docs/swarm/secrets.md (16 hits),
docs/swarm/README.md (15 hits) = 31 more of the 353 (130 total across
the three PRs so far).
Same method as #4551/#4553: minimal, meaning-preserving active-voice
rewrites, no restructuring beyond what each flagged sentence needed,
no vale-off silencing.
Genuine false positives left in place: predicate-adjective state
descriptions ("is required" x4, "is enabled", "is disabled"), one
systemd terminology reference ("is wanted (not required)" — literally
naming systemd's Wants=/Requires= dependency-type distinction, not
passive voice), and one design-intent idiom ("both are expected" —
same shape as earlier docs-lint work).
Part of #4548 — second batch. This PR: docs/integrations/matrix.md
(18 hits), docs/agent-lifecycle/persistence.md (17 hits) = 35 more of
the 353 (99 total across the two PRs so far).
Same method as the first batch (#4551): minimal, meaning-preserving
active-voice rewrites only, no restructuring beyond what each flagged
sentence needed, no vale-off silencing.
Genuine false positives left in place, with per-file reasoning in the
PR body: predicate-adjective state descriptions ("is wedged", "is
stopped", "are unrelated", "is unaffected", "is needed"), one
design-intent idiom ("was meant to prevent" — same shape as earlier
docs-lint work), and one vale parsing artifact: "is read" flagged
inside the hyphenated compound adjective "read-only", not real passive
voice at all.
Puts the default on #[arg(long, default_value = "origin")] instead of
duplicating unwrap_or("origin") at both call sites (REST/--push path
and agit_create). clap now renders the default in --help itself, so
the hand-written doc-comment default is dropped in favor of it.
Regenerated docs/tools/forge-cli.md to match.
Fix wording flagged on #4521 review: "Single nginx in front of every
hyperhive web surface" and "Runs on the host" claimed a deployment-wide
topology. There's one nginx per host that has something on it, and
potentially more inside service containers. Reword to describe what
this module builds on this host, with no count claim swarm-wide.
`services.hyperhive.gateway.enable`, `gateway.dns.enable` and
`network.enable` replace the `hyperhive.enable` gate on all three. Each
defaults to false; the modules that need one assert it with `mkDefault
true` from inside the guard their own deployment already carries, and
`swarm-required-services.nix` — the module that owns what the
swarm-services toggle implies — asserts all three explicitly.
hive-c0re asserts all three unconditionally, so an ordinary hive keeps
getting them with no opt-in: it is the host's only knowledge that agent
containers exist.
The resolver moves to its own `hive-gateway/dns.nix` so it can be gated
without reindenting the nginx half of the module.
Reinstates `network.enable`, dropping its `mkRemovedOptionModule` shim.
A config still carrying `network.enable = false` from before the removal
now switches the bridge off instead of failing eval.
Also deletes a duplicate `centralToggleOff` fixture in nix/module-eval.nix.
Two sibling slices added it independently (c5f60fd5, ce3b3d94); the merge
was textually clean and left `main` failing to evaluate at all, so this
file could not be gated without removing one.