autheliaCfg/forgeCfg/vmCfg/vlCfg were local re-exports of
hyperhiveCfg.swarm.<subpath> — a shape that hides the full option
path from grep, the same hazard #4356 fixed for the tls alias.
Removed the six let-bindings and spelled the full
hyperhiveCfg.swarm.<subpath>.<field> path at every use site instead.
None of the read fields (url, machine, hiveClientPrefix,
agentClientSuffix, domain, port) sit on the old side of a
mkRenamedOptionModule in deploy.nix, so inlining the alias's own
current path is correct as-is.
Refs #4363
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The previous commit left two delivery paths and a three-way gate:
`swarm-grafana-oidc-secret.service` copied authelia's minted plaintext
out of its host tree wherever the two were co-located,
`swarm-bao-grafana-oidc.service` read the same value from the swarm
secret store wherever they were not, and `ssoConfigured && (ssoLocal ||
haveClientIdentity)` decided whether Grafana got an OIDC block at all.
Delete the co-located path. The store reader is now THE delivery unit,
in every deployment — the publisher on authelia's host writes
`swarm/services/<id>/oidc/client` whether the reader is a network away
or in the container next door. The ruling behind it: the store exists so
a host holds ONE out-of-band secret, its client certificate, and reads
everything else with it. Skipping the store when the producer happens to
be local saves a round trip and costs a second delivery unit, a second
way for the file to be wrong, and a gate to choose between them.
The gate goes too, and both of its questions become assertions, scoped
to hosts that run Grafana:
- `swarm.authelia.url` must be set. `auth.disable_login_form` is
unconditional — Grafana ships an admin/admin account on a public
vhost — so dropping the OIDC block when the swarm names no IdP
produced a container with no SSO and no password box, silently. An
eval-time refusal naming the option is the only report that reaches
anyone, the shape swarm-nats.nix already uses for the same option.
- `deploy.bao.clientCertFile` / `clientKeyFile` must be set. This
replaces a warning that nothing reads back, and its message names both
options and where the leaf comes from.
Fixtures follow. `grafanaWithAuthelia` gains the cert pair, because a
co-located host is a store reader like any other. The old
`grafanaRemoteAutheliaNoIdentity` is kept rather than deleted, renamed
`grafanaNoIdentity`: the shape is still reachable, only its deliverable
changed from silence to a refusal, and an arm now reads that refusal
back. Its mirror `grafanaNoSso` covers the other assertion, each fixture
wrong in exactly one way so an arm can name which refusal fired. Every
positive keeps an explicit negative — the one-delivery-unit arm asserts
the deleted unit is absent in both topologies rather than merely that
the store reader is present.
Refs #4234
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Grafana's OIDC client secret only existed where authelia did. One
`ssoLocal` gate — `grafana.enable && authelia.enable` — decided the
client registration, the minted secret's delivery and the whole
`auth.generic_oauth` block, so a swarm whose authelia runs on another
host got Grafana with no SSO wiring at all. The local login form is
disabled unconditionally, so that is no way in.
Split the one gate into the two questions it was conflating:
- `ssoConfigured` — does this SWARM have an identity provider
(`swarm.authelia.url`, which is swarm-wide and whose own description
makes null mean "no SSO configured"). With a delivery route present
this is what emits Grafana's OIDC block.
- `ssoLocal` — is authelia on THIS host, now spelled as the forge and
matrix modules spell it. It decides only which unit delivers the
secret.
Where authelia is elsewhere, `swarm-bao-grafana-oidc.service` reads the
secret from the swarm secret store, shaped after
glue-queue-agent-credential.nix: cert login fails loudly because a retry
fixes every state it fails on, the read degrades quietly because no
retry turns "no value there" into a value, and nothing writes a
stand-in. The producer is the publisher that already runs on authelia's
host, which gains the swarm's service clients beside the per-hive ones
at `swarm/services/<id>/oidc/client` — with the write grant in
swarm-bao.nix and the hive read grant in `policy::render` to match.
Registration moved to glue-grafana-oidc-client.nix. It has to be
declared where authelia's config is rendered, and swarm-grafana.nix's
config block hangs off this host running Grafana.
Two judgement calls stated rather than buried: a hive's read policy now
grants the whole `services` prefix, because a service's path names the
service and nothing swarm-wide records which hive runs it (cost recorded
in docs/trust-boundary/security.md); and the client is registered on any
authelia host, because no swarm-wide "this swarm has a Grafana" fact
exists to gate it on.
Refs #4234
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The journald receiver runs with --lines=0, so every collector start
only ships what's written after it starts, and a restart silently
loses whatever landed while it was down. Point it at a file_storage
extension so the read cursor survives a restart; start_at stays at
its 'end' default since the cursor now covers everything after the
first run.
Refs #3818
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
hive-subagent-mcp already stamps a subagent=<name> resource attribute
(alongside the parent's own agent=<name>) on every claude_code.* sample
a spawned subagent turn emits -- the label already existed, it just
wasn't plotted anywhere. Adds five panels: subagent cost, subagent
turns, subagent cost share (of all cost), cost by subagent name, and
cost by parent agent. No backend changes needed.
The dashboard already templated an $agent variable and broke turns and
active-time out by agent, but cost and token totals only split by
model, effort and query_source -- there was no panel answering "how
much did each agent spend/use", the aggregate-by-agent view mara asked
for.
Three cases beside the existing ingest ones, since the read route has the
same trap and one property of its own.
- the route exists and carries `auth_request`;
- it has no login fallback — with the browser location as the positive
control, so a pass means the two routes differ rather than that
`error_page` and the login host are absent from the whole vhost;
- it injects no filter, on `extraConfig` and on `proxyPass` both, because
VictoriaLogs takes its filters as request parameters and those ride an
upstream URI as easily as a directive. A filter arriving later is then a
visible diff here rather than a quiet change of rule.
Each absence arm leads with a clause that proves the location resolved and
that `hasInfix` finds what is really in that string, so "not there" cannot
be read off an unreadable path.
Refs #3870
An agent can reach the log store only through the gateway, and the only
location that exists for a reader is `/`, which is the operator's browser
route. That route ends in `error_page 401 =302`, and an unauthenticated
caller which follows the redirect gets authelia's login page as HTTP 200
with an HTML body — so a client that checks the status code records a query
that succeeded and matched no logs. Measured on the live gateway; it is the
root cause behind four operator round-trips already.
So the query API gets its own location, `^~ /select/logsql/`, with bare
`auth_request` and no login fallback: an unauthenticated caller gets a 401
it cannot mistake for an empty result. `^~` keeps it ahead of the `/`
catch-all and of any regex location added later, and it stops short of
`/select/vmui/`, which is the browser UI and stays on the browser route.
The query is forwarded unmodified — no filter parameter is injected, so any
authenticated caller reads the whole swarm's logs. That is the rule mara
set: read permissions are a later thing, and this location is where one
attaches when it exists.
Refs #3870
systemd's default OOMPolicy=stop tears the whole unit down the moment the
kernel kills any process in its cgroup, so a single over-large subagent
takes the daemon and every sibling session with it — measured on a live
agent, both units show OOMPolicy=stop today, which is exactly the "every
live subagent session was cut mid-turn" symptom.
That blast radius is also what would make the preceding commit a bad
trade: deliberately putting this unit first in the OOM queue is only an
improvement if losing one subagent isn't losing all of them. OOMPolicy=
continue scopes the loss to the process the kernel actually chose, and
leaves the daemon alive to report the kill instead of vanishing and being
restarted with an empty session map.
Refs #4316
Both units ran at OOMScoreAdjust=0, so under container memory pressure the
kernel picked purely on footprint — and the agent's own claude is often
the fattest process in the container, which means the session supervising
the work died before the work did.
The sign is the load-bearing part and is easy to invert: a HIGHER
OOMScoreAdjust means MORE likely to be killed, because the kernel adds it
to the badness score it derives from the process's memory footprint and
then kills the highest scorer. So hive-subagent-daemon gets +500 (first in
line) and hive-agent gets -500 (last in line). Written backwards this
makes the reported bug worse rather than better, so module-eval pins the
order as an inequality.
Both values are inherited by the nested claude each unit spawns as a
child, so ordering the units orders the sessions underneath them. -500
rather than -1000 on the harness: fully exempting it would leave the
kernel nothing to kill in a container whose only large process is the
harness.
Refs #4316
The daemon spawns every nested claude as a plain child, so its cgroup is
already the "all subagents" cgroup — but it ran with MemoryHigh=infinity
and MemoryMax=infinity, so nothing slowed a subagent down before the
kernel's OOM killer stopped the unit and cut every live session with it.
MemoryHigh= and not MemoryMax=: a soft ceiling reclaims and stalls the
cgroup past two thirds of the container's cap, which turns a silent kill
into a visible throttle, while still letting a single subagent exceed its
share when the container has memory free. A hard per-agent cap would make
overprovisioning impossible, which is not wanted — most of the time
nothing in these sessions is compiling.
The fraction is taken from hyperhive.claudeMemoryMaxBytes, the container's
own effective MemoryMax= that meta.rs already bakes in per agent. When
that is null (an `infinity` or percentage cap) the unit renders no ceiling
rather than a fabricated constant, and module-eval pins both arms.
Refs #4316
nix/devshell.nix didn't carry vale, so
[4mdocs/README.md[0m
16:51 [33mwarning[0m Use first person (such as 'I') sparingly. Microsoft.FirstPerson
53:49 [33mwarning[0m 'Multiple' is too wordy. write-good.TooWordy
60:33 [33mwarning[0m Use first person (such as 'I') sparingly. Microsoft.FirstPerson
62:14 [33mwarning[0m Use first person (such as 'I') sparingly. Microsoft.FirstPerson
74:12 [33mwarning[0m Use first person (such as 'I') sparingly. Microsoft.FirstPerson
88:12 [33mwarning[0m Use first person (such as 'I') sparingly. Microsoft.FirstPerson
[4mdocs/agent-lifecycle/agent-hierarchy.md[0m
5:59 [33mwarning[0m 'are meant' may be passive voice. Use active voice if you can. write-good.Passive
6:24 [33mwarning[0m 'is finished' may be passive voice. Use active voice if you can. write-good.Passive
7:1 [33mwarning[0m When referring to a person, consider using 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'special'. alex.Ablist
23:36 [33mwarning[0m Consider 'stops responding' instead of 'hangs'. Microsoft.BiasFree
23:59 [33mwarning[0m 'is built' may be passive voice. Use active voice if you can. write-good.Passive
35:53 [33mwarning[0m 'is required' may be passive voice. Use active voice if you can. write-good.Passive
68:15 [33mwarning[0m 'are approved' may be passive voice. Use active voice if you can. write-good.Passive
104:12 [33mwarning[0m When referring to a person, consider using 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'special'. alex.Ablist
107:47 [33mwarning[0m When referring to a person, consider using 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'special'. alex.Ablist
126:3 [33mwarning[0m 'equivalent' is too wordy. write-good.TooWordy
135:15 [33mwarning[0m 'be gated' may be passive voice. Use active voice if you can. write-good.Passive
144:26 [33mwarning[0m When referring to a person, consider using 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'special'. alex.Ablist
153:31 [33mwarning[0m 'is planned' may be passive voice. Use active voice if you can. write-good.Passive
157:32 [33mwarning[0m 'is handled' may be passive voice. Use active voice if you can. write-good.Passive
217:58 [33mwarning[0m 'be owned' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/getting-started/setup.md[0m
15:17 [33mwarning[0m 'is generated' may be passive voice. Use active voice if you can. write-good.Passive
51:38 [33mwarning[0m Consider using 'the app froze', 'the app stopped responding', 'the app stopped responding to events', or 'the app became unresponsive' instead of 'hang' (which may be insensitive). alex.Suicide
127:25 [33mwarning[0m 'usually' is a weasel word! write-good.Weasel
134:7 [33mwarning[0m 'whether or not' is too wordy. write-good.TooWordy
148:20 [33mwarning[0m 'it was' is too wordy. write-good.TooWordy
148:23 [33mwarning[0m 'was minted' may be passive voice. Use active voice if you can. write-good.Passive
160:23 [33mwarning[0m 'is needed' may be passive voice. Use active voice if you can. write-good.Passive
163:40 [33mwarning[0m 'is bound' may be passive voice. Use active voice if you can. write-good.Passive
165:51 [33mwarning[0m 'is needed' may be passive voice. Use active voice if you can. write-good.Passive
177:19 [33mwarning[0m 'is issued' may be passive voice. Use active voice if you can. write-good.Passive
183:36 [33mwarning[0m 'is tracked' may be passive voice. Use active voice if you can. write-good.Passive
200:31 [33mwarning[0m 'are gated' may be passive voice. Use active voice if you can. write-good.Passive
291:26 [33mwarning[0m 'is stored' may be passive voice. Use active voice if you can. write-good.Passive
297:22 [33mwarning[0m 'is authenticated' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/integrations/github.md[0m
16:79 [33mwarning[0m 'is needed' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/integrations/knowledge.md[0m
17:58 [33mwarning[0m 'is read' may be passive voice. Use active voice if you can. write-good.Passive
56:61 [33mwarning[0m 'is needed' may be passive voice. Use active voice if you can. write-good.Passive
94:3 [33mwarning[0m 'is stopped' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/integrations/forge.md[0m
35:24 [33mwarning[0m 'sufficient' is too wordy. write-good.TooWordy
109:54 [33mwarning[0m 'are shared' may be passive voice. Use active voice if you can. write-good.Passive
160:19 [33mwarning[0m When referring to a person, consider using 'turned off', 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'disabled'. alex.Ablist
171:1 [33mwarning[0m When referring to a person, consider using 'turned off', 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'Disabled'. alex.Ablist
331:1 [33mwarning[0m 'subsequent' is too wordy. write-good.TooWordy
[4mdocs/integrations/matrix.md[0m
34:47 [33mwarning[0m 'be changed' may be passive voice. Use active voice if you can. write-good.Passive
76:16 [33mwarning[0m 'are minted' may be passive voice. Use active voice if you can. write-good.Passive
172:67 [33mwarning[0m 'is restricted' may be passive voice. Use active voice if you can. write-good.Passive
200:61 [33mwarning[0m 'is enabled' may be passive voice. Use active voice if you can. write-good.Passive
202:22 [33mwarning[0m 'is trusted' may be passive voice. Use active voice if you can. write-good.Passive
207:61 [33mwarning[0m 'maximum' is too wordy. write-good.TooWordy
221:17 [33mwarning[0m When referring to a person, consider using 'turned off', 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'invalid'. alex.Ablist
226:28 [33mwarning[0m 'is required' may be passive voice. Use active voice if you can. write-good.Passive
231:26 [33mwarning[0m 'is required' may be passive voice. Use active voice if you can. write-good.Passive
233:27 [33mwarning[0m 'is required' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/agent-lifecycle/persistence.md[0m
7:27 [33mwarning[0m Use first person (such as 'I') sparingly. Microsoft.FirstPerson
16:46 [33mwarning[0m 'all of' is too wordy. write-good.TooWordy
23:15 [33mwarning[0m 'are kept' may be passive voice. Use active voice if you can. write-good.Passive
26:62 [33mwarning[0m 'however' is too wordy. write-good.TooWordy
32:33 [33mwarning[0m When referring to a person, consider using 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'special'. alex.Ablist
92:13 [33mwarning[0m 'are kept' may be passive voice. Use active voice if you can. write-good.Passive
127:17 [33mwarning[0m 'was meant' may be passive voice. Use active voice if you can. write-good.Passive
177:23 [33mwarning[0m 'subsequent' is too wordy. write-good.TooWordy
185:6 [33mwarning[0m 'modify' is too wordy. write-good.TooWordy
249:38 [33mwarning[0m 'is wedged' may be passive voice. Use active voice if you can. write-good.Passive
249:65 [33mwarning[0m 'is stopped' may be passive voice. Use active voice if you can. write-good.Passive
278:5 [33mwarning[0m 'is set' may be passive voice. Use active voice if you can. write-good.Passive
349:12 [33mwarning[0m 'is read' may be passive voice. Use active voice if you can. write-good.Passive
356:11 [33mwarning[0m 'be confused' may be passive voice. Use active voice if you can. write-good.Passive
359:55 [33mwarning[0m 'are unrelated' may be passive voice. Use active voice if you can. write-good.Passive
413:43 [33mwarning[0m 'been removed' may be passive voice. Use active voice if you can. write-good.Passive
415:59 [33mwarning[0m 'be deleted' may be passive voice. Use active voice if you can. write-good.Passive
442:9 [33mwarning[0m 'is automigrated' may be passive voice. Use active voice if you can. write-good.Passive
454:58 [33mwarning[0m 'be removed' may be passive voice. Use active voice if you can. write-good.Passive
480:37 [33mwarning[0m 'subsequent' is too wordy. write-good.TooWordy
487:19 [33mwarning[0m 'is unaffected' may be passive voice. Use active voice if you can. write-good.Passive
494:18 [33mwarning[0m 'be started' may be passive voice. Use active voice if you can. write-good.Passive
582:51 [33mwarning[0m 'is set' may be passive voice. Use active voice if you can. write-good.Passive
591:28 [33mwarning[0m 'is needed' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/agent-lifecycle/approvals.md[0m
34:36 [33mwarning[0m 'all of' is too wordy. write-good.TooWordy
141:43 [33mwarning[0m When referring to a person, consider using 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'special'. alex.Ablist
150:42 [33mwarning[0m 'is overloaded' may be passive voice. Use active voice if you can. write-good.Passive
174:44 [33mwarning[0m 'Subsequent' is too wordy. write-good.TooWordy
190:27 [33mwarning[0m 'is set' may be passive voice. Use active voice if you can. write-good.Passive
295:23 [33mwarning[0m 'however' is too wordy. write-good.TooWordy
500:47 [33mwarning[0m 'additional' is too wordy. write-good.TooWordy
579:9 [33mwarning[0m When referring to a person, consider using 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'special'. alex.Ablist
615:55 [33mwarning[0m 'previously' is too wordy. write-good.TooWordy
618:18 [33mwarning[0m 'was cleared' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/networking/network.md[0m
4:20 [33mwarning[0m 'is enabled' may be passive voice. Use active voice if you can. write-good.Passive
10:52 [33mwarning[0m 'were removed' may be passive voice. Use active voice if you can. write-good.Passive
18:61 [33mwarning[0m 'is untouched' may be passive voice. Use active voice if you can. write-good.Passive
84:33 [33mwarning[0m 'monitor' is too wordy. write-good.TooWordy
197:50 [33mwarning[0m 'is unchanged' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/process/pr-review-gate.md[0m
44:1 [33mwarning[0m Use first person (such as 'I') sparingly. Microsoft.FirstPerson
45:36 [33mwarning[0m 'are expected' may be passive voice. Use active voice if you can. write-good.Passive
50:13 [33mwarning[0m 'be armed' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/networking/snapshot-store.md[0m
126:49 [33mwarning[0m 'being enabled' may be passive voice. Use active voice if you can. write-good.Passive
127:9 [33mwarning[0m 'being set' may be passive voice. Use active voice if you can. write-good.Passive
131:15 [33mwarning[0m 'sufficient' is too wordy. write-good.TooWordy
164:43 [33mwarning[0m 'is discovered' may be passive voice. Use active voice if you can. write-good.Passive
178:5 [33mwarning[0m Reconsider using 'trap', it may be profane. alex.ProfanityMaybe
178:67 [33mwarning[0m 'being called' may be passive voice. Use active voice if you can. write-good.Passive
194:53 [33mwarning[0m 'is wanted' may be passive voice. Use active voice if you can. write-good.Passive
200:1 [33mwarning[0m 'therefore' is too wordy. write-good.TooWordy
201:5 [33mwarning[0m 'is tracked' may be passive voice. Use active voice if you can. write-good.Passive
207:1 [33mwarning[0m 'is bounded' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/scheduler/ci.md[0m
34:1 [33mwarning[0m 'Several' is a weasel word! write-good.Weasel
37:7 [33mwarning[0m 'are required' may be passive voice. Use active voice if you can. write-good.Passive
69:27 [33mwarning[0m 'is stuck' may be passive voice. Use active voice if you can. write-good.Passive
131:98 [33mwarning[0m 'validate' is too wordy. write-good.TooWordy
146:33 [33mwarning[0m 'is set' may be passive voice. Use active voice if you can. write-good.Passive
186:55 [33mwarning[0m 'therefore' is too wordy. write-good.TooWordy
188:422 [33mwarning[0m 'is dropped' may be passive voice. Use active voice if you can. write-good.Passive
193:60 [33mwarning[0m Reconsider using 'attack', it may be profane. alex.ProfanityMaybe
193:87 [33mwarning[0m 'eliminate' is too wordy. write-good.TooWordy
249:58 [33mwarning[0m 'however' is too wordy. write-good.TooWordy
[4mdocs/networking/gateway.md[0m
22:92 [33mwarning[0m 'is misconfigured' may be passive voice. Use active voice if you can. write-good.Passive
37:518 [33mwarning[0m 'is closed' may be passive voice. Use active voice if you can. write-good.Passive
105:40 [33mwarning[0m When referring to a person, consider using 'careless', 'heartless', 'indifferent', or 'insensitive' instead of 'blind to'. alex.Ablist
140:44 [33mwarning[0m 'is unchanged' may be passive voice. Use active voice if you can. write-good.Passive
142:4 [33mwarning[0m 'subsequent' is too wordy. write-good.TooWordy
154:38 [33mwarning[0m 'was removed' may be passive voice. Use active voice if you can. write-good.Passive
169:14 [33mwarning[0m 'is configured' may be passive voice. Use active voice if you can. write-good.Passive
170:48 [33mwarning[0m 'is set' may be passive voice. Use active voice if you can. write-good.Passive
177:15 [33mwarning[0m 'is covered' may be passive voice. Use active voice if you can. write-good.Passive
182:9 [33mwarning[0m Try to avoid using first-person plural like 'Let's'. Microsoft.We
190:12 [33mwarning[0m Try to avoid using first-person plural like 'Let's'. Microsoft.We
206:122 [33mwarning[0m Try to avoid using first-person plural like 'Let's'. Microsoft.We
212:153 [33mwarning[0m 'is named' may be passive voice. Use active voice if you can. write-good.Passive
224:185 [33mwarning[0m 'be wired' may be passive voice. Use active voice if you can. write-good.Passive
226:235 [33mwarning[0m 'terminate' is too wordy. write-good.TooWordy
230:150 [33mwarning[0m 'validate' is too wordy. write-good.TooWordy
230:261 [33mwarning[0m 'be added' may be passive voice. Use active voice if you can. write-good.Passive
232:165 [33mwarning[0m 'is undisturbed' may be passive voice. Use active voice if you can. write-good.Passive
234:199 [33mwarning[0m 'is wired' may be passive voice. Use active voice if you can. write-good.Passive
238:36 [33mwarning[0m Try to avoid using first-person plural like 'Let's'. Microsoft.We
276:68 [33mwarning[0m 'was removed' may be passive voice. Use active voice if you can. write-good.Passive
292:49 [33mwarning[0m When referring to a person, consider using 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'special'. alex.Ablist
299:41 [33mwarning[0m 'is privileged' may be passive voice. Use active voice if you can. write-good.Passive
300:30 [33mwarning[0m 'be exposed' may be passive voice. Use active voice if you can. write-good.Passive
307:19 [33mwarning[0m 'was removed' may be passive voice. Use active voice if you can. write-good.Passive
311:40 [33mwarning[0m 'is required' may be passive voice. Use active voice if you can. write-good.Passive
358:25 [33mwarning[0m 'is left' may be passive voice. Use active voice if you can. write-good.Passive
361:11 [33mwarning[0m 'are opened' may be passive voice. Use active voice if you can. write-good.Passive
363:52 [33mwarning[0m 'is needed' may be passive voice. Use active voice if you can. write-good.Passive
393:40 [33mwarning[0m 'requirement' is too wordy. write-good.TooWordy
501:13 [33mwarning[0m 'is broken' may be passive voice. Use active voice if you can. write-good.Passive
513:68 [33mwarning[0m 'is required' may be passive voice. Use active voice if you can. write-good.Passive
528:1 [33mwarning[0m 'is required' may be passive voice. Use active voice if you can. write-good.Passive
532:26 [33mwarning[0m 'is exposed' may be passive voice. Use active voice if you can. write-good.Passive
550:19 [33mwarning[0m 'is required' may be passive voice. Use active voice if you can. write-good.Passive
586:18 [33mwarning[0m 'therefore' is too wordy. write-good.TooWordy
592:24 [33mwarning[0m When referring to a person, consider using 'turned off', 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'disabled'. alex.Ablist
620:7 [33mwarning[0m 'is encrypted' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/scheduler/jobq.md[0m
44:24 [33mwarning[0m 'is done' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/process/conventions.md[0m
81:55 [33mwarning[0m When referring to a person, consider using 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'special'. alex.Ablist
139:40 [33mwarning[0m 'be handled' may be passive voice. Use active voice if you can. write-good.Passive
140:8 [33mwarning[0m 'is set' may be passive voice. Use active voice if you can. write-good.Passive
189:31 [33mwarning[0m 'are scoped' may be passive voice. Use active voice if you can. write-good.Passive
248:13 [33mwarning[0m 'be shown' may be passive voice. Use active voice if you can. write-good.Passive
274:24 [33mwarning[0m 'are kept' may be passive voice. Use active voice if you can. write-good.Passive
326:17 [33mwarning[0m 'therefore' is too wordy. write-good.TooWordy
327:41 [33mwarning[0m 'are pushed' may be passive voice. Use active voice if you can. write-good.Passive
394:55 [33mwarning[0m 'Implement' is too wordy. write-good.TooWordy
432:1 [33mwarning[0m 'sufficient' is too wordy. write-good.TooWordy
434:12 [33mwarning[0m 'are factored' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/process/gotchas.md[0m
18:46 [33mwarning[0m 'are ignored' may be passive voice. Use active voice if you can. write-good.Passive
76:27 [33mwarning[0m 'be started' may be passive voice. Use active voice if you can. write-good.Passive
93:3 [33mwarning[0m 'enumerate' is too wordy. write-good.TooWordy
98:13 [33mwarning[0m 'it was' is too wordy. write-good.TooWordy
98:16 [33mwarning[0m 'was written' may be passive voice. Use active voice if you can. write-good.Passive
117:37 [33mwarning[0m 'is needed' may be passive voice. Use active voice if you can. write-good.Passive
141:17 [33mwarning[0m Reconsider using 'trap', it may be profane. alex.ProfanityMaybe
148:26 [33mwarning[0m 'be wrapped' may be passive voice. Use active voice if you can. write-good.Passive
150:9 [33mwarning[0m 'is expected' may be passive voice. Use active voice if you can. write-good.Passive
160:25 [33mwarning[0m 'be reclaimed' may be passive voice. Use active voice if you can. write-good.Passive
161:28 [33mwarning[0m 'are gone' may be passive voice. Use active voice if you can. write-good.Passive
180:36 [33mwarning[0m Use first person (such as 'my') sparingly. Microsoft.FirstPerson
256:18 [33mwarning[0m 'subsequent' is too wordy. write-good.TooWordy
324:46 [33mwarning[0m 'are unused' may be passive voice. Use active voice if you can. write-good.Passive
355:59 [33mwarning[0m 'was checked' may be passive voice. Use active voice if you can. write-good.Passive
425:29 [33mwarning[0m 'is configured' may be passive voice. Use active voice if you can. write-good.Passive
438:27 [33mwarning[0m Use 'select' instead of the input-specific verb 'click'. Microsoft.UIVerbs
484:23 [33mwarning[0m 'is rooted' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/swarm/ca.md[0m
55:59 [33mwarning[0m 'is expected' may be passive voice. Use active voice if you can. write-good.Passive
56:9 [33mwarning[0m 'be ignored' may be passive voice. Use active voice if you can. write-good.Passive
65:60 [33mwarning[0m 'are distributed' may be passive voice. Use active voice if you can. write-good.Passive
74:10 [33mwarning[0m 'is issued' may be passive voice. Use active voice if you can. write-good.Passive
100:1 [33mwarning[0m 'terminate' is too wordy. write-good.TooWordy
110:26 [33mwarning[0m 'is allowed' may be passive voice. Use active voice if you can. write-good.Passive
145:31 [33mwarning[0m 'is misconfigured' may be passive voice. Use active voice if you can. write-good.Passive
164:31 [33mwarning[0m 'is assembled' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/scheduler/coordinator.md[0m
19:15 [33mwarning[0m Consider using 'simple', 'indigenous', or 'hunter-gatherer' instead of 'primitive'. alex.Race
20:61 [33mwarning[0m 'multiple' is too wordy. write-good.TooWordy
21:1 [33mwarning[0m When referring to a person, consider using 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'Special'. alex.Ablist
22:43 [33mwarning[0m 'is expressed' may be passive voice. Use active voice if you can. write-good.Passive
23:21 [33mwarning[0m Consider using 'simple', 'indigenous', or 'hunter-gatherer' instead of 'primitive'. alex.Race
38:13 [33mwarning[0m 'be written' may be passive voice. Use active voice if you can. write-good.Passive
81:268 [33mwarning[0m 'multiple' is too wordy. write-good.TooWordy
81:354 [33mwarning[0m 'is typed' may be passive voice. Use active voice if you can. write-good.Passive
93:68 [33mwarning[0m 'finalize' is too wordy. write-good.TooWordy
102:17 [33mwarning[0m 'therefore' is too wordy. write-good.TooWordy
112:25 [33mwarning[0m 'finalize' is too wordy. write-good.TooWordy
209:51 [33mwarning[0m 'is satisfied' may be passive voice. Use active voice if you can. write-good.Passive
220:31 [33mwarning[0m 'multiple' is too wordy. write-good.TooWordy
235:40 [33mwarning[0m 'requirement' is too wordy. write-good.TooWordy
241:1 [33mwarning[0m 'exclusively' is too wordy. write-good.TooWordy
244:5 [33mwarning[0m 'requirement' is too wordy. write-good.TooWordy
248:34 [33mwarning[0m 'requirement' is too wordy. write-good.TooWordy
256:51 [33mwarning[0m 'acquire' is too wordy. write-good.TooWordy
259:19 [33mwarning[0m 'is unaffected' may be passive voice. Use active voice if you can. write-good.Passive
280:39 [33mwarning[0m 'is tracked' may be passive voice. Use active voice if you can. write-good.Passive
343:24 [33mwarning[0m 'be kept' may be passive voice. Use active voice if you can. write-good.Passive
399:67 [33mwarning[0m 'finalize' is too wordy. write-good.TooWordy
419:25 [33mwarning[0m 'minimum' is too wordy. write-good.TooWordy
424:71 [33mwarning[0m 'subsequent' is too wordy. write-good.TooWordy
476:21 [33mwarning[0m 'is left' may be passive voice. Use active voice if you can. write-good.Passive
500:66 [33mwarning[0m 'multiple' is too wordy. write-good.TooWordy
558:40 [33mwarning[0m 'is left' may be passive voice. Use active voice if you can. write-good.Passive
561:3 [33mwarning[0m 'additional' is too wordy. write-good.TooWordy
[4mdocs/swarm/secrets.md[0m
3:23 [33mwarning[0m 'are generated' may be passive voice. Use active voice if you can. write-good.Passive
7:23 [33mwarning[0m 'all of' is too wordy. write-good.TooWordy
17:37 [33mwarning[0m 'is published' may be passive voice. Use active voice if you can. write-good.Passive
32:42 [33mwarning[0m 'is responsible for' is too wordy. write-good.TooWordy
38:106 [33mwarning[0m 'is generated' may be passive voice. Use active voice if you can. write-good.Passive
46:110 [33mwarning[0m 'is set' may be passive voice. Use active voice if you can. write-good.Passive
57:139 [33mwarning[0m 'is set' may be passive voice. Use active voice if you can. write-good.Passive
70:78 [33mwarning[0m 'is made' may be passive voice. Use active voice if you can. write-good.Passive
80:10 [33mwarning[0m 'is made' may be passive voice. Use active voice if you can. write-good.Passive
80:36 [33mwarning[0m 'is enabled' may be passive voice. Use active voice if you can. write-good.Passive
81:47 [33mwarning[0m 'is registered' may be passive voice. Use active voice if you can. write-good.Passive
88:67 [33mwarning[0m 'is fed' may be passive voice. Use active voice if you can. write-good.Passive
120:39 [33mwarning[0m 'are required' may be passive voice. Use active voice if you can. write-good.Passive
167:44 [33mwarning[0m 'is wanted' may be passive voice. Use active voice if you can. write-good.Passive
187:36 [33mwarning[0m 'be fetched' may be passive voice. Use active voice if you can. write-good.Passive
207:34 [33mwarning[0m 'therefore' is too wordy. write-good.TooWordy
212:24 [33mwarning[0m 'is built' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/swarm/README.md[0m
6:5 [33mwarning[0m 'additional' is too wordy. write-good.TooWordy
6:51 [33mwarning[0m 'multiple' is too wordy. write-good.TooWordy
21:24 [33mwarning[0m 'be qualified' may be passive voice. Use active voice if you can. write-good.Passive
43:67 [33mwarning[0m 'is enabled' may be passive voice. Use active voice if you can. write-good.Passive
50:10 [33mwarning[0m 'is required' may be passive voice. Use active voice if you can. write-good.Passive
130:62 [33mwarning[0m Use first person (such as 'my') sparingly. Microsoft.FirstPerson
131:1 [33mwarning[0m 'is derived' may be passive voice. Use active voice if you can. write-good.Passive
158:34 [33mwarning[0m 'was removed' may be passive voice. Use active voice if you can. write-good.Passive
168:10 [33mwarning[0m 'be listed' may be passive voice. Use active voice if you can. write-good.Passive
171:52 [33mwarning[0m 'is tracked' may be passive voice. Use active voice if you can. write-good.Passive
274:1 [33mwarning[0m 'is required' may be passive voice. Use active voice if you can. write-good.Passive
278:48 [33mwarning[0m 'is documented' may be passive voice. Use active voice if you can. write-good.Passive
293:66 [33mwarning[0m 'is installed' may be passive voice. Use active voice if you can. write-good.Passive
377:41 [33mwarning[0m 'is opened' may be passive voice. Use active voice if you can. write-good.Passive
388:39 [33mwarning[0m 'been given' may be passive voice. Use active voice if you can. write-good.Passive
413:27 [33mwarning[0m 'are expected' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/scheduler/observability.md[0m
40:23 [33mwarning[0m 'therefore' is too wordy. write-good.TooWordy
60:24 [33mwarning[0m 'all of' is too wordy. write-good.TooWordy
64:48 [33mwarning[0m 'be read' may be passive voice. Use active voice if you can. write-good.Passive
75:60 [33mwarning[0m 'is unchanged' may be passive voice. Use active voice if you can. write-good.Passive
148:51 [33mwarning[0m 'therefore' is too wordy. write-good.TooWordy
193:19 [33mwarning[0m 'is needed' may be passive voice. Use active voice if you can. write-good.Passive
205:22 [33mwarning[0m 'therefore' is too wordy. write-good.TooWordy
215:8 [33mwarning[0m 'additional' is too wordy. write-good.TooWordy
221:11 [33mwarning[0m 'is enabled' may be passive voice. Use active voice if you can. write-good.Passive
243:51 [33mwarning[0m 'equivalent' is too wordy. write-good.TooWordy
251:11 [33mwarning[0m 'is unauthenticated' may be passive voice. Use active voice if you can. write-good.Passive
256:11 [33mwarning[0m 'is enabled' may be passive voice. Use active voice if you can. write-good.Passive
283:18 [33mwarning[0m 'is measured' may be passive voice. Use active voice if you can. write-good.Passive
284:13 [33mwarning[0m 'are measured' may be passive voice. Use active voice if you can. write-good.Passive
286:33 [33mwarning[0m 'is gone' may be passive voice. Use active voice if you can. write-good.Passive
311:49 [33mwarning[0m 'equivalent' is too wordy. write-good.TooWordy
351:59 [33mwarning[0m 'be repeated' may be passive voice. Use active voice if you can. write-good.Passive
391:67 [33mwarning[0m 'is unaffected' may be passive voice. Use active voice if you can. write-good.Passive
396:38 [33mwarning[0m When referring to a person, consider using 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'special'. alex.Ablist
[4mdocs/tools/README.md[0m
45:56 [33mwarning[0m 'multiple' is too wordy. write-good.TooWordy
[4mdocs/swarm/ui.md[0m
45:63 [33mwarning[0m 'were supposed' may be passive voice. Use active voice if you can. write-good.Passive
80:61 [33mwarning[0m 'is scoped' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/swarm/services.md[0m
20:61 [33mwarning[0m 'be set' may be passive voice. Use active voice if you can. write-good.Passive
49:55 [33mwarning[0m 'be set' may be passive voice. Use active voice if you can. write-good.Passive
79:1 [33mwarning[0m 'are created' may be passive voice. Use active voice if you can. write-good.Passive
102:1 [33mwarning[0m 'be restarted' may be passive voice. Use active voice if you can. write-good.Passive
123:48 [33mwarning[0m 'is configured' may be passive voice. Use active voice if you can. write-good.Passive
148:170 [33mwarning[0m 'being written' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/tools/bash.md[0m
26:61 [33mwarning[0m 'is created' may be passive voice. Use active voice if you can. write-good.Passive
33:36 [33mwarning[0m 'is created' may be passive voice. Use active voice if you can. write-good.Passive
58:53 [33mwarning[0m 'is expected' may be passive voice. Use active voice if you can. write-good.Passive
128:40 [33mwarning[0m 'is rendered' may be passive voice. Use active voice if you can. write-good.Passive
136:26 [33mwarning[0m 'been removed' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/swarm/sso.md[0m
5:8 [33mwarning[0m 'is declared' may be passive voice. Use active voice if you can. write-good.Passive
31:62 [33mwarning[0m 'been enabled' may be passive voice. Use active voice if you can. write-good.Passive
70:21 [33mwarning[0m 'multiple' is too wordy. write-good.TooWordy
98:61 [33mwarning[0m 'is tabulated' may be passive voice. Use active voice if you can. write-good.Passive
118:14 [33mwarning[0m 'is built' may be passive voice. Use active voice if you can. write-good.Passive
127:26 [33mwarning[0m 'is broken' may be passive voice. Use active voice if you can. write-good.Passive
241:23 [33mwarning[0m 'was verified' may be passive voice. Use active voice if you can. write-good.Passive
241:60 [33mwarning[0m 'was introduced' may be passive voice. Use active voice if you can. write-good.Passive
257:42 [33mwarning[0m 'are created' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/tools/lifecycle.md[0m
16:32 [33mwarning[0m 'is preserved' may be passive voice. Use active voice if you can. write-good.Passive
55:1 [33mwarning[0m 'Subsequent' is too wordy. write-good.TooWordy
[4mdocs/tools/forge.md[0m
21:8 [33mwarning[0m 'is assumed' may be passive voice. Use active voice if you can. write-good.Passive
28:36 [33mwarning[0m 'validate' is too wordy. write-good.TooWordy
35:18 [33mwarning[0m 'be seen' may be passive voice. Use active voice if you can. write-good.Passive
35:43 [33mwarning[0m 'is stated' may be passive voice. Use active voice if you can. write-good.Passive
40:3 [33mwarning[0m 'are removed' may be passive voice. Use active voice if you can. write-good.Passive
45:22 [33mwarning[0m 'validate' is too wordy. write-good.TooWordy
137:1 [33mwarning[0m 'Multiple' is too wordy. write-good.TooWordy
144:144 [33mwarning[0m 'are merged' may be passive voice. Use active voice if you can. write-good.Passive
154:51 [33mwarning[0m Use first person (such as 'I') sparingly. Microsoft.FirstPerson
158:8 [33mwarning[0m 'are read' may be passive voice. Use active voice if you can. write-good.Passive
178:61 [33mwarning[0m 'be run' may be passive voice. Use active voice if you can. write-good.Passive
313:38 [33mwarning[0m 'are structured' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/tools/scheduling.md[0m
23:1 [33mwarning[0m 'is set' may be passive voice. Use active voice if you can. write-good.Passive
25:45 [33mwarning[0m 'multiple' is too wordy. write-good.TooWordy
33:8 [33mwarning[0m 'are left' may be passive voice. Use active voice if you can. write-good.Passive
34:54 [33mwarning[0m 'previously' is too wordy. write-good.TooWordy
46:31 [33mwarning[0m 'is removed' may be passive voice. Use active voice if you can. write-good.Passive
96:16 [33mwarning[0m 'minimum' is too wordy. write-good.TooWordy
[4mdocs/tools/matrix.md[0m
6:9 [33mwarning[0m 'is configured' may be passive voice. Use active voice if you can. write-good.Passive
31:35 [33mwarning[0m 'is inferred' may be passive voice. Use active voice if you can. write-good.Passive
32:34 [33mwarning[0m 'is sent' may be passive voice. Use active voice if you can. write-good.Passive
46:20 [33mwarning[0m 'enumerate' is too wordy. write-good.TooWordy
63:43 [33mwarning[0m 'been invited' may be passive voice. Use active voice if you can. write-good.Passive
70:4 [33mwarning[0m 'Multiple' is too wordy. write-good.TooWordy
74:64 [33mwarning[0m 'is keyed' may be passive voice. Use active voice if you can. write-good.Passive
111:18 [33mwarning[0m 'multiple' is too wordy. write-good.TooWordy
113:5 [33mwarning[0m 'Multiple' is too wordy. write-good.TooWordy
130:51 [33mwarning[0m 'was dropped' may be passive voice. Use active voice if you can. write-good.Passive
141:1 [33mwarning[0m 'is cleared' may be passive voice. Use active voice if you can. write-good.Passive
149:39 [33mwarning[0m 'additional' is too wordy. write-good.TooWordy
[4mdocs/tools/subagent.md[0m
44:11 [33mwarning[0m 'are meant' may be passive voice. Use active voice if you can. write-good.Passive
44:24 [33mwarning[0m 'be bounded' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/tools/hivectl.md[0m
46:24 [33mwarning[0m 'is created' may be passive voice. Use active voice if you can. write-good.Passive
100:53 [33mwarning[0m 'is needed' may be passive voice. Use active voice if you can. write-good.Passive
136:67 [33mwarning[0m 'is hoisted' may be passive voice. Use active voice if you can. write-good.Passive
157:38 [33mwarning[0m 'equivalent' is too wordy. write-good.TooWordy
205:46 [33mwarning[0m 'previously' is too wordy. write-good.TooWordy
222:1 [33mwarning[0m 'requirement' is too wordy. write-good.TooWordy
224:12 [33mwarning[0m 'were added' may be passive voice. Use active voice if you can. write-good.Passive
231:1 [33mwarning[0m 'therefore' is too wordy. write-good.TooWordy
244:35 [33mwarning[0m 'be consumed' may be passive voice. Use active voice if you can. write-good.Passive
245:52 [33mwarning[0m 'is required' may be passive voice. Use active voice if you can. write-good.Passive
246:6 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/tools/swarmctl-cli.md[0m
70:62 [33mwarning[0m 'multiple' is too wordy. write-good.TooWordy
119:19 [33mwarning[0m 'was generated' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/tools/hivectl-cli.md[0m
250:118 [33mwarning[0m 'is created' may be passive voice. Use active voice if you can. write-good.Passive
506:331 [33mwarning[0m 'is required' may be passive voice. Use active voice if you can. write-good.Passive
506:357 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
644:9 [33mwarning[0m 'is staged' may be passive voice. Use active voice if you can. write-good.Passive
907:19 [33mwarning[0m 'was generated' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/tools/forge-cli.md[0m
146:68 [33mwarning[0m 'is blocked' may be passive voice. Use active voice if you can. write-good.Passive
176:192 [33mwarning[0m 'is created' may be passive voice. Use active voice if you can. write-good.Passive
246:161 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
284:48 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
296:37 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
345:51 [33mwarning[0m 'is blocked' may be passive voice. Use active voice if you can. write-good.Passive
351:54 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
351:90 [33mwarning[0m 'is blocked' may be passive voice. Use active voice if you can. write-good.Passive
352:47 [33mwarning[0m 'is blocked' may be passive voice. Use active voice if you can. write-good.Passive
363:43 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
363:79 [33mwarning[0m 'is blocked' may be passive voice. Use active voice if you can. write-good.Passive
371:37 [33mwarning[0m 'is blocked' may be passive voice. Use active voice if you can. write-good.Passive
401:51 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
407:87 [33mwarning[0m 'is set' may be passive voice. Use active voice if you can. write-good.Passive
418:40 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
461:159 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
483:78 [33mwarning[0m 'are left' may be passive voice. Use active voice if you can. write-good.Passive
490:72 [33mwarning[0m 'is blocked' may be passive voice. Use active voice if you can. write-good.Passive
654:67 [33mwarning[0m 'are left' may be passive voice. Use active voice if you can. write-good.Passive
706:161 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
744:48 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
756:37 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
805:55 [33mwarning[0m 'is blocked' may be passive voice. Use active voice if you can. write-good.Passive
811:54 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
811:90 [33mwarning[0m 'is blocked' may be passive voice. Use active voice if you can. write-good.Passive
812:47 [33mwarning[0m 'is blocked' may be passive voice. Use active voice if you can. write-good.Passive
823:43 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
823:79 [33mwarning[0m 'is blocked' may be passive voice. Use active voice if you can. write-good.Passive
831:37 [33mwarning[0m 'is blocked' may be passive voice. Use active voice if you can. write-good.Passive
861:51 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
867:87 [33mwarning[0m 'is set' may be passive voice. Use active voice if you can. write-good.Passive
878:40 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
921:159 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
980:94 [33mwarning[0m 'is given' may be passive voice. Use active voice if you can. write-good.Passive
988:84 [33mwarning[0m 'be cloned' may be passive voice. Use active voice if you can. write-good.Passive
1041:76 [33mwarning[0m 'in addition' is too wordy. write-good.TooWordy
1041:123 [33mwarning[0m 'is set' may be passive voice. Use active voice if you can. write-good.Passive
1042:23 [33mwarning[0m 'Maximum' is too wordy. write-good.TooWordy
1139:240 [33mwarning[0m When referring to a person, consider using 'correct', 'adequate', 'sufficient', 'consistent', 'valid', 'coherent', 'sensible', or 'reasonable' instead of 'sane'. alex.Ablist
1402:19 [33mwarning[0m 'was generated' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/trust-boundary/boundary.md[0m
5:32 [33mwarning[0m 'is tracked' may be passive voice. Use active voice if you can. write-good.Passive
52:64 [33mwarning[0m 'are served' may be passive voice. Use active voice if you can. write-good.Passive
58:61 [33mwarning[0m 'be bound' may be passive voice. Use active voice if you can. write-good.Passive
60:62 [33mwarning[0m 'is treated' may be passive voice. Use active voice if you can. write-good.Passive
99:27 [33mwarning[0m 'is shared' may be passive voice. Use active voice if you can. write-good.Passive
112:13 [33mwarning[0m 'is declared' may be passive voice. Use active voice if you can. write-good.Passive
122:46 [33mwarning[0m 'therefore' is too wordy. write-good.TooWordy
124:15 [33mwarning[0m 'all of' is too wordy. write-good.TooWordy
131:21 [33mwarning[0m 'be opened' may be passive voice. Use active voice if you can. write-good.Passive
151:53 [33mwarning[0m 'is added' may be passive voice. Use active voice if you can. write-good.Passive
157:4 [33mwarning[0m 'equivalent' is too wordy. write-good.TooWordy
[4mdocs/web-ui/README.md[0m
107:17 [33mwarning[0m Consider using 'simple', 'indigenous', or 'hunter-gatherer' instead of 'primitive'. alex.Race
131:28 [33mwarning[0m Use first person (such as 'I') sparingly. Microsoft.FirstPerson
[4mdocs/turn-loop/config.md[0m
107:8 [33mwarning[0m 'additional' is too wordy. write-good.TooWordy
205:33 [33mwarning[0m 'evaluate' is too wordy. write-good.TooWordy
219:21 [33mwarning[0m When referring to a person, consider using 'turned off', 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'Disabled'. alex.Ablist
263:42 [33mwarning[0m Use first person (such as 'I') sparingly. Microsoft.FirstPerson
292:66 [33mwarning[0m 'is documented' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/trust-boundary/security.md[0m
13:1 [33mwarning[0m 'is trusted' may be passive voice. Use active voice if you can. write-good.Passive
16:25 [33mwarning[0m 'is privileged' may be passive voice. Use active voice if you can. write-good.Passive
17:61 [33mwarning[0m 'therefore' is too wordy. write-good.TooWordy
25:64 [33mwarning[0m 'be stopped' may be passive voice. Use active voice if you can. write-good.Passive
39:35 [33mwarning[0m When referring to a person, consider using 'foolish', 'ludicrous', 'speechless', or 'silent' instead of 'dumb'. alex.Ablist
39:35 [33mwarning[0m Don't use 'dumb', it's profane. alex.ProfanityLikely
39:61 [33mwarning[0m 'is affected' may be passive voice. Use active voice if you can. write-good.Passive
82:17 [33mwarning[0m 'therefore' is too wordy. write-good.TooWordy
91:57 [33mwarning[0m 'equivalent' is too wordy. write-good.TooWordy
196:24 [33mwarning[0m 'therefore' is too wordy. write-good.TooWordy
212:62 [33mwarning[0m 'is tracked' may be passive voice. Use active voice if you can. write-good.Passive
226:49 [33mwarning[0m 'be steered' may be passive voice. Use active voice if you can. write-good.Passive
280:26 [33mwarning[0m 'is scoped' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/turn-loop/mcp.md[0m
117:34 [33mwarning[0m 'equivalent' is too wordy. write-good.TooWordy
143:3 [33mwarning[0m When referring to a person, consider using 'turned off', 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'disabled'. alex.Ablist
219:8 [33mwarning[0m 'is disallowed' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/turn-loop/claude-invocation.md[0m
116:54 [33mwarning[0m 'is gone' may be passive voice. Use active voice if you can. write-good.Passive
237:8 [33mwarning[0m 'is shared' may be passive voice. Use active voice if you can. write-good.Passive
245:32 [33mwarning[0m 'is set' may be passive voice. Use active voice if you can. write-good.Passive
247:30 [33mwarning[0m 'are mounted' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/web-ui/css-vars.md[0m
36:12 [33mwarning[0m 'all of' is too wordy. write-good.TooWordy
141:35 [33mwarning[0m 'all of' is too wordy. write-good.TooWordy
[4mdocs/web-ui/design-guide.md[0m
8:30 [33mwarning[0m Consider using 'simple', 'indigenous', or 'hunter-gatherer' instead of 'primitive'. alex.Race
18:24 [33mwarning[0m 'multiple' is too wordy. write-good.TooWordy
19:41 [33mwarning[0m 'multiple' is too wordy. write-good.TooWordy
34:30 [33mwarning[0m 'minimize' is too wordy. write-good.TooWordy
49:67 [33mwarning[0m 'is hidden' may be passive voice. Use active voice if you can. write-good.Passive
94:56 [33mwarning[0m 'is hidden' may be passive voice. Use active voice if you can. write-good.Passive
131:61 [33mwarning[0m 'minimum' is too wordy. write-good.TooWordy
145:21 [33mwarning[0m Consider using 'simple', 'indigenous', or 'hunter-gatherer' instead of 'primitive'. alex.Race
149:55 [33mwarning[0m 'consolidate' is too wordy. write-good.TooWordy
155:26 [33mwarning[0m Consider using 'simple', 'indigenous', or 'hunter-gatherer' instead of 'primitive'. alex.Race
158:1 [33mwarning[0m Consider using 'simple', 'indigenous', or 'hunter-gatherer' instead of 'primitive'. alex.Race
[4mdocs/web-ui/shape.md[0m
41:17 [33mwarning[0m 'is sanitized' may be passive voice. Use active voice if you can. write-good.Passive
127:51 [33mwarning[0m 'are unaffected' may be passive voice. Use active voice if you can. write-good.Passive
131:46 [33mwarning[0m Use first person (such as 'me') sparingly. Microsoft.FirstPerson
184:1 [33mwarning[0m 'multiple' is too wordy. write-good.TooWordy
195:66 [33mwarning[0m 'is sanitized' may be passive voice. Use active voice if you can. write-good.Passive
230:7 [33mwarning[0m 'is hidden' may be passive voice. Use active voice if you can. write-good.Passive
232:32 [33mwarning[0m 'are reused' may be passive voice. Use active voice if you can. write-good.Passive
247:43 [33mwarning[0m 'is gone' may be passive voice. Use active voice if you can. write-good.Passive
253:1 [33mwarning[0m Use 'select' instead of the input-specific verb 'swipes'. Microsoft.UIVerbs
301:28 [33mwarning[0m 'is set' may be passive voice. Use active voice if you can. write-good.Passive
[4mdocs/web-ui/agent.md[0m
49:62 [33mwarning[0m 'multiple' is too wordy. write-good.TooWordy
75:21 [33mwarning[0m 'is enabled' may be passive voice. Use active voice if you can. write-good.Passive
78:34 [33mwarning[0m Use 'select' instead of the input-specific verb 'click'. Microsoft.UIVerbs
95:59 [33mwarning[0m 'are typed' may be passive voice. Use active voice if you can. write-good.Passive
131:34 [33mwarning[0m Reconsider using 'trap', it may be profane. alex.ProfanityMaybe
135:43 [33mwarning[0m 'is unchanged' may be passive voice. Use active voice if you can. write-good.Passive
176:36 [33mwarning[0m 'been removed' may be passive voice. Use active voice if you can. write-good.Passive
270:48 [33mwarning[0m When referring to a person, consider using 'turned off', 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'invalid'. alex.Ablist
271:39 [33mwarning[0m When referring to a person, consider using 'turned off', 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'invalid'. alex.Ablist
273:12 [33mwarning[0m 'is called' may be passive voice. Use active voice if you can. write-good.Passive
335:3 [33mwarning[0m 'subsequent' is too wordy. write-good.TooWordy
367:35 [33mwarning[0m 'minimum' is too wordy. write-good.TooWordy
377:56 [33mwarning[0m Use 'select' instead of the input-specific verb 'clicks'. Microsoft.UIVerbs
[4mdocs/web-ui/dashboard.md[0m
61:51 [33mwarning[0m 'be confused' may be passive voice. Use active voice if you can. write-good.Passive
183:32 [33mwarning[0m 'equivalent' is too wordy. write-good.TooWordy
251:46 [33mwarning[0m 'is checked' may be passive voice. Use active voice if you can. write-good.Passive
261:35 [33mwarning[0m 'is set' may be passive voice. Use active voice if you can. write-good.Passive
314:25 [33mwarning[0m 'is unchanged' may be passive voice. Use active voice if you can. write-good.Passive
342:29 [33mwarning[0m 'is tooltipped' may be passive voice. Use active voice if you can. write-good.Passive
386:13 [33mwarning[0m 'however' is too wordy. write-good.TooWordy
447:50 [33mwarning[0m 'is written' may be passive voice. Use active voice if you can. write-good.Passive
451:65 [33mwarning[0m 'are named' may be passive voice. Use active voice if you can. write-good.Passive
466:22 [33mwarning[0m When referring to a person, consider using 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'special'. alex.Ablist
524:29 [33mwarning[0m When referring to a person, consider using 'turned off', 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'Disabled'. alex.Ablist
537:33 [33mwarning[0m 'originally-active' doesn't need a hyphen. Microsoft.Hyphens
763:29 [33mwarning[0m 'is stopped' may be passive voice. Use active voice if you can. write-good.Passive
771:48 [33mwarning[0m 'is enabled' may be passive voice. Use active voice if you can. write-good.Passive
778:14 [33mwarning[0m 'all of' is too wordy. write-good.TooWordy
798:22 [33mwarning[0m 'is stopped' may be passive voice. Use active voice if you can. write-good.Passive
830:3 [33mwarning[0m 'multiple' is too wordy. write-good.TooWordy
856:16 [33mwarning[0m 'exclusively' is too wordy. write-good.TooWordy
951:33 [33mwarning[0m Reconsider using 'trap', it may be profane. alex.ProfanityMaybe
1016:23 [33mwarning[0m When referring to a person, consider using 'turned off', 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'disabled'. alex.Ablist
1017:22 [33mwarning[0m 'is mixed' may be passive voice. Use active voice if you can. write-good.Passive
1027:22 [33mwarning[0m When referring to a person, consider using 'turned off', 'has a disability', 'person with a disability', or 'people with disabilities' instead of 'disabled'. alex.Ablist
1028:63 [33mwarning[0m 'satisfy' is too wordy. write-good.TooWordy
1038:9 [33mwarning[0m 'multiple' is too wordy. write-good.TooWordy
1075:47 [33mwarning[0m 'is set' may be passive voice. Use active voice if you can. write-good.Passive
1077:57 [33mwarning[0m 'be created' may be passive voice. Use active voice if you can. write-good.Passive
1109:50 [33mwarning[0m 'is focused' may be passive voice. Use active voice if you can. write-good.Passive
1256:17 [33mwarning[0m 'is written' may be passive voice. Use active voice if you can. write-good.Passive
1272:3 [33mwarning[0m 'is sent' may be passive voice. Use active voice if you can. write-good.Passive
1273:15 [33mwarning[0m 'previously' is too wordy. write-good.TooWordy
1286:3 [33mwarning[0m 'is untouched' may be passive voice. Use active voice if you can. write-good.Passive
1391:27 [33mwarning[0m 'is gone' may be passive voice. Use active voice if you can. write-good.Passive
✖ [31m0 errors[0m, [33m493 warnings[0m and [34m0 suggestions[0m in 50 files. failed
with "vale: not found" even though hive-rules says to gate locally with
the devshell — a builder had to reconstruct CI's incantation by hand.
Local command: XDG_DATA_HOME=$PWD/.vale-data nix develop -c sh -c 'vale sync && vale docs'
The queue's payload ceiling was justified by what the queue was about to
carry; it carries it now, so the comment says so.
The other two sites say "a hive with no queue configured". The swarm has
exactly one queue and a hive cannot lack it — only its coordinates, its
credential, or its ability to reach it. That wording is already used
everywhere else the absence is named; these two predate it.
The docs section on the agents' queue coordinates stopped at delivering
them and never said what the connection is for. It now names the subject
and the degrade rule, which is the part an operator reading an agent's
terminal at the swarm needs.
Refs #3805
The responder has had an agent arm since the principal was minted, but
no deployment ever passed `--agent-publish-subject`, and an empty list
is a refusal by design: `Policy::permissions` returns `None` rather than
a grant that can do nothing, so every agent was turned away at CONNECT.
The subject itself is the one this thread settled on, `$SWARM.term`
namespaced per hive.
The value has to reach the responder with a literal dollar. systemd
substitutes `$NAME` in `ExecStart` whether or not the word is quoted,
so a single dollar expands `SWARM` — unset, therefore empty — and the
responder is handed `.term.{hive}.>`. That grant validates (it carries
the `{hive}` placeholder), is accepted, and matches nothing any agent
publishes to, so the failure surfaces as an authorization violation far
from its cause. `$$` in the unit text is the escape for one dollar.
The module-eval case reads the rendered unit rather than the module
source, because the single-dollar version renders perfectly well; the
doubled dollar is the only thing that distinguishes them before deploy.
The grant is per-hive, not per-agent: an agent's identity names its
hive, so any agent in a hive can publish as another. That is the
tradeoff ruled acceptable for now, tracked separately for tightening.
Refs #3805
The swarm always has exactly one queue; a hive can only lack its
address. Reworded every prose site this PR added that stated or
implied the opposite, to name what is actually absent (coordinates,
credential, or address) instead of the queue itself.
Refs #3805
The host end: `HIVE_C0RE_AGENT_QUEUE_CREDENTIAL_DIR` tells the daemon
where the reader unit put the files, and a new
`deploy.hive-controller.queue.agentNatsUrl` says where the queue is as an
agent *container* reaches it. That address defaults to the bridge one and
never to loopback — `statusPublish.natsUrl` beside it is loopback and
correct, because hive-c0re shares the host netns and an agent does not.
Paired with the swarm's token endpoint, gated together, and forwarded by
`hive_c0re::meta` as both an env var and an agent option: the harness
reads the variable at runtime, its unit is built from the option.
The agent end: `nix/agent-modules/queue.nix` declares that option pair
and, when set, has the harness unit inherit the two credentials by name.
Bare-id `LoadCredential=` is the terse form documented for inheriting
what the service manager received, and is non-fatal when the credential
is absent — which a hive whose publisher has not run yet needs.
No `HIVE_AGENT_OIDC_CA_FILE`: the meta flake already embeds the hive CA
and the swarm root into each container's trust store at build time, and
reqwest's rustls backend verifies against it.
Refs #3805
Ruled: swarm-bao-queue-agent.service must run before hive-c0re.service
and be wanted (not required) by it, so no agent container renders
ahead of the reader's attempt at its credential. An unreachable store
delays hive-c0re's start by the reader's own start-limit window rather
than failing it outright.
Refs #4314
The swarm queue's rendered settings never named max_payload, so it ran
on nats-server's upstream default of 1 MiB. That default is about to
be too small: the broker is going to carry agent terminal rows
(whole TermMsg bodies) published as complete messages rather than
split, and a publish over the limit does not truncate — the server
answers -ERR 'Maximum Payload Violation' and closes the connection,
dropping the row.
Set max_payload = 8388608 explicitly in the settings merge, with a
comment on what it bounds and what bounds it (max_pending, which
nats-server refuses to start past). Add a module-eval case that reads
the rendered container config so a future edit that drops or shadows
the key fails eval instead of surfacing as a dropped row in
production.
Refs #3805
The publisher on the authelia host has been writing
`secret/swarm/hives/<hive>/queue/agent` — the OIDC client secret agent
containers present to the swarm queue, plus the client id it belongs to —
and nothing read it. This is the reader: a oneshot `swarm-bao-queue-agent`
that logs in with the host's certificate and lands the two fields as two
files under `deploy.hive-controller.queue.agentCredentialDir`, the secret
`0600` and the client id `0644`.
Two files rather than one because that is the consumer's shape:
`swarm_queue_client::QueueConfig::from_env` takes the secret as a path and
the client id as a value, so the split here is what keeps the next slice
from parsing anything.
Same shape as the store's first reader, `glue-matrix-bao-token.nix` — a
cert login that fails loudly under `Restart=on-failure` because every state
it fails on is one a retry fixes, then reads that degrade quietly because no
retry turns "no value there" into a value. Unlike the matrix token there is
no local fallback and none is possible, so absent files mean this hive's
agents do not connect, which is the ordinary state of a swarm before the
publisher has run.
Nothing consumes the files yet and this unit is ordered `Before=` nothing.
The next slice bind-mounts them into agent containers through hive-c0re and
adds the ordering edge along with them.
Refs #3805
`swarm-bao-matrix-token` reads `secret/swarm/matrix/registration-token`
and is refused with `Code: 403 — permission denied`, measured on this
host at 16:17:49Z after a successful cert login.
path.rs makes every swarm path `swarm/<kind>/<name>/…` where Kind is a
closed set of four: agents, hives, services, controller. `matrix` sits
where a kind belongs, so policy.rs's read document — which emits exactly
`swarm/agents/*` and `swarm/hives/<hive>/*` — cannot cover it. The
module's own doc predicted this: "a misspelled kind is a 403 at provision
time rather than anything a compiler sees".
Moves the token to `swarm/hives/<hive>/matrix/registration-token`, built
through principal_prefix(Kind::Hive, …) like its per-hive sibling
queue::agent_client_path. The policy is untouched: render already grants
that prefix. mara chose this over widening the namespace.
The nix reader interpolates hyperhiveCfg.hiveName, with the no-fallback
reasoning glue-bao-tls.nix already gives at its own use of it.
path.rs's MOUNT doc justified itself by citing the old literal, which
this commit deletes; rewritten to cite the nix reader instead.
Scope: this makes the read reachable, not the value present. Nothing
writes that path yet, and a 403 says nothing about presence — the two
are separate findings and only the first is fixed here. No migration:
nothing ever wrote the old path and no read ever succeeded.
Gate: cargo fmt 0, cargo test -p swarm-secret-client 0 — 32 passed
against a 29-passed baseline with the change stashed, so the three new
tests are accounted for rather than assumed.
Refs #4308
Every agent container reports the hostname `nixos`, so every log line it
ships carries that as its `_HOSTNAME`. Measured: host `muede-lpt2`,
`hive-matrix` (declared as `containers.hive-matrix`) `hive-matrix`, and
`h-atlas` `nixos`.
nixpkgs sets the hostname in the merge function of the
`containers.<name>.config` option (nixos-containers.nix:524), so it reaches
a guest evaluated through that option and nothing else. Agent containers are
`nixos-container create --flake meta#<name>` — an independent `nixosSystem`
off the meta flake, which never evaluates that submodule.
nspawn also names a container's hostname after the machine by default; that
is ruled out as the source here, because `h-atlas`'s machine name is
`h-atlas` and it reports `nixos`.
The machine name rather than the logical one: `stats/otel_metrics.rs:345`
already labels metrics `container.name = "h-<name>"`, so the logical name
would make logs say `atlas` while metrics say `h-atlas` — a prefix transform
on every join between the two signals. Declarative containers and nspawn
both use the machine name too, so this is one rule with no exception for
agents.
The emission sits next to `hyperhive.user.name = name;`, which already
derives the container's unix user from the agent name; the hostname was the
one identity attr nobody wired.
Also drops the prose in swarm-otel.nix and module-eval.nix that explained
`_MACHINE_ID` by "every container is `nixos`" — that motivating example is
what this commit removes, and the argument for `_MACHINE_ID` never depended
on it.
Checked before editing: nothing in the tree assumes the hostname is
"nixos" (0 hits across *.rs and *.nix), and nothing reads the hostname at
runtime.
Gate: cargo fmt 0, clippy -D warnings 0, cargo test -p hive-c0re meta::
0 (23 run, 22 passed, 1 ignored), nix fmt 0 changed.
Refs #4304
The retry comment claimed "every retry is time the homeserver may spend
waiting". That is the conservative reading, not a measured one, and argus
is right that it is probably wrong: systemd ordering typically resolves
once a unit's first start job completes, success or failure, and an
auto-restart after that is not a new ordering-relevant job.
If that holds, the container proceeds after the first failed attempt --
same timing as today -- and what the retries buy is the token file being
correct within ~60s for whatever starts next, rather than this boot's
race getting a second chance.
Both readings justify the same tight bound, for different reasons, so the
comment now says the ordering behaviour is unverified instead of picking
one. An agent container cannot reach a systemd manager to settle it.
Refs #4303
The unit treats every failure as permanent: it prints why and `exit 0`s,
with no `Restart=`, so one bad moment costs the whole boot. Two of its
failure modes deserve that and one does not.
`bao login` fails when the store is unreachable, sealed, or has not been
given this host's cert-auth role yet. All three are transient. Measured
on this morning's rebuild:
11:24:05 Started Container 'swarm-bao'
11:24:06 could not log in to swarm-bao with this host's certificate
11:24:07 Success! Data written to: auth/cert/certs/swarm-secret-publisher
It lost by one second, and stayed degraded for the boot. The publisher
next to it hit the same race and recovered on its first retry, because it
has `Restart=on-failure`.
`bao kv get` returning nothing is the opposite: the store answered, and
holds no token at that path. A retry cannot improve it, so that branch
keeps `exit 0` and the local token.
The bound is sized for this race, not for an unseal.
`swarm-bao-controller-policy` waits 2880 x 30s because a shamir unseal is
a human action and that unit blocks nothing. This one is `Before=` the
homeserver's container, so every retry is time the homeserver may spend
waiting -- 4 x 15s covers a container-start race with margin, and a store
still sealed after it degrades exactly as it does today.
`StartLimit*` are `[Unit]` settings and are ignored under `[Service]`, so
they are top-level attrs here. The module-eval case asserts the window
outlasts `RestartSec x burst`, since a burst that cannot be reached is a
unit that looks like it retries and does not.
Refs #4303
The previous commit's comments explained `_HOSTNAME` being `nixos` with
"no container sets `networking.hostName`", read off a `git grep` of this
tree. That grep answers a question about our source; the default is
nixpkgs': `nixos/modules/virtualisation/nixos-containers.nix` sets
`networking.hostName = mkDefault name` for every `containers.<name>`
guest, which is how every swarm service container here is declared.
So the stated cause is wrong, and why the default does not reach these
guests is still open. What is measured is narrower: an agent container
reports `nixos`, and `_MACHINE_ID` is per-machine by construction. The
comments now claim only that, and say the rest is unresolved.
The code is unchanged — `_MACHINE_ID` in `_stream_fields` is correct
regardless of which explanation turns out to be true.
Refs #4304
`_stream_fields=_HOSTNAME,_SYSTEMD_UNIT` was chosen to give "one stream
per unit per machine", which is what the comment above it claims. It does
not: no container sets `networking.hostName`, so `_HOSTNAME` is the NixOS
default `nixos` in every one of them, and every container's stream for a
given unit name merges into a single series.
Measured from inside an agent container:
journalctl -n1 -o json -> "_MACHINE_ID":"5d1427ea…", "_HOSTNAME":"nixos"
cat /etc/machine-id -> 5d1427ea…
git grep -i hostname -- nix/ -> 20 hits, all public vhost names,
zero `networking.hostName =`
`_MACHINE_ID` is written by journald per machine and is already on every
entry, so adding it to the stream key makes the partition what its own
comment says it is. That merge is also why the host collector and the
containerised one were one series: both log to
`opentelemetry-collector.service`, and the field meant to separate them
was a constant.
The second comment touched here asserted a reader "can still tell the
origins apart" from `_HOSTNAME`/`_SYSTEMD_UNIT`/`_MACHINE_ID`. True only
of the last one; it now says so.
Not a full fix for attribution: `_MACHINE_ID` is opaque hex and nothing
maps it to a container name. Naming is the other half and is a separate
change.
Refs #4304
`BAO_CLIENT_CERT` decides which certificate the TLS handshake presents. It is
not an identity: cert auth is a login, and a `bao kv` call without a token asks
its token helper for one instead — a `sh` neither unit carries on `path`.
Measured on this host, from `swarm-bao-matrix-token.service`:
swarm-bao did not return secret/swarm/matrix/registration-token
failed to get token helper: error expanding config path "":
exec: "sh": executable file not found in $PATH
So the first credential meant to travel through the store never has, and the
publisher added last week would not have either.
`-token-only` rather than a plain login: storing is the default, and it stores
through that same helper, so the obvious form reproduces the failure one line
further down. It is `-field=token -no-store`, which keeps the token on stdout
and out of the filesystem.
The two units degrade differently on purpose, and that is preserved. The matrix
fetch is `Wants=`-only and must not hold up the homeserver, so a refused login
reports why and keeps the token already in place. The publisher is
`Restart=on-failure`, where a store that cannot authenticate this host is worth
retrying and "published 0" would read as an ordinary quiet day.
`swarm-bao.nix` is untouched: it authenticates with the bootstrap token from a
file, which is a real identity and not a cert exchange. Its shape is where the
export idiom here comes from.
Closes#4282. mara: "scope looks good" — approving the plan posted
there (manifest + icons + minimal shell-only service worker + iOS meta
tags) and both explicit questions (network-first-with-offline-fallback,
never cache /api/*).
docs/web-ui/design-guide.md's "Layout & viewport" section already
asserted swarm-ui is installable as a PWA — this is what actually backs
it.
- manifest.webmanifest: name/icons/start_url/standalone display, theme
#cba6f7 / background #1e1e2e matching the mocha --purple/--bg values.
- sw.js: plain JS, not TypeScript — the DOM lib swarm-ui's own tsconfig
uses and the WebWorker lib a service worker's globals need are
mutually exclusive in one tsc program, not worth a second tsconfig for
a self-contained ~100-line file. Scoped to the app shell only, never
touches /api/* at all, network-first with offline-fallback-to-cache
(not cache-first) since main.js/main.css are unhashed filenames and a
cache-first SW would risk wedging an operator on stale JS after a
deploy.
- index.html: manifest link, theme-color meta, iOS
apple-mobile-web-app-* tags (Safari ignores the manifest spec).
- main.tsx: feature-detected SW registration.
- branding/hyperhive-maskable.svg: hyperhive.svg's own artwork already
fills nearly its whole canvas, so a maskable icon needs a padded
variant or an OS mask crops the outer ring/corner brackets — embeds
the original via a scaled <image> ref rather than duplicating markup.
- nix/packages/swarm-ui.nix: rasterizes icon-192/512/512-maskable.png
from the branding SVGs at build time via librsvg, rather than
checking in static PNGs.
Verified for real: typecheck+build green, and a real headless-chromium
tab driven over CDP confirms the service worker registers and becomes
the active controller, and a simulated-offline reload still serves the
full cached shell rather than a browser error page. nix build .#swarm-ui
also verified green, including the rasterized icon output.
Five cases: the leaf and the pairing that points at it, that the push
hands bao a path rather than the secret, a path per hive in the roster,
that a publisher holding an identity renders on a host with no store,
and the control that renders none without an identity.
They fail on this branch, and that is the finding. The suite was green
at 100 cases with the publisher already committed, because no fixture
enabled both authelia and a store identity — so the module's `config`
never activated and its script was never evaluated. An imported module
whose config never fires is as unmeasured as an unimported one.
What they surface: `swarm.authelia.agentClientSuffix` does not exist on
main. The agent principal these secrets belong to is minted by the PR
for the agent queue principal, whose branch adds both the option and the
`agentClients` list authelia mints from. Delivery of a secret nothing
mints cannot evaluate, let alone run — so that PR lands first and this
one rebases onto it.
The argv case strips comments before matching, which it earned: a
`script` renders its own comments into the text, and this unit's
comments name the hazard verbatim so the next editor does not
reintroduce it. Matching the raw text read that warning and failed —
a check the artifact defeats by describing the thing it is checked for.
Refs #3853
A hive that does not host authelia has no path to its own agent queue
client secret. The mint writes the plaintext to a host directory whose
other reader lives in a different container, so the host that mints is
the only place both trees are addressable — which is where this unit
runs.
Four pieces, in the order they depend on each other: the leaf
(glue-bao-tls.nix signs it, because the thing that owns a private key
owns issuing from it), the module declaring its own cert/key options,
the one-pairing glue file pointing them at that leaf, and the imports.
The unit is gated on holding a client identity, never on
deploy.bao.enable — that option is the co-location assumption itself,
and the publisher is the case that assumption excludes.
The secret is passed to bao as `value=@<path>`, never as an argv
element: bao is an external binary, so an argument is world-readable in
/proc for the life of the call.
Refs #3853
A sibling unit rather than more script in swarm-bao-controller-policy, because
that unit's name is an operator-facing string: docs/getting-started/setup.md
tells a reader to run `systemctl status swarm-bao-controller-policy`. Widening
it to two principals makes the name wrong; renaming it makes the instruction
wrong.
`after` and not `requires`. The controller's unit creates the KV and cert-auth
mounts this one writes into, so the ordering is real — but a failed oneshot
still counts as finished, so `requires` would neither wait for its success nor
re-run this unit when the sibling's own retry eventually lands. Ordering plus
this unit's `Restart=on-failure` is what converges.
Four module-eval cases, because the unit arrived with every claim about it in
prose and the suite still reporting the same count: the grant is write-only and
reaches the hive prefix alone (pinned as the whole capability list, since an
added capability is what a presence check misses, with negative arms for the
agent prefix, the bare swarm prefix and the policy path); it is ordered after
the unit that creates the mounts; it renders on the host; and the control, that
it does not render inside the store's container.
Refs #3853
Write-only `create`/`update` on `secret/data/swarm/hives/*`, and nothing else.
It copies secrets in and never reads one back; a read capability would let a
file-copier recover every hive's credentials rather than merely replace them.
`hives/` and not `swarm/*` because this principal has no business with an
agent's or a service's credentials, and the hive prefix is the only one whose
paths it produces. `secret/data/` is KV v2's ACL prefix, inserted by the engine
rather than written by the caller — the same trap the controller's grant
documents one binding up.
Named outside `hive-*`: the controller may create policies under that prefix,
and a policy it can rewrite is not a constraint on it.
The unit that writes this lands next. Refs #3853
The unit that will copy authelia's minted OIDC client secrets into the store
needs an identity of its own. Not the controller's: that grant includes
rewriting every hive's policy and login role, which a unit whose whole job is
copying one file has no business holding.
The subject joins `certAuthCns`, so it is unrepresentable as a hive name for
the same reason the controller's is — cert auth trusts the CA, and a hive's
own leaf carries its name as the CN.
The module-eval case collides with the SECOND list element and leaves the
controller's subject at its default. A list with one consulted element and one
dead one is indistinguishable from the first element's case, so without this
the addition could be inert and nothing would say so.
Refs #3853
mara, PR review: "i want to see it over time - sounds like you built
bar chart?" — correct, it was a bargauge (instant/lastNotNull). Switched
to timeseries, matching the CPU/memory-by-agent panels' own treatment of
a plain live gauge (raw value plotted across the range, no rate()/
increase() since it's not a cumulative counter).
Closes#4284. hyperhive.agent.claude_md.lines is already exported (see
hive-agent's claude_md_watch module) but had no dashboard panel — this
is the display half.
Same bargauge shape as the two sibling per-agent gauges already in this
file (container storage, active time): sum by (agent), instant query,
lastNotNull reduction, since it's a continuously-live gauge rather than
a cumulative counter. Metric name uses the dotted OTLP form per this
file's own documented convention for this store.
A collector's `refused` / `failed` / queue-depth counters are the only
signal that says telemetry is being dropped, and nothing read them at
any tier — so a collector losing records looked exactly like a quiet
system.
The hive tier could not be scraped without first naming its port. 8888
is the collector's built-in default and appeared in no config, which is
also why nothing comparing configured ports could see it clash with a
co-located collector — swarm-otel.nix sidesteps 8888 by hand for that
reason, and says so. Declaring the port and binding it explicitly makes
the value comparable; wiring the scrape is then one entry per tier.
Extending the port-collision assertion to cover it is deliberately left
out: that belongs with the other port work, and coupling a collision
fix to a scraping fix makes both harder to review.
Gate: 101 module properties hold, was 95. The six cases pin the rendered
scrape job rather than the option; the metrics pipeline naming the
prometheus receiver, a path never emitted on any hive before this since
the hive tier's scrapeTargets was empty everywhere; the `readers`
spelling, with a control so a missing telemetry block cannot pass the
port check vacuously; the swarm tier's own entry; the two tiers not
claiming the same port; and the absence arm, a hive with no collector
declaring no target.
scrapeTargets' description said "Empty by default, and that is the
shipped case". This makes that false, so the paragraph moves with it.
`agent-<hive>` reads as "the agent named <hive>" — which is the one thing that
identity does not carry, since it is minted per hive. It becomes
`hive-<hive>-agent`: the hive's own id, extended.
The rename is not a string swap. `hive-foo-agent` satisfies the hive parse too
(it strips to a hive named `foo-agent`), so the responder's agent rule now runs
BEFORE its hive rule — most specific wins. Hive-first would have handed every
agent its hive's grant, including writing that hive's status key, with nothing
to report it: the client authenticates and is merely able to do more than it
should.
`Policy::new`'s overlap check goes with the prefix it was written for. The
invariant the suffix form needs instead is that the suffix is non-empty: an
empty one makes `strip_suffix` succeed on every hive id, so the two principals
become one string and whichever arm runs first answers for both.
The suffix form also introduces a collision the prefix form did not have: a hive
genuinely named `foo-agent` mints `hive-foo-agent`, which is hive `foo`'s agent
id. The responder cannot see it — it has no roster, deliberately — so
`swarm-authelia.nix` asserts at eval that no hive name ends with the suffix. The
existing duplicate-id assertion does not cover this: it fires only when both
`foo` and `foo-agent` are on the roster, and with `foo-agent` alone there is no
duplicate, just a hive quietly receiving its agents' grant.
A test written by analogy with `the_prefix_alone_names_no_hive` failed, correctly
— `hive--agent` is a hive named `-agent` under the hive parse, which this module
cannot rule out. It now asserts only the part this module owns: no empty hive
name is ever expanded into a subject.
Agents have authelia *users*; they had no machine identity at all, so an
agent could not authenticate to the swarm queue as anything. This mints
one `agent-<hive>` OIDC client per hive beside the existing
`hive-<hive>` one, teaches the auth-callout responder an agent arm, and
opens the queue's client port on the bridge so a container can reach it.
One client per HIVE, not per agent: agents are created at runtime, and a
per-agent client would make creating one a config change plus an
authelia reload. The cost is that agents on a hive are indistinguishable
to the broker, which is deliberate and tracked separately.
The agent grant is deny-by-default twice over. An agent id matches no
hive rule, so it gets a hive's status-key grant from neither; and with
no agent subject configured the responder returns no grant at all rather
than an empty publish list, which would be a denial wearing a grant's
shape. What an agent may publish is a deployment's decision, taken
through `--agent-publish-subject` the same way `--hive-publish-subject`
already works.
`Policy::new` now refuses two prefixes where one contains the other. The
arms are tried in order, so that overlap does not error at match time -
it silently hands one principal the other's grant.
Not shipped here, and neither is reachable without it: no subject is
configured for agents anywhere in nix, and nothing yet delivers
`agent-<hive>.secret` into an agent container. Both belong to the stream
that will be the first consumer.
The reader had one failure branch, and it could not fail. It named three
states — the store holds nothing, the store is sealed, the store is
unreachable — treated all three as success, and sent bao's stderr to
/dev/null, which is the only thing that tells them apart.
The degraded mode is right and is unchanged: a missing registration token
means new agent accounts cannot be provisioned and nothing else breaks,
so this still exits 0 and leaves the local token alone. What changes is
that the journal now carries the store's own message instead of a
sentence of ours asserting all three at once.
Deliberately not branching on that message. Distinguishing "no value
found" from "permission denied" programmatically would mean matching
prose from an external tool that I cannot reach a live instance of to
confirm, which is inventing a discriminator rather than reporting one.
Nothing asserted this script before — eleven module-eval cases cover the
unit's existence and its ordering, none its contents, which is how the
branch stayed. The case added here covers the property that was missing.
Gated: `96 module properties hold`, exit 0 (95 on the base commit, +1 =
the case added). `nix fmt` reported 0 changed over 723 files emitted in a
fresh worktree. Tracker-tag, comment-block and doc-pointer lints all exit
0 after staging.
Both places that warned about a hive named after the controller's cert-auth
subject still told the reader it was unmitigated, and the option's description
recommended a migration — "change this to something outside the hive-name
grammar, at the cost of a role rename in any store that has already run the
granting unit" — that is no longer the answer and is not cheap.
swarm.nix now feeds the subject into the guard on `swarm.hives`, so a
colliding roster fails evaluation. Both paragraphs get SHORTER saying so: a
guarded hazard needs the consequence ("reserved as a hive name") rather than
the threat model, and the write-site keeps only the sentence the next person
needs — a role added beside this one must join that list.
Found by sweeping for the claim rather than for the symbol: the change that
made these stale touched swarm.nix, swarm-otel.nix and module-eval.nix, so no
diff-context or doc-comment rule covers a paragraph two files away. Grepping
the tree for "would satisfy" and for prose about a hive named after a subject
turned up exactly these two and nothing in docs/.
The two hive-name guards lived in swarm-otel.nix, inside its
`config = lib.mkIf (… && deployCfg.swarm-otel.enable)`. A swarm running the
secret store and the controller but no collector therefore had no hive-name
check at all, while the names were still composed into OIDC client ids, bao
policies and cert-auth roles exactly the same way. They move to swarm.nix,
which declares `swarm.hives` and is unconditional. swarm-otel keeps the
assertion that its own entry is still in the shared list — that one is about
this module's stake in a file it no longer controls.
The equality guard also takes the store's cert-auth subjects now. Cert auth
trusts the CA, so `allowed_common_names` is the whole of what narrows a role
to one identity, and the same CA signs every hive's leaf with the hive's name
as its CN. A hive named after a role's subject presents a certificate that
role accepts, which for the controller is write access to every hive's
credentials and policies.
A list rather than the one string, because the next role added beside it
widens what a hive name must not collide with, and because the subject is an
option an operator sets — a literal deny entry covers the default and nothing
else.
Four module-eval cases, two of them controls. The fixture overrides the
subject to `ctl` on purpose: the default contains `swarm`, which the substring
guard catches whatever the new arm does, so a fixture using it could not tell
the two apart. The controls are that a legal roster trips neither guard, and
that all three fixtures really do have the collector disabled — without the
second, every case would pass while testing the arrangement they exist to
rule out.
The crate had a single path convention and it was per-agent:
`swarm/agents/<agent>/matrix/<account>`. The secrets still to move into the
store do not fit it — one belongs to a hive, one to a swarm service, one to
the controller itself — so each would have picked its own shape, and each
would have been a separate grant to get wrong.
mara ruled the scheme on the epic: `swarm/<kind>/<name>/<secret>`, over
`agents`, `hives`, `services` and `controller`. This lands it.
`Kind` is an enum rather than free strings for one reason: the store's grant
is written in nix and cannot be reached from Rust, so a misspelled kind is a
403 at provision time and not a compile error. `Kind::ALL` lets a test
enumerate the set instead of restating it, which is what makes adding a kind
a deliberate edit rather than an accidental grant.
Note `Kind` sits beside `checked_segment`'s existing `kind` argument, which
means something else entirely — the label of the name being validated. They
are not the same concept and should not be merged.
Nothing about the rendered policy changes. `policy::render` still grants read
on the agent kind alone; the other kinds are absent on purpose, because what a
hive may read of its own kind is a boundary question and not a consequence of
the namespace growing. The controller's write grant likewise stays scoped to
`agents/` — it widens when a path outside it gains a writer, not when the
kinds are declared.
Verified: `cargo test -p swarm-secret-client` 23 passed, 0 failed. The two
tests pinning the rendered strings (`the_document_grants_read_over_the_whole_agent_prefix`
and matrix's path assertion) still assert the same literals they did before,
which is what shows this is a faithful port rather than a reshape. `nix fmt`
710 emitted, 10 formatted, 0 changed; the three scripts/check-*.sh lints pass
with the change staged. No reference to the removed `path::AGENT_PREFIX`
survives in the crate or in nix — checked with a scoped pattern, because the
unqualified name also belongs to hive-host-sock's container prefix and greps
for it are answering a different question.
The mode was declared twice in this file — the service unit's
RuntimeDirectoryMode and the socket unit's DirectoryMode — with only a
prose "must match" note tying them. Whichever unit activates first creates
the directory, so they cannot be allowed to disagree.
Both literals are in one file, so they become a `let`. Deleting a copy
beats checking it, and unlike rendering the mode into hive-priv it costs no
config knob for a value nobody should ever set.
hive-priv's tmpfiles.d entry for the same path is a third declaration that
cannot read this binding, and is left in step by hand. An earlier revision
of this branch added a CI check for exactly that pair; mara pointed out it
was keyed to one path rather than to the class, and looking for the general
case found two more paths declared by more than one mechanism — including
/run/hive-agent, where hive-gateway's tmpfiles rule and hive-priv's
generated one disagreed on the owner and the winner depended on systemd's
read order. That check is being reworked as a general one, tracked
separately, so nothing about it rides in here.
Verified: nix fmt (713 traversed, 5 formatted, 0 changed); the three
scripts/check-*.sh lints all exit 0 with the tree staged; .forgejo/ is now
byte-identical to main and the diff is this one file. checks.module-eval
reported 91 module properties hold on the previous revision of this branch
— the only nix change since is comment text inside the same let block,
which cannot affect evaluation.
`auth.disable_login_form` was gated on `ssoLocal` — `grafana.enable &&
authelia.enable`, i.e. "both of them run on THIS host". With authelia
elsewhere in the swarm that is false, so a deployment that is very much
using SSO still rendered grafana with its local login form enabled, on a
vhost the gateway publishes, for a product that ships an `admin`/`admin`
account.
The reason that matters was already in the module, three lines up
("Grafana ships an `admin`/`admin` account, and this vhost is on the
public gateway") — it was just attached to a conditional. Whether a
password box sits on a public login page is not a per-host question.
Per mara on the docs PR for this: "grafana requires sso - no local
login". The OIDC block below stays gated on locality; making that follow
the same swarm-wide question is a larger change with its own tracking.
The module-eval suite already had the fixture this needed: `grafanaOldPath`
enables grafana and not authelia, which is exactly the shape the login
form stayed enabled in, so the regression case needs no new hive. 90 -> 91
properties.
Closes#4218.
`docs/tools/subagent.md` and `docs/tools/bash.md` both described their MCP
server as injected "unconditionally". Both entries are `lib.mkDefault`, and
the module says why one line above each: "so an agent.nix can still
override/disable the entry", "so the operator's own agent.nix can override
the entry".
The word matters for the subagent one in particular. The same comment block
records the framing that it is default-on for now and should become a real
capability gate later, so "can I turn this off today?" is a question an
operator has — and "unconditionally" answers it as "patch nix/" when the
answer is one override in agent.nix.
Both pages now say default, and say what the default yields to.
The other direction on the same page: `subagentHttpPort`'s option
description and the unit comment beside it both listed three tools,
`start`/`continue`/`interrupt`. The daemon serves four. #4101, which
introduced it, is titled with the three-verb phrasing, so `status` landed
afterwards and never reached either description — while `subagent.md` had
the full set all along. The option description renders into the generated
options doc, so it is the one an operator reads.
Closes#4231.
`nix/host-modules/swarm-peers-removed.nix` exists to turn an "option does
not exist" error into a warning that says where the entries went. That
warning is its whole deliverable, and no fixture set `swarm.peers` — the
string appeared 0 times in module-eval.nix, so the shim was never
evaluated by anything.
It differs from its ten siblings in what a broken shim looks like. The
others re-route a value, so a failure shows up as a wrong rendered
config. This one renders prose nothing reads back, so a `mkIf` that
stops matching or a rename of the `swarm.hives` it points at fails
silently, and lands on the one operator who needed it.
The check already carries an old-path fixture for ten migrated
namespaces (wireguard, forge, matrix, nats, authelia, controller, ui,
stores, grafana, statusPublish). `swarm.peers` was the eleventh and the
only uncovered one.
Two peers, only one carrying `caCert`, because the module emits a second
warning filtered on exactly that attribute — with a single peer the
filtered list and `attrNames` are the same list, so a `withCaCert` that
had collapsed into `attrNames` would still read green. The third case is
the control: a hive that never set `peers` must get neither warning,
without which the other two pass on any config whose warning list
happens to carry the string.
Closes#4188.
`deploy.allSwarmServices` derives nine service enables and appeared
nowhere in the check, so the tier a service sits on was prose only. The
tenth, the swarm controller, already had this exact pair of cases — it
rides `singleHostSwarm` instead, and swarm-ui follows the controller.
Four cases: the switch turns its nine on, a hive that does not host them
runs none, an operator placing one elsewhere still wins over the
`mkDefault`, and hosting the shared services does not make a hive the
swarm's control plane.
The roster is counted before it is read: `lib.all` over an empty set
holds vacuously, so a roster that lost a member would otherwise turn the
case green by measuring nothing.
Closes#4186