diff --git a/flake.nix b/flake.nix index 8f8b51a..12f037c 100644 --- a/flake.nix +++ b/flake.nix @@ -61,6 +61,7 @@ ./modules/nixos/services/open_code_server.nix ./modules/nixos/services/ollama_init_custom_models.nix ./modules/nixos/services/openclaw_node.nix + ./modules/nixos/services/hyperspace.nix ./modules/nixos/security/ai-worker-restricted.nix ./users/gortium.nix ./users/ai-worker.nix diff --git a/modules/nixos/services/hyperspace.nix b/modules/nixos/services/hyperspace.nix new file mode 100644 index 0000000..0c2a39f --- /dev/null +++ b/modules/nixos/services/hyperspace.nix @@ -0,0 +1,134 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.hyperspace; + + hyperspacePkg = pkgs.stdenv.mkDerivation { + name = "hyperspace-pods-${cfg.version}"; + src = pkgs.fetchurl { + url = "https://github.com/hyperspaceai/aios-cli/releases/download/v${cfg.version}/aios-cli-x86_64-unknown-linux-gnu.tar.gz"; + hash = cfg.packageHash; + }; + sourceRoot = "."; + installPhase = '' + mkdir -p $out/libexec $out/bin + cp -r * $out/libexec/ + chmod +x $out/libexec/aios-cli + ln -s $out/libexec/aios-cli $out/bin/hyperspace + ''; + }; +in { + options.services.hyperspace = { + enable = lib.mkEnableOption "Hyperspace Pods P2P AI cluster agent"; + + version = lib.mkOption { + type = lib.types.str; + default = "5.45.30"; + description = "Hyperspace CLI version to download."; + }; + + packageHash = lib.mkOption { + type = lib.types.str; + default = "sha256-f6fJ8t3exqtYwUD5j+WvD+Hm0oN/Eef0X+R9Rj23dE0="; + description = '' + SRI hash of the hyperspace release tarball (sha256-). + Must be updated when version changes. Generate with: + nix store prefetch-file --hash-algo sha256 \\ + https://github.com/hyperspaceai/aios-cli/releases/download/v{version}/aios-cli-x86_64-unknown-linux-gnu.tar.gz + ''; + }; + + user = lib.mkOption { + type = lib.types.str; + default = "ai-worker"; + description = "System user to run the Hyperspace agent."; + }; + + apiPort = lib.mkOption { + type = lib.types.port; + default = 8080; + description = "OpenAI-compatible API port (configurable via --api-port)."; + }; + + profile = lib.mkOption { + type = lib.types.str; + default = "auto"; + description = '' + Agent profile. Options: auto (auto-detect hardware), full (all capabilities), + inference (GPU inference only), embedding (CPU embedding only), + relay (lightweight relay), storage (storage + memory). + ''; + }; + + autoStart = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Start the agent automatically on boot."; + }; + + openFirewall = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Open P2P mesh (4001 TCP+UDP, 30301 TCP) and API port in the firewall."; + }; + + extraArgs = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = "Extra arguments to pass to 'hyperspace start'."; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.hyperspace = { + description = "Hyperspace Pods P2P AI Cluster Agent"; + after = [ "network.target" "network-online.target" ]; + wants = [ "network-online.target" ]; + wantedBy = lib.mkIf cfg.autoStart [ "multi-user.target" ]; + + path = with pkgs; [ bash coreutils ]; + + serviceConfig = { + Type = "simple"; + User = cfg.user; + Group = cfg.user; + WorkingDirectory = "${hyperspacePkg}/libexec"; + ExecStart = "${hyperspacePkg}/bin/hyperspace start --profile ${cfg.profile} --api-port ${toString cfg.apiPort} ${lib.escapeShellArgs cfg.extraArgs}"; + Restart = "on-failure"; + RestartSec = 5; + + # AMD MI50 (ROCm) device access + DeviceAllow = [ "/dev/kfd rw" "/dev/dri rw" ]; + + # Supplementary groups for GPU/accelerator access + SupplementaryGroups = [ "video" "render" ]; + + # Hardening + NoNewPrivileges = true; + ProtectHome = "tmpfs"; + ProtectSystem = "strict"; + PrivateTmp = true; + PrivateDevices = false; # Needs /dev/kfd and /dev/dri + }; + + environment = { + HSA_OVERRIDE_GFX_VERSION = "9.0.6"; + HOME = "/home/${cfg.user}"; + }; + }; + + # Firewall ports for P2P mesh (libp2p 4001, chain 30301) and API + networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ 4001 30301 cfg.apiPort ]; + networking.firewall.allowedUDPPorts = lib.mkIf cfg.openFirewall [ 4001 ]; + + # Add GPU/accelerator groups to the service user (persistent beyond service restarts) + users.users = lib.mkIf (cfg.user == "ai-worker") { + ai-worker = { + extraGroups = [ "video" "render" ]; + }; + }; + + # ROCm override for AMD MI50 (gfx906) compatibility + environment.variables.HSA_OVERRIDE_GFX_VERSION = "9.0.6"; + }; +}