Skip to main content

index

wootc end-to-end walkthrough β€” Windows 11 β†’ wootc deployer β†’ native Linux β†’ Windows 11
β–Ά Latest green end-to-end run (sped-up): Windows 11 β†’ wootc deployer β†’ native Linux from root.disk β†’ Windows 11. Click to play the full timelapse. Only passing runs are ever published here.

Install a real, image-based Linux desktop from inside Windows β€” no repartitioning, no data loss, fully reversible.


wootc is a Windows-hosted installer for bootc Linux images. It writes a complete Linux system into root.disk, a single sparse file on your existing Windows NTFS volume, and adds a one-shot Windows Boot Manager entry that boots into it. There is no repartitioning of the Windows disk, and uninstalling is deleting a folder and a boot entry.

It's a modern, Secure-Boot-friendly take on the classic Wubi idea, built for container-native (OCI/ostree) Linux images and for people who have never touched a partition editor.

Why​

North Star β€” make it as easy as possible for non-technical Windows users to migrate to Linux without losing any of their data. Every decision is weighed against: would a nervous Windows user get through this without fear or data loss? Reversibility and data safety beat feature count; friendly language beats technical precision; nothing permanent changes on the machine until Linux is proven working.

Switching OS is scary because it usually means repartitioning, backups, and a point of no return. wootc removes all three: Linux lives in a file next to Windows, both boot from the same disk, and you decide if and when to make it permanent.

How it works​

Windows 11 β†’ wootc.exe (arms the system) β†’ reboot
β†’ signed shim β†’ GRUB β†’ deployer initramfs
β†’ fisherman: bootc install into root.disk
β†’ reboot β†’ native Linux, loop-mounted from root.disk
β†’ (optional) reclaim the disk and remove Windows
  1. Arm (in Windows). The GUI creates C:\wootc\disks\root.disk, stages a Microsoft/Fedora-signed shim β†’ GRUB β†’ deployer chain on the ESP, writes a credential vault, optionally slurps your Windows look, and sets a one-shot boot entry. Nothing else on the machine is touched.
  2. Deploy (one reboot). Under Secure Boot, the signed chain launches the deployer initramfs, which mounts the NTFS volume, attaches root.disk, and uses fisherman to run bootc install β€” partitioning and populating the disk from the chosen OCI image, with optional LUKS/TPM2 encryption.
  3. Boot Linux. A dracut hook (99wootc-boot) attaches root.disk on every boot and pivots into the native system. The OS itself is unmodified β€” the same image boots whether it lives in a file on NTFS or on a real partition.
  4. Commit (optional, later). When you're ready to go Linux-only, graduate the system onto a native partition and reclaim the Windows space.

The Windows installer is a Wails app (Go + web UI); the deployer and migration tooling are POSIX shell and run inside the initramfs and the target system.

Features​

AreaWhat it does
Guided installerFixed-size GUI: pick an image, set user/hostname/disk, choose encryption. Live preflight (BitLocker, Fast Startup, UEFI, Secure Boot, free space).
Image catalogGNOME / KDE / Niri / XFCE variants across Enterprise Linux, Fedora, Arch, and Debian bases. Override with a custom OCI ref or C:\wootc\images.json.
Data safety firstroot.disk sits beside Windows; a one-shot boot entry means a failed deploy falls back to Windows. Reversible, partition-aware uninstall.
BitLocker-safe (Β§3.5)Never forces decryption. Offers an unencrypted partition for Linux (create new or reuse) while C: stays encrypted.
Disk encryption (Β§2.6)LUKS for the Linux root: TPM2 auto-unlock (default) or passphrase.
User Data Bridge (Β§4)Brings your files, browser profiles (Firefox/Chrome/Edge), Steam libraries, and MS Office β†’ LibreOffice settings across β€” honestly, with a consent tier that never copies secrets silently.
Windows-Style Mode (Β§4.4)Opt-in. Wallpaper, accent color, keyboard layout, taskbar pins, and desktop shortcuts brought over on first login. Off by default β€” the image maker's look wins.
WSL migration (Β§4.6)Copies a WSL user's dotfiles (public SSH keys only) and turns their installed packages into a Homebrew Brewfile.
Bring your Windows overA Linux-side GUI to import data from a Windows install on another disk β€” second drive, external/USB, or a backup β€” unlocking BitLocker read-only (cryptsetup bitlk).
Try in VM (Β§6)Boot root.disk in a QEMU window without rebooting, or build a fresh preview from an image and promote it to the real install if you like it.
Themeable / lockablePartners can re-skin the installer and lock it to a single image family for a branded on-ramp.

See docs/SPEC.md for the full specification and section numbers.

Project status​

This is active, boot-path-focused development β€” not yet a released installer.

Verified end-to-end on the KVM E2E rig (Windows 11 + TPM 2.0 + Secure Boot):

  • βœ… Arm (rung 1): the real wootc.exe arms a virgin Windows VM over QGA β€” root disk, signed chain, one-shot BCD, state.json = armed (24/24).
  • βœ… Deploy: the Secure Boot chain launches the deployer and fisherman lays down a full bootc image into root.disk β€” every post-deploy check passes (dracut hook, services, loop-root BLS args, ESP kernel-sync, host-esp.conf).
  • βœ… Native Phase-2 boot (rung 2): the signed Windows BCD β†’ shim β†’ GRUB chain boots the installed Bluefin system from the NTFS-hosted root.disk. The initramfs mounts NTFS with the kernel driver, attaches the raw disk with losetup, resolves the root UUID, runs OSTree prepare-root, switches to the real deployment, reaches the graphical system, and exposes Linux QGA.
  • βœ… GUI + migration: installer GUI (Playwright-tested), User Data Bridge and WSL/Office/Steam/browser bridges (unit-tested), external-disk import engine, Try-in-VM orchestration, Phase-3 planner.
  • βœ… Graduate to native disk (Phase 3 / rung 3): the VM boots Phase 2, independently verifies a blank /dev/sdb, runs the native bootc install, reboots into the graduated system, and confirms the file seeded in Windows survived onto the native disk β€” Windows and root.disk untouched (29/29).
  • βœ… GUI-driven full run: the entire Phase-1 β†’ 2 β†’ 3 chain armed by the real wootc.exe GUI (drive mode β€” the app drives its own live form), green end-to-end on bluefin:lts. The timelapse at the top of this README is that run.

Follow the verification ladder in docs/milestones.md.

Build/test matrix​

Red/green status per combination, from the KVM E2E rig (laptop runners) and the hosted-runner matrix (.github/workflows/e2e-matrix.yml). Legend: βœ… proven green Β· 🟑 in progress / partially proven Β· πŸ”΄ known-red (tracked issue) Β· βšͺ not yet run.

Last full matrix sweep: 2026-07-25 β€” 12 of 22 cases green. A case is only marked βœ… once the whole chain passes (Windows seed β†’ deploy β†’ Phase-2 boot β†’ seeded file readable from Linux).

⚠️ The βœ…s below predating 2026-07-27 are currently UNVERIFIED (per-row exceptions are called out β€” e.g. the dakota composefs cell was re-proven on the failure-ledger harness on 2026-08-02). Until commit 3d7f9e2, fail() only printed β€” a failed check that did not itself abort could not stop the run reaching "ALL TESTS PASSED". A real BitLocker run was recorded PASS with [FAIL] User data NOT visible in Phase 2 $HOME in its own log, which is the North Star assertion itself. The harness now records every failure to a ledger and gates the banner on it, so this class of false green cannot recur β€” but the counts above were produced by the old harness and are being re-run. Treat them as claims awaiting evidence, not as status.

ℹ️ Resolved (2026-07-27): the hosted-runner TPM blocker (#59) was ours, not GitHub's β€” our sshd wrapper took PID 1 from tini, so swtpm's -d daemonization never wrote its pid file and dockur silently disabled TPM. Fixed in 4a087eb; GitHub-hosted runners are back in scope.

Image family Γ— phase (Windows 11 Pro, Secure Boot + TPM 2.0):

Image familyBackend / rootfsArm (P1)DeployPhase-2 bootPhase-3 graduateGUI-driven full run
bluefin:ltsostree Β· ext4-sealedβœ…βœ…βœ…βœ… (29/29)βœ…
yellowfin:gnome (EL10)ostree Β· ext4-sealedβœ…βœ…βœ…βœ…βšͺ
yellowfin:kde / :xfce (EL10)ostree Β· ext4-sealedβœ…βœ…βœ…βšͺβšͺ
bonito:gnome / :kde / :niri (Fedora)ostree Β· xfs (unsealed)βœ…βœ…βœ…βšͺβšͺ
dakotacomposefs-nativeβœ…βœ…βœ…βšͺβšͺ
marlin (Arch) / flounder (Debian)ostree Β· xfs (unsealed)βœ…πŸ”΄βšͺβšͺβšͺ

dakota (composefs-native) went green on the failure-ledger harness β€” composefs full chain, run 30710282014 (2026-08-02); #28 is closed. Phase-3 graduation for composefs is a follow-on rung. marlin/flounder fail in bootc install with "bootupd is required for ostree-based installs": they are ostree images that ship no bootupd.

Axes (against the EL10 / bluefin:lts baseline):

AxisStatusNotes
Windows 11 Proβœ…primary proven path
Windows 11 Home / Enterprise / LTSCβœ…all three green on EL10
Windows 10 Proβœ…green in 37 min via the restored Windows base image
Windows 10 Home / Enterprise / LTSCπŸ”΄Setup stops on its edition picker β€” the answer file's product key matches no image in the ISO for those editions β€” #58
Root filesystem: xfs (unsealed)βœ…mounted with explicit -t (a typeless mount tried ext4 on xfs)
Root filesystem: ext4 (sealed, fs-verity)βœ…proven sealed default
Root filesystem: btrfs (sealed)🟑works on Fedora-kernel images (bonito) via wootc.filesystem=btrfs; #35 fixed β€” blocked on EL10 kernels whose out-of-tree btrfs kmod is rejected under Secure Boot
Encryption: noneβœ…
Encryption: tpm2-luks🟑#33 fixed (bffd284, 2026-08-10) β€” Phase-2 dracut regen works; green cell re-verification pending under the failure-ledger harness
BitLocker FDE (unencrypted-volume path)βœ…proven green β€” #34. Setup carves unencrypted volume E: for root.disk while C: stays encrypted; drive letter resolver dynamically discovers tree location.

The full three-phase chain (Windows seed β†’ deploy β†’ Phase-2 bridge β†’ Phase-3 native disk β†’ seeded file on the native disk) is green end-to-end on bluefin:lts β€” both via the script path (29/29) and driven entirely through the real wootc.exe GUI (drive mode). The timelapse above is that GUI-driven green run.

Safety model​

  • Nothing permanent until proven. The first boot into the deployer is a one-shot entry; the default boot order stays Windows until Linux is verified.
  • The source is never mutated. External-disk and BitLocker imports mount read-only; BitLocker volumes are never decrypted in place.
  • Secrets stay put. Passwords, private keys, tokens, and credential stores are never copied silently β€” you sign in again where it matters.
  • Reversible uninstall. Removes the boot entry and C:\wootc\, and can reclaim a dedicated Linux partition, restoring Windows to its prior state.

Architecture​

wootc keeps a deliberate boundary between generic Windows→Linux migration machinery and the bootc-specific provisioner, so the design can be adapted to other distributions and deployment methods. The seam is documented in docs/architecture-boundary.md; deploy.sh marks its provisioner region explicitly.

app/ Wails Windows installer (Go backend + web UI)
payload/deployer/ one-shot deployer initramfs + deploy.sh
payload/migration/ User Data Bridge, WSL, external-disk import, Phase-3
payload/builder/ Try-in-VM Alpine builder (headless OCI→disk)
payload/wubildr/ reproducible custom GRUB EFI build
platform/dracut/99wootc-boot/ Phase-2 loop-root attach hook
platform/grub/ external GRUB configuration
fisherman/ bootc install / partitioning (submodule, tuna-os fork)
tests/ e2e (KVM Windows 11), gui (Playwright), migration
docs/ SPEC, boundary, milestones, walkthroughs

Build and test​

just build # deployer initramfs + custom GRUB

# GUI unit tests (Playwright over the real frontend bundle, mocked backend)
cd tests/gui && npx playwright test

# Migration bridge tests (containerized, no Windows needed)
bash tests/migration/test-bridge.sh

End-to-end (KVM)​

The E2E harness drives a Windows 11 VM (via dockur/windows) over the QEMU Guest Agent β€” no guest networking required. It needs a host with KVM, UEFI Secure Boot, and TPM 2.0.

just remote-sync # push + reset a runner to origin/main
just remote-e2e # fresh install + deploy (~30 min)
just remote-e2e-quick # reuse the installed disk (~5 min)

just remote-logs # tail the run
just remote-serial # watch the deployer serial console
just remote-status # grep PASS/FAIL markers

Every E2E run records a sped-up timelapse to tests/e2e/storage/artifacts/<run>/video/. To refresh the walkthrough at the top of this README, publish a passing run's clip:

tests/e2e/publish-visual.sh --from-host himachal # or a local artifact dir
git add pages && git commit -m 'docs: refresh E2E walkthrough' && git push origin main

A GitHub-hosted workflow (.github/workflows/pages.yml) then deploys it to Pages β€” no self-hosted runner required. The README hero is a committed relative path, so it renders inline on GitHub even before Pages redeploys.

Documentation​

License​

The Windows installer components derived from WubiUEFI are GPL-2.0 (LICENSE-GPL-2.0); the deployer initramfs and GRUB configuration are MIT (LICENSE-MIT). fisherman, bootc, bootupd, podman, and skopeo are separate binaries under their own (Apache-2.0) licenses, invoked over a process boundary. See docs/SPEC.md for details.