Hyperspace Pods let multiple machines pool their GPUs into one private P2P mesh AI cluster. Models are split across all connected GPUs — e.g. two machines with 16GB VRAM each can run Qwen 3.5 32B together. Changes: - Add modules/nixos/services/hyperspace.nix — NixOS module that: * Fetches the Hyperspace CLI binary (v5.45.30) via fetchurl * Sets up systemd service for the agent * Opens firewall ports (libp2p 4001, chain 30301, API 8080) * Configures GPU passthrough for AMD MI50 (ROCm) - Register module in flake.nix for lazyworkhorse - Enable hyperspace service on lazyworkhorse (ai-worker user, port 8080) Usage after deployment: hyperspace pod create "tdnde-lab" # create pod hyperspace pod invite # share invite with cyt-pi curl http://localhost:8080/v1/chat/completions # OpenAI API See skill: nixos-hyperspace-pods
236 lines
6.9 KiB
Nix
236 lines
6.9 KiB
Nix
{ 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
|
|
];
|
|
};
|
|
};
|
|
}
|