diff --git a/hive-c0re/src/actions.rs b/hive-c0re/src/actions.rs index 73d66bc3..85c05c7b 100644 --- a/hive-c0re/src/actions.rs +++ b/hive-c0re/src/actions.rs @@ -237,8 +237,6 @@ pub async fn run_approval_spawn( coord.dashboard_port, &coord.operator_pronouns, &coord.context_window_tokens, - &coord.agent_cpu_quota, - &coord.agent_memory_max, ) .await; if result.is_ok() { @@ -591,8 +589,6 @@ async fn run_apply_commit( applied_dir, claude_dir, notes_dir, - &coord.agent_cpu_quota, - &coord.agent_memory_max, &|step| coord.set_queue_step(queue_entry_id, step), ) .await; diff --git a/hive-c0re/src/auto_update.rs b/hive-c0re/src/auto_update.rs index a0c5b329..7f52cdc6 100644 --- a/hive-c0re/src/auto_update.rs +++ b/hive-c0re/src/auto_update.rs @@ -97,8 +97,6 @@ pub async fn rebuild_agent( coord.dashboard_port, &coord.operator_pronouns, &coord.context_window_tokens, - &coord.agent_cpu_quota, - &coord.agent_memory_max, &|step| coord.set_queue_step(queue_entry_id, step), ) .await; @@ -207,8 +205,6 @@ pub async fn ensure_root_agent(coord: &Arc) -> Result<()> { coord.dashboard_port, &coord.operator_pronouns, &coord.context_window_tokens, - &coord.agent_cpu_quota, - &coord.agent_memory_max, ) .await?; if let Some(rev) = current_rev { diff --git a/hive-c0re/src/coordinator.rs b/hive-c0re/src/coordinator.rs index ea09b8f8..bb9bd98a 100644 --- a/hive-c0re/src/coordinator.rs +++ b/hive-c0re/src/coordinator.rs @@ -81,11 +81,6 @@ pub struct Coordinator { /// compaction / auto-reset watermarks and exposes the active value /// on `/api/state` as `context_window_tokens`. pub context_window_tokens: std::collections::HashMap, - /// Per-agent systemd `CPUQuota=` value (e.g. `"200%"`). Written into - /// the `container@h-.service.d/` drop-in on every spawn/rebuild. - pub agent_cpu_quota: String, - /// Per-agent systemd `MemoryMax=` value (e.g. `"4G"`). Same drop-in. - pub agent_memory_max: String, agents: Mutex>, /// Agents whose lifecycle action (currently just spawn) is in flight. /// Read by the dashboard to render a spinner; cleared when the action @@ -233,8 +228,6 @@ impl Coordinator { dashboard_port: u16, operator_pronouns: String, context_window_tokens: std::collections::HashMap, - agent_cpu_quota: String, - agent_memory_max: String, ) -> Result { let broker = Broker::open(db_path).context("open broker")?; let approvals = Approvals::open(db_path).context("open approvals")?; @@ -268,8 +261,6 @@ impl Coordinator { dashboard_port, operator_pronouns, context_window_tokens, - agent_cpu_quota, - agent_memory_max, agents: Mutex::new(HashMap::new()), transient: Mutex::new(HashMap::new()), recent_transient: Mutex::new(HashMap::new()), diff --git a/hive-c0re/src/lifecycle.rs b/hive-c0re/src/lifecycle.rs index 7340a7db..247d1950 100644 --- a/hive-c0re/src/lifecycle.rs +++ b/hive-c0re/src/lifecycle.rs @@ -50,6 +50,11 @@ const GIT_EMAIL: &str = "c0re@hyperhive.local"; const WEB_PORT_BASE: u16 = 8100; const WEB_PORT_RANGE: u16 = 900; +/// Default resource caps applied to every managed container via a systemd +/// drop-in under `/run/systemd/system/container@.service.d/`. +const DEFAULT_MEMORY_MAX: &str = "2G"; +const DEFAULT_CPU_QUOTA: &str = "50%"; + /// FNV-1a hash of a string — shared by `agent_web_port` and /// `agent_network_ip` so the derivation rule is identical. fn fnv1a(s: &str) -> u32 { @@ -235,8 +240,6 @@ pub async fn spawn( dashboard_port: u16, operator_pronouns: &str, context_window_tokens: &std::collections::HashMap, - cpu_quota: &str, - memory_max: &str, ) -> Result<()> { validate(name)?; if let Some(other) = port_collision(name).await { @@ -266,7 +269,7 @@ pub async fn spawn( let container = container_name(name); priv_run("create", name).await?; set_nspawn_flags(&container, agent_dir, claude_dir, notes_dir).await?; - set_resource_limits(&container, cpu_quota, memory_max).await?; + set_resource_limits(&container).await?; systemd_daemon_reload().await?; priv_run("start", name).await } @@ -395,8 +398,6 @@ pub async fn rebuild( dashboard_port: u16, operator_pronouns: &str, context_window_tokens: &std::collections::HashMap, - cpu_quota: &str, - memory_max: &str, on_step: &(dyn Fn(&str) + Send + Sync), ) -> Result<()> { // Sync the meta flake (idempotent — no-op when the rendered @@ -419,7 +420,7 @@ pub async fn rebuild( // `applied//main` currently points at (deployed/). // Commits the lock if it changed. crate::meta::lock_update_for_rebuild(name).await?; - rebuild_no_meta(name, agent_dir, applied_dir, claude_dir, notes_dir, cpu_quota, memory_max, on_step).await + rebuild_no_meta(name, agent_dir, applied_dir, claude_dir, notes_dir, on_step).await } /// Container-level rebuild without touching the meta repo. Callers @@ -438,8 +439,6 @@ pub async fn rebuild_no_meta( applied_dir: &Path, claude_dir: &Path, notes_dir: &Path, - cpu_quota: &str, - memory_max: &str, on_step: &(dyn Fn(&str) + Send + Sync), ) -> Result<()> { validate(name)?; @@ -459,7 +458,7 @@ pub async fn rebuild_no_meta( // See `docs/coordinator.md::Container lifecycle`. let was_running = is_running(name).await; set_nspawn_flags(&container, agent_dir, claude_dir, notes_dir).await?; - set_resource_limits(&container, cpu_quota, memory_max).await?; + set_resource_limits(&container).await?; systemd_daemon_reload().await?; if was_running { on_step("nix build"); @@ -531,7 +530,7 @@ pub async fn rebuild_no_meta( on_step("nixos-container create"); priv_run("create", name).await?; set_nspawn_flags(&container, agent_dir, claude_dir, notes_dir).await?; - set_resource_limits(&container, cpu_quota, memory_max).await?; + set_resource_limits(&container).await?; systemd_daemon_reload().await?; on_step("nixos-container start"); priv_run("start", name).await @@ -1050,8 +1049,9 @@ pub async fn git_update_ref(dir: &Path, refname: &str, target: &str) -> Result<( /// Write a systemd drop-in for `container@.service` that applies /// our default resource caps. Goes under `/run/systemd/system/...` so it's /// ephemeral (regenerated on every spawn / rebuild). -async fn set_resource_limits(container: &str, cpu_quota: &str, memory_max: &str) -> Result<()> { - crate::priv_client::write_resource_limits(container, memory_max, cpu_quota).await +async fn set_resource_limits(container: &str) -> Result<()> { + crate::priv_client::write_resource_limits(container, DEFAULT_MEMORY_MAX, DEFAULT_CPU_QUOTA) + .await } async fn systemd_daemon_reload() -> Result<()> { diff --git a/hive-c0re/src/main.rs b/hive-c0re/src/main.rs index 273de9ac..8bbf0850 100644 --- a/hive-c0re/src/main.rs +++ b/hive-c0re/src/main.rs @@ -73,15 +73,6 @@ enum Cmd { default_value = r#"{"haiku":200000,"sonnet":1000000,"opus":1000000}"# )] context_window_tokens: String, - /// systemd `CPUQuota=` applied to every agent container via a drop-in. - /// Expressed as a percentage of one CPU core (e.g. `"200%"` = 2 cores). - /// Set via `services.hyperhive.agentCpuQuota`. - #[arg(long, default_value = "200%")] - agent_cpu_quota: String, - /// systemd `MemoryMax=` applied to every agent container. - /// Set via `services.hyperhive.agentMemoryMax`. - #[arg(long, default_value = "4G")] - agent_memory_max: String, }, /// Spawn a new agent container directly (`hive-agent-`). Bypasses /// the approval queue — use only as an operator on the host. For @@ -150,8 +141,6 @@ async fn main() -> Result<()> { dashboard_port, operator_pronouns, context_window_tokens, - agent_cpu_quota, - agent_memory_max, } => { cmd_serve( hyperhive_flake, @@ -161,8 +150,6 @@ async fn main() -> Result<()> { dashboard_port, operator_pronouns, context_window_tokens, - agent_cpu_quota, - agent_memory_max, &cli.socket, ) .await @@ -212,8 +199,6 @@ async fn cmd_serve( dashboard_port: u16, operator_pronouns: String, context_window_tokens: String, - agent_cpu_quota: String, - agent_memory_max: String, socket: &std::path::Path, ) -> Result<()> { let cwt: std::collections::HashMap = serde_json::from_str(&context_window_tokens) @@ -226,8 +211,6 @@ async fn cmd_serve( dashboard_port, operator_pronouns, cwt, - agent_cpu_quota, - agent_memory_max, )?); manager_server::start(coord.clone())?; // Idempotent pre-flight: rewrite pre-meta-layout applied diff --git a/hive-c0re/src/server.rs b/hive-c0re/src/server.rs index 2c815dea..074d5ff0 100644 --- a/hive-c0re/src/server.rs +++ b/hive-c0re/src/server.rs @@ -97,8 +97,6 @@ async fn dispatch(req: &HostRequest, coord: Arc) -> HostResponse { coord.dashboard_port, &coord.operator_pronouns, &coord.context_window_tokens, - &coord.agent_cpu_quota, - &coord.agent_memory_max, ) .await { @@ -193,8 +191,6 @@ async fn dispatch(req: &HostRequest, coord: Arc) -> HostResponse { coord.dashboard_port, &coord.operator_pronouns, &coord.context_window_tokens, - &coord.agent_cpu_quota, - &coord.agent_memory_max, &|_| (), ) .await; diff --git a/nix/modules/hive-c0re.nix b/nix/modules/hive-c0re.nix index d13df487..4e5cb42c 100644 --- a/nix/modules/hive-c0re.nix +++ b/nix/modules/hive-c0re.nix @@ -414,35 +414,6 @@ in on the next `↻ R3BU1LD` — no per-agent approval needed. ''; }; - - agentCpuQuota = lib.mkOption { - type = lib.types.str; - default = "200%"; - example = "400%"; - description = '' - systemd `CPUQuota=` applied to every agent container via a - `container@h-.service.d/` drop-in written on each - spawn/rebuild. Expressed as a percentage of one CPU core — - `"200%"` allows each agent to use up to 2 cores. The old - hard-coded value was `"50%"`; bump this if agents are hitting - CPU limits during builds or heavy tool use. - - For a hive-wide cap across all containers, set - `systemd.slices.machine.serviceConfig.CPUQuota` in your NixOS - config (all nspawn containers live in `machine.slice`). - ''; - }; - - agentMemoryMax = lib.mkOption { - type = lib.types.str; - default = "4G"; - example = "8G"; - description = '' - systemd `MemoryMax=` applied to every agent container via the - same drop-in as `agentCpuQuota`. The old hard-coded value was - `"2G"`. - ''; - }; }; config = lib.mkIf cfg.enable { @@ -645,7 +616,7 @@ in ); }; serviceConfig = { - ExecStart = "${cfg.package}/bin/hive-c0re --socket /run/hyperhive/host.sock serve --hyperhive-flake ${cfg.hyperhiveFlake} --nixpkgs-flake ${cfg.nixpkgsFlake} --nixpkgs-unstable-flake ${cfg.nixpkgsUnstableFlake} --dashboard-port ${toString cfg.dashboardPort} --operator-pronouns ${lib.escapeShellArg cfg.operatorPronouns} --context-window-tokens ${lib.escapeShellArg (builtins.toJSON cfg.contextWindowTokens)} --agent-cpu-quota ${lib.escapeShellArg cfg.agentCpuQuota} --agent-memory-max ${lib.escapeShellArg cfg.agentMemoryMax}"; + ExecStart = "${cfg.package}/bin/hive-c0re --socket /run/hyperhive/host.sock serve --hyperhive-flake ${cfg.hyperhiveFlake} --nixpkgs-flake ${cfg.nixpkgsFlake} --nixpkgs-unstable-flake ${cfg.nixpkgsUnstableFlake} --dashboard-port ${toString cfg.dashboardPort} --operator-pronouns ${lib.escapeShellArg cfg.operatorPronouns} --context-window-tokens ${lib.escapeShellArg (builtins.toJSON cfg.contextWindowTokens)}"; # Migrate hive-c0re's *own* state to the service user after an # upgrade from a root-run install (systemd's StateDirectory only # chowns the top-level dir, not pre-existing files inside it). The