docs: split credentials.md into an overview and a per-secret reference
The overview keeps the store invariant, the mTLS exception and the minter/reader/renewal contract. The table of every credential and the operating notes (forge token checks, re-creating agents, revoking a queue credential, store login retries) move unchanged to credentials-reference.md, and the links that asked where a credential lives point there.
This commit is contained in:
parent
7c10b79677
commit
2d29711df9
7 changed files with 101 additions and 83 deletions
|
|
@ -78,7 +78,8 @@ declarations.
|
|||
[`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 and target state);
|
||||
(the overview) and [`swarm/credentials-reference.md`](swarm/credentials-reference.md)
|
||||
(each credential, current state and target state);
|
||||
[`swarm/secrets.md`](swarm/secrets.md) for where each file lives today.
|
||||
- **How does the secret store come up, who writes its grants, how's it
|
||||
sealed?** → [`swarm/bao.md`](swarm/bao.md).
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ when the token changed. It needs a store identity
|
|||
(`services.hyperhive.agent.bao.addr`); an agent without one gets no token.
|
||||
The unit never deletes the file: a `github-token` already in place stays
|
||||
when the store holds none or doesn't answer. Where the token lives and who
|
||||
reads it: [credentials.md](../swarm/credentials.md).
|
||||
reads it: [credentials-reference.md](../swarm/credentials-reference.md).
|
||||
|
||||
## Security
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ grafana, the controller, authelia, …) →
|
|||
→ [`sso.md`](sso.md)
|
||||
- **secrets** — every credential the swarm holds, who mints it and where it
|
||||
lives → [`secrets.md`](secrets.md) · the per-secret minter/reader/renewal
|
||||
contract → [`credentials.md`](credentials.md) · how the store comes up,
|
||||
contract → [`credentials.md`](credentials.md), per credential in
|
||||
[`credentials-reference.md`](credentials-reference.md) · how the store comes up,
|
||||
who writes its grants and how it unseals → [`bao.md`](bao.md)
|
||||
- **swarm CA** — the store root every hive's gateway leaf chains to, and what to
|
||||
hand a peer. → [`ca.md`](ca.md)
|
||||
|
|
@ -168,7 +169,7 @@ it's stored in the queue, not in the daemon.
|
|||
|
||||
An agent connects to the queue as itself, with a credential `swarm-controller`
|
||||
mints for it when it creates the agent (`swarm/agents/<agent>/queue` in the
|
||||
secret store, see [`credentials.md`](credentials.md)). The harness reads it from
|
||||
secret store, see [`credentials-reference.md`](credentials-reference.md)). The harness reads it from
|
||||
the store under the agent's own certificate before each connection attempt and
|
||||
holds it in memory only. The one thing to configure is where the queue is:
|
||||
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ status. The same `git` fetches a config's own inputs, so the builder registers
|
|||
the helper for the forge's URL only, and the helper answers only an `https`
|
||||
request for the forge's host: any other host gets nothing. The builder holds no write credential; swarm-controller writes the
|
||||
status with its own forge token. Details per path:
|
||||
[`credentials.md`](credentials.md).
|
||||
[`credentials-reference.md`](credentials-reference.md).
|
||||
|
||||
## Isolation
|
||||
|
||||
|
|
|
|||
84
docs/swarm/credentials-reference.md
Normal file
84
docs/swarm/credentials-reference.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# Credentials: the per-secret reference
|
||||
|
||||
Every credential in the swarm's secret store, in one table: who mints it, who
|
||||
reads it, and whether it renews on its own. The overview and the rules a
|
||||
credential follows are in [`credentials.md`](credentials.md); this page is
|
||||
the detail, so it changes whenever someone adds a credential or moves its minting.
|
||||
[`secrets.md`](secrets.md) maps the files that exist on disk today.
|
||||
|
||||
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.
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
|
||||
| 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 7-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>/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 |
|
||||
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
## Operating notes
|
||||
|
||||
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:
|
||||
|
||||
```sh
|
||||
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.
|
||||
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
|
||||
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`](secrets.md) maps the files that exist right now.
|
||||
The swarm's credential store is bao. This page is the overview of how the swarm
|
||||
treats credentials; [`credentials-reference.md`](credentials-reference.md) lists
|
||||
each one: who mints it, who reads it, and how it renews, with the notes for
|
||||
operating them. It describes the target, not what's on disk today;
|
||||
[`secrets.md`](secrets.md) maps the files that exist right now.
|
||||
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
|
|
@ -19,77 +21,7 @@ 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.
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
|
||||
| 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 7-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>/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 |
|
||||
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
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:
|
||||
|
||||
```sh
|
||||
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.
|
||||
**Each credential has a minter, a reader and a renewal story.** Who mints it, who
|
||||
reads it, and whether it renews without an operator are what the
|
||||
[reference table](credentials-reference.md) gives for every credential, with the
|
||||
operating notes that go with them.
|
||||
|
|
|
|||
|
|
@ -726,7 +726,7 @@ it next lists the store, within two minutes. Its forge and GitHub units never
|
|||
delete a file, so a token already fetched into `<state>` stays there.
|
||||
|
||||
Where each credential lives and who reads it:
|
||||
[`credentials.md`](credentials.md).
|
||||
[`credentials-reference.md`](credentials-reference.md).
|
||||
|
||||
## Enabling
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue