- table cells: cut to what the credential is, where it comes from and what the operator does - prose sections: cut the matrix-prefix, appservice-token and leaf-delivery sections; trimmed revocation and retry notes to their operator-visible effect - progressive enhancement section: removed (contributor rules) - intro: cut to the store, the on-disk certificate and the column meanings - docs index: drop the "progressive-enhancement rule" clause from the credentials.md entry in docs/README.md - matrix rows: one sentence saying the name `main` is reserved
19 KiB
Credentials: the target shape
The swarm's credential store is bao. This page lists each credential: who
mints it, who reads it, and how it renews. It describes the target, not what's
on disk today; secrets.md maps the files that exist right now.
Store secrets stay off disk. A process pulls a value from the store at runtime and holds it in memory only.
The mTLS client certificate is the only credential on disk. Each identity (an agent, a hive, a swarm-level service) has one. It's what logs a principal in to the store, so you place a hive's or service's certificate by hand. The nix option for it takes a path to the certificate, never its bytes, because a literal in a nix expression lands in the world-readable nix store. A hive hands each agent its certificate; the agent pulls everything else itself.
In the table, automatic re-mint is something replacing the stored value without an operator. Automatic re-pull is the reader picking up a replaced value without a restart. ✅ means it happens today, ❌ means it doesn't, and the rest of the cell says how.
| store path | minter | reader | automatic re-mint | automatic re-pull |
|---|---|---|---|---|
swarm/agents/<agent>/matrix/main |
swarm-controller, at agent creation and every 5 minutes |
the agent | ✅ when missing or rejected by the homeserver | ✅ the daemon restarts within 5 minutes of a rejection |
swarm/agents/<agent>/matrix/<account> |
swarm-controller, from a credential you hand it |
the agent | must be stated | ✅ retried within 2 to 16 minutes after a rejection |
swarm/agents/<agent>/matrix-recovery/<account> |
swarm-controller, a random passphrase per account |
the agent | ❌ never replaced: the account's key backup opens only with this passphrase | ❌ read when the account comes up |
swarm/controller/swarm-controller/matrix/appservice-token |
swarm-matrix-ctl in hive-matrix, once |
swarm-controller |
❌ minted once | ✅ read every 5 minutes |
swarm/controller/swarm-controller/oidc/client |
authelia, first boot | swarm-controller |
❌ minted once; swarm-secret-publish republishes a re-mint |
✅ re-read when authelia refuses it |
swarm/controller/swarm-controller/forge/token |
the forge, once | swarm-controller |
❌ minted once; delete /var/lib/forgejo/swarm-controller-token-name in the forge container to mint a new one |
✅ re-read on a 401 |
swarm/controller/swarm-controller/grafana/token |
swarm-bao-grafana-controller-token on Grafana's host |
swarm-controller |
✅ every 10 minutes, when Grafana refuses it or none is stored | ✅ re-read on a 401 |
swarm/agents/<agent>/bao-mtls |
the store's agent PKI mount (deploy.bao.agentPkiMountPath), at agent creation |
hive-c0re, then the agent |
✅ re-issued every 5 minutes once past half its 90-day validity | ✅ the agent re-reads every 10 minutes; it renews only while its current leaf is valid |
swarm/agents/<agent>/queue |
swarm-controller, at agent creation |
the agent; the queue, log and metrics auth services | ✅ re-minted at 45 days; skipped for Destroyed agents, whose path is deleted |
✅ read on every reconnect; an open connection keeps the old secret for up to an hour; the log and metrics stores honour a replaced secret for up to deploy.victorialogs.authVerdictTtl and deploy.victoriametrics.agentAuthVerdictTtl (60 s by default) |
swarm/agents/<agent>/forge-token |
swarm-controller, at agent creation and every 5 minutes |
the agent | ✅ when missing or no longer matching the forge | ✅ every 10 minutes |
swarm/agents/<agent>/forge/<label> |
swarm-controller, when you link an external forge account in the swarm UI |
the agent | ❌ your token; delete it and link the label again | ✅ every 2 minutes |
swarm/agents/<agent>/github-token |
swarm-controller, when you link a GitHub account in the swarm UI |
the agent | ❌ your token; delete it and link again | ✅ every 2 minutes |
swarm/agents/<agent>/claude-credentials |
swarm-claude-login, when you log the agent in from its card; one version per login |
the agent, while it waits for a login | ❌ log in again | ✅ every 30 seconds while waiting |
swarm/hives/<hive>/matrix/sender-token |
swarm-controller, every 5 minutes |
swarm-controller and the hive |
✅ when missing or rejected by the homeserver | ✅ the hive re-reads on every sweep |
swarm/hives/<hive>/forge/token |
swarm-controller, every 5 minutes |
swarm-controller and the hive |
✅ when missing or no longer matching the forge | ✅ read on every forge call |
swarm/hives/<hive>/queue/agent |
authelia | swarm-bao-queue-agent on the hive's host |
must be stated | must be stated |
swarm/hives/<hive>/oidc/client |
authelia, first boot; swarm-secret-publish copies it in |
the hive | ❌ minted once; swarm-secret-publish republishes a re-mint |
✅ re-read when authelia refuses it |
swarm/services/<clientId>/oidc/client |
authelia | that service | must be stated | must be stated |
swarm/services/ci-runner/registration-token |
swarm-controller, from the forge, at start and every 5 minutes |
swarm-bao-ci-runner on the runner's host |
❌ the forge keeps one token until an admin resets it; the controller then writes the new one within 5 minutes | ✅ every 10 minutes; restarts the runner on a change |
swarm/services/<grafana client id>/metrics/bearer |
swarm-bao-grafana-metrics-bearer on Grafana's host, when none is stored |
swarm-metrics-auth and swarm-logs-grafana-auth |
❌ delete it and restart swarm-bao-grafana-metrics-bearer |
✅ the auth services re-read within a minute; ❌ Grafana restarts when its copy changes |
swarm/services/review-classifier/anthropic-api-key |
you, in the store's UI, field api_key |
swarm-review-classifier |
❌ write a new key | ✅ read on every run |
swarm/services/review-classifier/acp-provider |
you, in the store's UI, field api_key |
swarm-review-classifier |
❌ write a new key | ✅ read on every run |
swarm/services/review-classifier/forge/token |
swarm-controller, at start and every 5 minutes |
swarm-controller |
✅ when missing or no longer matching the forge | ✅ read on every review |
swarm/services/builder/forge/token |
swarm-controller, at start and every 5 minutes |
swarm-builder, swarm-controller |
✅ when missing or no longer matching the forge | ✅ read on every build |
swarm/services/ci-metrics/forge/token |
swarm-controller, at start and every 5 minutes |
swarm-ci-metrics, swarm-controller |
✅ when missing or no longer matching the forge | ✅ read on every pass |
| (not in the store) a hive's mTLS leaf | the store's PKI, or you by hand | the hive itself, from disk | must be stated | must be stated |
The name main belongs to the swarm's own homeserver account, which the
controller mints, so the controller rejects any account you link under it.
swarm-controller checks every forge token itself: it looks at every agent
that has a store identity at start and every five minutes. For any whose
stored token is missing or stale it creates the forge user if there isn't one,
then mints the token. To check one agent now:
swarmctl agent mint-forge-token <agent>
An agent without a store identity gets no swarm token and keeps using the
forge-token file in its state dir, if it has one; re-create it with
swarmctl agent create to give it one. The swarm leaves an agent it already
declares as it is. For an agent with no declaration, such as one only its hive
knows about, agent create declares it paused; set it up from its card in
the swarm UI.
⚠️ Re-create existing agents before deploying a hive-side change that makes a
container require a credential it may not have. A container whose store
identity is absent doesn't start, so create the identities first, setting any
newly paused agent up as above.
Revoking an agent's queue credential. Declaring an agent destroyed —
PUT /api/hives/{hive}/agents/{agent}/state — deletes
swarm/agents/<agent>/queue and every version it held. An open queue
connection closes within an hour. An agent recreated under the same name gets a
fresh secret. It leaves the agent's mTLS leaf alone. If the store refuses the
delete, swarm-controller logs an error naming the agent and the teardown
continues; declare the agent destroyed again to re-run the revocation.
A failed store login retries every 30 seconds for a day. When a later attempt
lands a changed value, the service that loaded it restarts. If the fetch unit
can't compare file times it logs cannot compare the mtime of <path> … — leaving <unit> as it is at err, and the service keeps the old value until
the next timer tick.