feat: add Hyperspace Pods NixOS module and enable on lazyworkhorse
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
This commit is contained in:
@@ -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
|
||||
./users/gortium.nix
|
||||
./users/ai-worker.nix
|
||||
];
|
||||
|
||||
@@ -277,6 +277,16 @@
|
||||
displayName = "lazyworkhorse-host";
|
||||
};
|
||||
|
||||
# Hyperspace Pods — P2P mesh AI cluster (combine GPUs across machines)
|
||||
services.hyperspace = {
|
||||
enable = true;
|
||||
user = "ai-worker";
|
||||
apiPort = 8080;
|
||||
profile = "auto";
|
||||
openFirewall = true;
|
||||
extraArgs = [ "--verbose" ];
|
||||
};
|
||||
|
||||
# Public host ssh key (kept in sync with the private one)
|
||||
environment.etc."ssh/ssh_host_ed25519_key.pub".text =
|
||||
"${keys.hosts.lazyworkhorse.main}";
|
||||
|
||||
235
modules/nixos/services/hyperspace.nix
Normal file
235
modules/nixos/services/hyperspace.nix
Normal file
@@ -0,0 +1,235 @@
|
||||
{ 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
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user