# SilverMetal Windows — First-Boot Experience & Branding

> **Status**: design — 2026-06-09. Approved in brainstorming. Fills the `Invoke-Brand`
> stub in [`installer/build.ps1`](../../../installer/build.ps1) (M4 branding milestone) and
> adds the hardened onboarding kiosk + branded first-boot presentation.
> Bound by [`../../../iso-builder.md`](../../../iso-builder.md), [`../../../hardening-spec.md`](../../../hardening-spec.md),
> and the product principles in [`../../../../docs/`](../../../../docs).

## 1. Goal

Give SilverMetal Windows one cohesive, declaratively-built identity across every surface a
user sees from power-on to desktop, and present the existing SilverOS Welcome onboarding
wizard as a hardened, escape-proof, branded first-boot experience.

Everything is **baked declaratively** — offline registry/file servicing of the WIM in
`build.ps1` plus a SYSTEM configuration step at end-of-setup. **No VM capture / golden-image**
(that idea remains parked).

## 2. Scope — three components, one initiative

| # | Component | Where it runs |
|---|---|---|
| **A** | **Declarative branding** — the four brand layers | Offline (WIM) *and* online (self-apply) — shared module |
| **B** | **Hardened kiosk** — Shell Launcher v2 + Keyboard Filter for the one-time `sm-bootstrap` session | Build / OOBE path only |
| **C** | **First-boot presentation** — Welcome app as fullscreen Hybrid glass card | MAUI Welcome app |

Out of scope: renaming the `SilverOS.*` app/namespace/paths to SilverMetal (tracked as a
separate follow-up); a graphical OEM pre-boot/boot-logo splash (Secure Boot — out, per
earlier brainstorm); bit-identical reproducibility (non-goal per `iso-builder.md §5`).

## 3. Decisions locked in brainstorming

- **Presentation**: Hybrid — fullscreen branded backdrop + centered frosted-glass card.
- **Kiosk**: hardened via Shell Launcher v2 (per-user → only `sm-bootstrap`), escapes disabled.
- **Branding**: all four layers — BitLocker pre-boot message, lock/sign-in, desktop
  wallpaper+theme, OEM About. Custom bootloader / firmware logo OUT (Secure Boot).
- **Build**: declarative (`build.ps1` + offline registry). VM used only to design/verify visuals.
- **Aesthetic**: dark "void" canvas, cyan (`#00d4ff`) core mark, teal-green (`#00e5a0`)
  secondary. Mockup: [`.superpowers/mockups/02-branding-layers.html`](../../../../.superpowers/mockups/02-branding-layers.html)
  and `01-presentation-model.html`.
- **Name shown to users**: **SilverMetal Windows** on every branding surface. (The Enhanced
  line is hardened Windows, not our own OS, so "SilverOS" would overclaim. The `SilverOS.*`
  app strings are working-title leftovers → separate rename follow-up.)
- **Content**: support URL = `https://silverlabs.uk` (until a dedicated domain is locked);
  OEM Model = generic `SilverMetal Windows`; BitLocker recovery message = minimal, URL-only.
- **Code structure**: split — shared dual-mode **branding** module + build-only **kiosk**.

## 4. Component A — Declarative branding (`windows/branding/`)

A shared, dual-mode module, mirroring the `hardening/` "write once, used by ISO + self-apply"
pattern.

```
windows/branding/
├── Apply-Branding.ps1        # -Mode Offline -MountPath <wim mount> | -Mode Online
├── branding.manifest.json    # all strings (names, URLs, OEM fields) — single source of truth
├── assets/
│   ├── lockscreen.jpg
│   ├── wallpaper.jpg
│   ├── oemlogo.bmp           # ~120x120 OEM About logo
│   └── SilverMetal.theme     # dark + cyan accent .theme
└── README.md
```

- **Offline mode**: `reg load` the mounted image's `SOFTWARE` and `C:\Users\Default\NTUSER.DAT`
  hives, write values, `reg unload` (with the `[gc]::Collect()` + sleep guard already used
  elsewhere in `build.ps1`). Stage asset files into the mounted image.
- **Online mode**: write live `HKLM` / default-user hive and copy assets to the running system.
  Same value set both ways.

### Layers, mechanism, lock policy

| Layer | Registry / file | Locked? |
|---|---|---|
| **1 · BitLocker pre-boot** | `SOFTWARE\Policies\Microsoft\FVE` — pre-boot recovery message + URL policy values. Message ≈ "SilverMetal Windows. Locked out? silverlabs.uk". | n/a (firmware) |
| **2 · Lock / sign-in** | `SOFTWARE\…\PersonalizationCSP` lock-screen image (per-device reliable path) **and** `SOFTWARE\Policies\Microsoft\Windows\Personalization\NoChangingLockScreen=1`. Stage `lockscreen.jpg` to `C:\Windows\Web\Screen\SilverMetal\`. | **Locked** |
| **3 · Wallpaper + theme** | default-user `NTUSER.DAT`: `Control Panel\Desktop\WallPaper` (+ `WallpaperStyle`), dark mode (`…\Themes\Personalize\AppsUseLightTheme=0`, `SystemUsesLightTheme=0`), cyan accent. Stage `wallpaper.jpg` + `SilverMetal.theme`. Applies to **every new account** incl. the real end user. | **Changeable** |
| **4 · OEM About** | `SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation`: `Manufacturer=SilverLABS`, `Model=SilverMetal Windows`, `SupportURL=https://silverlabs.uk`, `Logo=<oemlogo.bmp path>`. | n/a |

### Honest limitation — BitLocker pre-boot (Layer 1)

Only the BitLocker **recovery** screen's message + URL are customizable. The normal
**PIN-entry** screen text ("Enter the PIN to unlock this drive") is fixed Windows UI and
**cannot** be branded. The mockup's branded PIN title is aspirational; Layer 1's real
deliverable is the recovery message + URL only. Exact `FVE` value names are pinned during
implementation (the M1 hardening `02-data-at-rest.ps1` already touches `FVE` for PIN enrolment).

## 5. Component B — Hardened kiosk (build-only)

Locks the ephemeral `sm-bootstrap` onboarding session so the user cannot escape the wizard.
The `sm-bootstrap` account, AutoLogon, and teardown already exist
([`autounattend.xml`](../../../installer/autounattend/autounattend.xml),
[`SetupComplete.cmd`](../../../installer/oem/SetupComplete.cmd), the Welcome app's `ApplyService`).

### Offline (in `build.ps1`)
- `DISM /Enable-Feature /All` for `Client-EmbeddedShellLauncher` (Shell Launcher v2) and
  `Client-KeyboardFilter`, applied to the mounted WIM. (Both ship in IoT Enterprise LTSC.)
- Stage `windows/installer/oem/Configure-Kiosk.ps1` into `C:\Windows\Setup\Scripts\`.

### At end-of-setup (`SetupComplete.cmd`, runs as SYSTEM, after accounts exist, before first logon)
`Configure-Kiosk.ps1`:
- **Shell Launcher v2** (WMI `WESL_UserSetting`, online-only — hence configured here, not
  offline): default shell = `explorer.exe`; `sm-bootstrap`'s shell = a small launcher that
  starts the Welcome app **elevated** (reuses the baked UAC auto-approve:
  `ConsentPromptBehaviorAdmin=0`). With no Explorer in that session there is **no taskbar and
  no Start menu** — the escape the operator saw is structurally gone.
- **Keyboard Filter** (WMI `WEKF_PredefinedKey` / `WEKF_Settings`): block Win, Win+L,
  Ctrl+Esc, and similar shell hotkeys; `DisableKeyboardFilterForAdministrators=false`.
- **Security-screen / escape policies**: `DisableTaskMgr=1`, `DisableLockWorkstation=1`,
  hide fast-user-switching and Log off. Applied scoped to the `sm-bootstrap` session and
  reverted at teardown (so the real user is unaffected).

### Interaction with the existing flow
- The `autounattend.xml` `FirstLogonCommands` app-launch is now **redundant and removed** —
  Shell Launcher launches the Welcome app as the session shell.
- `SetupComplete.cmd` keeps its existing "defer hardening to Welcome when the app is present"
  branch; it gains the `Configure-Kiosk.ps1` call.

### Teardown (Welcome app `ApplyService`, on wizard success)
Already deletes `sm-bootstrap` + removes AutoLogon. **Adds**: remove the `sm-bootstrap` WESL
custom-shell entry, revert the escape policies, (optionally clear Keyboard Filter rules). The
features remain enabled but inert. The real end-user account then logs in to a normal,
branded Explorer desktop.

## 6. Component C — First-boot presentation (MAUI Welcome app)

The Welcome app
([`windows/welcome/src/SilverOS.Welcome.App`](../../../welcome/src/SilverOS.Welcome.App)) is
MAUI Blazor (WebView2). Today its window is the plain default and `MainLayout` is the stock
template.

### Native — window chrome
In the Windows lifecycle handler, customize the WinUI `AppWindow`:
- `OverlappedPresenter` with border + title bar off, not resizable / minimizable / maximizable;
  use the FullScreen presenter so it covers the whole display.
- Non-closable (suppress/ignore close); Alt+F4 is additionally blocked by the Keyboard Filter.
This is the only native requirement — it removes the title bar and makes the app own the screen.

### Visual — Blazor + CSS only
The Hybrid look (full-bleed branded backdrop + centered frosted-glass card) is rendered
**entirely in the WebView with CSS** (`backdrop-filter: blur(...)` over the in-WebView wall),
exactly as the mockup demonstrates. **No OS-level Mica/Acrylic** — in a Shell-Launcher kiosk
there is no desktop behind the app to blur, so OS backdrop buys nothing.

Work: restyle `MainLayout` and the wizard step shell from the stock MAUI template to the brand
identity — branded backdrop, centered glass card, step rail, cyan/teal accents, the type and
motion direction from the SilverLABS aesthetic. The step components' logic is unchanged.

## 7. Build-flow wiring — what changes

1. `installer/build.ps1` `Invoke-Brand` → call `branding\Apply-Branding.ps1 -Mode Offline -MountPath $mount` and stage assets (inside the existing WIM-mounted block).
2. `installer/build.ps1` → new offline step: enable Shell Launcher + Keyboard Filter features; stage `Configure-Kiosk.ps1`.
3. `installer/oem/SetupComplete.cmd` → invoke `Configure-Kiosk.ps1` before first logon.
4. `installer/autounattend/autounattend.xml` → remove the `FirstLogonCommands` Welcome launch.
5. `welcome/...` → fullscreen borderless window + Hybrid CSS shell; `ApplyService` → kiosk teardown.

## 8. Testing

- **Branding module**: Pester tests for `Apply-Branding.ps1` — offline (load a throwaway hive
  into a temp mount, apply, assert each value + asset staged) and online (apply, read back,
  revert). Runs on the Windows runner; no hardware needed.
- **Kiosk + presentation**: the existing VM e2e harness (SLAB01 VM 102, `_stageiso.py` /
  `_pverun.py` / `_shot.py`). Boot the built ISO → assert: no taskbar / Start in the bootstrap
  session, Task Manager / Win+L / Ctrl+Alt+Del options blocked, the glass card is fullscreen,
  the wizard completes → real user logs into a branded Explorer desktop with wallpaper, accent,
  locked lock-screen, and correct OEM About.
- **Honest scope**: per `iso-builder.md §5`, "reproducible" = pinned inputs + recorded tool
  versions + output SHA-256 + SBOM; bit-identical rebuild stays a documented stretch goal.

## 9. Open items (resolve during implementation, not blocking)

1. Pin exact `FVE` pre-boot recovery-message value names against the base media.
2. Confirm `PersonalizationCSP` vs `Policies\…\Personalization\LockScreenImage` reliability on
   IoT Enterprise LTSC 24H2/25H2; pick the one that survives a clean OOBE.
3. Decide whether `Configure-Kiosk.ps1` sets the `sm-bootstrap` shell to the app directly
   (with `requireAdministrator` manifest) or to an elevating launcher script — pick the
   robust one during the elevation spike.
4. Final logo asset (`oemlogo.bmp`, lock-screen, wallpaper) — placeholder void/cyan mark used
   until brand identity is finalized (`shared/branding/README.md` is still "to be defined").
5. Separate follow-up: rename `SilverOS.*` app / namespace / install path to SilverMetal.
