Commit graph

31 commits

Author SHA1 Message Date
b7838f5501 vms/windows: keep RDP VMs awake -- disable sleep in guest, fix the qemu global
An idle Windows VM was suspending itself off the network after 15 min:
the Balanced power plan sleeps on idle, and the qemu S3/S4 disable was a
no-op -- `-global ICH9-LMB.disable_s3` is the wrong device class (q35's
bridge is ICH9-LPC), which qemu rejected as "invalid class name", so the
sleep states stayed on offer.

Fix both ends: correct the global to ICH9-LPC so the firmware stops
advertising S3/S4, and in post-oobe (any enableRDP image) switch to the
High Performance scheme, zero the standby/hibernate/monitor idle timeouts
and turn hibernate off -- a machine exposed as a service must not sleep.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
2026-09-18 10:39:49 -03:00
1510b6c5ff windows/seal: per-VM account on the config medium, not baked
username/password move from the sealed image to makeConfigMedium, so two
VMs get distinct logins (and, as before, distinct SIDs). The sealed
image bakes only a generic bootstrap account ("vmixsetup") whose sole job
is to carry OOBE to a logon; post-oobe then creates the real account from
the config CD, switches autologon to it, and reboots so it builds its own
SID-bound profile on D:\Users\<username>. A one-shot cleanup (RunOnce,
first logon of the real account) retires the bootstrap and its profile
and applies the per-user tint. So nothing about the account is shared or
baked, and the on-disk profile folder matches the real username.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
2026-09-18 09:24:04 -03:00
ee002586e5 windows/seal: keep the config CD off D:, and win the ProfilesDirectory race
Two first-boot hazards the sealed path hits that the baked path does not,
fixed in a configMedium-only variant of the data-disk init (the shared
path is byte-identical):

