{ pkgs, lib, inputs, config, ... }: { imports = [ ./hardware-configuration.nix inputs.self.nixosModules.vm-guest inputs.self.nixosModules.seed-ssh inputs.self.nixosModules.localisation ]; networking.hostName = "sandbox"; vm-guest = { enable = true; headless = true; }; seed-ssh = { enable = true; user = "sandbox"; }; localisation = { enable = true; timeZone = "UTC"; defaultLocale = "en_US.UTF-8"; }; users.users.sandbox = { isNormalUser = true; extraGroups = [ "wheel" "docker" ]; initialPassword = "nn"; }; # 9p mounts — silently fail if shares not provided at runtime fileSystems."/home/sandbox/projects" = { device = "projects"; fsType = "9p"; options = [ "trans=virtio" "version=9p2000.L" "msize=65536" "nofail" "x-systemd.automount" "x-systemd.device-timeout=2s" ]; }; fileSystems."/mnt/host-claude" = { device = "hostclaude"; fsType = "9p"; options = [ "trans=virtio" "version=9p2000.L" "msize=65536" "nofail" "x-systemd.device-timeout=2s" ]; }; fileSystems."/mnt/host-home" = { device = "hosthome"; fsType = "9p"; options = [ "trans=virtio" "version=9p2000.L" "msize=65536" "nofail" "x-systemd.device-timeout=2s" "ro" ]; }; # pre-auth claude-code from host config systemd.services.claude-auth = { description = "Copy claude-code credentials from host mount"; after = [ "mnt-host\\x2dclaude.mount" "mnt-host\\x2dhome.mount" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; ExecStart = let mountpoint = "${pkgs.util-linux}/bin/mountpoint"; in pkgs.writeShellScript "claude-auth" '' # wait for mounts to appear for i in $(seq 1 10); do ${mountpoint} -q /mnt/host-claude && break ${mountpoint} -q /mnt/host-home && break sleep 1 done if ! ${mountpoint} -q /mnt/host-claude && ! ${mountpoint} -q /mnt/host-home; then echo "no host mounts found, skipping" exit 0 fi mkdir -p /home/sandbox/.claude if ${mountpoint} -q /mnt/host-claude; then cp -a /mnt/host-claude/. /home/sandbox/.claude/ fi if ${mountpoint} -q /mnt/host-home; then cp /mnt/host-home/.claude.json /home/sandbox/.claude.json || true fi chown -R sandbox:sandbox /home/sandbox/.claude /home/sandbox/.claude.json 2>/dev/null || true ''; }; }; virtualisation.docker = { enable = true; logDriver = "json-file"; }; environment.systemPackages = with pkgs; [ claude-code ]; # image builder VM needs more than the default 1G to copy closure image.modules = let imageMemOverride = { config, modulesPath, ... }: { system.build.image = lib.mkForce ( import (modulesPath + "/../lib/make-disk-image.nix") { inherit lib config pkgs; inherit (config.virtualisation) diskSize; inherit (config.image) baseName format; partitionTableType = if config.image.efiSupport then "efi" else "legacy"; memSize = 16384; } ); }; in { qemu = imageMemOverride; qemu-efi = imageMemOverride; }; system.stateVersion = "25.11"; }