# SilverMetal Enhanced — Windows: ISO Builder > **Status**: v1 design — 2026-06-08. Implements the productized SKU build for the hardening defined in [`hardening-spec.md`](hardening-spec.md). Reference device: GPD Pocket 4. This document describes the **reproducible build pipeline** that turns an official Windows 11 IoT Enterprise LTSC ISO plus the SilverMetal config layer into a hardened, branded, UEFI-bootable **custom packed ISO**, with a signed build attestation. It is bound by the same documents as the hardening spec: [`../docs/threat-model.md`](../docs/threat-model.md), [`../docs/design-principles.md`](../docs/design-principles.md), and [`../docs/trust-model.md`](../docs/trust-model.md). ## 1. Licensing frame (why this is legitimate) Windows 11 **IoT Enterprise LTSC** is licensed through the OEM / embedded channel, whose explicit purpose is building **customized, locked-down, preinstalled images** on devices a solution-builder ships. Creating a custom packed image and preinstalling it on hardware we sell is the *intended, blessed* use — not a grey area. | Track | Status under IoT Enterprise LTSC | |---|---| | **Preflashed SilverMetal SKU** (we sell the device) | ✅ Build + ship our custom image — licensed per device, intended use | | **Self-apply free track** (user hardens own device) | ⚠️ No public prebuilt-ISO redistribution. Ships as a **builder** that consumes the user's own official ISO + license — the same `hardening/` modules, applied to their image | The Microsoft ISO is therefore always an **input**, never committed to this repo. We ship *our layer*; the licensee provides the base. > **Procurement note (verify before pricing the SKU):** IoT Enterprise LTSC is obtained via authorized OEM distributors; per-device licensing may carry minimum-order / agreement terms. Confirm with the distributor. ## 2. Inputs All inputs are pinned by hash in [`installer/inputs.manifest.json`](installer/inputs.manifest.json): | Input | Source | In repo? | |---|---|---| | Windows 11 IoT Enterprise LTSC ISO | Licensed (operator/distributor) | No — pinned by SHA-256 | | GPD Pocket 4 driver pack | GPD (verified source) | `drivers/` or sourced manifest | | SilverLABS Stack (native Windows builds) | Stack component repos / artifacts | Referenced by version | | SilverMetal config layer | This repo (`hardening/`, `policies/`, `wdac/`, `debloat/`, `autounattend/`, `oem/`, branding) | Yes | ## 3. Build stages Orchestrated by [`installer/build.ps1`](installer/build.ps1). Runs on a **Windows host with the Windows ADK** (DISM + `oscdimg`). 1. **Verify input** — assert the source ISO SHA-256 matches the pinned manifest (supply-chain integrity). 2. **Extract** — expand the ISO to a working directory. 2b. **Force legacy Setup** — patch `sources\boot.wim` (index 2) so WinPE launches the legacy installer (see note below). *Required on 24H2/25H2 for a hands-off install.* 3. **Service the WIM offline (DISM)**: - Select the IoT Enterprise LTSC image index. - `/Add-Driver` the GPD Pocket 4 driver pack (Strix Point GPU, sensors/auto-rotate, Wi-Fi, fingerprint). - `/Add-Package` the latest cumulative update (slipstream). - Trim residual provisioned appx (LTSC is already lean — minimal list in `debloat/`). - Load offline registry hives → apply the **offline-able baseline** (telemetry floor, service disables) from `policies/`. - Stage the Stack installers + first-boot payload into the image. - Commit + unmount. 4. **Inject answer file + first-boot payload**: - `autounattend.xml` — OOBE automation, **local account** (no MSA), regional settings, BitLocker-ready disk layout. - `$OEM$\SetupComplete.cmd` — first-boot entry point that runs the online `hardening/` modules and installs the Stack. 5. **Brand** — boot/OOBE wallpaper + computer-name pattern from [`../shared/branding`](../shared/branding). 6. **Repack** — `oscdimg` produces a UEFI-bootable ISO (efisys boot image, El Torito). 7. **Attest** — emit output ISO SHA-256, an **SBOM** (every component + version), and a signed build attestation (design-principle #4). ### 3a. Windows 11 24H2/25H2: forcing legacy Setup (critical) Windows 11 **24H2 (build 26100) and 25H2** ship a **redesigned "ConX" Setup front-end** (`setup.exe → SetupHost.exe → SetupPrep.exe`, launched in WinPE) that **does not honor the `windowsPE` pass** of a root `autounattend.xml`. Symptom (confirmed on our VM test): the new *Select language/keyboard* pages and the OOBE *region* prompt require key presses despite the answer file. **No answer-file element toggles legacy vs new Setup.** The verified, scriptable fix — applied by `build.ps1` stage 2b — is to point WinPE's shell at the **legacy** installer in `sources\boot.wim` **index 2** (the Setup image; index 1 is WinPE): ``` DISM /Mount-Image sources\boot.wim index 2 → mount reg load HKLM\SM_BOOT \Windows\System32\config\SYSTEM reg add HKLM\SM_BOOT\Setup /v CmdLine /t REG_SZ /d X:\sources\setup.exe /f reg unload HKLM\SM_BOOT DISM /Unmount-Image /commit ``` WinPE's winlogon launches whatever `HKLM\SYSTEM\Setup\CmdLine` holds; overriding it to `X:\sources\setup.exe` runs the legacy engine, which consumes all four passes (windowsPE/offlineServicing/specialize/oobeSystem) → fully hands-off. The answer file is placed at **both** the ISO root and `\sources\`. **Rejected alternatives** (community-tested, unreliable): `winpeshl.ini` `[LaunchApp]` variants and the answer-file-embedded `RunSynchronous` reg trick (the latter only works if the answer file contains *nothing else*, else it reboot-loops WinPE). **Boot image — use the prompt variant.** Stage 6 uses the standard `efisys.bin` ("press any key to boot…"), *not* `efisys_noprompt.bin`. With a media-first boot order the no-prompt image causes a **reinstall loop** — every post-file-copy reboot re-boots the media before the disk install finishes. The prompt lets reboots fall through to the installed disk. Initial media boot is therefore one keypress (or a firmware boot-menu selection — expected for a USB-installed SKU; in an automated VM boot-test, eject the CD after the file-copy phase or send one key at start). **Verified end-to-end (VM, 2026-06-08):** with legacy Setup forced + `/unattend`, the answer file drives disk wipe/partition, edition, EULA, and image install **automatically** — Setup jumps straight to "Installing Windows." **Residual:** the single WinPE *language/keyboard* page still needs one click (the `International-Core-WinPE` settings do not reliably suppress it even under legacy Setup on 24H2 — a known, still-open item; everything after that page is hands-off). **Caveat:** this is ConX-specific behaviour that Microsoft may change in a future cumulative update / ADK refresh — re-verify against the exact base media before each batch. Sources: ElevenForum/NTLite 24H2–25H2 threads + MS Learn setup-automation docs (the Learn pages predate the redesign and describe only the legacy mechanism). Open question: whether MS later ships a supported way to pre-answer the ConX front-end. ## 4. Where each hardening control is applied The control domains in [`hardening-spec.md`](hardening-spec.md) split across three layers: | Layer | Controls | Mechanism | |---|---|---| | **Baked offline (in WIM)** | Telemetry floor, service disables, appx trim, drivers, baseline registry policy, Stack staging | DISM + offline hive edits | | **First-boot (online)** | Local account, VBS/HVCI/Credential Guard, ASR rules, **WDAC (audit)**, firewall, encrypted DNS, Stack + SilverVPN install, scheduled `Verify` task | `SetupComplete.cmd` → `hardening/` modules | | **Interactive / firmware — NOT in ISO** | Secure Boot custom-key enrollment, **BitLocker PIN**, BIOS admin password | Operator at provisioning (SKU) or documented user step (self-apply). The ISO *enables* BitLocker and *forces* PIN enrollment; it cannot ship a PIN or touch firmware. | **The `hardening/` modules are shared.** The same module set is invoked by the ISO's first-boot path *and* by the self-apply / milestone-1 standalone path. Write once, used both ways. ## 5. Reproducibility (honest scope) Windows image servicing is **not bit-for-bit deterministic** the way the Linux ISO pipeline is (WIM internal timestamps, servicing-stack non-determinism). So for this product, "reproducible" means: - **Pinned inputs** (every source hashed in the manifest) - **Recorded tool versions** (ADK, DISM, servicing stack) - **Output ISO SHA-256 + SBOM** published per build - **Signed build attestation** linking published artifact ↔ published source + inputs Bit-identical rebuild is a **stretch goal**, documented as such — we do not claim it (design-principle #2). This is weaker than the Linux line's reproducible-build guarantee, and we say so to buyers. ## 6. Directory layout ``` windows/ ├── installer/ │ ├── build.ps1 # pipeline orchestrator │ ├── inputs.manifest.json # pinned ISO SHA, driver-pack ver, Stack vers, tool vers │ ├── autounattend/ │ │ └── autounattend.xml # OOBE automation + local account + disk layout │ ├── oem/ │ │ └── SetupComplete.cmd # first-boot entry → runs hardening/ modules │ └── README.md ├── hardening/ # §A–H PowerShell modules + Verify ← SHARED (ISO + self-apply) │ ├── 00-provisioning.ps1 # A │ ├── 01-boot-firmware.ps1 # B (stages keys; firmware steps documented) │ ├── 02-data-at-rest.ps1 # C (BitLocker TPM+PIN) │ ├── 03-kernel-credential.ps1# D (VBS/HVCI/CredGuard/DMA) │ ├── 04-app-control.ps1 # E (WDAC audit, ASR, Defender) │ ├── 05-network-radios.ps1 # F (firewall, DNS, WiFi-only) │ ├── 06-physical-lock.ps1 # G (lock-screen, DMA lock, cam/mic) │ ├── 07-privacy-update.ps1 # H (telemetry trim, update integrity) │ ├── 08-stack-install.ps1 # SilverLABS Stack │ └── Verify-SilverMetalWindows.ps1 ├── policies/ # GP/ADMX exports + offline .reg/.pol baseline ├── wdac/ # WDAC base policy (XML) + compiled .cip ├── debloat/ # appx removal list, service-disable scripts ├── stack-installer/ # Stack package builders/installers ├── drivers/ # GPD Pocket 4 driver pack (or sourced manifest) └── tests/ # telemetry-leak test, hardening-baseline test ``` ## 7. Milestones | Milestone | Deliverable | Needs hardware? | |---|---|---| | **M0** | This design + scaffolded tree | No | | **M1** | `autounattend.xml` + `hardening/` modules runnable standalone — hardens the Pocket 4 with no pipeline | Yes (the unit) | | **M2** | DISM servicing + `oscdimg` repack → first packed ISO, built locally on a Windows + ADK box | Driver pack | | **M3** | `.gitea/workflows/build-iso-windows.yaml` (Windows runner) + attestation/SBOM + telemetry-leak gate | Windows runner | | **M4** | Branding, full Stack integration, all verification gates green | Stack Windows builds | ## 8. Build environment & deferred items - **Build host**: Windows + Windows ADK (DISM + `oscdimg`). A Windows CI runner is required for M3 (mirrors the existing Linux `.gitea/workflows/build-iso-linux.yaml`). - **Cross-build on Linux** (wimlib + xorriso) is possible but UEFI boot-file assembly is fiddly — **deferred**, Windows-runner path is canonical. - **Stack Windows builds**: some Stack components are still "Linux MVP" per [`README.md`](README.md); their native Windows builds may lag M4. - **Driver-pack sourcing**: confirm redistribution terms for the GPD Pocket 4 driver pack, or source at build time from the verified GPD location. ## 9. Open questions Carried from [`hardening-spec.md`](hardening-spec.md) §8 (resolve on the physical unit), plus builder-specific: 1. Does the IoT Enterprise LTSC media expose the expected image index and OOBE bypass path for `autounattend.xml`? 2. Which Pocket 4 drivers are absent from a vanilla LTSC install and must be injected? 3. Does `oscdimg` produce a Pocket 4-bootable UEFI image with the device's firmware (test on the unit)?