Watch
0
0
Fork
You've already forked hyperhive
0

Make agent creation swarm-only and refuse a name placed on another hive

swarm-controller's POST /api/agents now refuses (409) a name the swarm
has already placed on a different hive: a non-Destroyed declaration in
that hive's wanted state, or a SetAgentWanted node still queued for it.
The same name on the same hive is that agent being re-created and goes
through. A wanted state that cannot be read refuses (503/500) instead of
reading as "placed nowhere". Creations are serialised from that read to
the graph insert so two concurrent creations of one name cannot both
pass.

Hive-level creation is removed: hivectl `agent create` / `request-create`,
HostRequest::Spawn / RequestSpawn, the dashboard POST /api/request-spawn
route, and ApprovalKind::Spawn with its approve/resolve arms and the
approval-carrying `templates::spawn`. The swarm path (deploy request or
wanted-state sweep -> queue_first_deploy -> templates::first_deploy) used
none of them. Old `spawn` approval rows are skipped by collect_lenient,
as `init_config` rows were in a3b672d1.

policy.rs's comment on agent_object_name stated swarm-wide name
uniqueness as a fact; it now says where it is enforced and what that
check cannot see.

Refs #4396
This commit is contained in:
atlas 2026-09-29 15:11:57 +02:00
commit 5785c0024c
35 changed files with 376 additions and 434 deletions

View file

@ -52,7 +52,7 @@ pub enum DashboardEvent {
/// enough to render the dashboard row without a `/api/state`
/// refetch.
///
/// The approval's own kind (`"merge_config_pr"` / `"spawn"`) lives
/// The approval's own kind (`"merge_config_pr"` / `"schedule_prompt"`) lives
/// on `approval_kind` rather than `kind` because the latter is taken
/// by the serde tag identifying which `DashboardEvent` variant
/// this is.
@ -119,8 +119,8 @@ pub enum DashboardEvent {
name: String,
transient_kind: String,
},
/// One container row changed — new container appeared (post-spawn
/// finalise), an existing one flipped `running` / `needs_update` /
/// One container row changed — a new container appeared, an existing
/// one flipped `running` / `needs_update` /
/// `sha`, etc. Clients upsert by `container.name`. Payload carries
/// the full row so cold-loaded clients and event-driven clients
/// converge on the same render.
@ -136,10 +136,8 @@ pub enum DashboardEvent {
/// `nixos-container destroy` (operator-driven or otherwise) on the
/// next rescan.
ContainerRemoved { seq: u64, name: String },
/// Full snapshot of the tombstones list. Emitted on every
/// mutation that could add / remove a tombstone: destroy
/// (with or without purge), purge-tombstone, spawn approval
/// (which can consume a tombstone of the same name). Snapshot
/// Full snapshot of the tombstones list. Emitted by destroy (with
/// or without purge) and purge-tombstone. Snapshot
/// shape (not diff) because the list is tiny (single-digit
/// typical) and recomputing avoids the add/remove races a
/// per-row event would have.