diff --git a/flake.lock b/flake.lock index 026e255..bcc7c45 100644 --- a/flake.lock +++ b/flake.lock @@ -18,6 +18,40 @@ "type": "github" } }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "illustris-lib": { + "flake": false, + "locked": { + "lastModified": 1772324173, + "narHash": "sha256-1Fy9mbNheMr7YBDZ0MJF21NvVLLy1YyPsbwwqrGmAEA=", + "owner": "illustris", + "repo": "flake", + "rev": "b8fcfb188509e044269c79ecbc5dd506bc7d48e3", + "type": "github" + }, + "original": { + "owner": "illustris", + "repo": "flake", + "type": "github" + } + }, "microvm": { "inputs": { "nixpkgs": [ @@ -39,7 +73,43 @@ "type": "github" } }, + "nix-mcp": { + "inputs": { + "flake-utils": "flake-utils", + "illustris-lib": "illustris-lib", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1772325604, + "narHash": "sha256-ZSYl+YyapjY4BZcuqhvHLQ9xxtQYbByTUDfrSWuSptQ=", + "owner": "illustris", + "repo": "nix-mcp", + "rev": "023934c4943b402864aebd93b79e668c02cbcd87", + "type": "github" + }, + "original": { + "owner": "illustris", + "repo": "nix-mcp", + "type": "github" + } + }, "nixpkgs": { + "locked": { + "lastModified": 1772198003, + "narHash": "sha256-I45esRSssFtJ8p/gLHUZ1OUaaTaVLluNkABkk6arQwE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "dd9b079222d43e1943b6ebd802f04fd959dc8e61", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { "locked": { "lastModified": 1772198003, "narHash": "sha256-I45esRSssFtJ8p/gLHUZ1OUaaTaVLluNkABkk6arQwE=", @@ -55,7 +125,7 @@ "type": "github" } }, - "nixpkgs_2": { + "nixpkgs_3": { "locked": { "lastModified": 1770107345, "narHash": "sha256-tbS0Ebx2PiA1FRW8mt8oejR0qMXmziJmPaU1d4kYY9g=", @@ -71,7 +141,7 @@ "type": "github" } }, - "nixpkgs_3": { + "nixpkgs_4": { "locked": { "lastModified": 1771724586, "narHash": "sha256-The//BXCIdGDkYK23LZOZKDt7xGrUogp8jxbyIlX6Bg=", @@ -92,15 +162,16 @@ "zig2nix": "zig2nix" }, "locked": { - "lastModified": 1772328825, - "narHash": "sha256-xAez0hPWyChoOTTjOt/OHCJsmqEa+DdS4+0g/drjV2Q=", - "owner": "nullclaw", + "lastModified": 1772565037, + "narHash": "sha256-ToafgQCwxEmV9W+GIZELupvRFJXHRoLwvfyLHrQCk1g=", + "owner": "illustris", "repo": "nullclaw", - "rev": "9a64fd94c65ee409a1c523653463ef43e1961aac", + "rev": "b558b0429ae4c21df0df0c4708d1c1cb6be10b85", "type": "github" }, "original": { - "owner": "nullclaw", + "owner": "illustris", + "ref": "fix-nix", "repo": "nullclaw", "type": "github" } @@ -108,7 +179,8 @@ "root": { "inputs": { "microvm": "microvm", - "nixpkgs": "nixpkgs", + "nix-mcp": "nix-mcp", + "nixpkgs": "nixpkgs_2", "nullclaw": "nullclaw" } }, @@ -143,9 +215,24 @@ "type": "github" } }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "treefmt-nix": { "inputs": { - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs_3" }, "locked": { "lastModified": 1770228511, @@ -163,8 +250,8 @@ }, "zig2nix": { "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs_3" + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs_4" }, "locked": { "lastModified": 1771727470, diff --git a/flake.nix b/flake.nix index 15d6e1b..190025e 100644 --- a/flake.nix +++ b/flake.nix @@ -1,10 +1,10 @@ { description = "Sandboxed MicroVMs"; - nixConfig = { - extra-substituters = [ "https://microvm.cachix.org" ]; - extra-trusted-public-keys = [ "microvm.cachix.org-1:oXnBc6hRE3eX5rSYdRyMYXnfzcCxC7yKPTbZXALsqys=" ]; - }; + # nixConfig = { + # extra-substituters = [ "https://microvm.cachix.org" ]; + # extra-trusted-public-keys = [ "microvm.cachix.org-1:oXnBc6hRE3eX5rSYdRyMYXnfzcCxC7yKPTbZXALsqys=" ]; + # }; inputs = { nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; @@ -12,51 +12,39 @@ url = "github:microvm-nix/microvm.nix"; inputs.nixpkgs.follows = "nixpkgs"; }; - nullclaw.url = "github:nullclaw/nullclaw"; + nullclaw.url = "github:illustris/nullclaw?ref=fix-nix"; + nix-mcp.url = "github:illustris/nix-mcp"; }; - outputs = { self, nixpkgs, microvm, nullclaw }: let + outputs = { self, nixpkgs, microvm, nullclaw, nix-mcp }: let system = "x86_64-linux"; - in { - packages.${system} = { - cc-sandbox = self.nixosConfigurations.claude-code.config.microvm.declaredRunner; - nullclaw = self.nixosConfigurations.nullclaw.config.microvm.declaredRunner; - }; - nixosConfigurations.claude-code = nixpkgs.lib.nixosSystem { + macFromName = name: let + hash = builtins.hashString "sha256" name; + b = i: builtins.substring (i * 2) 2 hash; + in "02:${b 0}:${b 1}:${b 2}:${b 3}:${b 4}"; + + mkMicrovm = name: { + vcpu ? 2, + mem ? 2048, + extraModules ? [] + }: nixpkgs.lib.nixosSystem { inherit system; modules = [ microvm.nixosModules.microvm ({ pkgs, ... }: { - nixpkgs.config.allowUnfree = true; - - networking.hostName = "claude-code"; + networking.hostName = name; users.users.root.password = ""; services.getty.autologinUser = "root"; - - nix.settings.experimental-features = [ "nix-command" "flakes" ]; - - environment.systemPackages = with pkgs; [ - claude-code-bin - git - curl - vim - ]; - microvm = { hypervisor = "qemu"; - vcpu = 4; - mem = 4096; - socket = "claude-code.socket"; - - writableStoreOverlay = "/nix/.rw-store"; - + inherit vcpu mem; + socket = "${name}.socket"; interfaces = [{ type = "user"; id = "usernet"; - mac = "02:00:00:00:00:01"; + mac = macFromName name; }]; - shares = [ { proto = "9p"; @@ -64,6 +52,56 @@ source = "/nix/store"; mountPoint = "/nix/.ro-store"; } + { + proto = "9p"; + tag = "${name}-data"; + source = "/home/illustris/Documents/microvms/data/${name}"; + mountPoint = "/var/lib/${name}"; + } + ]; + # qemu.extraArgs = [ + # "-fw_cfg" "name=opt/claude-auth,file=/home/illustris/.claude.json" + # ]; + }; + nix = { + nixPath = [ "nixpkgs=${pkgs.path}" ]; + settings.experimental-features = [ "nix-command" "flakes" ]; + }; + system.stateVersion = "25.11"; + }) + ] ++ extraModules; + }; + in { + packages.${system} = nixpkgs.lib.mapAttrs (_: v: v.config.microvm.declaredRunner) self.nixosConfigurations; + + nixosModules.storeOverlay = { + microvm.writableStoreOverlay = "/nix/.rw-store"; + fileSystems."/nix/.rw-store" = { + fsType = "tmpfs"; + options = [ "size=4G" "mode=0755" ]; + neededForBoot = true; + }; + }; + + nixosConfigurations = nixpkgs.lib.mapAttrs mkMicrovm { + cc-sandbox = { + vcpu = 8; + mem = 8192; + extraModules = [({ pkgs, ... }: { + nixpkgs.config.allowUnfree = true; + nix.settings.experimental-features = [ "nix-command" "flakes" ]; + + environment.systemPackages = with pkgs; [ + claude-code-bin + git + curl + vim + nix-mcp.packages.x86_64-linux.default + ]; + + microvm = { + writableStoreOverlay = "/nix/.rw-store"; + shares = [ { proto = "9p"; tag = "claude-config"; @@ -72,81 +110,56 @@ readOnly = true; } ]; - - volumes = [{ - image = "claude-code-data.img"; - mountPoint = "/var/lib/claude-code"; - size = 1024; - label = "claude-code-data"; - }]; + qemu.extraArgs = [ + "-fw_cfg" + "name=opt/claude-auth,file=/home/illustris/.claude.json" + ]; }; - # tmpfs backing for the writable nix store overlay - fileSystems."/nix/.rw-store" = { - fsType = "tmpfs"; - options = [ "size=2G" "mode=0755" ]; - neededForBoot = true; - }; - - # Claude config: persistent overlay on top of host's ~/.claude - fileSystems."/root/.claude" = { - overlay = { - lowerdir = [ "/var/lib/claude-lower" ]; - upperdir = "/var/lib/claude-code/claude-upper"; - workdir = "/var/lib/claude-code/claude-work"; + systemd.services.claude-auth = { + description = "Copy Claude auth token from fw_cfg"; + wantedBy = [ "multi-user.target" ]; + before = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "/bin/sh -c 'cp /sys/firmware/qemu_fw_cfg/by_name/opt/claude-auth/raw /root/.claude.json && chmod 600 /root/.claude.json'"; + RemainAfterExit = true; }; }; - system.stateVersion = "24.11"; - }) - ]; - }; + fileSystems = { + # tmpfs backing for the writable nix store overlay + "/nix/.rw-store" = { + fsType = "tmpfs"; + options = [ "size=4G" "mode=0755" ]; + neededForBoot = true; + }; - nixosConfigurations.nullclaw = nixpkgs.lib.nixosSystem { - inherit system; - modules = [ - microvm.nixosModules.microvm - ({ pkgs, ... }: { - networking.hostName = "nullclaw"; - users.users.root.password = ""; - services.getty.autologinUser = "root"; - - environment.systemPackages = [ - nullclaw.packages.${system}.default - pkgs.curl - pkgs.vim - ]; - - microvm = { - hypervisor = "qemu"; - vcpu = 2; - mem = 2048; - socket = "nullclaw.socket"; - - interfaces = [{ - type = "user"; - id = "usernet"; - mac = "02:00:00:00:00:02"; - }]; - - shares = [{ - proto = "9p"; - tag = "ro-store"; - source = "/nix/store"; - mountPoint = "/nix/.ro-store"; - }]; - - volumes = [{ - image = "nullclaw-data.img"; - mountPoint = "/var/lib/nullclaw"; - size = 512; - label = "nullclaw-data"; - }]; + # Claude config: ephemeral overlay on top of host's ~/.claude + "/var/lib/claude-rw" = { + fsType = "tmpfs"; + options = [ "size=128M" "mode=0700" ]; + }; + "/root/.claude".overlay = { + lowerdir = [ "/var/lib/claude-lower" ]; + upperdir = "/var/lib/claude-rw/upper"; + workdir = "/var/lib/claude-rw/work"; + }; }; + })]; + }; - system.stateVersion = "24.11"; - }) - ]; + nullclaw = { + vcpu = 2; + mem = 4096; + extraModules = [({ pkgs, ... }: { + imports = [ self.nixosModules.storeOverlay ]; + environment.systemPackages = with pkgs; [ + nullclaw.packages.${system}.default + tmux + ]; + })]; + }; }; }; }