{ inputs, pkgs, lib, ... }: let pkgsUnstable = inputs.nixpkgs-unstable.legacyPackages.${pkgs.stdenv.hostPlatform.system}; # Pin to b9828; nixpkgs-unstable lags too far behind for the features we need. # To update: nix-prefetch-url --unpack https://github.com/ggml-org/llama.cpp/archive/refs/tags/bNNNN.tar.gz # then: nix run nixpkgs#prefetch-npm-deps -- /tools/ui/package-lock.json llama-cpp-b9828 = (pkgsUnstable.llama-cpp.override { vulkanSupport = true; }).overrideAttrs (_: { version = "9828"; src = pkgsUnstable.fetchFromGitHub { owner = "ggml-org"; repo = "llama.cpp"; tag = "b9828"; hash = "sha256-CRj+smOs5T80fT5OqHZGbko7/PwChhYsc8MGZZkE/dQ="; }; npmDepsHash = "sha256-X1DZgmhS/zHTqDT5zq0kywwntthcJ9vRXeqyO3zz6UU="; }); in { imports = [ ./hardware-configuration.nix ../../configs/desktop.nix ../../configs/qemu.nix ../../configs/mcpkg.nix ../../configs/mcp.nix # MCP agent + mcp user (straylight is becoming the core host) ]; config = { # llama.cpp server with Vulkan backend for gfx1151 (Strix Halo). # Model is downloaded from HuggingFace on first start and cached in StateDirectory. # NOTE: BIOS "UMA Frame Buffer Size" must be ≥32GB for a 23GB model to fit on-GPU. users.users.llama-server = { isSystemUser = true; group = "llama-server"; home = "/var/lib/llama-server"; }; users.groups.llama-server = {}; systemd.services.llama-server = { description = "llama.cpp inference server"; wantedBy = [ "multi-user.target" ]; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; environment = { HOME = "/var/lib/llama-server"; HF_HOME = "/var/lib/llama-server/huggingface"; }; serviceConfig = { ExecStart = '' ${llama-cpp-b9828}/bin/llama-server \ --jinja \ -hf deepreinforce-ai/Ornith-1.0-35B-GGUF:Q4_K_M \ --host 127.0.0.1 \ --port 11434 \ -c 262144 \ --no-mmap \ -fa on \ --cache-type-k q8_0 \ --cache-type-v q8_0 \ -ngl all ''; User = "llama-server"; Group = "llama-server"; StateDirectory = "llama-server"; WorkingDirectory = "/var/lib/llama-server"; SupplementaryGroups = [ "render" "video" ]; Restart = "on-failure"; RestartSec = "10s"; TimeoutStartSec = "600"; }; }; # straylight is the unikernel host. The shared mcp.nix locks the agent # down with PrivateDevices=true, which hides /dev/kvm and /dev/net/tun. # Relax that here (only on straylight) so the agent can boot Nanos # unikernel VMs under QEMU/KVM and (Phase 2) manage TAP devices. systemd.services.mcp-agent.serviceConfig = { PrivateDevices = lib.mkForce false; DeviceAllow = [ "/dev/kvm rw" "/dev/net/tun rw" ]; SupplementaryGroups = [ "kvm" ]; AmbientCapabilities = [ "CAP_NET_ADMIN" ]; # The agent launches each unikernel as a daemonized QEMU process in its # own cgroup. With the default KillMode=control-group, restarting the # agent would SIGKILL every running VM. KillMode=process kills only the # agent's main process on stop/restart, so VMs survive an agent upgrade. # (If a VM does die, the agent's startup Recover restarts it.) KillMode = lib.mkForce "process"; }; # Let the mcp user reach /dev/kvm directly as well. users.users.mcp.extraGroups = [ "kvm" ]; # Isolated host-only bridge for unikernel VMs (Phase 2). Each unikernel # gets a TAP on this bridge and a 10.99.0.0/24 static IP. The bridge has # NO uplink and NO NAT, and the firewall drops any VM traffic leaving the # bridge, so a VM can reach only the host gateway (10.99.0.1) -- mediation # is enforced by network topology, not convention. networking.bridges.mcp-br0.interfaces = [ ]; networking.interfaces.mcp-br0.ipv4.addresses = [ { address = "10.99.0.1"; prefixLength = 24; } ]; # The host accepts traffic from VMs (so mc-proxy on the gateway can serve # them); the FORWARD drop prevents VMs from routing anywhere off-bridge. networking.firewall.trustedInterfaces = [ "mcp-br0" ]; networking.firewall.extraCommands = '' iptables -D FORWARD -i mcp-br0 ! -o mcp-br0 -j DROP 2>/dev/null || true iptables -A FORWARD -i mcp-br0 ! -o mcp-br0 -j DROP ''; networking.firewall.extraStopCommands = '' iptables -D FORWARD -i mcp-br0 ! -o mcp-br0 -j DROP 2>/dev/null || true ''; # Allow rootless containers (podman) to bind low ports (53 for MCNS, # 443/8443/9443 for mc-proxy) as straylight takes over the core role. boot.kernel.sysctl."net.ipv4.ip_unprivileged_port_start" = 53; hardware.bluetooth.enable = true; # enables BlueZ + bluetooth.service programs.steam.enable = true; services.open-webui = { enable = true; host = "0.0.0.0"; port = 8080; environment = { SCARF_NO_ANALYTICS = "True"; DO_NOT_TRACK = "True"; ANONYMIZED_TELEMETRY = "False"; # Point at the local llama.cpp server (OpenAI-compatible API). OPENAI_API_BASE_URLS = "http://127.0.0.1:11434/v1"; OPENAI_API_KEYS = "none"; ENABLE_OLLAMA_API = "False"; }; }; # Open ports: DNS (53), mc-proxy (443/8443/9443), agent (9444), master (9555), open-webui (8080). networking.firewall.allowedTCPPorts = [ 53 443 8080 8443 9443 9444 9555 ]; networking.firewall.allowedUDPPorts = [ 53 ]; # DNS: MCNS for internal zones, public resolvers as fallback. networking.nameservers = [ "192.168.88.181" "100.95.252.120" "1.1.1.1" "8.8.8.8" ]; services.resolved.settings.Resolve.Domains = [ "~mcp.metacircular.net" ]; }; }