Watch
0
0
Fork
You've already forked hyperhive
0

Compare commits

...
Author SHA1 Message Date
atlas
0c50913db9 docs/swarm/credentials.md: mTLS cert is per-identity, not per-host
Two lines still framed the invariant as one file total: line 19 ('plus
exactly one file') and line 21 ('the only credential on disk') implied
a single mTLS cert page-wide, contradicting the earlier fix that made
line 26 say 'every host has at least one'. Agents and swarm-level
services each need their own identity certificate, so a host running
several holds several. Restate both as per-identity, keep the target
shape (store + certs on disk, nothing else) unchanged.

Also relax the touching-a-credential rule: moving a secret into bao
without resolving its renewal strategy in the same change is
acceptable while the migration to this shape is in progress, provided
a follow-up issue is opened to settle renewal.

Refs #4445
2026-09-16 23:29:39 +02:00
atlas
596e8f006f docs/swarm/credentials.md: every host has at least one certificate
- Change 'Every host has one' to 'Every host has at least one' to clarify
  that a host may hold multiple mTLS client certificates.
2026-09-16 23:24:54 +02:00
atlas
972b80d7b8 docs/swarm/credentials.md: fix citation range to include 'A path, never a value'
The citation for the nix option description was 529-531, but the quoted
phrase 'A path, never a value.' appears on line 533. The correct range
529-533 includes both the 'operator places by hand' claim and the
'path, never a value' assertion that the doc attributes to that file.

Refs #4445
2026-09-16 22:46:29 +02:00
atlas
cc77db0e7e docs/swarm/credentials.md: nix fmt table alignment 2026-09-16 22:41:53 +02:00
atlas
940e11ecda docs/swarm/credentials.md: cut the page down to the target shape only
Drop the current-state table and the prose describing how the swarm
currently breaks the target (unrotated secrets persisted to disk outside
the store, no agent store identity, the appservice token's second
minter). Scope the path-never-value rule to the mTLS client certificate
— the one credential a nix option can carry, now that everything else
goes through bao. Trim the migration checklist to unconditional rules
for any credential-touching change, since there's no current-state table
left for the old existing-functionality carve-outs to reference.

Refs #4445
2026-09-16 22:41:23 +02:00
atlas
f1e5fd5592 docs/swarm/credentials.md: make the target section bao-shaped, not file-shaped
The target section described the target as a shorter list of secret files —
the intro promised "what changes is how many paths there are", and the
per-secret contract pointed at the current-state table's file-shaped columns.
That is today's shape with fewer rows, not the target.

Rewrite it around the invariant: no secret the store holds is ever written to
disk. The mTLS client certificate is the sole credential on disk, and the
section now says why — it authenticates the puller to the store, so it is the
one credential that cannot be fetched from it. Per secret the target still
states minter, reader and renewal, but the reader is now a process pulling a
store path at runtime under its own certificate, not a path being read off
disk.

The current-state section and all of its citations are untouched: the current
state genuinely is files, and that contrast is what the page is for.
2026-09-16 22:41:23 +02:00
atlas
ae388c48a7 docs/swarm/credentials.md: restore claims changed by vale rewrite
Two vale rewrites in c33cc441 changed meaning, not just wording:
- "on track to become" asserted a trajectory the page's own inventory
  contradicts (no bao identity anywhere, all renewals NONE, all
  secrets plaintext). Restored intent: "what it should become".
- "most often misread" claimed an observed reader frequency nobody
  measured. Dropped the claim about readers entirely; states the fact
  directly instead.

Both stay vale-clean; no suppressions.
2026-09-16 22:41:23 +02:00
atlas
b2c1378fd3 docs/swarm/credentials.md: reword prose to pass vale
Fixes the Microsoft.Contractions/write-good.ThereIs/alex.Condescending/
Microsoft.Auto errors vale flagged in CI (prose-lint-errors, required):
contractions throughout, no sentences opening with 'There is', dropped
the 'auto-mints' hyphenation and the 'easily' condescension flag, and
renamed the credentials table's 'what it is' header to 'kind' (also
flagged once styles were resynced). No technical claim or file:line
citation changed. Same fix applied to the one new line in
docs/README.md's pointer to this page.
2026-09-16 22:41:23 +02:00
atlas
52fabe7096 docs/swarm/credentials.md: fix 7 drifted/wrong file:line citations
Audited all 22 file:line citations against source on this head, per
argus's PR review. Fixed:

