{ config, pkgs, lib, vmixLib, ... }: with lib; { options = { autostart = mkOption { type = types.bool; default = false; description = "Start VM on host boot."; }; enable = mkOption { type = types.bool; default = true; description = "Enable/disable VM service creation."; }; vnc = { enable = mkOption { type = types.bool; default = false; description = "Enable VNC."; }; addr = mkOption { type = types.str; default = "0.0.0.0"; description = "VNC bind address inside the VM namespace."; }; port = mkOption { type = types.ints.between 5900 5999; default = 5900; description = "VNC TCP port inside the VM namespace."; }; forwardHostPort = mkOption { type = types.nullOr types.port; default = null; description = "Optional host TCP port to auto-forward to this VM VNC port."; }; websocketPort = mkOption { type = types.nullOr types.port; default = null; description = "Optional websocket port for VNC (for noVNC-style clients)."; }; passwordFile = mkOption { type = types.nullOr types.str; default = null; description = "Path to a runtime file containing the VNC password. When set, VNC auth is enabled via password-secret."; }; sharePolicy = mkOption { type = types.enum [ "allow-exclusive" "force-shared" "ignore" ]; default = "allow-exclusive"; description = "VNC client sharing policy."; }; }; spice = { enable = mkOption { type = types.bool; default = false; description = "Enable SPICE display server."; }; port = mkOption { type = types.int; default = 5930; description = "SPICE TCP port inside the VM namespace."; }; forwardHostPort = mkOption { type = types.nullOr types.port; default = null; description = "Optional host TCP port to auto-forward to this VM SPICE port."; }; addr = mkOption { type = types.str; default = "0.0.0.0"; description = "SPICE bind address inside the VM namespace."; }; passwordFile = mkOption { type = types.nullOr types.str; default = null; description = "Path to a runtime file containing SPICE password. When set, ticketing is enabled automatically, otherwise ticketing is disabled."; }; agent.enable = mkOption { type = types.bool; default = true; description = "Enable SPICE guest agent channel (clipboard/resolution helpers)."; }; usbRedir = { enable = mkOption { type = types.bool; default = false; description = "Enable SPICE USB redirection channels."; }; channels = mkOption { type = types.ints.between 1 16; default = 4; description = "Number of SPICE USB redirection channels to expose."; }; }; displayDevice = mkOption { type = types.enum [ "virtio" "qxl" "std" "vmware" "none" ]; default = "qxl"; description = "QEMU -vga type to use with SPICE (qxl, virtio, std, vmware, none). macOS has no QXL/virtio-gpu driver: it always uses vmware (or std)."; }; vgamem = mkOption { type = types.nullOr types.int; default = null; description = "Video memory in MB for QXL device. When set, uses -device qxl-vga instead of -vga qxl."; }; }; nographic = mkOption { type = types.bool; default = true; description = "Run QEMU without a graphical window (-nographic)."; }; cpu.cores = mkOption { type = types.int; default = 2; description = "Number of CPU cores."; }; cpu.model = mkOption { type = types.str; default = "host"; description = "CPU model."; }; cpu.hideVirtualized = mkOption { type = types.bool; default = true; description = "Hide hypervisor from guest. Prevents GPU driver Code 43 errors by stripping hypervisor CPUID leaf."; }; kvm = mkOption { type = types.bool; default = true; description = "Enable KVM."; }; arch = mkOption { type = types.str; default = "x86_64"; description = "Architecture of the VM."; }; pc.type = mkOption { type = types.str; default = "q35"; description = "PC type."; }; bios.efi = mkOption { type = types.bool; default = true; description = "Enable EFI BIOS."; }; bios.tpm = mkOption { type = types.bool; default = false; description = "Enable TPM BIOS."; }; mem.size = mkOption { type = types.int; default = 1024; description = "Memory size in MB."; }; mem.balloon = mkOption { type = types.bool; default = false; description = "Enable memory ballooning."; }; disks.os.file = mkOption { type = types.nullOr types.path; default = null; description = "Path to the OS disk image. Null for ISO-only VMs. For Windows, use a vmixLib.windows.* image — config auto-detects via _vmixOsType metadata."; }; disks.os.persist = mkOption { type = types.bool; default = false; description = "Persist OS disk changes. The store image is copied to persistPath on first boot and QEMU writes to that mutable copy."; }; disks.os.persistPath = mkOption { type = types.str; default = ""; description = "Mutable path for the persistent OS disk (e.g. /storage/vms/myvm/os.qcow2). Required when persist = true."; }; disks.iso.file = mkOption { type = types.nullOr (types.either types.path types.str); description = "Path to the ISO file. Can be a Nix store path or a string path to a local file."; default = null; }; disks.add = mkOption { default = {}; type = types.attrsOf (types.submodule { options = { file = mkOption { type = types.str; description = "String literal path to the additional disk."; }; format = mkOption { type = types.str; description = "raw/qcow2 etc"; }; mounts = mkOption { type = types.attrsOf types.str; description = "Mount points for the additional disk."; }; opts = mkOption { type = types.str; description = "additional options in QEMU args for this disk"; }; }; }); description = "Additional disks."; }; shares = mkOption { default = {}; type = types.attrsOf (types.submodule { options = { source = mkOption { type = types.path; description = "Source path for the shared directory."; }; target = mkOption { type = types.str; description = "Target path inside the VM for the shared directory. macOS: the share named `automount` (or the first one) appears at /Volumes/My Shared Files; others are mounted at target through the guest agent."; }; }; }); description = "Shared directories (9p for Linux, virtio-fs via virtiofsd for macOS)."; }; disks.bus = mkOption { type = types.str; default = "virtio"; description = "Bus type for the disks."; }; boot.order = mkOption { type = types.listOf (types.enum [ "os" "iso" "net" "floppy" ]); description = "Boot order."; default = [ "os" "iso" ]; }; boot.menu = mkOption { type = types.bool; default = false; description = "Enable boot menu."; }; nicModel = mkOption { type = types.str; default = "virtio-net-pci"; description = "QEMU NIC device model (e.g. virtio-net-pci, e1000)."; }; windows = { enable = mkOption { type = types.bool; default = false; description = "Enable Windows-optimized QEMU flags. Auto-enabled when disks.os.file carries _vmixOsType = \"windows\" metadata."; }; }; macos = { enable = mkOption { type = types.bool; default = false; description = "Enable macOS QEMU flags (OpenCore/AppleSMC, Skylake CPU spoof, AHCI disk, pinned NIC). Auto-enabled when disks.os.file carries _vmixOsType = \"macos\" metadata."; }; cpu = mkOption { type = types.str; default = vmixLib.macos.qemu.defaultCpu; description = "QEMU -cpu string used for macOS VMs when cpu.model is \"host\"."; }; mac = mkOption { type = types.nullOr types.str; default = null; description = "MAC address of en0. Defaults to the image's macAddress (must match OpenCore's ROM for Apple ID / iMessage)."; }; guestAgent.enable = mkOption { type = types.bool; default = true; description = "Attach Apple's built-in QEMU guest agent (virtio console port org.qemu.guest_agent.0). Socket: /run/vmix/qga-.sock; guest-exec runs as root."; }; homeDisk = { enable = mkOption { type = types.bool; default = false; description = "Persistent home volume: a host disk image attached as virtio-blk, formatted APFS with label `label` by the PE on first start. The image must be generalized with persistHome = true (fstab mounts it at /Users), which makes the OS disk safely ephemeral (disks.os.persist = false)."; }; file = mkOption { type = types.str; default = ""; description = "Path of the home disk image, e.g. /storage/vms/mac/home.qcow2 (created if missing)."; }; format = mkOption { type = types.enum [ "qcow2" "raw" ]; default = "qcow2"; description = "Image format; use raw for a zvol/block device (created only for files)."; }; size = mkOption { type = types.str; default = "64G"; description = "Size when the image is created."; }; label = mkOption { type = types.str; default = "vmix-home"; description = "APFS volume label (must match generalize's homeVolumeLabel)."; }; }; }; tpm = { stateDir = mkOption { type = types.str; default = "/tmp"; description = "Directory for TPM state persistence. Set to a /storage path for persistence across reboots."; }; }; pci.passthrough = mkOption { type = types.listOf types.str; default = []; description = "PCI device addresses to passthrough via VFIO (e.g. [\"0000:03:00.0\" \"0000:03:00.1\"])."; }; pci.vgaPassthrough = mkOption { type = types.bool; default = true; description = '' Route legacy VGA to the first passthrough device (x-vga=on), which a guest needs in order to drive that card as its own display. Turn it off when the guest only forwards the device onward to a nested guest: x-vga=on claims the VGA path the emulated adapter wants, and the nested guest does its own routing anyway. ''; }; pci.romFile = mkOption { type = types.nullOr types.path; default = null; description = "GPU VBIOS ROM file for the first passthrough device. Required when GPU PCI ROM BAR doesn't expose the full VBIOS (common with AMD Navi+)."; }; pci.viommu.enable = mkOption { type = types.bool; default = false; description = '' Give the guest a virtual Intel IOMMU, so a guest that is itself a hypervisor can bind a passed-through device to vfio-pci and hand it on to a nested guest. Without one the guest sees no IOMMU and cannot re-assign anything it was given. Implies kernel-irqchip=split, which interrupt remapping requires and which replaces the full in-kernel irqchip cpu.hideVirtualized asks for. ''; }; usb.hostDevices = mkOption { default = []; type = types.listOf (types.submodule { options = { vendorId = mkOption { type = types.str; description = "USB vendor ID (e.g. \"1d6b\")."; }; productId = mkOption { type = types.str; description = "USB product ID (e.g. \"0104\")."; }; }; }); description = "USB host devices to passthrough to the VM."; }; networks.user.enable = mkOption { type = types.bool; default = false; description = "enable qemu user networking"; }; networks.lans = mkOption { default = {}; type = types.attrsOf (types.submodule { options = { mac = mkOption { type = types.str; description = "MAC address for the LAN interface."; }; ip = mkOption { type = types.nullOr (types.strMatching vmixLib.network.regex.ipv4); default = null; description = "assign static IP from the lan pool."; }; }; }); description = "LAN interfaces."; }; networks.macvtaps = mkOption { default = {}; type = types.attrsOf (types.submodule { options = { mac = mkOption { type = types.nullOr types.str; default = null; description = "MAC address for the MACVTap interface."; }; }; }); description = "MACVTap interfaces."; }; }; }