Three handbook pages carried sections an operator never reads, named after source paths and build internals. They move to pages under 4-implementation, and each handbook page keeps a short pointer. - forge.md: the notification poller (what it polls, how it formats, de-duplicates and marks notifications read) is now agent-tools/forge-notify.md. The forge page keeps a short Notifications section. - matrix.md: the retired hive-side matrix code, the evaluation assertions and the fluffychat-web build fixes are now networking/matrix-homeserver.md. - github.md: the Implementation section (how `gh` and `git push` authenticate, and the GitHub poller) is now agent-tools/github.md. docs/README.md and the agent-tools README list the new pages, and AGENTS.md's note on mixed operator and implementation docs now says to move the detail and leave a pointer.
7.4 KiB
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; 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-<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/<hive>/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-<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/<agent>/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 <state>/forge-token,
the file hive-c0re wrote before. hive-c0re no longer creates agent users or
mints agent tokens. swarm-controller deletes the hyperhive-<unix-seconds>
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/<name>). The agent is a write collaborator on its own repo — it can push config-change branches and open config PRs — butmainis branch-protected by swarm-controller: the merge and approval allowlists are theoperatorsteam, and the agent can neither pushmaindirectly nor self-merge. An operator's merge in the Forgejo UI deploys the merged commit (see 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'smainhead, pinned by sha ininternal/meta(agent-<n>.url = git+<forge>/agent-configs/<n>.git?rev=<sha>; see config-changes.md § Meta flake).
The hive-forge CLI (separate workspace crate, see
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.