diff --git a/docs/process/conventions.md b/docs/process/conventions.md index 756816f7..b4ed176e 100644 --- a/docs/process/conventions.md +++ b/docs/process/conventions.md @@ -316,19 +316,30 @@ none. swarm-controller logs every refused `PUT` or `DELETE` on that route at `wa **`logs` and `metrics`** — off for every agent until granted, and separate because logs are the more sensitive. Grant one with `swarmctl agent permissions set ` (listing it with the others) or the -create-agent screen. An agent without it gets a 403 from the store's gateway -when `swarm-logs` or `swarm-metrics` queries; a bad secret is still a 401. The -controller refuses the grant, with a 409 naming the hive, unless the agent's -hive advertises `tolerant-permissions` in its status snapshot -(`swarm_wire::hive_status`): a hive that predates the permission would fail to -decode the record. It also mirrors the two grants to the store leaf +create-agent screen; the setup assistant creates ruth with both. An agent +without it gets a 403 from the store's gateway when `swarm-logs` or +`swarm-metrics` queries; a bad secret is still a 401. + +An agent decodes its own record, in its harness +(`hive-agent/src/swarm_permissions.rs`, through `swarm_queue_client::agent_permissions::read`), +and a build that doesn't know a name would drop to no permissions. So +swarm-controller refuses a change that adds `logs` or `metrics` to an existing +agent, with a 409, unless both the agent's hive (`tolerant-permissions` in its +status snapshot) and its running harness (the same entry in its turn-state +header, `swarm_wire::hive_status`) advertise it. A harness not yet updated, a +stopped agent and one that hasn't sent its first header (up to a minute after +a restart) all refuse; keeping a grant it already holds needs no report. A +create has no harness yet and checks the hive only. + +The controller mirrors the two grants to the store leaf `swarm/agents//observability` (`swarm_secret_client::observability`), -which the gateways read; the gateway denies when it can't read that leaf. A -revoked grant stops working within `authVerdictTtl` (60 seconds by default) of -the agent's last allowed query. Grant it only after the agent runs a harness -that skips names it doesn't know (its next update after the release that adds -these two); an older harness that reads such a record holds no permissions at -its next start. +which the gateways read, and the gateway denies when it can't read that leaf. +A change that drops a grant writes the leaf first and the record second, so a +failed write changes nothing; a change that adds one writes the record first, +so a failed write leaves the gateway denying. Every five minutes the controller +also makes each agent's leaf equal its record and logs a warning for each leaf +it corrects. A revoked grant stops working within `authVerdictTtl` (60 +seconds by default) of the agent's last allowed query. **Internet flag** — the record also carries `unrestricted_internet`, a boolean that isn't a `Permission`. An absent value means on, so every agent diff --git a/docs/swarm/credentials-reference.md b/docs/swarm/credentials-reference.md index 4e841984..85283d1a 100644 --- a/docs/swarm/credentials-reference.md +++ b/docs/swarm/credentials-reference.md @@ -29,7 +29,7 @@ the rest of the cell says how. | `swarm/controller/swarm-controller/forge/webhook-secret` | `swarm-controller`, at start, from its old `webhook-secret` file; a new one only when there is no file | `swarm-controller` | ❌ minted once; a rotated secret fails every delivery until the forge hooks are re-registered | ❌ read at start | | `swarm/agents//bao-mtls` | the store's agent PKI mount (`deploy.bao.agentPkiMountPath`), at agent creation, and by the renewal pass when none is stored or its cert-auth role is missing | `hive-c0re`, then the agent | ✅ re-issued every 5 minutes once past half its 7-day validity; minted when none is stored or its cert-auth role is missing, for agents not `Destroyed` | ✅ the agent re-reads every 10 minutes; it renews only while its current leaf is valid | | `swarm/agents//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//observability` | `swarm-controller`, when an operator sets, clears or creates an agent's permissions with `logs` or `metrics` in the old or new list, and when it destroys the agent; the leaf stays absent for an agent that never held either | the agent (read-only, its own prefix); `swarm-logs-auth` and `swarm-metrics-agent-auth`, which deny when it's absent or unreadable | ❌ the controller rewrites it with the permission; nothing refreshes it | ✅ the stores honour a changed grant for up to `deploy.victorialogs.authVerdictTtl` and `deploy.victoriametrics.agentAuthVerdictTtl` (60 s by default) | +| `swarm/agents//observability` | `swarm-controller`, when an operator sets, clears or creates an agent's permissions with `logs` or `metrics` in the old or new list, and when it destroys the agent; the leaf stays absent for an agent that never held either | the agent (read-only, its own prefix); `swarm-logs-auth` and `swarm-metrics-agent-auth`, which deny when it's absent or unreadable | ✅ the controller rewrites it with the permission, and every five minutes makes it equal the record's | ✅ the stores honour a changed grant for up to `deploy.victorialogs.authVerdictTtl` and `deploy.victoriametrics.agentAuthVerdictTtl` (60 s by default) | | `swarm/agents//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//forge/