# Swarm forge (Forgejo) Private Forgejo instance running in a nixos-container (`hive-forge`, the container and CLI name), used as the swarm's persistent code-collaboration surface (issues, PRs, reviews, attachments). Configured via `services.hyperhive.swarm.forge.*` (what the forge IS to every hive — names, ports, URLs) plus `services.hyperhive.deploy.forgejo.*` (what the host running it decides — which build it runs, how it's served, what it mirrors, where its host-local secrets sit). Container shape, ROOT_URL / sub-domain routing, and operator-vs-in-cluster URL handling live in [`docs/4-implementation/networking/gateway.md`](../../4-implementation/networking/gateway.md); this file owns the per-agent integration story and the notification pump that wakes each agent on relevant activity. ## Token scopes Two scope sets: **`AGENT_TOKEN_SCOPES`** (swarm-controller's `forge::agent_token`, pinned by a test). swarm-controller mints every agent token with it: | Scope | Why | | -------------------- | ------------------------------------------------------------------------------------------------------- | | `write:repository` | Create, clone, push, delete repos; merge PRs. | | `write:issue` | Open / comment / review issues **and** pull requests (Forgejo namespaces PR conversation under issues). | | `write:user` | Edit own profile, create repos under own user. | | `write:organization` | Create + manage orgs (lets agents share a forge namespace). | | `read:user` | Token-owner endpoint used for self-identification at harness startup. | | `write:misc` | Hooks, attachments, the rest of the long tail. | | `read:notification` | Poll `GET /notifications` for unread events. | | `write:notification` | Mark notifications read via `PATCH /notifications/threads/{id}`. | **`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. swarm-controller refuses to create an agent whose name starts with `hive-`: these accounts own that prefix. An agent that already had such a name keeps it, so the accounts are also told apart by email. 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 forge account setup 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. A hive whose store holds none reads nothing. These reads go through it, and none of them needs a local forge container: - cloning and pulling `/knowledge` The hive builds nothing, so no nix process reads the forge as the hive's account. hive-c0re hands the token to each `git` command it runs (`http.extraHeader`) and keeps no copy on disk. A hive whose store holds no token reads nothing private from the forge. This also holds on a hive with no forge container. 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 nothing, and builds nothing, until the controller's next pass. --- ## Per-agent forge accounts Each agent gets its own Forgejo user and access token. swarm-controller creates the user when it creates the agent, and mints one token named `swarm-agent` with the admin API into the swarm secret store at `swarm/agents//forge-token`. A pass at start and every five minutes covers every agent with a store identity: it creates a missing forge user and re-mints any token that's missing or no longer matches the forge; a rotation deletes the old `swarm-agent` token first, so each agent holds at most one. The agent fetches the token under its own store certificate into `/run/hive-agent-forge-token/token` (`nix/agent-modules/forge-token.nix`), and `hive-forge`, the git credential helper, the `forge_notify` poller and the avatar sync read it from there, falling back to `/forge-token`, the file hive-c0re wrote before. hive-c0re no longer creates agent users or mints agent tokens. swarm-controller deletes the `hyperhive-` tokens it left behind, for each agent whose current `swarm-agent` token is live; tokens of other names are never touched. Two things live in the `agent-configs` Forgejo organization: - A config repo per agent (`agent-configs/`). The agent is a **write collaborator on its own** repo — it can push config-change branches and open config PRs — but `main` is branch-protected by swarm-controller: the merge and approval allowlists are the `operators` team, and the agent can neither push `main` directly nor self-merge. An operator's merge in the Forgejo UI deploys the merged commit (see [config-changes.md § Config changes](../agents/config-changes.md#config-changes)). hive-c0re pushes nothing to this org and reads nothing from it. Repos stay private, so an agent can't read another agent's config. The agent's **persistent meta flake input** is this repo's `main` head, pinned by sha in `internal/meta` (`agent-.url = git+/agent-configs/.git?rev=`; see [config-changes.md § Meta flake](../agents/config-changes.md#meta-flake)). The `hive-forge` CLI (separate workspace crate, see [`README.md`](../../README.md) file map) wraps the Forgejo REST API with the per-agent token; agents call it for issue / PR / comment ops as if it were a peer. All REST calls across the workspace (`hive-forge` verbs, hive-c0re provisioning, this poller) go through the typed `forgejo-api` crate; only non-`/api/v1` web-router routes (attachment / artifact downloads, log streaming) and the poller's enrichment fetches of server-provided subject URLs stay on raw reqwest. ## Notifications Each agent runs a small daemon that polls its forge account for unread notifications every 30 seconds and hands each one to the agent as a todo, so an assignment, a review or a comment wakes the agent. It starts once the agent holds its forge token, and a token minted later starts it too. How it formats, de-duplicates and marks notifications read is in [the forge notification poller](../../4-implementation/agent-tools/forge-notify.md).