- matrix_account.rs writer cell pointed at path-computation line 163;
  the actual store.write() call is at 189-197 (now anchored to the
  put_matrix_account function name too).
- swarm-secret-publisher.nix appservice-token minter cited the
  "put on every run" comment (280-286) instead of the mint itself
  (271-278).
- glue-matrix-bao-token.nix persisted-token cell cited the
  registration-restamp comment (186-188) instead of the printf+chmod
  (180-181).
- glue-queue-agent-credential.nix persisted-files cell missed the
  secret's chmod 0600 at 219, citing 224-231; corrected to 218-224.
- swarm-otel.nix persisted-file cell cited 763-767, one line short of
  the chmod 0400 at 768; corrected to 764-768.
- hive-priv/src/main.rs:368,413 (forge/github token cell) landed in
  unrelated DestroyContainer/ListContainers/WriteAgentGithubToken match
  arms; replaced with the write_forge_token/write_github_token function
  names as a greppable, edit-resistant anchor.
- swarm-bao.nix:527-528 (the "operator places by hand" quote) missed
  the quoted text, which starts at 529; corrected to 529-531.

15 of 22 already resolved exactly; no claim lacked supporting code, so
nothing was removed. 22 of 22 citations checked.
2026-09-16 22:41:23 +02:00
atlas
937289383c Add a credential docs page: current state, target state, the rule between
The swarm's credential docs say where every file lives. They do not say
whether it should be a file at all, so a discussion about direction has
had nothing to point at and each one re-derived the same table.

This page carries that table with the three columns the target contract
is written in — minter, reader, renewal — plus the column the target is
really about: whether the value is persisted outside the store. Stating
it flatly is the point. All four stored families are plaintext files on
disk, the appservice token twice; every renewal cell reads NONE; no agent
container holds a store identity at all, so the hive reads on its behalf
and writes a file in; and the appservice token has a second, uncoordinated
local mint that can diverge from the published one.

The target section is marked as a target throughout, because its first
line is the one most easily misread as fact: every host needing a store
mTLS certificate is where this is going, while today only the store's own
host auto-mints and swarm-bao.nix calls it the credential an operator
places by hand everywhere else.

The progressive-enhancement rule is stated as a table of questions a
reviewer applies to a pull request rather than as prose, since a rule
nobody can check is a preference. New functionality matches the target
immediately; existing functionality moves stepwise, and the questions
distinguish a step from churn.

Indexed from the docs root and the swarm README. It supersedes
secrets.md when the migration completes — at which point that file is
deleted and this one moves into its place.
2026-09-16 22:41:23 +02:00
3 changed files with 87 additions and 0 deletions

View file

@ -76,6 +76,10 @@ declarations.
- **Where do agent snapshots go? How does the swarm's `btrfs receive`
endpoint authenticate a pushing hive?** →
[`networking/snapshot-store.md`](networking/snapshot-store.md).
- **Who mints each credential, who reads it, and how does it rotate — and
where's that shape headed?** → [`swarm/credentials.md`](swarm/credentials.md)
(current state, target state, and the progressive-enhancement rule);
[`swarm/secrets.md`](swarm/secrets.md) for where each file lives today.
## Scheduler, CI, observability

View file

@ -104,6 +104,12 @@ Every credential the swarm holds, who mints it, where it must live, and
which of the three topologies makes it the operator's job to place:
[`secrets.md`](secrets.md).
Where that shape is **going** — the per-secret minter/reader/renewal
contract, the target of one mTLS identity per host and everything else
through the store, and the test a change has to pass to count as movement
toward it: [`credentials.md`](credentials.md). It supersedes `secrets.md`
when the migration completes.
## Swarm UI
The operator-only web surface on the swarm apex, why reaching it needs

