Watch
0
0
Fork
You've already forked hyperhive
0

drop the applied repo: the hive keeps no copy of an agent's config, only the deploy markers. Refs #5913

This commit is contained in:
damocles 2026-11-01 17:19:41 +01:00
commit 862495530c

View file

@ -41,22 +41,17 @@ No MCP tool exists for config changes: opening the PR IS the request.
and publishes a deploy request carrying that commit on that hive's
deploy subject. With no such hive, or more than one, it deploys
nothing and logs a warning naming them.
5. If the hive's `applied/main` already is that commit, it does nothing.
Otherwise it fetches the config repo's `main`. A commit that
`applied/main` already contains is a no-op too: requests for two merges
can arrive in either order. A commit that descends from `applied/main`
is fast-forwarded to and a rebuild queued. A failure before the rebuild
(the fetch, or a commit that has diverged from `applied/main`) deploys
nothing and logs `swarm events: rev not
applied; nothing queued` at error, which fires the Grafana alert
`config rev refused`. An agent with no container on that hive yet ignores
the commit; its first deploy builds what it seeds.
5. If the commit already is the one the agent's last successful build pinned
(the `applied/.<n>.config-rev` marker), the hive does nothing. Otherwise it
queues a rebuild, which resolves its pins from `internal/meta` like any
rebuild. The request's commit only feeds that check: the hive keeps no copy
of the config. An agent with no container on that hive yet ignores the
commit; its first deploy builds whatever `internal/meta` pins.
This path runs **no eval-verify**. A merged config that doesn't
evaluate or build fails the rebuild, and `applied/main` stays at that
commit, so the agent's rebuilds keep failing until a fix merges. A
failed rebuild shows on the hive like any other. A merge whose webhook delivery never reaches the controller
deploys nothing.
evaluate or build fails the rebuild, so the agent's rebuilds keep failing
until a fix merges. A failed rebuild shows on the hive like any other. A merge
whose webhook delivery never reaches the controller deploys nothing.
Creating a brand-new agent is swarm-level (`swarmctl agent create`,
`POST /api/agents` on the swarm controller), and no hive can originate
@ -100,12 +95,11 @@ body strings before calling it.
`HostRequest::Destroy { name, purge }` is the lifecycle tear-down.
Stops + removes the nspawn container and drops the systemd drop-in.
Persistent state (the applied repo, claude credentials, `/state/`
notes) is
Persistent state (claude credentials, `/state/` notes) is
**kept by default** — recreating the agent with the same name
reuses prior config + login. With `purge = true` the agent's
`/var/lib/hyperhive/{agents,applied}/<name>/` trees are also
wiped (config history + creds + notes gone forever). The
reuses prior notes + login. With `purge = true` the agent's
`/var/lib/hyperhive/agents/<name>/` tree is also
wiped (creds + notes gone forever). The
root/bootstrap container is destroyable like any other, and stays gone
until you create ruth again from the swarm dashboard.
@ -123,8 +117,8 @@ agent's forge config repo, the one config PRs merge into) and one
address) aren't in it:
`hive-priv` writes them to the container's `/etc/hyperhive-hive-facts` at
start (see `docs/process/conventions.md`).
A config that sits only in the hive's `applied/<n>` mirror, not on forge
`main`, never deploys.
A config that sits only in a local checkout, not on forge `main`, never
deploys.
What `hive-c0re` still keeps under `/var/lib/hyperhive/meta/` is plain
state, bound read-only into the root agent's container as `/meta`:
@ -184,59 +178,41 @@ the swarm builder built the agent from the updated repo (the
reconcile ([below](#autoupdate-on-startup)) and the dashboard's update chip
read them.
## The applied repo per agent
## Deploy markers per agent
```
/var/lib/hyperhive/applied/<name>/ applied - core-only
├── .git/ # tag-rich history
├── flake.nix # tracked, fixed
│ # boilerplate exporting
│ # nixosModules.default
├── agent.nix # working tree of main
└── <other committed files> # also tracked
/var/lib/hyperhive/applied/ deploy markers - core-only
├── .<name>.hyperhive-rev # hyperhive commit of the last good build
└── .<name>.config-rev # config commit of the last good build
/var/lib/hyperhive/meta/ plain hive state - core
├── topology.json # the live agent set
└── swarm-resource-limits.json # the swarm's per-agent caps
```
hive-c0re seeds the applied repo at first spawn from the forge's
`agent-configs/<name>` `main`, or from a template when the forge has none yet,
and fast-forwards it to each merged config commit that deploys. An agent never
edits it: it isn't mounted into any container, and a config change is a PR
against the agent's forge repo.
Older hives also kept a *proposed* clone at `agents/<name>/config`. Nothing
reads it any more, and you can delete it.
hive-c0re keeps no copy of an agent's config: the forge's `agent-configs/<name>`
`main` is the only one, and a config change is a PR against it. The two markers
record what the agent's last successful build pinned, and nothing else.
Older hives also kept an *applied* repo at `applied/<name>` and a *proposed*
clone at `agents/<name>/config`. The startup migration removes the applied
repos. Nothing reads the proposed clone any more, and you can delete it.
The container's `--flake` ref is `internal/meta` (see "Meta flake" above).
The agent's own `applied/<n>/flake.nix`
The agent's `agent-configs/<n>/flake.nix`
is a fixed boilerplate that exports `nixosModules.default =
import ./agent.nix`; the meta flake imports that module and
wraps it with identity + `HIVE_PORT` / `HIVE_LABEL`.
### Tag state machine
hive-c0re plants `deployed/0` on the seed commit at first spawn. A
merged config commit that deploys plants no tag: the merged PR on the
forge and `internal/meta`'s commits record it. A config PR nobody merges
carries no extra state on the forge side: the PR stays open, and
the submitter pushes again (or closes it) to retry.
### Forge config repos
The bundled `hive-forge` container runs on the swarm's forge host
(`deploy.forgejo.enable`, see [`../swarm/services.md`](../swarm/services.md)).
Every agent created through the swarm has a repo in the private
`agent-configs` Forgejo org, created and seeded by swarm-controller.
hive-c0re pushes nothing to it: the applied repo's tags (`deployed/0`)
exist only locally, and forge `main` advances only by an operator's merge.
hive-c0re reads from it - reseeding a purged `applied`, catching `applied` up, and
and seeding `applied` at first spawn (`forge::fetch_config_main_into_applied`).
A legacy agent spawned by the hive alone has no swarm-seeded repo; if its
`agent-configs/<name>` has no `main`, a first spawn falls back to the local
template, and reseeding a purged `applied` fails with a `destroy --purge`
and re-spawn instruction.
hive-c0re pushes nothing to it and keeps no clone: forge `main` advances only
by an operator's merge. The hive reads the head of `main`, to resolve the
commit `internal/meta` pins.
Each agent is a **write collaborator on its own** `agent-configs/<name>`
repo — so it can push a branch and open a config PR — but not a member
@ -245,7 +221,7 @@ the forge. Branch protection keeps the agent off the `main` push
allowlist and allowlists merging to the `operators` team only, so an
agent can't fast-forward its own config or self-merge its PR (see
[Config changes](#config-changes) above). hive-c0re passes the credential to
each `git` call inline, never writing it into `applied/<n>/.git/config`, so
each `git` call inline, never writing it into a repo's config, so
no stored token sits in a repo an agent might one day read.
The dashboard deep-links into this org — a `config repo` link
@ -291,15 +267,14 @@ it never touches a key that's already set.
## Startup migrations (older hosts)
hive-c0re runs a couple of idempotent migrations on every startup so a
host set up before the tag-driven-deploy scheme (described above)
host set up before the current deploy scheme (described above)
converges to it automatically. Each phase is a no-op
once already applied:
- **Tags**: hive-c0re tags agents from before the tag-driven scheme
`deployed/0` on `main` once. Non-destructive — it doesn't touch live
containers, state dirs, or claude creds.
- **Agent files**: rewrites each `applied/<n>/flake.nix` to the
module-only boilerplate and refreshes the files that follow the agent set
- **Applied repos**: deletes the per-agent `applied/<n>` repos an earlier hive
kept. The deploy markers beside them stay. Non-destructive for everything
else — it doesn't touch live containers, state dirs, or claude creds.
- **Agent files**: refreshes the files that follow the agent set
(`agent_set::refresh`) from the current agent list. Set
`HIVE_SKIP_META_MIGRATION=1` on the service to defer this phase.
- **Legacy meta repo**: deletes the leftover `.git` in
@ -308,7 +283,7 @@ once already applied:
stay. The forge-side `core/meta` repo and `core` account aren't touched.
No state loss in either migration: claude creds, `/state/` notes, the
events DB, and the applied history all survive. The root
events DB all survive. The root
agent keeps its session; sub-agents stay logged in.
## The root/bootstrap container
@ -316,8 +291,7 @@ agent keeps its session; sub-agents stay logged in.
The root agent container runs through the **same lifecycle as
sub-agents**. Nothing creates it at hive boot: you create ruth from the
swarm dashboard like any agent, and an existing ruth container starts with
the hive like every other. The root agent's flake lives at
`/var/lib/hyperhive/applied/ruth/`. The root agent can clone
the hive like every other. The root agent can clone
`agent-configs/ruth` to read its own `agent.nix` and open a config PR there
for operator review, same as any other agent.
@ -414,8 +388,8 @@ config, that rebuild fails with the error that names the agent, and the
agent stays stopped until the update lands and you ask for the start again.
The dashboard surfaces pending config changes per agent: a clickable
"needs update ↻" badge appears whenever the agent's applied repo has a
commit that its last build didn't pin. The badge POSTs `/api/rebuild/<name>`, which inserts the
"needs update ↻" badge appears whenever the config commit that `internal/meta`
pins for the agent's next build differs from the one its last build pinned. The badge POSTs `/api/rebuild/<name>`, which inserts the
same `job_queue::templates::rebuild` DAG so manual triggers and the
startup scan can't drift. When at least one container is stale, a
top-level `↻ UPD4TE 4LL` button appears that loops over every