From 21e3f626f4df6ccffbfae01c368381df16915736 Mon Sep 17 00:00:00 2001 From: atlas Date: Wed, 7 Oct 2026 20:10:41 +0200 Subject: [PATCH] hives read the forge through their own swarm-minted read account swarm-controller now gives every hive in its directory a forge account, `hive-`, in a new `hives` team of `agent-configs` (read on `repo.code` + `repo.pulls`, every repo including later ones), and mints one `read:repository` token named `swarm-hive` into the store at `swarm/hives//forge/token`. A five-minute pass queues a `MintHiveForgeToken` node for a hive whose token is missing or no longer matches the forge, whose account is missing, or whose team membership or team settings drifted. A hive whose store or forge read failed is never queued, so an outage cannot rotate every hive's token. The token decision reuses `agent_token`'s classifier and replace step, now parameterised by name and scopes. An agent may be called `hive-`. The hive account is created with `hive-@hives.hyperhive.local` and the job refuses an account by that name with any other email before it locks it down, adds it to the team or mints for it; agent creation already refuses the hive's account by the same email check. The controller's bao policy gains the `swarm/hives/+/forge/token` leaf (create/read/update), pinned by the bao-grants module-eval assertion. The hive's own policy already reads `swarm/hives//*`. hive-c0re reads that token through `forge::reader_token()`, falling back to the core token file when the store holds none, and uses it for: fetching a merged config rev (swarm deploy, `hivectl forge reconcile-config`), reseeding a purged `applied`, the meta-update cascade's fast-forward, seeding `proposed` on first deploy, and finding the PR that merged a refused rev. Those reads no longer require a local `hive-forge` container. The `/knowledge` clone moves from the forge host's boot sweep into the knowledge pull, so every hive clones it. Posting the deploy-failure comment on that PR stays on the core token. Refs #4851 --- docs/integrations/forge.md | 49 +- docs/integrations/knowledge.md | 10 +- docs/swarm/credentials.md | 1 + hive-c0re/src/forge/mod.rs | 30 +- hive-c0re/src/forge/pr_comment.rs | 10 +- hive-c0re/src/forge/reader.rs | 159 +++++ hive-c0re/src/forge/reconcile.rs | 14 +- hive-c0re/src/forge/repos.rs | 41 +- hive-c0re/src/lifecycle/setup.rs | 2 +- hive-c0re/src/workers/knowledge.rs | 32 +- nix/host-modules/swarm-bao.nix | 4 + nix/module-eval/bao-grants.nix | 14 +- swarm-controller/src/forge.rs | 31 +- swarm-controller/src/forge/agent_token.rs | 112 ++-- swarm-controller/src/forge/hive_token.rs | 671 ++++++++++++++++++++++ swarm-controller/src/main.rs | 114 +++- swarm-secret-client/src/forge.rs | 80 ++- 17 files changed, 1229 insertions(+), 145 deletions(-) create mode 100644 hive-c0re/src/forge/reader.rs create mode 100644 swarm-controller/src/forge/hive_token.rs diff --git a/docs/integrations/forge.md b/docs/integrations/forge.md index 38586440..4d1ff43e 100644 --- a/docs/integrations/forge.md +++ b/docs/integrations/forge.md @@ -15,7 +15,7 @@ each agent on relevant activity. ## Token scopes -Two scope sets: +Three scope sets: **`AGENT_TOKEN_SCOPES`** (swarm-controller's `forge::agent_token`, pinned by a test). swarm-controller mints every agent token with it: @@ -40,6 +40,53 @@ hive-c0re only mints the token when the file at a token minted with admin scopes keeps them until an operator deletes that file and restarts hive-c0re. +**`HIVE_TOKEN_SCOPES`** (swarm-controller's `forge::hive_token`, pinned by a +test): `read:repository` alone, for each hive's read-only account (next +section). + +--- + +## How a hive reads the forge + +Each hive in swarm-controller's hive directory has its own forge account, +`hive-`. swarm-controller creates it, makes it a member of the `hives` +team in `agent-configs`, and mints one token named `swarm-hive` into the +swarm secret store at `swarm/hives//forge/token`. The team has read +permission on `repo.code` and `repo.pulls` of every repo in the org, +including repos created later. A pass at start and every five minutes +creates what's missing and re-mints a token that's missing or no longer +matches the forge. A pass whose store or forge reads fail queues nothing, so +an outage never rotates every hive's token. + +The account name isn't reserved, so an agent may carry the name `hive-` too. +swarm-controller creates the hive's account with the email +`hive-@hives.hyperhive.local` and refuses an account by that name with +any other email: it never adds an agent's account to the team or mints a +token for it. An agent's own creation refuses the hive's account the same +way. + +hive-c0re reads the token with `forge::reader_token()`, under the hive's own +store certificate, every time it reads the forge. Where the store holds none, +it falls back to the `core` token file, which only the host running the forge +container has. These reads go through it, and none of them needs a local +forge container: + +- fetching a merged config commit for a swarm deploy, and + `hivectl forge reconcile-config` +- reseeding a purged `applied` repo, and the meta-update cascade's + fast-forward of `applied/main` +- seeding `proposed` from `agent-configs/` on first deploy +- finding the PR that merged a config commit the hive refused to deploy +- cloning and pulling `/knowledge` + +Posting the refusal as a comment on that PR still needs the `core` token, so +only the forge host posts it. + +Roll a change to any of this out store host first (its granter writes the +controller's grant on `swarm/hives/+/forge/token`), then the controller host, +then the hives. A hive that starts before its token exists reads with the +core file, or not at all, until the controller's next pass. + --- ## Per-agent forge accounts diff --git a/docs/integrations/knowledge.md b/docs/integrations/knowledge.md index 36b41176..7ef42196 100644 --- a/docs/integrations/knowledge.md +++ b/docs/integrations/knowledge.md @@ -87,10 +87,12 @@ message only, no forced wake, and it carries the standard "this was a broadcast" hint. hive-c0re logs a diff or per-agent send failure without blocking the pull itself. -`knowledge::ensure_local_clone` creates (or refreshes) the local -clone once at startup. If the repo is brand new and -empty, `ensure_local_clone` seeds it with the default README -before returning. +On a hive with no local clone yet, `knowledge::pull()` takes the clone +instead of pulling, so every hive gets `/knowledge` from its first pull, +whether or not it runs the forge. It authenticates with the hive's forge +read token when it has one (see +[forge.md](forge.md#how-a-hive-reads-the-forge)) and clones the public +repo anonymously otherwise. ## State diff --git a/docs/swarm/credentials.md b/docs/swarm/credentials.md index 26b46724..18b004ae 100644 --- a/docs/swarm/credentials.md +++ b/docs/swarm/credentials.md @@ -67,6 +67,7 @@ of the cell says how. | `swarm/agents//forge/