77
docs/swarm/credentials.md Normal file
View file

@ -0,0 +1,77 @@
# Credentials: the target shape
The swarm's credential store is bao. This page describes the shape every
credential is meant to have — who mints it, who reads it, and how it renews
— not what's on disk today. [`secrets.md`](secrets.md) remains the map of
the files that exist right now; this page replaces it, and `secrets.md` gets
deleted, once the swarm's credential path matches what's described below.
**Public material is a value.** A certificate or a public nkey is handed to
every client that connects, so the store is a fine place for it. Nothing
below is about those.
**No secret the store holds is ever written to disk.** That's the invariant,
and everything else in this page follows from it. A value pulled from the
store — bao — lives in the memory of the process that asked for it and
nowhere else: not in a state directory, not in a bind-mounted file, not in a
systemd credential, not in a rendered config, not for a moment before a unit
deletes it. The target isn't a shorter list of secret files. It's the store,
plus one file per identity.
**Those files are mTLS client certificates, one per identity, and they're
the only credential on disk.** Each has to be a file, and the reason is the
whole asymmetry: the certificate is what authenticates a principal to the
store, so it's the one credential that can't be fetched from the store.
Something has to exist on disk before the first request, or there's nothing
to make the request with. Every identity — an agent, a swarm-level service —
needs one; a host running several holds several, and its only power is to
ask the store for the rest.
`swarm-bao.nix:529-533` states the rule for the nix option that carries it:
this is _"the credential an operator places by hand"_, and _"a path, never a
value."_ A literal in a nix expression lands in the nix store —
world-readable and permanent — so that option takes a path to the
certificate on disk, never the certificate's bytes.
**The hive hands an agent an identity, never a secret.** An agent container
is passed an mTLS certificate from its hive and from then on authenticates
to the store under its own name, pulling what it needs when it needs it. No
process reads a secret on another principal's behalf: the principal that
needs a value is the principal that authenticates for it.
Two per-agent credential files — the forge token and the github token — sit
outside this page: they're operator-supplied and never pass through the
store, so the table below doesn't govern them.
**Per secret, the target specifies minter, reader, and renewal strategy.**
Those three are the contract, and the reader is a process pulling a store
path at runtime — not a path on disk, and not a unit whose job is to turn a
store value into a file. A renewal cell may never read `NONE`: state the
strategy for every credential, including the mTLS leaf.
| store path | minter | reader — pulls at runtime, holds in memory | renewal |
| -------------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ | -------------- |
| `swarm/agents/<agent>/matrix/<account>` | `swarm-controller` | the agent container itself, under the certificate its hive passed in | must be stated |
| `swarm/hives/<hive>/matrix/appservice-token` | one minter, on the authelia host | the hive process that presents the token to its homeserver, under the hive's own certificate | must be stated |
| `swarm/hives/<hive>/queue/agent` | authelia | the agent container presenting the OIDC client to the swarm queue, under its own certificate | must be stated |
| `swarm/services/<clientId>/oidc/client` | authelia | the service process that presents the client secret, under the certificate of the host it runs on | must be stated |
| _(not in the store)_ the mTLS client leaf | the store's own PKI, or an operator placing it by hand | its own client, off disk — the sole exception above, because it's what makes every other row's pull possible | must be stated |
## Touching a credential
A pull request that touches a credential can't:
- add a minter outside the swarm's existing mint path
- persist a store-provided secret to disk — a state directory, a bind mount,
a rendered config
- add a credential whose renewal strategy is `NONE` — state the strategy,
even if it's "operator reissues and restarts the reader"
- read a secret on another principal's behalf and hand it over — the
principal that needs the value authenticates for it
- give a host or container an out-of-band credential that isn't the store
mTLS leaf — one out-of-band credential per principal is the whole point of
the store
While the swarm's credential path is still moving to this shape, a PR that
moves a secret into bao is allowed to leave its renewal strategy unresolved,
provided it opens a follow-up issue to settle renewal. That's a migration-era
allowance, not a standing exception to the renewal-strategy rule above.