From 90e8158f37e9e82a805af9a622e76959a0916c0d Mon Sep 17 00:00:00 2001 From: atlas Date: Fri, 16 Oct 2026 17:26:14 +0200 Subject: [PATCH 1/4] remove the hive-side auto-spawn of ruth and the ruthless option ruth is created from the swarm dashboard like any agent. An existing ruth container starts at boot through the boot reconcile, like every other agent. A definition of services.hyperhive.ruthless now fails evaluation with a removal message. --- docs/agent-lifecycle/agent-roster.md | 11 +- docs/agent-lifecycle/config-changes.md | 18 +-- docs/agent-lifecycle/persistence.md | 14 +- docs/getting-started/setup.md | 24 +-- docs/scheduler/coordinator.md | 2 +- hive-c0re/src/actions.rs | 7 +- hive-c0re/src/coordinator.rs | 2 +- hive-c0re/src/lifecycle/mod.rs | 19 --- hive-c0re/src/main.rs | 10 +- hive-c0re/src/workers/auto_update.rs | 174 +-------------------- nix/host-modules/hive-c0re/environment.nix | 4 - nix/host-modules/hyperhive.nix | 22 +-- nix/module-eval/core-toggle.nix | 20 +++ 13 files changed, 59 insertions(+), 268 deletions(-) diff --git a/docs/agent-lifecycle/agent-roster.md b/docs/agent-lifecycle/agent-roster.md index c8e1ec65..14189b60 100644 --- a/docs/agent-lifecycle/agent-roster.md +++ b/docs/agent-lifecycle/agent-roster.md @@ -80,19 +80,14 @@ module docs for the exact call chain. The **manager (`ruth`)** differs from other agents in naming/bootstrap, its socket flavour and its default permissions; its prompt, tool -allow-list and state dirs work as for every other agent. A hive with -`services.hyperhive.ruthless = true` runs no manager: `hive-c0re` skips -its root-agent create/start sweep -(`hive-c0re/src/workers/auto_update.rs`), and this section doesn't -apply. +allow-list and state dirs work as for every other agent. - **Naming/bootstrap** — the manager's broker recipient name, state-dir key, and nixos-container name are all `ruth` (container `h-ruth`). - `hive-c0re` spawns it directly at boot if missing, with no operator - approval step — every other agent is created at swarm level - (`swarmctl agent create`). + Like every agent it comes from the swarm dashboard or + `swarmctl agent create`; `hive-c0re` creates nothing at boot. Roster-wise, `ruth` is just another entry. - **Wire-protocol** — `hive-core-agent-sock`'s unified `Request` enum is the same on both socket flavours. Container lifecycle diff --git a/docs/agent-lifecycle/config-changes.md b/docs/agent-lifecycle/config-changes.md index 03364edc..76f218dd 100644 --- a/docs/agent-lifecycle/config-changes.md +++ b/docs/agent-lifecycle/config-changes.md @@ -106,9 +106,8 @@ notes) is reuses prior config + login. With `purge = true` the agent's `/var/lib/hyperhive/{agents,applied}//` trees are also wiped (config history + creds + notes gone forever). The -root/bootstrap container is destroyable like any other — hive-c0re -recreates it on the next startup if it's absent, so destroying it's -transient. +root/bootstrap container is destroyable like any other, and stays gone +until you create ruth again from the swarm dashboard. ## Meta flake @@ -143,7 +142,7 @@ 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 `spawn`, +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, runs `nix flake lock` + commits as `regenerate meta flake` (or @@ -314,11 +313,12 @@ No state loss in either migration: claude creds, `/state/` notes, the events DB, and both proposed + applied history all survive. The root agent keeps its session; sub-agents stay logged in. -## The root/bootstrap container is hive-c0re-managed +## The root/bootstrap container The root agent container runs through the **same lifecycle as -sub-agents**. On `hive-c0re serve` startup, if `ruth` is missing, -hive-c0re creates it. The root agent's flake lives at +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/`; its proposed config at `/var/lib/hyperhive/agents/ruth/config/`. The root agent can edit its own `agent.nix` (visible inside the container at `/agents/ruth/config/`) @@ -342,8 +342,8 @@ Differences from sub-agents: not a special manager-only path. **Migration note** (for older hosts): drop any `containers.root = -{ ... }` block from your host NixOS config. hyperhive creates and -updates the root agent itself. +{ ... }` block from your host NixOS config, and any `services.hyperhive.ruthless` +definition, then create ruth from the swarm dashboard. ## Root-agent policy diff --git a/docs/agent-lifecycle/persistence.md b/docs/agent-lifecycle/persistence.md index 06b255b4..bdcc6bf8 100644 --- a/docs/agent-lifecycle/persistence.md +++ b/docs/agent-lifecycle/persistence.md @@ -31,9 +31,9 @@ Beyond that: restart and rebuild** — only an explicit purge (or a hive `--purge`-style host operation, or the agent's own choices) touches them. -- The **root/bootstrap agent is special**: it isn't really destroyable - in practice — hive-c0re recreates it automatically on its next - startup if it's ever gone. +- The **root/bootstrap agent has no guard**: destroying ruth is as final as + for any agent, and she stays gone until you create her again from the + swarm dashboard. Everything below this point is implementation detail: exact table schemas, file layouts, and internal migration mechanics. @@ -432,11 +432,9 @@ an agent's state. The mechanics, for completeness: - `PURG3` wipes `/var/lib/hyperhive/{agents,applied}//` — the union of everything `DESTR0Y` left behind. -`actions::destroy` implements the root/bootstrap agent's specialness as a soft policy -guard that refuses to destroy it, backstopped by -`auto_update::ensure_root_agent`, which recreates it on the next -hive-c0re startup if it's ever absent — so even without the guard, -destroying it would only be transient. +`actions::destroy` has no guard for the root/bootstrap agent: it destroys ruth +like any other agent, and she stays gone until you create her again from the +swarm dashboard. ### btrfs subvolumes for `/var/lib/hyperhive/agents/` diff --git a/docs/getting-started/setup.md b/docs/getting-started/setup.md index 11b3fb72..913a2bcc 100644 --- a/docs/getting-started/setup.md +++ b/docs/getting-started/setup.md @@ -89,24 +89,16 @@ swarmctl forge make-admin mara If the forge already has a local account with your username, the first SSO login asks for its forge password once, to link the two. -## 4 · Ruth's store identity +## 4 · Ruth -hive-c0re creates the manager agent, ruth, on its own at startup — so -unlike agents made through the swarm, she has no store identity yet: +hive-c0re creates no agent at startup. You create ruth, the manager agent, +like any other: open the swarm UI start page and click the paperclip, which +opens the create form with `ruth` filled in. The form declares her, creates +her config repo and deploys her to the hive you pick. -```bash -swarmctl agent create ruth --hive # on the swarm-controller's host -hivectl agent ruth rebuild # on ruth's hive: hands her the identity -``` - -`agent create` declares an agent the swarm has no declaration for as -`paused`, so without a further step ruth keeps running but drives no turns. -`swarmctl` has no verb for wanted state: open `/agents` in the swarm UI and set -her `up` from her card. - -Within about ten minutes the controller creates her forge user and matrix -account and mints their tokens into the store, where she fetches them. -`swarmctl agent mint-forge-token ruth` skips the wait for the forge token. +On a hive that already has a ruth container from an earlier release, create +her once the same way, so that her config repo and store identity exist. The +container keeps starting with the hive meanwhile. ## 5 · Matrix diff --git a/docs/scheduler/coordinator.md b/docs/scheduler/coordinator.md index 82010b29..366b9638 100644 --- a/docs/scheduler/coordinator.md +++ b/docs/scheduler/coordinator.md @@ -178,7 +178,7 @@ Power toggles never commit to the meta repo. Every operator power surface — dashboard buttons, the MCP tools, and `hivectl stop/start/restart/kill` — rides the queue through that power layer, so intent, lease serialization, and crash-watch suppression can't drift per surface; the only direct starts -left are the root-agent bootstrap and infra containers (no lease, no +left are infra containers (no lease, no harness). Cancelling a still-queued power DAG reverts `wanted` to the observed state — a cancel means "don't do it," not "do it later." hive-c0re seeds agents without a row from observed state on first touch (running ⇒ diff --git a/hive-c0re/src/actions.rs b/hive-c0re/src/actions.rs index 841e123e..dd9d72e6 100644 --- a/hive-c0re/src/actions.rs +++ b/hive-c0re/src/actions.rs @@ -126,10 +126,9 @@ async fn ff_applied_main( /// is cleared because its contents (the mcp socket) don't survive restarts /// anyway. With `purge=true` the persistent trees are also wiped — config /// history, claude creds, notes — there is no undo. -/// The bootstrap/root container is destroyable like any other: it's -/// imperative infra that `auto_update::ensure_root_agent` recreates on the -/// next hive-c0re startup if absent, so destroying it is transient rather -/// than something to refuse at the API. +/// The bootstrap/root container is destroyable like any other. Nothing +/// recreates it afterwards: ruth stays gone until she is created again from +/// the swarm dashboard. /// /// Submits the teardown DAG and returns — it does not wait for the container to /// go away. Same contract as every other lifecycle op (`rebuild`, `kill`, diff --git a/hive-c0re/src/coordinator.rs b/hive-c0re/src/coordinator.rs index 1592ed09..8fe5730c 100644 --- a/hive-c0re/src/coordinator.rs +++ b/hive-c0re/src/coordinator.rs @@ -496,7 +496,7 @@ impl Coordinator { } /// Snapshot the hive-wide configuration fields as a [`HiveEnv`]. - /// Pass the result to `lifecycle::spawn` / `rebuild` / `meta::sync_agents` + /// Pass the result to `lifecycle::provision_container` / `rebuild` / `meta::sync_agents` /// instead of threading the individual fields separately. #[must_use] pub fn hive_env(&self) -> HiveEnv { diff --git a/hive-c0re/src/lifecycle/mod.rs b/hive-c0re/src/lifecycle/mod.rs index 580e509b..4decfdac 100644 --- a/hive-c0re/src/lifecycle/mod.rs +++ b/hive-c0re/src/lifecycle/mod.rs @@ -225,15 +225,6 @@ async fn port_collision(self_name: &str) -> Result> { Ok(None) } -pub async fn spawn(name: &str, hive: &HiveEnv, paths: &AgentPaths) -> Result<()> { - create_container(name, hive, paths).await?; - // Runtime dir must exist before nixos-container start (nspawn bind-mount - // source). Create it here so callers don't need a separate preamble step. - ensure_agent_runtime_dir(name)?; - write_dropins(name, hive, paths).await?; - priv_run("start", name).await -} - /// First-spawn pre-create provisioning — the job queue's `Provision` /// node. Fail fast on a port collision, set up the proposed/applied /// repos + state subvolume + claude/notes dirs, then register the new @@ -266,16 +257,6 @@ pub async fn create_only(name: &str) -> Result<()> { priv_run("create", name).await } -/// First-spawn provisioning + `nixos-container create`, without the -/// drop-in write or the start. `spawn` composes this with -/// `write_dropins` + start for direct callers (root-agent bootstrap); -/// the job queue instead runs `provision_container` (`Provision` node) -/// and `create_only` (`Create` node) as separate DAG steps. -pub async fn create_container(name: &str, hive: &HiveEnv, paths: &AgentPaths) -> Result<()> { - provision_container(name, hive, paths).await?; - create_only(name).await -} - /// Rebuild-path preamble, run by the job queue's `Prebuild` node: fail fast on /// a port collision, then make sure the applied repo + state dirs exist. /// Container untouched. diff --git a/hive-c0re/src/main.rs b/hive-c0re/src/main.rs index 761d32ea..06c8a8ac 100644 --- a/hive-c0re/src/main.rs +++ b/hive-c0re/src/main.rs @@ -272,21 +272,13 @@ async fn cmd_serve( // repos, ensure proposed repos carry the `applied` // remote, bootstrap the meta repo, repoint containers at // `meta#` (one-shot, guarded by a marker file). - // Runs before manager auto-spawn so the new manager is - // built against meta from the first attempt. + // Runs before the boot reconcile, so agents are built against meta. if let Err(e) = migrate::run(&coord).await { tracing::warn!(error = ?e, "startup migration failed"); } // Before any agent starts, so the bind mounts it gets from the stored // permissions are the meta repo's grants until the swarm answers. crate::workers::permissions_import::seed_local(); - // Auto-create the root agent container if it isn't there yet. Block - // on this — without root the system has no manager harness. - // Failures are logged but allowed: a broken auto-spawn shouldn't - // make the dashboard unreachable for debugging. - if let Err(e) = auto_update::ensure_root_agent(&coord).await { - tracing::warn!(error = ?e, "auto-spawn root agent failed"); - } // Auto-update in the background — don't block service start. // Sub-agent rebuilds can take tens of seconds; we want the admin // socket up immediately. diff --git a/hive-c0re/src/workers/auto_update.rs b/hive-c0re/src/workers/auto_update.rs index b02305b8..a11fbafd 100644 --- a/hive-c0re/src/workers/auto_update.rs +++ b/hive-c0re/src/workers/auto_update.rs @@ -22,7 +22,7 @@ use std::sync::Arc; use anyhow::Result; use crate::coordinator::Coordinator; -use crate::lifecycle::{self, AGENT_PREFIX, MANAGER_NAME}; +use crate::lifecycle::{self, AGENT_PREFIX}; /// Resolve the current rev of `hyperhive_flake`. For a path on disk we /// canonicalize (following symlinks) so a /etc/hyperhive → /nix/store/... @@ -66,150 +66,6 @@ pub async fn agent_config_pending(name: &str, deployed_sha: Option<&str>) -> boo } } -/// Whether this hive is "ruthless" — running with no root/manager agent at -/// all (no ruth). When true, hive-c0re skips the root-agent create/start -/// sweep entirely. Controlled by the host option -/// `services.hyperhive.ruthless`, threaded in via the `HYPERHIVE_RUTHLESS` -/// env var. Defaults to `false` when the var is unset (back-compat: the -/// root agent was always auto-managed before this opt-out existed); only -/// an explicit `true` / `1` / `yes` enables ruthless mode. -fn ruthless() -> bool { - match std::env::var("HYPERHIVE_RUTHLESS") { - Ok(v) => matches!(v.trim().to_ascii_lowercase().as_str(), "true" | "1" | "yes"), - Err(_) => false, - } -} - -/// What `ensure_root_agent` does with a `lifecycle::list()` result, before -/// it even gets to check whether the manager is present. -#[derive(Debug, PartialEq, Eq)] -enum RootAgentPlan { - /// The manager is in this readable list. - Present, - /// The manager is absent from this readable list: spawn it. - Absent, - /// The list could not be read. That is not "manager absent" — it - /// proves nothing either way, so the auto-spawn decision is skipped - /// for this attempt rather than risking a spawn over a manager that's - /// actually there. - Unreadable, -} - -/// Turn a `lifecycle::list()` result into `ensure_root_agent`'s plan. -fn plan_root_agent(list_result: anyhow::Result>) -> RootAgentPlan { - match list_result { - Ok(names) - if names - .iter() - .any(|c| c.strip_prefix(AGENT_PREFIX) == Some(MANAGER_NAME)) => - { - RootAgentPlan::Present - } - Ok(_) => RootAgentPlan::Absent, - Err(_) => RootAgentPlan::Unreadable, - } -} - -/// Auto-create the manager container on startup if it isn't already there. -/// hive-c0re manages the manager end-to-end: operators no longer declare -/// `containers.h-ruth` in their host NixOS config. The root/manager is -/// auto-managed by default. Operators who -/// don't want a root agent at all set `services.hyperhive.ruthless = true`, -/// which short-circuits this whole function. Idempotent. -pub async fn ensure_root_agent(coord: &Arc) -> Result<()> { - if ruthless() { - tracing::info!( - "ruthless mode (services.hyperhive.ruthless = true) - skipping root agent create/start" - ); - return Ok(()); - } - let list_result = lifecycle::list().await; - if let Err(e) = &list_result { - tracing::warn!( - error = ?e, - "manager container list unreadable — skipping auto-spawn check for this attempt" - ); - } - let plan = plan_root_agent(list_result); - let current_rev = current_flake_rev(&coord.hyperhive_flake); - if plan == RootAgentPlan::Unreadable { - // An unreadable list is not "manager absent" — spawning on it would - // both waste `provision_container`'s work and hit `nixos-container - // create`'s "already exists" failure if the manager is actually - // there. Skip the decision this attempt; there is no periodic - // retry for this boot-time call, so the next chance is the next - // `hive-c0re` restart. - return Ok(()); - } - if plan == RootAgentPlan::Present { - // Container exists already. If it predates the unified lifecycle - // (no applied flake on disk) we must rebuild — otherwise it's - // running whatever the host-declarative config was at create - // time, with a wrong systemd unit and port. - let applied_flake = crate::paths::applied_dir(MANAGER_NAME).join("flake.nix"); - if !applied_flake.exists() && current_rev.is_some() { - tracing::warn!( - "manager container exists but no applied flake — forcing rebuild to migrate" - ); - if let Err(e) = coord.job_queue.insert_job(|b| { - crate::job_queue::templates::rebuild(b, MANAGER_NAME, true); - Vec::new() - }) { - tracing::warn!(error = ?e, "manager migration rebuild insert failed"); - } - } else { - tracing::debug!("manager container already present"); - } - // hive-c0re auto-manages the root/manager by default, so a - // present-but-stopped root (e.g. a first-start failure on a fresh - // install) is brought back up here: the startup sweep's rebuild only - // restarts a container that was already running, so without this it - // stays down until a manual `nixos-container start`. The sub-agent - // `was_running` guard is intentionally left untouched. (Operators - // opt out of this whole auto-management with - // `services.hyperhive.ruthless = true`, gated at the top of - // this function.) - if !lifecycle::is_running(MANAGER_NAME).await { - tracing::info!("manager container present but not running — starting"); - if let Err(e) = coord.power.set(MANAGER_NAME, crate::power::Wanted::Up) { - tracing::warn!(error = ?e, "agent_power: set manager wanted=up failed"); - } - // Through the preamble: after a reboot its bind sources and limits - // drop-in in `/run` are gone, and nothing else recreates them. - let hive = coord.hive_env(); - let started = async { - let paths = Coordinator::agent_paths( - MANAGER_NAME, - crate::paths::agent_runtime_dir(MANAGER_NAME), - )?; - let token = lifecycle::converge_start_preamble(MANAGER_NAME, &hive, &paths).await?; - lifecycle::start_with_fallback(token).await - } - .await; - if let Err(e) = started { - tracing::warn!(error = ?e, "manager start failed"); - } - } - return Ok(()); - } - tracing::info!("manager container missing — spawning"); - // lifecycle::spawn creates the runtime dir internally; no manual - // ensure_agent_runtime_dir needed here. - let runtime = crate::paths::agent_runtime_dir(MANAGER_NAME); - let hive = coord.hive_env(); - let paths = Coordinator::agent_paths(MANAGER_NAME, runtime)?; - lifecycle::spawn(MANAGER_NAME, &hive, &paths).await?; - if let Err(e) = coord.power.set(MANAGER_NAME, crate::power::Wanted::Up) { - tracing::warn!(error = ?e, "agent_power: set manager wanted=up failed"); - } - if let Some(rev) = current_rev - && let Err(e) = std::fs::write(crate::paths::applied_rev_marker(MANAGER_NAME), &rev) - { - tracing::warn!(name = MANAGER_NAME, error = ?e, "write rev marker failed"); - } - Ok(()) -} - /// Boot reconcile (see the module doc): classify every agent by rev /// freshness + persisted `wanted` intent, submit one `Boot` DAG /// (hyperhive lock bump growing an in-DAG rebuild subgraph per stale @@ -463,35 +319,9 @@ fn submit_boot_tree( #[cfg(test)] mod tests { - use super::{BootAction, MANAGER_NAME, RootAgentPlan, boot_action, plan_root_agent}; - use crate::lifecycle::AGENT_PREFIX; + use super::{BootAction, boot_action}; use crate::power::Wanted; - // ----------------------------------------------------------------------- - // `ensure_root_agent`'s spawn-or-not decision. An unreadable list must - // never be read as "manager absent" — see `plan_root_agent`'s doc. - - /// The regression this fix exists for: a read failure must not spawn. - #[test] - fn unreadable_list_skips_without_spawning() { - let result: anyhow::Result> = - Err(anyhow::anyhow!("connect to hive-priv socket")); - assert_eq!(plan_root_agent(result), RootAgentPlan::Unreadable); - } - - /// Control: a genuinely empty, readable list still plans a spawn. - #[test] - fn readable_list_missing_manager_spawns() { - let result: anyhow::Result> = Ok(Vec::new()); - assert_eq!(plan_root_agent(result), RootAgentPlan::Absent); - } - - #[test] - fn readable_list_with_manager_present_is_a_noop() { - let result: anyhow::Result> = Ok(vec![format!("{AGENT_PREFIX}{MANAGER_NAME}")]); - assert_eq!(plan_root_agent(result), RootAgentPlan::Present); - } - // The regression the `Unreadable` variant exists for: the arm used to // substitute `from_running(running)`, which is `Noop` against BOTH // observations — so no combination of inputs could reach a reconcile. diff --git a/nix/host-modules/hive-c0re/environment.nix b/nix/host-modules/hive-c0re/environment.nix index ad8ba96a..818afe55 100644 --- a/nix/host-modules/hive-c0re/environment.nix +++ b/nix/host-modules/hive-c0re/environment.nix @@ -46,10 +46,6 @@ in # prompts). `hive_sh4re::assets::*` reads paths underneath. # `forge/users.rs` reads the core avatar PNG from here on startup. HIVE_ASSETS_DIR = "${cfg.assets}/share/hyperhive"; - # Whether this hive runs ruthless — no root/manager agent at all - # (`auto_update::ensure_root_agent`). Default false = root - # auto-managed; true makes the sweep a no-op. - HYPERHIVE_RUTHLESS = lib.boolToString config.services.hyperhive.ruthless; } // { # Identity env vars threaded into c0re's own service env and diff --git a/nix/host-modules/hyperhive.nix b/nix/host-modules/hyperhive.nix index 8d92f381..8741446a 100644 --- a/nix/host-modules/hyperhive.nix +++ b/nix/host-modules/hyperhive.nix @@ -19,6 +19,11 @@ in [ "services" "hyperhive" "swarmName" ] [ "services" "hyperhive" "swarm" "name" ] ) + (lib.mkRemovedOptionModule [ "services" "hyperhive" "ruthless" ] '' + hive-c0re no longer creates ruth at boot. Remove this definition, and create + ruth from the swarm dashboard like any other agent; a ruth container that + already exists keeps starting with the hive. + '') ]; # Canonical hive DNS domain shared by every subsystem that needs a @@ -194,23 +199,6 @@ in # the values it asserts. It is a deployment mode rather than a setting # this module's options read, so it lives with its consequences. - # Whether this hive runs "ruthless" — with no root/manager agent at - # all. Some hives don't want a root agent — see issue tracker - # "scope concept: special agents". - options.services.hyperhive.ruthless = lib.mkOption { - type = lib.types.bool; - default = false; - example = true; - description = '' - Run this hive "ruthless" — with no root (manager) agent at all (no - ruth). When `true`, hive-c0re skips the root-agent auto-management - sweep entirely (it otherwise creates the root agent's container when - missing and restarts it when present but stopped). Defaults to - `false` (the root agent is auto-managed as required - infrastructure). Exposed to hive-c0re as `HYPERHIVE_RUTHLESS`. - ''; - }; - options.services.hyperhive.github.enable = lib.mkOption { type = lib.types.bool; default = true; diff --git a/nix/module-eval/core-toggle.nix b/nix/module-eval/core-toggle.nix index 38837ea0..15d4f69b 100644 --- a/nix/module-eval/core-toggle.nix +++ b/nix/module-eval/core-toggle.nix @@ -1044,6 +1044,26 @@ let } && !(refusedHiveName { deploy.hive-controller.enable = false; }); } + { + # The control is a host that defines no such option; the removal message + # must not appear for it. + name = "a definition of the removed services.hyperhive.ruthless option fails evaluation, pointing at the swarm dashboard"; + ok = + let + refused = + m: + lib.any ( + a: + !a.assertion + && lib.hasInfix "services.hyperhive.ruthless" a.message + && lib.hasInfix "swarm dashboard" a.message + ) m.assertions; + in + refused (hive { + ruthless = true; + }) + && !(refused allLocal); + } { # Refused wherever any hyperhive service runs, the hive or a swarm # service alone. The host enabling nothing is the control that it is From df2394963a496cb72b752129a266debb50713e22 Mon Sep 17 00:00:00 2001 From: atlas Date: Fri, 16 Oct 2026 17:35:52 +0200 Subject: [PATCH 2/4] setup: say a new agent is declared paused and ruth must be set up --- docs/getting-started/setup.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/getting-started/setup.md b/docs/getting-started/setup.md index 913a2bcc..973fdf8a 100644 --- a/docs/getting-started/setup.md +++ b/docs/getting-started/setup.md @@ -96,6 +96,10 @@ like any other: open the swarm UI start page and click the paperclip, which opens the create form with `ruth` filled in. The form declares her, creates her config repo and deploys her to the hive you pick. +The swarm declares a new agent `paused`, so without a further step ruth runs +but drives no turns. Open `/agents` in the swarm UI and set her `up` from her +card. + On a hive that already has a ruth container from an earlier release, create her once the same way, so that her config repo and store identity exist. The container keeps starting with the hive meanwhile. From 746c522f4eaec7a1912755e7b1dc94d37a001d29 Mon Sep 17 00:00:00 2001 From: atlas Date: Fri, 16 Oct 2026 18:30:31 +0200 Subject: [PATCH 3/4] drop the manager socket, the no-op migrate branch and other ruth special cases start_manager bound the same path register_agent binds. The migrate branch, hive-priv-sock::MANAGER_NAME and MANAGER_CONTAINER were no-ops or dead. The send allow-list docs promised a manager exemption the code never had. Tests that used ruth as the manager fixture use plain names. --- docs/agent-lifecycle/config-changes.md | 3 +- docs/process/conventions.md | 4 +- hive-agent-mcp/src/mcp/args.rs | 4 +- hive-agent-mcp/src/mcp/mod.rs | 2 +- hive-agent-mcp/src/send_allow.rs | 3 +- hive-c0re/README.md | 4 +- hive-c0re/src/agent_config/topology.rs | 7 +-- hive-c0re/src/gateway_nginx.rs | 19 +++----- hive-c0re/src/lifecycle/mod.rs | 6 --- hive-c0re/src/loose_ends.rs | 2 +- hive-c0re/src/main.rs | 1 - hive-c0re/src/migrate.rs | 8 +-- hive-c0re/src/socket_server/mod.rs | 67 ++++---------------------- hive-c0re/src/workers/agent_sockets.rs | 7 ++- hive-priv-sock/src/lib.rs | 4 -- nix/agent-modules/mcp.nix | 12 ++--- 16 files changed, 36 insertions(+), 117 deletions(-) diff --git a/docs/agent-lifecycle/config-changes.md b/docs/agent-lifecycle/config-changes.md index 76f218dd..c39ca99f 100644 --- a/docs/agent-lifecycle/config-changes.md +++ b/docs/agent-lifecycle/config-changes.md @@ -336,8 +336,7 @@ Differences from sub-agents: and `/var/lib/hyperhive/applied` → `/applied` (RO) so the root agent can `git fetch` deployed/failed/denied tags from any agent's authoritative applied repo (see "Root-agent view of applied" below). -- `socket_server::start_manager` binds the root agent's socket, - pure transport with no dedicated helpers — it uses the same +- Its socket comes from the generic per-agent registration, in the same per-agent runtime dir as any other agent (`/run/hyperhive/agents/ruth/`), not a special manager-only path. diff --git a/docs/process/conventions.md b/docs/process/conventions.md index 5fa052b6..0e5ad74a 100644 --- a/docs/process/conventions.md +++ b/docs/process/conventions.md @@ -174,9 +174,7 @@ Each row carries enough context that the caller renders it directly as a bulleted list, no follow-up fetch needed. `GetLooseEnds` takes no target — it always returns the caller's own -rows, on both the agent and manager sockets alike (the manager, `ruth`, -is a normal agent here, just deployed automatically with different -default permissions). `Reminder` rows always carry `owner == self`. +rows. `Reminder` rows always carry `owner == self`. Per-variant fields: diff --git a/hive-agent-mcp/src/mcp/args.rs b/hive-agent-mcp/src/mcp/args.rs index cce44c42..a8f30f25 100644 --- a/hive-agent-mcp/src/mcp/args.rs +++ b/hive-agent-mcp/src/mcp/args.rs @@ -6,7 +6,7 @@ use rmcp::schemars; #[derive(Debug, serde::Deserialize, schemars::JsonSchema)] pub struct SendArgs { - /// Logical agent name to deliver the message to (e.g. `"manager"`, + /// Logical agent name to deliver the message to (e.g. `"bob"`, /// `"alice"`). `"operator"` is refused: reach the operator over matrix /// if you have it. pub to: String, @@ -106,7 +106,7 @@ pub struct SetStatusArgs { #[derive(Debug, serde::Deserialize, schemars::JsonSchema)] pub struct GetAgentMetaArgs { - /// Logical name of the agent to query (e.g. `"iris"`, `"manager"`). + /// Logical name of the agent to query (e.g. `"iris"`, `"bob"`). /// Omit to query your own identity + status — replaces the /// previous `whoami` self-introspection tool. #[serde(default)] diff --git a/hive-agent-mcp/src/mcp/mod.rs b/hive-agent-mcp/src/mcp/mod.rs index d3fc036c..11e27d9c 100644 --- a/hive-agent-mcp/src/mcp/mod.rs +++ b/hive-agent-mcp/src/mcp/mod.rs @@ -148,7 +148,7 @@ impl AgentServer { let log = format!("{args:?}"); let to = args.to.clone(); // Check per-agent allow-list (services.hyperhive.agent.allowedRecipients). When no - // policy file is present (e.g. manager containers) the check is a no-op. + // policy file is present the check is a no-op. if let Err(refusal) = crate::send_allow::check_send_allowed(&to) { return run_tool_envelope("send", log, async move { refusal }).await; } diff --git a/hive-agent-mcp/src/send_allow.rs b/hive-agent-mcp/src/send_allow.rs index 585c1ac3..5d3fbb8c 100644 --- a/hive-agent-mcp/src/send_allow.rs +++ b/hive-agent-mcp/src/send_allow.rs @@ -7,8 +7,7 @@ /// Where the NixOS module writes the per-agent send allow-list (see /// `nix/agent-modules/mcp.nix`). Empty list = unrestricted (the /// default). Non-empty list constrains `mcp__hyperhive__send`'s `to` -/// field; the manager is always implicitly permitted regardless of -/// the list contents. +/// field. const SEND_ALLOW_PATH: &str = "/etc/hyperhive/send-allow.json"; /// Enforce the per-agent send allow-list. Returns `Ok` when the diff --git a/hive-c0re/README.md b/hive-c0re/README.md index 758aed69..fb8c3ed7 100644 --- a/hive-c0re/README.md +++ b/hive-c0re/README.md @@ -37,8 +37,8 @@ to track it: - **`agent_config/`** — per-agent registries (permissions, resource limits, topology). - **`stats/`** — dashboard metrics aggregation + OTEL export. -- **`socket_server/`** — the unix-socket request server shared by - per-agent + manager sockets. +- **`socket_server/`** — the unix-socket request server for the + per-agent sockets. - **`forge/`** — optional Forgejo wiring (`docs/integrations/forge.md`). - **`coordinator.rs`** — top-level wiring for `serve`. - **`meta.rs`**, **`migrate.rs`** — the meta flake + schema/state diff --git a/hive-c0re/src/agent_config/topology.rs b/hive-c0re/src/agent_config/topology.rs index e7b13b84..e2374596 100644 --- a/hive-c0re/src/agent_config/topology.rs +++ b/hive-c0re/src/agent_config/topology.rs @@ -165,14 +165,11 @@ mod tests { #[test] fn apply_reconcile_adds_missing_live_agent() { - let live = vec![ - crate::lifecycle::MANAGER_NAME.to_owned(), - "newbie".to_owned(), - ]; + let live = vec!["alice".to_owned(), "newbie".to_owned()]; let (next, changed) = apply_reconcile(&BTreeSet::new(), &live, &[]); assert!(changed); assert!(next.contains("newbie")); - assert!(next.contains(crate::lifecycle::MANAGER_NAME)); + assert!(next.contains("alice")); } #[test] diff --git a/hive-c0re/src/gateway_nginx.rs b/hive-c0re/src/gateway_nginx.rs index 40ffd856..26824385 100644 --- a/hive-c0re/src/gateway_nginx.rs +++ b/hive-c0re/src/gateway_nginx.rs @@ -296,7 +296,6 @@ async fn reload_gateway_nginx() { #[cfg(test)] mod tests { use super::*; - use crate::lifecycle::MANAGER_NAME; // ── legacy mode (frontend_dir = None) ────────────────────────────── @@ -309,13 +308,10 @@ mod tests { } #[test] - fn render_includes_manager() { - let names: Vec = [MANAGER_NAME, "iris"] - .iter() - .map(|s| (*s).to_owned()) - .collect(); + fn render_includes_every_agent() { + let names: Vec = ["alice", "iris"].iter().map(|s| (*s).to_owned()).collect(); let body = render(&names, None); - assert!(body.contains(&format!("/agent/{MANAGER_NAME}/"))); + assert!(body.contains("/agent/alice/")); assert!(body.contains("/agent/iris/")); } @@ -461,13 +457,10 @@ mod tests { } #[test] - fn split_mode_includes_manager() { - let names: Vec = [MANAGER_NAME, "iris"] - .iter() - .map(|s| (*s).to_owned()) - .collect(); + fn split_mode_includes_every_agent() { + let names: Vec = ["alice", "iris"].iter().map(|s| (*s).to_owned()).collect(); let body = render(&names, Some(FAKE_FRONTEND)); - assert!(body.contains(&format!("/agent/{MANAGER_NAME}/"))); + assert!(body.contains("/agent/alice/")); assert!(body.contains("/agent/iris/")); } diff --git a/hive-c0re/src/lifecycle/mod.rs b/hive-c0re/src/lifecycle/mod.rs index 4decfdac..e488b7af 100644 --- a/hive-c0re/src/lifecycle/mod.rs +++ b/hive-c0re/src/lifecycle/mod.rs @@ -34,12 +34,6 @@ pub const MAX_AGENT_NAME: usize = 9; /// Logical name of the manager agent (broker recipient, state-dir key, /// meta flake attribute). All persistent state lives under `ruth/`. pub const MANAGER_NAME: &str = "ruth"; -/// Container name of the manager. Uses the same `h-` prefix as sub-agents -/// so `nixos-container list` output is uniform and the list filter is -/// a single `starts_with(AGENT_PREFIX)` check. Logical name → container -/// name: `ruth` → `h-ruth`. -pub const MANAGER_CONTAINER: &str = "h-ruth"; - /// Mount point of the per-agent runtime directory inside the container. pub const CONTAINER_RUNTIME_MOUNT: &str = "/run/hive"; diff --git a/hive-c0re/src/loose_ends.rs b/hive-c0re/src/loose_ends.rs index 28c57329..e783edd5 100644 --- a/hive-c0re/src/loose_ends.rs +++ b/hive-c0re/src/loose_ends.rs @@ -1,6 +1,6 @@ //! Loose-ends aggregator. Assembles a `Vec` for a single agent //! (`for_agent`) — always the caller's own. `Request::GetLooseEnds` from -//! either the agent or manager socket lands here. Reminders and todos are +//! an agent socket lands here. Reminders and todos are //! agent-local (in-container `hive-agent` stores) and never sourced from //! here. //! diff --git a/hive-c0re/src/main.rs b/hive-c0re/src/main.rs index 06c8a8ac..a4f4bfaa 100644 --- a/hive-c0re/src/main.rs +++ b/hive-c0re/src/main.rs @@ -267,7 +267,6 @@ async fn cmd_serve( // Copy value first for the dashboard + knowledge-webhook tasks. let dashboard_port = env.dashboard_port; let coord = Arc::new(Coordinator::open(&db, env, model_prices, build_slots)?); - socket_server::start_manager(coord.clone())?; // Idempotent pre-flight: rewrite pre-meta-layout applied // repos, ensure proposed repos carry the `applied` // remote, bootstrap the meta repo, repoint containers at diff --git a/hive-c0re/src/migrate.rs b/hive-c0re/src/migrate.rs index 29bce958..002ae255 100644 --- a/hive-c0re/src/migrate.rs +++ b/hive-c0re/src/migrate.rs @@ -27,7 +27,7 @@ use anyhow::{Context, Result}; use tokio::process::Command; use crate::coordinator::Coordinator; -use crate::lifecycle::{self, AGENT_PREFIX, MANAGER_CONTAINER, MANAGER_NAME}; +use crate::lifecycle::{self, AGENT_PREFIX}; use crate::meta; const KILL_SWITCH: &str = "HIVE_SKIP_META_MIGRATION"; @@ -157,11 +157,7 @@ async fn enumerate_agents() -> Result> { Ok(containers .into_iter() .filter_map(|c| { - let name = if c == MANAGER_CONTAINER { - MANAGER_NAME - } else { - c.strip_prefix(AGENT_PREFIX)? - }; + let name = c.strip_prefix(AGENT_PREFIX)?; hive_types::Ident::parse(name).ok() }) .collect()) diff --git a/hive-c0re/src/socket_server/mod.rs b/hive-c0re/src/socket_server/mod.rs index b90145ed..7920801e 100644 --- a/hive-c0re/src/socket_server/mod.rs +++ b/hive-c0re/src/socket_server/mod.rs @@ -1,11 +1,8 @@ -//! Unix-socket request server, shared by the per-agent sockets and the -//! (pure-transport) manager socket. The socket file's existence on disk -//! authenticates the caller: connecting to `<.../agents/foo/mcp.sock>` means -//! you are `foo`; the manager socket simply serves as `ruth`. There is no -//! privilege flag — both transports run the same [`serve`] / [`dispatch`] -//! code, and authority derives uniformly from the caller's identity. An -//! agent-targeting verb may name any agent, so `ruth` reaches every agent -//! exactly the way every other agent does, not via any hardcoded name match. +//! Unix-socket request server for the per-agent sockets. The socket file's +//! existence on disk authenticates the caller: connecting to +//! `<.../agents/foo/mcp.sock>` means you are `foo`. There is no privilege +//! flag; [`serve`] / [`dispatch`] derive authority uniformly from the caller's +//! identity. An agent-targeting verb may name any agent. use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -13,7 +10,6 @@ use std::sync::Arc; use anyhow::{Context, Result}; use hive_core_agent_sock::{Request, Response}; use hive_sh4re::inbox::Message; -use hive_sh4re::manager::MANAGER_AGENT; use tokio::io::{AsyncBufRead, AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader}; use tokio::net::{UnixListener, UnixStream}; use tokio::task::JoinHandle; @@ -77,48 +73,6 @@ pub fn start(agent: &str, socket_path: &Path, coord: Arc) -> Result Ok(AgentSocket { path, handle }) } -/// Bind + serve the manager socket. This is now **pure transport**: it grants -/// no authority of its own — it just serves requests as `agent = MANAGER_AGENT` -/// ("ruth"), and ruth's reach comes entirely from the permissions it -/// holds, identical to connecting on a per-agent socket — ruth -/// uses the standard per-agent runtime dir + socket, with no dedicated helpers. -pub fn start_manager(coord: Arc) -> Result<()> { - use std::os::unix::fs::PermissionsExt as _; - let dir = crate::paths::agent_runtime_dir(crate::lifecycle::MANAGER_NAME); - std::fs::create_dir_all(&dir) - .with_context(|| format!("create manager dir {}", dir.display()))?; - let socket = Coordinator::socket_path(crate::lifecycle::MANAGER_NAME); - if socket.exists() { - std::fs::remove_file(&socket).context("remove stale manager socket")?; - } - let listener = UnixListener::bind(&socket) - .with_context(|| format!("bind manager socket {}", socket.display()))?; - // 0666 so the in-container root user (non-root) can connect; the bind - // source dir is manager-only on host (see the per-agent socket above). - std::fs::set_permissions(&socket, std::fs::Permissions::from_mode(0o666)) - .with_context(|| format!("chmod manager socket {}", socket.display()))?; - tracing::info!(socket = %socket.display(), "manager socket listening"); - - tokio::spawn(async move { - let err = accept_until_fatal( - MANAGER_AGENT, - || async { listener.accept().await.map(|(stream, _)| stream) }, - |stream| { - let coord = coord.clone(); - tokio::spawn(async move { - // Pure transport: serve as `ruth`, no privilege grant. - if let Err(e) = serve(stream, MANAGER_AGENT.to_owned(), coord).await { - tracing::warn!(error = ?e, "manager connection failed"); - } - }); - }, - ) - .await; - exit_on_dead_listener(MANAGER_AGENT, &err); - }); - Ok(()) -} - /// Pause after an accept error that is not about a single connection. EMFILE /// and ENFILE leave the listener readable, so retrying at once would spin until /// some fd is closed. Same policy as axum's `serve`, which the dashboard runs. @@ -164,9 +118,9 @@ where } /// Exit on a listener that can no longer accept. A running hive-c0re re-binds -/// an agent socket only on that agent's next start, and the manager socket -/// never; on restart (`Restart = "on-failure"` on the unit) `start_manager` and -/// `mcp_sockets::sync_on_start` re-bind every one. +/// an agent socket only on that agent's next start; on restart +/// (`Restart = "on-failure"` on the unit) `mcp_sockets::sync_on_start` re-binds +/// every one. fn exit_on_dead_listener(agent: &str, e: &std::io::Error) -> ! { tracing::error!( %agent, @@ -176,7 +130,7 @@ fn exit_on_dead_listener(agent: &str, e: &std::io::Error) -> ! { std::process::exit(1) } -/// Longest request line accepted on the agent and manager sockets, newline +/// Longest request line accepted on the agent sockets, newline /// included. The largest request a client sends is an `OperatorMsg` from the /// agent web UI's `/send` (`hive-agent/src/web_ui/actions.rs:21`), whose body /// axum's default limit caps at 2 MiB; JSON escaping can roughly double that to @@ -286,8 +240,7 @@ pub(crate) fn recv_timeout(wait_seconds: Option) -> std::time::Duration { } } -/// Unified dispatch for every socket connection — per-agent sockets and the -/// (now pure-transport) manager socket alike. There is no privilege bit; +/// Unified dispatch for every socket connection. There is no privilege bit; /// authority derives uniformly from the caller's identity. async fn dispatch( req: &hive_core_agent_sock::Request, diff --git a/hive-c0re/src/workers/agent_sockets.rs b/hive-c0re/src/workers/agent_sockets.rs index 3de04282..60f71d98 100644 --- a/hive-c0re/src/workers/agent_sockets.rs +++ b/hive-c0re/src/workers/agent_sockets.rs @@ -209,7 +209,6 @@ pub fn spawn_poll() { #[cfg(test)] mod tests { use super::*; - use crate::lifecycle::MANAGER_NAME; #[test] fn socket_path_for_uses_subdir_layout() { @@ -235,13 +234,13 @@ mod tests { } #[test] - fn build_map_includes_manager() { - let names: Vec = ["iris", MANAGER_NAME, "argus"] + fn build_map_includes_every_agent() { + let names: Vec = ["iris", "alice", "argus"] .iter() .map(|s| (*s).to_owned()) .collect(); let map = build_map_with(&names, |_| true); - assert!(map.contains_key(MANAGER_NAME)); + assert!(map.contains_key("alice")); assert!(map.contains_key("iris")); assert!(map.contains_key("argus")); } diff --git a/hive-priv-sock/src/lib.rs b/hive-priv-sock/src/lib.rs index 24844cf5..3de8f572 100644 --- a/hive-priv-sock/src/lib.rs +++ b/hive-priv-sock/src/lib.rs @@ -25,10 +25,6 @@ pub const PRIV_SOCK: &str = "/run/hive/priv.sock"; /// exactly the failure mode a shared constant exists to prevent. pub const PAUSED_MARKER_FILE: &str = "paused"; -/// Manager logical agent name. The manager's system container name is -/// `h-ruth` (same `h-` prefix convention as every other agent). -pub const MANAGER_NAME: &str = "ruth"; - /// Sub-agent container prefix. System container name = `h-`. pub const AGENT_PREFIX: &str = "h-"; diff --git a/nix/agent-modules/mcp.nix b/nix/agent-modules/mcp.nix index d88831d2..82e32433 100644 --- a/nix/agent-modules/mcp.nix +++ b/nix/agent-modules/mcp.nix @@ -27,23 +27,19 @@ in default = [ ]; example = [ "alice" - "manager" + "bob" ]; description = '' Names this agent is allowed to `send` to via `mcp__hyperhive__send`. Empty list (the default) means - unrestricted — the agent can message any peer or the - manager. Non-empty list constrains the - surface: only the listed names + the manager (always - allowed) get through; anything else returns an error + unrestricted — the agent can message any peer. A non-empty list + allows only the listed names; anything else returns an error string to claude without touching the broker. `operator` is refused either way: agents reach the operator over matrix. Useful for sandboxing untrusted sub-agents — set - `[ "manager" ]` to scope them to manager-only chatter. - The manager itself is always exempt; this option only - affects sub-agent `send`. + `[ "alice" ]` to scope one to chatter with alice. ''; }; From 0fc77df1a9bec1d86bd1e1c3331d424b6f1ccfda Mon Sep 17 00:00:00 2001 From: atlas Date: Fri, 16 Oct 2026 20:20:51 +0200 Subject: [PATCH 4/4] say per-agent socket where the manager socket is gone the manager socket no longer exists; ruth's socket is the per-agent one. Fixes the wire-type crate docs, hive-sh4re manager.rs header and README, and two other mentions. --- docs/turn-loop/README.md | 2 +- hive-agent/src/web_ui/state.rs | 2 +- hive-core-agent-sock/README.md | 4 ++-- hive-core-agent-sock/src/lib.rs | 12 +++++------- hive-sh4re/README.md | 2 +- hive-sh4re/src/manager.rs | 2 -- 6 files changed, 10 insertions(+), 14 deletions(-) diff --git a/docs/turn-loop/README.md b/docs/turn-loop/README.md index f3a70e16..b686c942 100644 --- a/docs/turn-loop/README.md +++ b/docs/turn-loop/README.md @@ -98,7 +98,7 @@ variants regardless of who sends them): A small `Surface` trait, with one zero-sized impl, factors `hive-agent`'s wire types (`hive_core_agent_sock::{Request, Response}` — -one unified enum shared by the agent and manager sockets) and its turn +one unified enum shared by every agent socket) and its turn loop, so the loop itself has no per-role branches. See `hive-agent/src/main.rs`'s module doc for the trait shape. diff --git a/hive-agent/src/web_ui/state.rs b/hive-agent/src/web_ui/state.rs index 1a3190bc..e86a00b7 100644 --- a/hive-agent/src/web_ui/state.rs +++ b/hive-agent/src/web_ui/state.rs @@ -394,7 +394,7 @@ struct ExtraLink { } /// Best-effort: pull the last 30 messages addressed to us via the -/// per-agent / manager socket. Empty list on any transport / decode +/// per-agent socket. Empty list on any transport / decode /// failure — the inbox section is decorative, not authoritative. async fn recent_inbox(socket: &std::path::Path) -> Vec { const LIMIT: u64 = 30; diff --git a/hive-core-agent-sock/README.md b/hive-core-agent-sock/README.md index 37bb158b..b249cb42 100644 --- a/hive-core-agent-sock/README.md +++ b/hive-core-agent-sock/README.md @@ -1,8 +1,8 @@ # hive-core-agent-sock -Wire types for the **per-agent + manager socket** (`/run/hive/mcp.sock`) — the +Wire types for the **per-agent socket** (`/run/hive/mcp.sock`) — the unified `Request` / `Response` protocol spoken between an agent's in-container -harness (and the manager) and the `hive-c0re` daemon. +harness and the `hive-c0re` daemon. ## Why it's its own crate diff --git a/hive-core-agent-sock/src/lib.rs b/hive-core-agent-sock/src/lib.rs index 426d430d..ad7d4c36 100644 --- a/hive-core-agent-sock/src/lib.rs +++ b/hive-core-agent-sock/src/lib.rs @@ -1,7 +1,7 @@ -//! Per-agent + manager socket wire types (`/run/hive/mcp.sock`). +//! Per-agent socket wire types (`/run/hive/mcp.sock`). //! //! The unified `Request` / `Response` protocol spoken between an agent's -//! in-container harness (and the manager) and the `hive-c0re` daemon over the +//! in-container harness and the `hive-c0re` daemon over the //! per-agent mcp socket. Re-homed out of `hive-sh4re` so this one socket owns //! its own protocol crate, mirroring `hive-host-sock` / `hive-priv-sock`. The //! shared payload types it references (`Message`, `LooseEnd`, …) @@ -17,7 +17,7 @@ fn default_true() -> bool { true } -/// Unified request enum for both agent and manager sockets. The agent's +/// Unified request enum for the per-agent socket. The agent's /// identity is the socket it arrived on. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "cmd", rename_all = "snake_case")] @@ -59,9 +59,7 @@ pub enum Request { /// Non-mutating — pulls from the broker without delivering. The /// per-agent web UI uses this to render its own inbox section. Recent { limit: u64 }, - /// Loose-ends view — always the caller's own. Same shape on the agent - /// and manager sockets; the manager (`ruth`) is a normal agent here, it - /// just sees its own threads like anyone else. See + /// Loose-ends view — always the caller's own. See /// `docs/process/conventions.md::Loose-ends wire shape`. GetLooseEnds, /// Set a free-text status string visible on the dashboard. The harness @@ -106,7 +104,7 @@ pub enum Request { PauseAcknowledged, } -/// Unified response enum for both agent and manager sockets. +/// Unified response enum for the per-agent socket. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "kind", rename_all = "snake_case")] pub enum Response { diff --git a/hive-sh4re/README.md b/hive-sh4re/README.md index 8d4c01da..f5bbd947 100644 --- a/hive-sh4re/README.md +++ b/hive-sh4re/README.md @@ -13,7 +13,7 @@ split into their own smaller crates so specialised binaries don't have to pull in all of `hive-sh4re`: - `hive-host-sock` — host admin socket (`hivectl` ↔ `hive-c0re`) -- `hive-core-agent-sock` — per-agent/manager socket (`/run/hive/mcp.sock`) +- `hive-core-agent-sock` — per-agent socket (`/run/hive/mcp.sock`) - `hive-priv-sock` — the privileged-helper socket Those crates re-export or reference the payload types that still live here diff --git a/hive-sh4re/src/manager.rs b/hive-sh4re/src/manager.rs index 50689dbd..f5b24920 100644 --- a/hive-sh4re/src/manager.rs +++ b/hive-sh4re/src/manager.rs @@ -1,5 +1,3 @@ -//! Manager socket — `/run/hyperhive/manager/mcp.sock` on the host, -//! bind-mounted into the manager container at `/run/hive/mcp.sock`. //! Reserved recipient names/senders and the out-of-band `HelperEvent` //! payload hive-c0re pushes into the manager's inbox.