hyperhive/docs/swarm/credentials.md
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

70 lines
4.9 KiB
Markdown

# 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 exactly one file.
**That one file is the mTLS client certificate, and it's the only credential
on disk.** It 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 host has one; 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