- The per-VM config rides an optical drive. On the target's first boot
  the data disk is still raw and unlettered, so Windows gives the CD D:
  -- where the profile volume must go. The plain `if exist D:\` guard
  then sees the CD and skips, stranding ProfilesDirectory on read-only
  media. Now a marker (not the letter) tracks first boot, and any
  occupant of D: is parked on Y: before the data disk claims it.
- Left to Shell-Setup's FolderLocations, ProfilesDirectory can be
  evaluated before the disk exists (unordered within specialize) and
  fall back to C:. It is now written to the registry in the same step
  that just created the volume, so the volume always exists first.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
2026-09-16 13:11:06 -03:00
702f723e6d windows: seal mode -- generic OOBE-deferred base, per-VM data on a config CD
A sealed image bakes no per-VM data. generalize.nix gains a gated
configMedium flag (false path byte-identical, so the shared macOS
generalize path is untouched): the baked answer file stays generic and
the target's first boot reads hostname/static-IP/timezone/tint off a
small removable CD via a finder (vmix-load-config.cmd) + applier
(vmix-apply-config.ps1), with the static-IP script dot-sourcing the same
config. templates.seal is a thin preset (delayOobeRun + configMedium +
D:\Users profiles + a data disk); images gain a .seal leaf next to
.generalize; makeConfigMedium renders the per-VM ISO.

So one sealed store path is shared by every VM, each mints its own SID
on first boot and builds the whole profile on D:, and only a cheap ISO
is per-VM. Also folds in the delayOobeRun reconcile: extraDisk (and the
audit-mode data-disk init) are gated off under deferral, so a sealed
image ships with no throwaway `data` output -- the target's specialize
formats the host zvol instead.

vms: disks.config.file attaches the config medium as a second CD-ROM.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
2026-09-16 13:01:12 -03:00
Git Sagar
60013006d6 generalize: folderRedirect -- keep the SID generalized, persist user data on D:
The alternative to keepMachineSid for "survives an OS rebuild". Instead of
moving the whole SID-bound profile to D: (which forces a fixed SID), keep
/generalize -- so every machine and every rebuild gets its own random SID --
and redirect only the user's data folders (Desktop, Documents, Downloads, ...)
to the persistent data volume. Files survive a rebuild; per-user registry
settings do not, which is the accepted trade for not touching the SID.

Three pieces. A per-user script calls SHSetKnownFolderPath to point each known
folder at D:\UserData\<folder>; it is registered through Active Setup, which
runs it once per profile at first logon -- including the fresh profile each
generalized rebuild creates. And the onstart SYSTEM boot script grants the
well-known Users group inheritable full control on the data tree, so the
account behind whatever SID this rebuild produced can reach files an earlier
SID created.

The heal/relocation path is now gated on profilesDirectory, so it and
folderRedirect stay mutually exclusive and neither breaks the other's null.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
2026-09-13 06:44:08 -03:00
Git Sagar
d3ac2bf475 generalize: finalize OOBE state under keepMachineSid, or the image reboots into Setup
The keepMachineSid image built and had everything right in the registry --
account, profile on D:, RDP enabled, MountedDevices intact -- but booted to a
black screen with no services. The shipped image was set to run windeploy.exe
(OOBE) on every boot: SetupType=2, OOBEInProgress=1, CmdLine=oobe\windeploy.exe.
On the target there is no unattend left for it to consume, so it hangs.

The cause is our shutdown in FirstLogonCommands. It cuts OOBE off before
windeploy finalizes and resets that Setup state itself. The /generalize path
finalizes it through its own specialize->oobe cycle; /oobe alone does not, so
the flags are left set. Clearing them in post-oobe, only under keepMachineSid,
sends the target straight to logon. The default /generalize path is untouched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
2026-09-10 21:18:04 -03:00
Git Sagar
f7405b8e70 generalize: keepMachineSid, for a profile that survives an OS rebuild
sysprep /generalize regenerates the machine SID on every build, so an account
built by one image does not match a profile left on a persistent disk by an
earlier one -- different SID, so file ACLs, the NTUSER.DAT hive and ProfileList
all mismatch, and the profile will not load.

keepMachineSid drops /generalize and uses /oobe alone. The SID is then
inherited from the cached base install derivation, which is content-addressed
and so identical across every rebuild of the layers above it; the account,
always RID 1000, comes out the same each time. A profile kept on a data disk
then matches exactly, with no ownership or ProfileList fixups.

Without /generalize the specialize pass does not run, so the profile relocation
cannot ride the unattend there. It is written to the registry offline instead,
before the build's OOBE, which virt-win-reg applies ahead of the Audit Mode
boot. And because /generalize is also what strips MountedDevices, dropping it
means the data disk keeps its drive letter into the shipped image -- the
letterless-first-boot race that sent profiles temporary goes away at the root.

Default is unchanged (/generalize), correct for an image deployed to many
hosts; keepMachineSid is for an image that is always the same one machine.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
2026-09-10 20:05:27 -03:00
Git Sagar
5251bf7290 generalize: keep the static address from stranding the box after a reboot
It worked on the first boot and was gone after a later internal reboot -- no
IPv4 at all, not even DHCP. DHCP is turned off before the address is set, so
anything that stops the set mid-way leaves the interface with nothing. A stale
ARP entry for the address, left on the network by the previous instance,
tripped duplicate-address detection and made New-NetIPAddress throw; with
-ErrorAction Stop that aborted the script with DHCP already off.

DadTransmits 0 turns that detection off so the static binds regardless of what
the network remembers, and the assignment is now retried a few times rather
than fatal on the first throw. Moved to its own .ps1 -- a wait loop and a retry
are not worth keeping correct inside a cmd one-liner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
2026-09-10 19:49:40 -03:00
Git Sagar
d94c576df2 generalize: assign the data-disk letter every boot, and heal a temp profile
The relocated profile went temporary on the target and stayed that way. The
cause was not the profile: the image ships with ProfilesDirectory set to
D:\Users but with no drive letter for the data disk. generalize strips
MountedDevices, and the specialize pass that re-asserts the letter runs only in
the build VM, never on the target -- so the zvol boots letterless, the first
autologon cannot find D:\Users\sagar (event 1511), and Windows falls back to a
temporary profile, renames the real ProfileList key to .bak, and the fault
sticks on every later logon.

Confirmed by reading the shipped image offline: ProfileList has the SID at
D:\Users\TEMP with a .bak sibling at D:\Users\sagar, MountedDevices carries no
\DosDevices\D:, and the sagar hive on the zvol is intact -- so nothing was
wrong but the letter.

An onstart SYSTEM task now runs the existing (idempotent) data-disk init, whose
diskpart assign writes MountedDevices and so makes D: persistent for every
later boot. Only the first boot is exposed to the race; if it left a .bak, a
small PowerShell heal puts the key back, drops the temp profile, and reboots
once -- after which D: is persistent and the real profile loads. Same onstart /
SYSTEM mechanism the static address already uses.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
2026-09-10 17:43:11 -03:00
Git Sagar
6a62a649bd generalize: assert the static address per boot, not once during the build
Setting it from post-oobe.cmd could never have worked, and the reason is worth
writing down. Without delayOobeRun, OOBE runs inside the build VM -- whose NIC
is qemu user networking, on a different subnet, with a different MAC. The
address was being applied to an adapter that does not exist on the real host.
Windows then meets the target's NIC as new hardware and defaults to DHCP.

RDP came through the same script unharmed because its settings are
registry-wide rather than per-adapter, which is why one worked and the other
did not despite sitting a few lines apart.

So the script is now registered as an onstart scheduled task running as SYSTEM.
It was already idempotent, and per-boot also survives the adapter being
replaced again later.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
2026-09-10 07:28:14 -03:00
Git Sagar
a378eb4ad7 generalize: turn DHCP off before asserting a static address, and answer pings
The VM came up holding a DHCP lease rather than the address it was told to
take, while RDP -- configured a few lines earlier in the same script -- worked
fine. So post-oobe.cmd was running; only the addressing failed.

Two reasons, both fixed. The interface arrives DHCP-managed and nothing turned
DHCP off, so New-NetIPAddress had no lasting effect. And FirstLogonCommands can
run before the adapter is up, so it is now waited for rather than assumed.

Moved out of post-oobe.cmd into its own file. The command is long and full of
quotes and pipes, which is not a thing to leave at the mercy of cmd's parsing.
It also logs, so the next failure can be read off the disk instead of inferred.

Pings are now allowed too. Windows blocks ICMP by default, which makes a box
at a fixed address look dead to everything that checks it the obvious way --
including me, for a while.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
2026-09-10 06:56:00 -03:00
Git Sagar
c0e1293405 generalize: drop CopyProfile when relocating profiles, and name FolderLocations twice
The data volume comes out correctly partitioned, formatted and labelled, and
containing nothing but $RECYCLE.BIN and System Volume Information -- so the
disk work lands and the relocation does not. Two candidates, both cheap to
address together.

CopyProfile is now dropped whenever profilesDirectory is set. Sysprep choosing
a profile to copy into Default while the profile root is being moved is the
likelier of the two, and a profile that persists is worth more than the Audit
Mode customizations that CopyProfile preserves.

FolderLocations is now named in oobeSystem as well as specialize. Which pass
honours it is not something the documentation is crisp about, and saying it
twice costs nothing.

This matters more than it looks: with UWF protecting C: and the profile still
on C:, every profile write lands in the overlay and is discarded on reboot,
which makes the whole VM stateless.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
2026-09-10 06:10:35 -03:00
Git Sagar
23c539dbe2 generalize: lay the data disk out in Audit Mode, not only in specialize
Formatting it from a specialize RunSynchronousCommand is not enough on its
own. FolderLocations is applied by Shell-Setup while the disk is prepared by
Deployment, and component order within a pass is not guaranteed -- so the
relocation can be evaluated before the volume it names exists, which fails
silently and leaves profiles on C:. That is what a correctly formatted data
disk carrying nothing but NTFS metadata was telling us.

Audit Mode is a fully booted OS with the disk already attached, so doing it
before sysprep makes the volume unconditionally present by the time any pass
looks for it. The specialize copy stays, now purely to re-assert the drive
letter after generalize clears MountedDevices.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
2026-09-10 05:11:40 -03:00
Git Sagar
0e4619eb39 pci: keep multifunction devices together; data disk: diskpart, not Storage cmdlets
Two defects found while getting a GPU through Proxmox to a nested guest.

Passthrough gave every address its own pcie-root-port, which splits a GPU from
its own HDMI audio: 05:00.0 and 05:00.1 arrived in the guest as two devices on
two buses instead of functions 0 and 1 of one device. Navi needs both halves on
one device to reset or power-manage either, so the guest got a card stuck in D3
and a reset that could not be performed. Addresses are now grouped by
everything left of the function digit, and each group goes behind one root port
at one slot with multifunction=on on function 0 -- which is also where the
VBIOS and the VGA route belong.

The data-disk setup used Initialize-Disk/New-Partition/Format-Volume. Only the
first of those works that early in specialize; the rest need services that are
not up yet, and with ErrorActionPreference=Stop the script gave up straight
after writing a GPT header. The result was a 50G disk carrying 24KB of nothing
and a ProfilesDirectory pointing at a volume that never existed. diskpart works
at that stage. It also tries assigning the letter before laying the disk out,
so a disk that already holds a profile is lettered rather than cleaned.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
2026-09-10 04:58:54 -03:00
Git Sagar
9a8f5da998 generalize: writeFilter, putting UWF's overlay on the data volume
A disk-mode overlay defaults to C:\uwfswap.sys -- on the very volume being
protected, which is the opposite of the point. uwfmgr grew a create-swapfile
subcommand for exactly this, and it only accepts the call while the filter is
off and the overlay is already in disk mode, so the ordering in the generated
script is forced rather than stylistic.

Configuration is deferred to the target's first boot through RunOnce instead
of running in the build. Two reasons: uwfmgr does not exist until the DISM
feature has been through a reboot, and the swapfile belongs on the real data
volume rather than on the throwaway copy the build attaches. Enabling the
filter needs one more restart after that, which the script asks for itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
2026-09-09 23:22:40 -03:00
Git Sagar
c213fc4db9 windows/pci: vIOMMU for nested passthrough, and a data disk built with the image
Three things, all in service of putting Proxmox in a VM that can still hand a
GPU to its own guests, and of a Windows VM whose profile survives its OS disk.

pci.viommu.enable emits `-device intel-iommu,intremap=on,caching-mode=on` and
forces kernel-irqchip=split, which interrupt remapping requires. Without an
IOMMU of its own a guest cannot bind a passed-through device to vfio-pci, so
it can never forward one on. The device leads the command line because QEMU
realizes devices in order and intel-iommu must precede what it translates.

pci.vgaPassthrough (default true, so nothing changes for existing VMs) makes
x-vga=on optional. It was forced on the first passthrough device, which is
wrong for a card the guest only forwards onward: it claims the VGA path the
emulated console adapter needs.

customizeImage gains extraDisk, a blank disk attached for the Audit Mode boot
and emitted as the derivation's `data` output. generalize uses it for dataDisk
and profilesDirectory, so the disk is partitioned and the profile relocated
under OOBE in the build VM. That is what removes the need for delayOobeRun --
previously the volume ProfilesDirectory names could not exist until the image
reached real hardware. A second output rather than a directory keeps ${image}
meaning the OS qcow2 for every existing consumer.

generalize also picks up staticIP and profilesDirectory.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
2026-09-09 22:27:44 -03:00
55697e5d89 switch from HWID to TSforge activation
- Switch MAS from /HWID to /Z-Windows (TSforge ZeroCID) which is
  hardware-independent and survives VM migration
- Re-install product key and restart SPP service before TSforge
  to restore licensing state after sysprep
- Add nicModel option to customizeImage and generalize for images
  without VirtIO drivers
- Update MAS activation script to latest version

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-09 13:18:12 +05:30
4c1b710308 add nicModel option to customizeImage and generalize
Allows overriding the QEMU NIC model during builds (e.g. e1000 for
images without VirtIO drivers). Enables MAS activation on upstream
images that lack VirtIO network drivers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-08 17:46:52 +05:30
4f83cf24ef update MAS activation script to latest version
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-08 17:26:23 +05:30
b9375c572f re-enable MAS activation for all images
Win11 LTSC 2024 RDP works with MAS. The edition switch issue was
specific to Win10 LTSC 2021.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-08 17:01:10 +05:30
af764989c1 use New-NetFirewallRule for RDP and set UserAuthentication=1
New-NetFirewallRule with -Profile Any is more reliable than
Enable-NetFirewallRule (predefined rules may not exist or be
profile-scoped). Set UserAuthentication=1 (NLA) per standard
RDP configuration. Settings take effect after reboot.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-08 15:15:28 +05:30
84bcc9b66c skip MAS activation when enableRDP is set
MAS HWID switches Enterprise LTSC to IoT Enterprise S which lacks
the RDP server listener. Skip activation to preserve the edition.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-08 14:51:41 +05:30
096d690f57 remove slmgr key restore after MAS activation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-08 14:19:47 +05:30
dd5aeafae9 restore Enterprise LTSC key after MAS activation to keep RDP server
MAS HWID activation switches the edition from Enterprise LTSC to IoT
Enterprise LTSC (which lacks the RDP server listener). Re-apply the
Enterprise LTSC product key after activation to restore RDP capability.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-08 12:11:51 +05:30
be2fd209e4 fix RDP services: set SessionEnv and UmRdpService start type via registry
sc config fails silently for these services. Use reg add to set
Start=2 (automatic) directly in the registry instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-08 09:24:21 +05:30
e658cd5404 fix RDP: auto-start SessionEnv and UmRdpService, enable firewall rules for all profiles
TermService alone doesn't create the RDP listener — SessionEnv (Remote
Desktop Configuration) and UmRdpService (Port Redirector) must also be
running. Use PowerShell Enable-NetFirewallRule to enable the built-in
Remote Desktop firewall rules for all network profiles instead of
creating custom netsh rules.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-07 22:18:26 +05:30
def21bca57 add enableRDP flag to generalize and fix AutoLogon
- generalize.nix: add enableRDP option that re-enables RDP in
  post-oobe.cmd after sysprep resets registry (firewall rules,
  TermService auto-start, disable NLA)
- Fix OOBE AutoLogon: create user with blank password (Windows
  ignores unattend passwords), set real password via net user in
  post-oobe.cmd, and explicitly set AutoAdminLogon registry values
- Add LogonCount=999 for persistent AutoLogon across reboots
- Remove unused rdpEntries import from registry/default.nix

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-07 15:57:17 +05:30
bfca98166a AHCI storage for laptop images, plaintext password fix
Laptop images now use AHCI storage + e1000 network instead of VirtIO.
This fixes "inaccessible boot device" on real hardware — the AHCI→NVMe
driver transition is handled by Windows, unlike VirtIO→NVMe which isn't.

- makeImage: useAHCI flag switches disk to ide-hd and network to e1000
- customizeImage: auto-detects useAHCI from original image, propagates it
- win10/win11 laptop images: useAHCI = true
- vmix run: --ahci flag for running laptop images in QEMU
- generalize: PlainText password tags in OOBE unattend XML

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-24 11:41:27 -03:00
89a0673f54 SDL fallback, partition fix, lz4 remote streaming, delay-oobe-run
SDL display:
- try SDL, auto-fallback to headless if it fails (no crash)
- SDL_VIDEODRIVER=x11 to avoid wayland socket path issues
- suppress XDG_RUNTIME_DIR warnings

Disk copy:
- zap-all before writing to clear old partition tables
- delete recovery partition (4) before resizing partition 3
- use parted resizepart (preserves partition GUID for BCD)
- remote: nix-shell for sgdisk/parted/ntfsresize on target
- remote: lz4 compression for faster streaming
- remote: pv progress bar with disk size
- -y/--yes flag to skip confirmation prompt

Generalize:
- delay-oobe-run=true defers OOBE + activation to real hardware
- clean cached Autounattend from Windows\Panther before sysprep
- taskkill sysprep.exe on first login (CopyProfile artifact)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-24 11:01:39 -03:00
ebfb10b3b3 vmix run command, virtio-drivers template, delay-oobe-run flag
CLI:
- `vmix run <qcow2>` boots image with QEMU (SDL if DISPLAY, snapshot mode)
- --generalize supports delay-oobe-run=true to defer OOBE + activation
  to first boot on real hardware (for physical disk deployments)

Templates:
- essentials.virtioDrivers: installs VirtIO drivers only (no guest agent)
  used in laptop bundle for network access during Office download
- generalize: delayOobeRun flag controls sysprep /shutdown vs /reboot
  delays OOBE, user creation and HWID activation to target device

Build:
- suppress XDG_RUNTIME_DIR and homeless-shelter warnings in SDL mode
- remove invalid ICH9-LMB global properties

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 23:06:28 -03:00
94f299bb81 sync with labv2.nix + standalone flake with toDisk app
Previous history:
- c359054 daku working!
- 8de5cff fix integer overflow in vmix network lib
- 9c25a66 daku on 25.05. with ollama
- 385a3bf vmix enables relaxed sandbox
- c363da1 restructure vmixLib into linux/windows subattrs with OS-specific customizeImage
- edd4dc2 vmix: port namespace model and module improvements from conf.nix
- 6666ecf vmix: add SPICE support, install virtio guest tools with SPICE agent
- 46f5671 vmix: add QEMU guest agent channel for Windows VMs
- e1fea34 vmix: add Win11 LTSC 2024 image, refactor VirtIO driver selection
- c27ae68 vmix: make customizeImage chroot-sandboxed by default, opt-in impure
- 305fbac virt customize needs chroot for now due to usr bin env things. could be fixed later
- 264d30f vmix: add win10 VM on desk, disable SMB signing for guest Samba access
- 9b64f51 vmix: split Windows templates into per-category files, add comprehensive debloat
- ef91bf8 vmix: fix missing parent registry keys in Windows templates
- f87f340 win10 VM on panda with AMD GPU + USB passthrough
- 38e474f vmix: split Windows build into Audit Mode install + composable templates
- a6a8db3 vmix: win11 support, remove build VNC, switch VMs to SPICE
- 6cf5a21 generalize stage sets bg color, accent color and sets visual effects to performance
- a84849f remove rdp template since it doesn't even work
- 5245263 vmix: best performance template + generalize cleanup
- ab12dd3 vmix: use CopyProfile for best performance visual effects
- bce3326 vmix: CopyProfile for best performance visual effects
- 2496107 vmix: add app templates (7zip, VLC, ImageGlass, Edge WebView, VC++ runtimes)
- 29a6123 wip: debug default associations xml
- 2a2e5f5 vmix: fix DefaultAssociations.xml cmd.exe escaping
- cc6ff9d vmix: move DefaultAssociations.xml to template only
- a4a78ec vmix: add removeWMP template to remove Windows Media Player
- 3fe56de vmix: improved Edge removal (files, shortcuts, scheduled tasks)
- a491767 vmix: fully remove Edge via post-oobe AppxPackage removal
- 6ca1619 vmix: remove Edge DevToolsClient SystemApps + AppxPackage
- 0c1ec35 vmix: sandboxie windows app template
- 628bbd2 vmix: add Sandboxie-Plus template
- f055a41 vmix: reorganize templates, add file associations, remove Paint
- 34326f4 vmix: set Thorium as default browser via PS-SFTA in post-oobe
- 86af258 vmix: Active Setup for default browser (all users, no post-oobe needed)
- 35b8cb0 remove vnc display from thorium template
- c7e0af6 vmix: fix Win11 generalize timeout + UCPD disable for URL associations
- 43a1345 vmix: add Office 2024 template + Ohook activation in generalize
- 03bbce0 vmix: updated office installation xml. more privacy options enabled
- 790a0ee vmix: thorium installation - hide SFTA window
- a0e5c18 vmix: fix office install.bat call + add privacy registry policies
- 3df38ca vmix: fix Ohook activation + suppress Office theme dialog
- df39ba3 vmix: remove sandboxie shortcut from desktop
- 50d5972 vmix: skip Sandboxie desktop shortcut via installer flag
- ee2fa0f vmix: fix win10 default browser
- 938315b vmix: windows: set accent color to automatic. remove accent color from unnecessary elements
- beceda8 vmix: allow ISO-only VMs without OS disk, add WinPE VM to panda

Flake outputs: overlays.default, nixosModules.default, lib, apps.toDisk

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 19:18:10 -03:00