# SilverMetal WinPE Pre-Config Collector > **Status**: design — 2026-06-10. Approved in brainstorming. Adds a branded WinPE > collector that runs *before* Windows Setup, captures identity + install-shaping > choices, generates the answer file so Setup creates the real account natively > (eliminating the `sm-bootstrap` account + its teardown), and hands the rest to a > simplified run-once-then-persist first-boot toolbox (the existing MAUI Welcome app, > trimmed). Builds on `windows/installer/build.ps1`, `windows/installer/autounattend/`, > and `windows/welcome/` (the MAUI wizard). ## 1. Goal Let the user pre-configure the installation **before** the unattended installer runs. A WinPE collector gathers **identity + install-shaping** choices, writes them into a generated answer file (native account, computer name, locale, auto-logon) plus a carried-forward config file (flavour, BitLocker PIN, app defaults). Windows Setup then creates the real local administrator itself — so the first-boot app no longer creates an account or removes a bootstrap user, and becomes a **config/toolbox**: it applies the remaining post-install config once (apps, BitLocker), shows the recovery key, then persists as a launchable tool. ## 2. Decisions (locked in brainstorming) - **Collector scope = identity + install shaping.** It owns: the user **account** (display name, username, password), **computer name**, **locale/keyboard**, **flavour**, and the **BitLocker** choice + PIN. The toolbox owns **apps and ongoing config**; hardening runs headless via `SetupComplete.cmd` (see the last bullet). - **Collector UI = PowerShell + WinForms.** WinPE cannot run the MAUI/WebView2 app (no Edge/Chromium, no modern .NET), so the collector is a separate, branded full-screen WinForms form. `WinPE-NetFx` + `WinPE-PowerShell` (and dependencies) are added to `boot.wim`. - **Single real admin account.** One local **Administrators** account that the user defines — replacing both the ephemeral `sm-bootstrap` *and* today's "daily user + SilverOS Admin" split. (The two-account split can return later as a flavour option; out of scope here.) - **Toolbox = run-once apply, then persist.** On first logon (as the real user) it auto-runs once, applies the collected config (apps, BitLocker PIN, flavour config), shows progress + the BitLocker recovery key, reaches Done — then stays installed as a launchable "SilverMetal" app. The heavy kiosk lockdown (Shell Launcher / Keyboard Filter / `sm-bootstrap` teardown) is dropped. - **Handoff = generated answer file + embedded config (Approach 1).** Setup keeps owning the disk. The collector generates the answer file and embeds the non-OS config as base64 that a `specialize`-pass command writes to `C:` once it exists. - **Hardening is canonical in SetupComplete.** Hardening runs headless from `SetupComplete.cmd` as SYSTEM (as the answer file already intends). The toolbox Apply focuses on **apps + BitLocker** — no duplicate hardening — so hardening is guaranteed even if the toolbox is closed. (A read-only hardening-status view in the toolbox is SP2.) ## 3. Architecture / flow ``` Boot ISO -> WinPE (boot.wim) winpeshl.ini -> SilverMetal Collector (PowerShell + WinForms) collects: account . computer name . locale . flavour . BitLocker PIN -> New-SmAnswerFile -> X:\sm\unattend.generated.xml (embeds preconfig.json base64) -> setup.exe /unattend:X:\sm\unattend.generated.xml Windows Setup: windowsPE pass : wipe disk0 (WillWipeDisk) -> install LTSC index 1 -> locale specialize pass: decode embedded preconfig.json -> C:\ProgramData\SilverMetal\preconfig.json oobeSystem pass: create REAL local admin -> AutoLogon(once) -> ComputerName SetupComplete.cmd (SYSTEM): hardening (canonical) + scrub C:\Windows\Panther\unattend.xml First logon (real user, auto, once): Toolbox run-once -> read preconfig -> install apps + enrol BitLocker -> show recovery key -> Done -> clear PIN -> set "configured" marker Subsequent launches -> toolbox-home mode ``` ## 4. Components ### 4a. WinPE collector (`windows/collector/`) - **Launch**: `boot.wim` `winpeshl.ini` runs `X:\sm\Start-Collector.cmd` (instead of the default Setup shell), which calls `powershell -ExecutionPolicy Bypass -File X:\sm\Collector.ps1`. - **UI** (`Collector.ps1`): a branded full-screen WinForms form (dark theme + SilverMetal logo) with fields: - Account: display name, username, password + confirm. - Computer name. - Locale / keyboard (defaults to today's en-GB / `0809:00000809`). - Flavour (the existing flavours, read from a small bundled list). - BitLocker: enable toggle + PIN + confirm (or skip). - A **"Use defaults"** fast path that fills sensible defaults so a user can click through. - **Validation** (`Test-SmInput.ps1`, separated from the WinForms shell so it is unit-testable headless): username rules (non-empty, valid local-account chars, not a reserved name), password present + confirm-match (+ minimum length), PIN numeric + length (>= 6) + confirm-match when BitLocker enabled, computer-name rules (<= 15 chars, valid NetBIOS charset). - **Output**: calls `New-SmAnswerFile` then launches Setup with the generated file. ### 4b. Answer-file generator (`windows/collector/New-SmAnswerFile.ps1`) Pure function: collected values (a hashtable) -> answer XML string. Mirrors the current `autounattend.xml` structure with these differences: - **windowsPE**: keep `DiskConfiguration` (wipe disk 0, EFI/MSR/Primary) + `ImageInstall` index 1; locale from the collector. - **oobeSystem**: emit **one** `LocalAccount` in `Administrators` (the user's account); `AutoLogon` Enabled with `LogonCount=1` as that user; `ComputerName`; `Microsoft-Windows-International-Core` locale; the existing `OOBE` hide-pages block. **No `sm-bootstrap`.** - **specialize**: a `RunSynchronousCommand` that base64-decodes the embedded `preconfig.json` to `C:\ProgramData\SilverMetal\preconfig.json` (creating the dir). - **FirstLogonCommands**: launch the toolbox elevated (as today, but as the real user). - The **scrub** runs from `SetupComplete.cmd` (SYSTEM, end of Setup — guaranteed after account creation): it deletes `C:\Windows\Panther\unattend.xml` and the cached answer copy. ### 4c. `preconfig.json` contract Written to `C:\ProgramData\SilverMetal\preconfig.json`: ```json { "schemaVersion": 1, "flavour": "developer", "bitlocker": { "enable": true, "pin": "246810" }, "apps": { "useFlavourDefaults": true } } ``` - `flavour`: the chosen flavour id (drives the toolbox's app defaults + flavour config). - `bitlocker.pin`: consumed by the toolbox to enrol TPM+PIN, then **the field is cleared** (rewritten without `pin`) once enrolment succeeds. - `apps`: `useFlavourDefaults:true` (toolbox pre-checks the flavour defaults) or an explicit `selected: ["id", ...]` list (future: collector app picking — not in SP1; SP1 always emits `useFlavourDefaults:true`). ### 4d. Simplified first-boot toolbox (`windows/welcome/`, trimmed) - **Remove**: the Account step + `AccountService` account creation; `BootstrapService` `sm-bootstrap` teardown; the heavy kiosk lockdown (`Configure-Kiosk.ps1` Keyboard Filter / DisableTaskMgr / Shell Launcher path). Branding stays (baked offline; online re-apply unaffected). - **Add**: a `PreconfigLoader` that reads `C:\ProgramData\SilverMetal\preconfig.json` and pre-seeds `WizardState` (flavour, app selection = flavour defaults, BitLocker PIN). On **first run** (no "configured" marker) the toolbox auto-applies: apps install + BitLocker enrol (using the preconfig PIN) + recovery-key display + Done; then it clears the PIN from preconfig and writes a `configured` marker (`C:\ProgramData\SilverMetal\configured`). - **Apply pipeline** (trimmed): `apps -> bitlocker -> done`. No account step, no hardening (SetupComplete owns it), no teardown. Idempotent / re-runnable. - **Persist**: the app stays installed with a Start-menu shortcut "SilverMetal". Subsequent launches (marker present) open **toolbox-home** (a simple landing page; rich ongoing-config surfaces are SP2). - **Run mode selection**: marker present -> toolbox-home; marker absent + preconfig present -> first-run auto-apply; neither -> toolbox-home with flavour defaults (fail-open). ### 4e. `build.ps1` changes - **Stage 2b (boot.wim servicing)**: in addition to forcing legacy Setup, add the WinPE optional components (`WinPE-NetFx`, `WinPE-PowerShell`, and their deps `WinPE-WMI`, `WinPE-Scripting`) via `Add-WindowsPackage`; copy `windows/collector/*` to the mounted boot.wim at `\sm\`; write `winpeshl.ini` to launch `\sm\Start-Collector.cmd`. - **Fallback**: keep the static `autounattend.xml` on the ISO as the **default** answer file. The collector uses it as its template and as the fallback if cancelled. An env/marker `SM_UNATTENDED=1` (or a build flag) makes `Start-Collector.cmd` skip the UI and launch Setup with the static default — so **CI ISO builds remain non-interactive**. ## 5. Data flow / handoff Single source of truth = the collector's captured values. From them: - **OS-native fields** (account, computer name, locale, auto-logon) -> the generated answer file -> applied by Setup. - **Non-OS config** (flavour, BitLocker PIN, app defaults) -> base64-embedded in the answer file -> written to `C:\ProgramData\SilverMetal\preconfig.json` in the `specialize` pass -> read by the toolbox. No extra partition; Setup still owns disk partitioning (kept simplest + safest). ## 6. Error handling (must never brick boot) - **Collector cancelled / crashes** -> launch Setup with the bundled **default** answer file (sensible defaults) so install still proceeds. The collector wraps its UI in try/catch and always has a path to Setup. - **Generated XML fails validation** (schema/parse check before launch) -> use the default answer file. - **`preconfig.json` missing / corrupt** at first boot -> toolbox proceeds with **flavour defaults** (fail-open, mirroring the app-catalog loader). - **winget unavailable** -> already handled (graceful skip; PR #19). - **BitLocker enrol fails** -> toolbox surfaces it but still reaches Done (apps/onboarding not blocked); PIN retained in preconfig only on failure so a re-run can retry. ## 7. Security - **Local account only** (no Microsoft account / no cloud key escrow) — unchanged. - **Account password** transits the generated answer file briefly; `SetupComplete.cmd` **scrubs `C:\Windows\Panther\unattend.xml`** (and the cached copy) after account creation. - **BitLocker PIN** transits `preconfig.json`; the toolbox **clears it after enrolment**. - **Residual (documented):** plaintext secrets exist transiently in WinPE memory and on the freshly-formatted disk until the scrub/clear runs. Acceptable for a local, operator-present install; a future hardening pass could DPAPI-wrap the PIN or prompt it interactively at first logon instead. ## 8. Testing - **Pester — `New-SmAnswerFile`**: given inputs, the XML contains the real `LocalAccount` in Administrators, `AutoLogon` once as that user, the `ComputerName`, the locale, the `specialize` base64-write command, and the Panther scrub; and contains **no `sm-bootstrap`**. Base64 round-trips back to the original `preconfig.json`. - **Pester — `Test-SmInput`**: username/password/PIN/computer-name validation rules (valid + each invalid case). - **xUnit — toolbox `PreconfigLoader`**: parses a sample, fails open to flavour defaults on missing/corrupt, clears the PIN after a simulated enrol, honours the `configured` marker for run-mode selection. - **`Assert-IsoStructure.ps1`**: boot.wim contains `winpeshl.ini` + `\sm\Collector.ps1`; WinPE-NetFx present (e.g. `Get-WindowsPackage` shows the package or a marker file); the toolbox payload no longer references `sm-bootstrap`. - **VM e2e**: collector form renders in WinPE -> fill in account/flavour/PIN -> install -> first logon auto-applies (apps skipped if no network, BitLocker per env) -> recovery key shown -> Done -> relaunch shows toolbox-home. ## 9. Scope / phasing - **SP1 (this spec):** WinPE collector (account, flavour, computer name, locale, BitLocker PIN) + answer-file generation + `preconfig.json` handoff + simplified run-once-then-persist toolbox; remove `sm-bootstrap` + account step + heavy kiosk; hardening canonical in SetupComplete. - **SP2 (later):** rich toolbox-home — ongoing config surfaces (re-run/extend apps, change settings, view hardening status). - **SP3 (later):** install-time disk target / partitioning + BitLocker pre-provisioning on the blank drive (the deferred "maximal" collector scope). ## 10. Out of scope (SP1) - App selection inside the collector (SP1 emits `useFlavourDefaults:true`; the toolbox remains the place to pick apps). - The two-account (daily + admin) split — SP1 is single admin. - Disk target / partition UI; BitLocker pre-provision in WinPE. - DPAPI-wrapping or interactive-at-first-logon secret handling (noted as a future hardening option in §7).