{ config, lib, pkgs, ... }: with lib; let cfg = config.services.hyperspace; # Hyperspace CLI release from github.com/hyperspaceai/aios-cli # The binary bundles Node.js runtime + llama.cpp + sidecars (~914MB) # It auto-updates via `hyperspace update` post-install hyperspacePkg = pkgs.stdenv.mkDerivation rec { pname = "hyperspace"; version = cfg.release; src = pkgs.fetchurl { url = "https://github.com/hyperspaceai/aios-cli/releases/download/v${version}/aios-cli-x86_64-unknown-linux-gnu.tar.gz"; hash = "sha256-f6fJ8t3exqtYwUD5j+WvD+Hm0oN/Eef0X+R9Rj23dE0="; }; sourceRoot = "."; installPhase = '' mkdir -p $out/bin $out/lib/hyperspace # Main CLI binary cp aios-cli $out/bin/hyperspace chmod +x $out/bin/hyperspace # Sidecar binaries for f in _aios-cli pod-raft hyperspace-*; do [ -f "$f" ] && install -m755 "$f" $out/lib/hyperspace/ || true done # WASM, native modules, Python shards cp -r *.wasm $out/lib/hyperspace/ 2>/dev/null || true cp -r *.node $out/lib/hyperspace/ 2>/dev/null || true mkdir -p $out/lib/hyperspace/python cp -r python/* $out/lib/hyperspace/python/ 2>/dev/null || true # Skills directory mkdir -p $out/share/hyperspace cp -r skills $out/share/hyperspace/ 2>/dev/null || true # Set HYPERSPACE_PATH so the binary finds sidecars wrapProgram $out/bin/hyperspace \ --set HYPERSPACE_PATH "$out/lib/hyperspace" \ --set HYPERSPACE_SKILLS_DIR "$out/share/hyperspace/skills" ''; nativeBuildInputs = with pkgs; [ makeWrapper ]; meta = { description = "Hyperspace CLI — P2P mesh AI inference network (Pods)"; longDescription = '' Hyperspace Pods let multiple machines pool their GPUs into one private AI cluster. Install the CLI, create a pod, share an invite link — your machines form a P2P mesh and can run models split across all connected GPUs. Exposes an OpenAI-compatible API for use with Cursor, Claude Code, Aider, etc. ''; homepage = "https://hyperspace.sh"; sourceProvenance = with lib; [ sourceTypes.binaryNativeCode ]; license = lib.licenses.unfree; platforms = [ "x86_64-linux" ]; maintainers = [ ]; }; }; in { options.services.hyperspace = { enable = mkEnableOption "Hyperspace P2P AI agent (Pods)"; release = mkOption { type = types.str; default = "5.45.30"; description = "Hyperspace CLI release version (from GitHub releases)."; }; user = mkOption { type = types.str; default = "ai-worker"; description = "System user to run the Hyperspace agent."; }; apiPort = mkOption { type = types.port; default = 8080; description = "Port for the OpenAI-compatible API server."; }; autoStart = mkOption { type = types.bool; default = true; description = "Auto-start the Hyperspace agent on boot."; }; openFirewall = mkOption { type = types.bool; default = true; description = "Open firewall ports for P2P traffic (libp2p 4001, chain 30301, API)."; }; profile = mkOption { type = types.enum [ "auto" "full" "inference" "embedding" "relay" "storage" ]; default = "auto"; description = '' Agent profile: - auto: auto-detect hardware - full: all 9 capabilities - inference: GPU inference only - embedding: CPU embedding only - relay: lightweight relay - storage: storage + memory ''; }; extraArgs = mkOption { type = types.listOf types.str; default = [ ]; description = "Extra arguments passed to `hyperspace start`."; }; dataDir = mkOption { type = types.str; default = "/var/lib/hyperspace"; description = "Data directory for agent state (models, config, logs)."; }; }; config = mkIf cfg.enable { # Ensure the service user exists users.users.${cfg.user} = { isSystemUser = true; group = cfg.user; home = "/home/${cfg.user}"; createHome = true; shell = pkgs.bash; }; users.groups.${cfg.user} = { }; # Install the hyperspace binary environment.systemPackages = [ hyperspacePkg ]; # Data directories systemd.tmpfiles.rules = [ "d ${cfg.dataDir} 0755 ${cfg.user} ${cfg.user} -" "d ${cfg.dataDir}/models 0755 ${cfg.user} ${cfg.user} -" "d ${cfg.dataDir}/data 0755 ${cfg.user} ${cfg.user} -" ]; # Systemd service: runs the Hyperspace agent as a system daemon systemd.services.hyperspace = { description = "Hyperspace P2P AI Agent — Pods mesh cluster"; documentation = [ "https://hyperspace.sh" "https://github.com/hyperspaceai/aios-cli" ]; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; wantedBy = mkIf cfg.autoStart [ "multi-user.target" ]; environment = { HYPERSPACE_HOME = cfg.dataDir; HYPERSPACE_API_PORT = toString cfg.apiPort; HYPERSPACE_PATH = "${hyperspacePkg}/lib/hyperspace"; }; path = with pkgs; [ bash curl nodejs ]; script = '' # Wait for network connectivity before starting ${pkgs.bash}/bin/bash -c ' for i in $(seq 1 30); do ping -c 1 -W 1 8.8.8.8 >/dev/null 2>&1 && break sleep 2 done ' || true exec ${hyperspacePkg}/bin/hyperspace start \ --profile ${cfg.profile} \ --api-port ${toString cfg.apiPort} \ ${lib.escapeShellArgs cfg.extraArgs} ''; serviceConfig = { Type = "exec"; User = cfg.user; Group = cfg.user; WorkingDirectory = cfg.dataDir; Restart = "always"; RestartSec = 10; TimeoutStartSec = 180; TimeoutStopSec = 30; KillMode = "mixed"; # File limits for network-heavy P2P agent LimitNOFILE = 65536; LimitNPROC = 4096; # GPU access — AMD MI50 (ROCm) through /dev/kfd and /dev/dri DeviceAllow = [ "/dev/kfd" "rw" "/dev/dri" "rw" ]; SupplementaryGroups = [ "video" "render" ]; # Security hardening NoNewPrivileges = true; ProtectSystem = "strict"; ProtectHome = true; PrivateTmp = true; PrivateDevices = false; # needs GPU access ReadWritePaths = [ cfg.dataDir "/tmp" ]; BindPaths = [ # GPU devices for AMD MI50 "/dev/kfd" "/dev/dri" ]; }; }; # Firewall: open P2P ports for the mesh network networking.firewall = mkIf cfg.openFirewall { allowedTCPPorts = [ 4001 # libp2p P2P (agent gossip, DHT, circuits) 30301 # Chain P2P (blockchain consensus) cfg.apiPort # OpenAI-compatible API ]; allowedUDPPorts = [ 4001 # libp2p QUIC transport 30301 # Chain UDP discovery ]; }; }; }