Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/hive-c0re/src/workers/mcp_sockets.rs
atlas 5785c0024c 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
2026-09-29 15:47:40 +02:00

54 lines
2.2 KiB
Rust

//! MCP socket listener boot sync.
//!
//! On hive-c0re startup, any agent containers that survived the daemon restart
//! still have their bind-mount source dirs but no live MCP listener (the
//! `Coordinator` is freshly empty). `sync_on_start` does a one-shot sweep to
//! re-register all running agents.
//!
//! After startup, listeners are managed event-driven:
//! - `run_start` (the job queue's `Start` node) calls `register_agent`
//! once the container is started.
//! - `kill`/`destroy` paths call `unregister_agent`.
//!
//! No recurring poll is needed because c0re owns the listener lifecycle. An
//! accept loop retries every error except one that leaves its fd unusable,
//! and on that one hive-c0re exits (`socket_server::exit_on_dead_listener`),
//! so a listener only disappears across a restart — exactly the case
//! `sync_on_start` covers.
use std::sync::Arc;
use crate::coordinator::Coordinator;
/// One-shot MCP listener sync run at daemon startup.
///
/// Iterates all currently-running agent containers and calls `register_agent`
/// for any that have no live listener in the `Coordinator`. Safe to call
/// concurrently with the rest of startup — `register_agent` is idempotent
/// (drops and rebinds) and the coordinator lock serialises concurrent calls.
pub async fn sync_on_start(coord: Arc<Coordinator>) {
let running = match crate::lifecycle::list().await {
Ok(names) => names,
Err(e) => {
tracing::warn!(error = ?e, "mcp_sockets: startup sync failed to list agents; MCP listeners may be missing until next start");
return;
}
};
let registered = coord.list_agents();
for container in running {
let Some(name) = container.strip_prefix(crate::lifecycle::AGENT_PREFIX) else {
continue;
};
if !registered.contains(&name.to_owned()) {
if let Err(e) = coord.register_agent(name) {
tracing::warn!(
agent = %name,
error = ?e,
"mcp_sockets: startup register_agent failed"
);
} else {
tracing::debug!(agent = %name, "mcp_sockets: registered listener on startup");
}
}
}
}