gui phase1 architecture
Status: proposal, 2026-07-17 β historical (2026-08-11): the GUI now
compiles, drives the E2E install in drive mode (WOOTC_E2E_DRIVE=1), and
green GUI-driven runs are published in the README build/test matrix.
Companion to docs/SPEC.md Β§3 and the E2E
learnings accumulated on kanpur. Scope: everything that runs on Windows β
the Wails GUI (app/), the install pipeline it drives, and the contract
between the GUI and the deployer across reboots. Phase-2 boot internals are
out of scope (owned by the E2E track).
1. Current stateβ
app/ is a Wails v2 app (fixed 820Γ620, vanilla-JS frontend, 4 screens:
launchpad β progress β done, plus a control screen when root.disk
already exists). The Go backend exposes GetImages, GetSystemInfo,
StartInstall (9-step pipeline), CancelInstall, Uninstall, Reboot.
Vault handling (sha512-crypt hash, ACL-restricted vault.json, deployer
shreds after ingest) is designed correctly and matches SPEC Β§2.3.
Hard facts to anchor planning:
- The Windows binary does not compile.
GOOS=windows go build ./appfails:downloadFile,copyFile,marshalJSONare referenced but never defined;espDriveis declared and unused inconfigureBCD. The pipeline has never been executed end-to-end. (Not introduced by recent work β it predates the VHDX branch.) - The app installs a boot chain the E2E effort has abandoned.
setupGRUB2copieswubildr.efi+ configs toESP:EFI\wootc\and points BCD at\EFI\wootc\wubildr.efi. The chain proven on kanpur is: BCD βESP:EFI\fedora\shimx64.efi(MS-signed) βgrubx64.efi(embedded prefix/EFI/fedora) βgrub.cfgβ deployer kernel + initramfs on the ESP (signed GRUB cannot read NTFS). The reference implementation of the working chain istests/e2e/setup-wootc.ps1steps 7β8, not the app. - BCD handling differs from what E2E validated. The app sets both
displayorder /addfirst(permanent β silently changes the user's default boot) andbootsequence /addfirst(one-shot). E2E uses only the one-shot, which is the right UX: nothing permanent changes until TunaOS actually works. setupSystemdBootis a stub, but the GUI already offers a "Bootloader" choice.
2. Target architectureβ
2.1 Componentsβ
βββββββββββββββββββββββββββββ Windows ββββββββββββββββββββββββββββββ
β wootc.exe (Wails) β
β ββ ui/ screens: launchpad, progress, done, control β
β ββ catalog embedded images.json + C:\wootc\images.json β
β ββ preflight SystemInfo + hazard checks (Β§2.4) β
β ββ pipeline ordered, resumable steps (Β§2.2) β
β ββ bootchain ESP layout + BCD (one implementation, shared β
β β logic with setup-wootc.ps1 by construction: β
β β the .ps1 should shrink to a thin caller or be β
β β generated β today they are two divergent copies)β
β ββ statebus reads/writes C:\wootc\state.json (Β§2.3) β
β β
β C:\wootc\ β
β ββ install\ deployer-vmlinuz, deployer-initramfs.img, vault.jsonβ
β ββ disks\ root.disk β
β ββ logs\ live-journal.log, deployer-last-journal.log β
β ββ state.json single source of truth for lifecycle (Β§2.3) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
2.2 Install pipeline (revised step list)β
Replace the current 9 steps with the E2E-proven order; every step must be idempotent so a failed install can be re-run without manual cleanup:
- Preflight gate (Β§2.4) β abort before touching anything.
- Disable Fast Startup.
- Create directories.
- Create
root.diskβ RAW, sparse-allocated, VDL-extended withfsutil setvaliddata(Linux ntfs3 EIOs past VDL otherwise). VHDX was retired in 8136ae6: target images ship losetup, not qemu-nbd. - Download deployer kernel+initramfs+signed shim/grub with sha256 verification (SPEC Β§3.1 promises this; not implemented β release pipeline must publish checksums alongside artifacts).
- Stage ESP:
EFI\fedora\{shimx64,grubx64}.efi, deployer pair toEFI\wootc\, deployer grub.cfg at the signed GRUB's prefix. ESP capacity is a first-class constraint (Β§3, D2). - Write vault.json (unchanged).
- Configure BCD: create "wootc" firmware entry pointing at shim, arm
one-shot
bootsequenceonly. Nodisplayorderchange. - Write
state.jsonβarmed.
2.3 Lifecycle state machine + deployer contractβ
The single biggest UX gap: after the reboot, the GUI currently knows
nothing. kanpur debugging repeatedly needed exactly this information; the
deployer already emits it (phase markers, journal streaming to
C:\wootc\logs\) β it just has no consumer.
States (persisted in C:\wootc\state.json, written by both sides):
absent β staged β armed β deploying β deployed β healthy
β β β
βββββββββββ΄ββ> failed(phase, error)
- GUI writes
staged,armed. - Deployer (deploy.sh) writes
deployingon start, thendeployedorfailedwith{phase, error, ts}β onejq-free echo of JSON to/mnt/ntfs/wootc/state.jsonnext to the existing log streaming; it already has every needed value in/run/wootc-phaseand the fatal message. - Phase-2 firstboot (wootc-passthrough or a oneshot unit) writes
healthyβ closing the loop that a real boot succeeded. - Control screen renders the state:
failedshows the phase + tail ofdeployer-last-journal.logwith a "Retry deploy" button.
Re-arming must restore the deployer grub.cfg first. A successful
deploy overwrites the ESP grub.cfg with the Phase-2 menu (this cost a
full debugging cycle on kanpur β re-arming the BCD entry without
restoring grub.cfg boots Phase-2, not the deployer, and if the Phase-2
kernel was pruned from the ESP it dead-ends at a GRUB error). "Retry
deploy" = restore deployer grub.cfg + verify deployer pair on ESP +
one-shot bootsequence + reboot. This belongs in bootchain as one
operation; the ad-hoc restore-deployer-grub.ps1 pattern from kanpur is
the prototype.
2.4 Preflight gates (SPEC Β§3.5, mostly unimplemented)β
Blockers (refuse install): not UEFI; no admin; ESP unusable (Β§D2); free
space < root.disk size + deployer scratch headroom (the kanpur
skopeo copy: exit status 2 failure is what running out looks like β
surface it before reboot, not in dracut); BitLocker with unexportable
recovery path.
Warnings (accept + mitigate): BitLocker on (suspend for next boot, per
SPEC Β§3.5 script); Fast Startup (already handled); Secure Boot state
recorded into state.json (chain differs only in confidence, not layout).
3. Open architecture decisions (need explicit calls)β
D1 β EFI\fedora\ collision on real dual-boot machines. The signed
Fedora grub has embedded prefix /EFI/fedora; on a machine with an
actual Fedora install, wootc would overwrite that distro's grub.cfg
and shim. The E2E VM never sees this; real users will. Options:
(a) detect an existing EFI\fedora\ with a BLS-populated grub.cfg and
refuse (MVP-safe, cheap);
(b) coexist: preserve + chainload the original cfg;
(c) longer term: ship a differently-prefixed signed chain (requires our
own shim signing β heavy).
Recommendation: (a) now, design toward (b).
D2 β ESP capacity. The deployer initramfs is ~135 MB; OEM ESPs are
commonly 100β260 MB and kanpur's 512 MB ESP already needed manual
pruning. Options: (a) preflight-measure and refuse when it can't fit
(with clear message); (b) shrink the deployer initramfs (it embeds
podman/skopeo/fisherman β a slimmer net-boot-style second stage is a
real project); (c) FAT32 helper partition created by shrinking C: and
pointing shim's grub there via search. Recommendation: (a) for MVP with
telemetry on how often it bites, spike (c) after.
D3 β systemd-boot in the GUI. Under Secure Boot, sd-boot is not
loadable via shim for EL-family targets (not signed by a trusted vendor
key), and nothing in the E2E chain exercises it. Keep the field in
InstallConfig for forward-compat but hide the choice in the UI for
MVP β an option that can't work is a support ticket. Revisit alongside
UKI-signed images post-MVP.
D4 β where the boot-chain logic lives. Today it exists twice
(setup-wootc.ps1 for E2E, Go for the app) and they drifted in opposite
directions. Either the app's bootchain package becomes the only
implementation and E2E invokes wootc.exe --headless-install (best:
E2E then tests the product, not a parallel script), or the .ps1 stays
canonical and the app shells out to it. Recommendation: headless mode in
wootc.exe; it also gives enterprises unattended install for free.
4. Roadmap (ranked)β
- Make it compile + CI gate β add the three missing helpers, fix
espDrive, addGOOS=windows go build ./app(andgo vet) to CI. - Port the proven chain into
bootchainβ shim/grub staging, ESP deployer pair, prefix grub.cfg, one-shot-only BCD (dropdisplayorder), D1(a) guard, D2(a) preflight. - state.json contract β GUI writer + deployer writer (3-line change in deploy.sh's log-streaming block) + control-screen consumer with log tail and "Retry deploy".
- Preflight gates β space math, BitLocker suspend, checksum verification of downloads.
- Headless install mode (D4) β then swap E2E's setup-wootc.ps1 body for it.
- GUI polish: hide bootloader choice (D3), catalog from embedded JSON
file instead of Go literals,
GetStatusmutex (races today), surfacestate.jsonhistory on the control screen.
Items 1β4 are the MVP cut. 5 is the structural payoff that stops the Windows/E2E drift from ever recurring.