diff --git a/flake.nix b/flake.nix index 5213729a..8aca61c7 100644 --- a/flake.nix +++ b/flake.nix @@ -236,6 +236,12 @@ # nix/reference-docs.nix. (`docs` above is the auto-generated # nix-options reference, a different artifact.) reference-docs = pkgs.callPackage ./nix/reference-docs.nix { }; + # XDG icon set + .desktop entries for hyperhive processes. + # Narrow input: only the branding SVG, so unrelated source changes + # don't bust this derivation's cache. + xdg-icons = pkgs.callPackage ./nix/packages/hive-xdg-icons.nix { + hyperhiveSvg = ./branding/hyperhive.svg; + }; # Pre-built per-container system closures. Exposed as packages # so operators can `nix build .#agent-base-toplevel` (or wire # them into their host system closure via the @@ -298,6 +304,7 @@ hyperhivePackage = system: self.packages.${system}.default; hyperhiveFrontend = system: self.packages.${system}.frontend; hyperhiveAssets = system: self.packages.${system}.assets; + hyperhiveXdgIcons = system: self.packages.${system}.xdg-icons; hyperhiveFlake = "${hyperhiveFlakeSource}"; # Narrow docs/ source, threaded as its own meta-flake input so # doc edits don't re-hash the whole flake source. diff --git a/hive-priv/src/main.rs b/hive-priv/src/main.rs index e57bbe23..cdb3f9df 100644 --- a/hive-priv/src/main.rs +++ b/hive-priv/src/main.rs @@ -172,7 +172,11 @@ async fn exec(req: PrivRequest, writer: &mut OwnedWriteHalf) -> Result<(String, PrivRequest::KillContainer { ref name } => { validate_container_name(name)?; - container_run(&["kill", &container_system_name(name)]).await + // nixos-container has no kill verb. Use machinectl to send SIGKILL + // to all processes in the container — the right semantics for a + // forced shutdown after a graceful stop has already been attempted. + let machine = container_system_name(name); + machinectl_run(&["kill", &machine, "--signal=SIGKILL"]).await } PrivRequest::UpdateContainer { ref name, stream } => { @@ -1026,6 +1030,34 @@ async fn container_run(args: &[&str]) -> Result<(String, String)> { Ok((stdout, stderr)) } +/// Invoke `machinectl` with the given args, log output to journald. +/// Used for operations that nixos-container doesn't expose (e.g. sending +/// signals to running containers). +async fn machinectl_run(args: &[&str]) -> Result<(String, String)> { + let out = Command::new("machinectl") + .args(args) + .output() + .await + .context("invoke machinectl")?; + let stdout = String::from_utf8_lossy(&out.stdout).into_owned(); + let stderr = String::from_utf8_lossy(&out.stderr).into_owned(); + for line in stdout.lines() { + tracing::info!(target: "machinectl", "{line}"); + } + for line in stderr.lines() { + tracing::warn!(target: "machinectl", "{line}"); + } + if !out.status.success() { + bail!( + "machinectl {} failed ({}): {}", + args.join(" "), + out.status, + stderr.trim() + ); + } + Ok((stdout, stderr)) +} + /// Invoke `nixos-container` with the given args and forward output lines /// to the caller as `PrivEvent::Line` messages in real time, logging each /// line to journald as it arrives. Returns `(String::new(), String::new())` diff --git a/hive-sh4re/src/priv_proto.rs b/hive-sh4re/src/priv_proto.rs index 49fb99f0..618f312c 100644 --- a/hive-sh4re/src/priv_proto.rs +++ b/hive-sh4re/src/priv_proto.rs @@ -230,7 +230,8 @@ pub enum PrivRequest { /// `nixos-container stop ` StopContainer { name: String }, - /// `nixos-container kill ` + /// `machinectl kill --signal=SIGKILL` — force-kills all processes + /// in the container. nixos-container has no kill verb. KillContainer { name: String }, /// `nixos-container update --flake ` diff --git a/nix/docs/default.nix b/nix/docs/default.nix index e03df075..fdcf36ea 100644 --- a/nix/docs/default.nix +++ b/nix/docs/default.nix @@ -48,6 +48,7 @@ let hyperhiveAssets = _system: pkgs.emptyDirectory; hyperhiveFlake = ""; hyperhiveDocs = ""; + hyperhiveXdgIcons = _system: pkgs.emptyFile; agentBaseToplevel = pkgs.emptyFile; managerToplevel = pkgs.emptyFile; }) diff --git a/nix/modules/hive-c0re.nix b/nix/modules/hive-c0re.nix index 8d748f8f..bea4c230 100644 --- a/nix/modules/hive-c0re.nix +++ b/nix/modules/hive-c0re.nix @@ -4,6 +4,7 @@ hyperhiveAssets, hyperhiveFlake, hyperhiveDocs, + hyperhiveXdgIcons, agentBaseToplevel, managerToplevel, }: @@ -794,6 +795,9 @@ in environment.systemPackages = [ cfg.package pkgs.git + # XDG icons + .desktop entries so desktop environments can match + # hyperhive processes to their icon (task managers, CPU monitors, etc.). + (hyperhiveXdgIcons pkgs.stdenv.hostPlatform.system) ]; # Serve config at a stable /etc path so hive-c0re's ExecStart diff --git a/nix/packages/hive-xdg-icons.nix b/nix/packages/hive-xdg-icons.nix new file mode 100644 index 00000000..fae5ed2e --- /dev/null +++ b/nix/packages/hive-xdg-icons.nix @@ -0,0 +1,85 @@ +{ + stdenv, + librsvg, + # Absolute path to the hyperhive.svg source so this derivation's input + # hash is narrow: a change to any other source file does NOT bust it. + hyperhiveSvg, +}: + +# XDG icon set + .desktop entries for hyperhive processes so desktop +# environments can match running processes to icons (task managers, CPU +# monitors, etc.). +# +# Output layout: +# $out/share/icons/hicolor/x/apps/hyperhive.png (N = 16,32,48,64,128,256) +# $out/share/pixmaps/hyperhive.png (48px fallback) +# $out/share/applications/hive-c0re.desktop +# $out/share/applications/hive-ag3nt.desktop +stdenv.mkDerivation { + pname = "hive-xdg-icons"; + version = "0.1.0"; + + # No source tree — everything is built from the single SVG path. + dontUnpack = true; + + nativeBuildInputs = [ librsvg ]; + + buildPhase = '' + runHook preBuild + mkdir -p icons + for size in 16 32 48 64 128 256; do + rsvg-convert --width "$size" --height "$size" \ + -o "icons/hyperhive-''${size}.png" \ + ${hyperhiveSvg} + done + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + # Per-size hicolor tree + for size in 16 32 48 64 128 256; do + mkdir -p "$out/share/icons/hicolor/''${size}x''${size}/apps" + cp "icons/hyperhive-''${size}.png" \ + "$out/share/icons/hicolor/''${size}x''${size}/apps/hyperhive.png" + done + # Flat pixmaps fallback (48px) + mkdir -p "$out/share/pixmaps" + cp icons/hyperhive-48.png "$out/share/pixmaps/hyperhive.png" + + # .desktop entries — NoDisplay so they don't show up in app launchers + # but are still discovered by process-to-icon matchers (GNOME, etc.). + mkdir -p "$out/share/applications" + cat > "$out/share/applications/hive-c0re.desktop" < "$out/share/applications/hive-ag3nt.desktop" <