build agents only from internal/meta; delete the hive's local meta flake
The hive no longer renders, locks or commits a meta flake. A rebuild's MetaSync node picks the internal/meta commit and records it with the hyperhive and config pins it holds; Prebuild, Swap and Create build from it, and a head that does not pin the agent's current config fails the build naming the agent. hive-priv's meta_rev is required. Removed: meta.rs sync/lock/render, the manual-rebuild relock, the boot sweep's hyperhive bump (MetaLock), the update-meta-inputs form and its panel, MetaWindow, the hive's hyperhive_rev setting. Kept: agent_set::refresh holds what sync_agents also did (topology.json, the gateway's agent-socket map and nginx include), called from spawn, destroy, rebuild and the migration. The boot reconcile and the stale-start upgrade compare the agent's marker with the hyperhive pin of internal/meta; the dashboard's deployed chip reads the config marker a successful build writes.
This commit is contained in:
parent
cea4827918
commit
4aa11b3a94
64 changed files with 1042 additions and 3793 deletions
|
|
@ -25,7 +25,7 @@ create` (or the swarm UI, via `POST /api/agents`) writes that identity
|
|||
once, by calling `ensure_agent_identity`.
|
||||
|
||||
This page is about a different, **hive-local** file: the hive-c0re-owned
|
||||
**meta repo**, alongside `flake.nix`, at
|
||||
`meta/` directory's `topology.json`, at
|
||||
`/var/lib/hyperhive/meta/topology.json`:
|
||||
|
||||
```json
|
||||
|
|
@ -102,10 +102,8 @@ state dirs work as for every other agent.
|
|||
`/var/lib/hyperhive/agents` bind-mounted RW at `/agents` (so it can
|
||||
manage any agent's state dir — config isn't authored there, since a
|
||||
real config change is a PR from a clone), plus RO mounts for
|
||||
`/applied` (diff against what's deployed) and `/meta` (system-wide
|
||||
deploy log). Any agent holding it gets the same mounts.
|
||||
The operator dashboard's `POST /api/meta-update` is the only path
|
||||
that writes `flake.lock`.
|
||||
`/applied` (diff against what's deployed) and `/meta` (the hive's
|
||||
topology and limits files). Any agent holding it gets the same mounts.
|
||||
- **Prompt** — identical for every container but one block (below):
|
||||
`prompts/system.md`
|
||||
carries `<!-- role:agent -->` / `<!-- role:manager -->` marker
|
||||
|
|
|
|||
|
|
@ -113,9 +113,11 @@ until you create ruth again from the swarm dashboard.
|
|||
|
||||
## Meta flake
|
||||
|
||||
The hive-c0re-owned repo at `/var/lib/hyperhive/meta/`
|
||||
declares one flake input per agent (`agent-<n>.url =
|
||||
"git+<forge>/agent-configs/<n>.git?rev=<sha>"`) and one
|
||||
A hive has no meta flake of its own. It builds every agent from the swarm's
|
||||
`internal/meta`, which swarm-controller commits ([below](#the-swarms-meta-flake)).
|
||||
It declares one flake input per agent (`agent-<n>.url =
|
||||
"git+<forge>/agent-configs/<n>.git?rev=<sha>"`, the head of `main` on the
|
||||
agent's forge config repo, the one config PRs merge into) and one
|
||||
`nixosConfigurations.<n>` output per agent. Each output wraps
|
||||
`inputs.agent-<n>.nixosModules.default` with the identity +
|
||||
`HIVE_PORT` / `HIVE_LABEL` injection module. Values that differ per host
|
||||
|
|
@ -123,53 +125,32 @@ declares one flake input per agent (`agent-<n>.url =
|
|||
address) aren't in it:
|
||||
`hive-priv` writes them to the container's `/etc/hyperhive-hive-facts` at
|
||||
start (see `docs/process/conventions.md`).
|
||||
Containers run against `--flake /var/lib/hyperhive/meta#<n>`, or against
|
||||
`internal/meta` when it pins the agent ([below](#the-swarms-meta-flake)).
|
||||
A config that sits only in the hive's `applied/<n>` mirror, not on forge
|
||||
`main`, never deploys.
|
||||
|
||||
The declared input url is the head of `main` on the agent's forge config repo
|
||||
(`agent-configs/<n>`, the one config PRs merge into), which `sync_agents`
|
||||
reads at render time. The flake text depends only on those shas and the
|
||||
agent set, so any machine that can reach the forge resolves it. `sync_agents` fetches
|
||||
each repo's `main` with the hive's read token (`forge::reader_token()`),
|
||||
pins its head, and takes the canonical `follows` lines from the
|
||||
`flake.lock` at that commit. If the forge is unreachable or a repo has no
|
||||
`main`, `sync_agents` fails for that agent with its name and writes
|
||||
nothing; there is no fallback to a local path. A config that sits only in
|
||||
the hive's `applied/<n>` mirror, not on forge `main`, never deploys.
|
||||
The rebuild paths re-lock the input with `nix flake update agent-<n>` against that
|
||||
same pinned url, so `flake.nix` and `flake.lock` name no local path.
|
||||
What `hive-c0re` still keeps under `/var/lib/hyperhive/meta/` is plain
|
||||
state, bound read-only into the root agent's container as `/meta`:
|
||||
`topology.json`, the swarm resource limits, and the permission seed files of a
|
||||
hive that predates the swarm's permissions. `agent_set::refresh(agents:
|
||||
&[AgentSpec])` keeps `topology.json`, the gateway's agent-socket map and its
|
||||
nginx include in line with the live agents. `provision_container`, `destroy`,
|
||||
`rebuild` and the startup migration call it. It refuses an empty agent list,
|
||||
which means `nixos-container list` failed, and it fails when `HIVE_FORGE_URL`
|
||||
is unset, since every agent would come up with no forge.
|
||||
|
||||
Lock updates are single-phase and commit when the lock changed:
|
||||
`meta::lock_update_for_rebuild(name)` relocks one agent's input for a
|
||||
relocking rebuild (the manual `↻ R3BU1LD` button, and the rebuild a
|
||||
merged config commit queues), and `meta::lock_update_hyperhive()` is the
|
||||
autoupdate flake-rev bump (one shot before per-agent rebuilds).
|
||||
|
||||
`meta::sync_agents(hive: &HiveEnv, agents: &[AgentSpec])` — `hive`
|
||||
carries `hyperhive_flake`, `dashboard_port`, and the rest of the
|
||||
per-hive config — is the idempotent reconciler called by `provision_container`,
|
||||
`destroy`, `rebuild`, and the startup migration. Renders `flake.nix`
|
||||
from the agent list; if it differs from disk, or `flake.nix` or `flake.lock`
|
||||
has an uncommitted change (a sync stopped before its commit), runs
|
||||
`nix flake lock` + commits as `regenerate meta flake` (or
|
||||
`seed meta from N agent(s)` on the first call). A clean tree whose
|
||||
`flake.nix` already matches returns without running nix.
|
||||
|
||||
The root agent has `/meta` RO-bound inside its container:
|
||||
`git -C /meta log --oneline` is the swarm-wide deploy log,
|
||||
`cat /meta/flake.lock | jq '.nodes["agent-<n>"].locked'`
|
||||
resolves which sha the flake pins each agent at right now.
|
||||
Dashboard surfaces the same info as a `deployed:<sha12>` chip
|
||||
per container row.
|
||||
The dashboard's `deployed:<sha12>` chip per container row shows the config
|
||||
commit the agent's last successful build pinned.
|
||||
|
||||
## The swarm's meta flake
|
||||
|
||||
swarm-controller also commits a meta flake of its own, `internal/meta`, with
|
||||
the same renderer: one `flake.nix` and `flake.lock` for every agent any hive
|
||||
places (the pins: [`docs/swarm/builder.md`](../swarm/builder.md#meta-flake-lock)).
|
||||
A hive builds an agent from it when the repo has a commit that pins the
|
||||
agent's current config, and from its own meta flake above otherwise, so a hive
|
||||
older or newer than the controller keeps building.
|
||||
swarm-controller commits `internal/meta`: one `flake.nix` and `flake.lock` for
|
||||
every agent any hive places (the pins:
|
||||
[`docs/swarm/builder.md`](../swarm/builder.md#meta-flake-lock)). A hive builds
|
||||
every agent from it. While the repo has no commit that pins an agent's current
|
||||
config, that agent's build fails and names the agent. The controller queues
|
||||
its update of the repo before the deploy request for a merged config commit
|
||||
and for a new agent, so a failure here is rare. It sends the deploy even when
|
||||
that update failed, and the build then fails as described.
|
||||
|
||||
- **Choosing the commit.** The rebuild's `MetaSync` node reads the repo's
|
||||
default-branch head with the hive's read token (`forge::reader_token()`)
|
||||
|
|
@ -178,8 +159,9 @@ older or newer than the controller keeps building.
|
|||
`main`, an archetype agent from the archetype it has now. An agent switched
|
||||
between custom and archetype, or to another archetype, fails that test.
|
||||
A hive without the `core` token file that nix's git reads, a repo with no
|
||||
commit, a head that lacks the agent and a head behind the config all select
|
||||
the hive's own flake. `MetaSync` records the commit in `hive-c0re`'s own state,
|
||||
commit, a head that lacks the agent and a head behind the config each fail
|
||||
the build with an error that names the agent. `MetaSync` records the commit,
|
||||
with the hyperhive and config commits the head pins, in `hive-c0re`'s own state,
|
||||
`/var/lib/hyperhive/meta-rev/<n>`, which the agent can't write (the container mounts its runtime
|
||||
directory read-write); `Prebuild` and `Swap` read that
|
||||
record, so both build one flake. A first deploy resolves the commit in its `Create`
|
||||
|
|
@ -190,10 +172,8 @@ older or newer than the controller keeps building.
|
|||
`CreateContainer`). `hive-priv` accepts only a 40-digit lowercase hex
|
||||
commit and builds the flake from its own forge URL
|
||||
(`HIVE_PRIV_FORGE_URL`); the URL never comes from a request. On the wire the
|
||||
`hive_priv_sock::PrivRequest` variants carry an optional field:
|
||||
`hive_priv_sock::PrivRequest` variants carry a required field:
|
||||
`{"op":"update_container","name":"atlas","stream":true,"meta_rev":"<40 hex>"}`.
|
||||
Without it, as from a hive-c0re that predates the field, hive-priv builds the
|
||||
hive's own flake.
|
||||
- **Fetching.** nix fetches the forge's inputs with `git`. Both `hive-c0re`
|
||||
and `hive-priv` carry a `$HOME/.gitconfig` that answers for the forge host
|
||||
through `git-credential-hive-forge` (the `core` token file) and rewrites the
|
||||
|
|
@ -201,6 +181,11 @@ older or newer than the controller keeps building.
|
|||
`http`.
|
||||
- **Hyperhive.** The commit pins one hyperhive rev for the swarm, so an agent
|
||||
runs the hyperhive of the controller's deploy, not its hive's.
|
||||
- **Markers.** After a successful build `hive-c0re` writes the hyperhive
|
||||
commit the head pins to `applied/.<n>.hyperhive-rev`, and the config
|
||||
commit to `applied/.<n>.config-rev` (none for an archetype agent). The boot
|
||||
reconcile ([below](#autoupdate-on-startup)) and the dashboard's update chip
|
||||
read them.
|
||||
|
||||
## Two repos per agent
|
||||
|
||||
|
|
@ -223,11 +208,9 @@ older or newer than the controller keeps building.
|
|||
├── agent.nix # working tree of main
|
||||
└── <other committed files> # also tracked
|
||||
|
||||
/var/lib/hyperhive/meta/ swarm-wide flake — core
|
||||
├── .git/ # one commit per lock
|
||||
│ # change
|
||||
├── flake.nix # generated from agent set
|
||||
└── flake.lock # pins each agent's sha
|
||||
/var/lib/hyperhive/meta/ plain hive state — core
|
||||
├── topology.json # the live agent set
|
||||
└── swarm-resource-limits.json # the swarm's per-agent caps
|
||||
```
|
||||
|
||||
Why two physical repos: the submitting agent's `/agents/<n>/config/` is
|
||||
|
|
@ -236,8 +219,8 @@ proposed tree. The applied repo is never bind-mounted (except
|
|||
the read-only `.git` exposure described below) so a destructive
|
||||
move inside the container can't reach it.
|
||||
|
||||
The container's `--flake` ref is `/var/lib/hyperhive/meta#<name>` (or `internal/meta`)
|
||||
(see "Meta flake" above). The agent's own `applied/<n>/flake.nix`
|
||||
The container's `--flake` ref is `internal/meta` (see "Meta flake" above).
|
||||
The agent's own `applied/<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`.
|
||||
|
|
@ -246,7 +229,7 @@ wraps it with identity + `HIVE_PORT` / `HIVE_LABEL`.
|
|||
|
||||
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 meta's lock commits record it. A config PR nobody merges
|
||||
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.
|
||||
|
||||
|
|
@ -335,9 +318,6 @@ git -C /agents/<n>/config log applied/main --oneline
|
|||
git -C /agents/<n>/config show applied/refs/tags/deployed/0 # the seed commit
|
||||
git -C /agents/<n>/config show applied/refs/tags/denied/<id> # body = operator note
|
||||
git -C /agents/<n>/config rebase applied/main # base in-flight work on what's deployed
|
||||
|
||||
git -C /meta log --oneline # swarm-wide deploy history
|
||||
cat /meta/flake.lock | jq '.nodes | with_entries(select(.key | startswith("agent-")))'
|
||||
```
|
||||
|
||||
The RO binds block push at the kernel level — git plumbing inside the
|
||||
|
|
@ -346,16 +326,17 @@ container can't corrupt either authoritative repo.
|
|||
## 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 + meta-flake scheme (both
|
||||
described above) converges to it automatically. Each phase is a no-op
|
||||
host set up before the tag-driven-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.
|
||||
- **Meta flake**: rewrites each `applied/<n>/flake.nix` to the
|
||||
- **Agent files**: rewrites each `applied/<n>/flake.nix` to the
|
||||
module-only boilerplate, wires the `applied` remote in each proposed
|
||||
repo, and bootstraps the meta repo from the current agent list. Set
|
||||
repo, and 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.
|
||||
|
||||
No state loss in either migration: claude creds, `/state/` notes, the
|
||||
|
|
@ -450,19 +431,25 @@ swarm-level alert could carry the same signal) — only reach for a new
|
|||
|
||||
`hive-c0re serve` runs `auto_update::run` in a background task right
|
||||
after opening the coordinator. It enumerates managed containers and
|
||||
rebuilds any whose recorded hyperhive rev differs from the current
|
||||
one — sub-agents and the root agent go through the same
|
||||
`job_queue::templates::rebuild` DAG.
|
||||
rebuilds any whose recorded hyperhive rev differs from the one
|
||||
`internal/meta` pins now. That's how a hive that missed the swarm's
|
||||
fan-out catches up. Sub-agents and the root agent go through the same
|
||||
graceful rebuild subgraph.
|
||||
|
||||
"Rev" = canonical filesystem path of
|
||||
`services.hyperhive.c0re.hyperhiveFlake`. Marker
|
||||
file: `/var/lib/hyperhive/applied/.<name>.hyperhive-rev`. If the
|
||||
flake input has no canonical path (for example a `github:` URL),
|
||||
autoupdate is a no-op — rebuild manually.
|
||||
"Rev" = the hyperhive commit pinned by the head of `internal/meta`, as
|
||||
the hive reads it with its forge token. Marker
|
||||
file: `/var/lib/hyperhive/applied/.<name>.hyperhive-rev`, written after
|
||||
a successful build. If the hive can't read the pin (no forge token, the
|
||||
forge down, `internal/meta` not published yet), the scan rebuilds
|
||||
nothing; a rebuild would have no flake to build from either. A hive deploy
|
||||
alone doesn't rebuild agents.
|
||||
|
||||
The dashboard surfaces pending updates per agent: a clickable
|
||||
"needs update ↻" badge appears whenever the marker differs from
|
||||
current rev. The badge POSTs `/api/rebuild/<name>`, which inserts the
|
||||
A stopped agent gets its rebuild at its next start: the start path upgrades
|
||||
the start of a stale, stopped agent to a rebuild followed by the start.
|
||||
|
||||
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
|
||||
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
|
||||
|
|
|
|||
|
|
@ -438,7 +438,7 @@ this hive's archetype agents runs, as last read from the swarm's
|
|||
with an entry has no `applied/<n>` repo and no `agent-<n>` meta input; see
|
||||
[archetypes](../swarm/archetypes.md).
|
||||
|
||||
hive-c0re renders containers onto `meta#<n>` at creation.
|
||||
hive-c0re creates containers from `internal/meta` (`nixosConfigurations.<n>`).
|
||||
|
||||
## Destroy vs purge
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue