docs(windows): first-boot experience & branding design spec
Design for SilverMetal Windows first-boot: declarative branding build (4 layers baked offline into the WIM, shared dual-mode module), hardened onboarding kiosk (Shell Launcher v2 + Keyboard Filter for the one-time sm-bootstrap session), and the Hybrid fullscreen glass-card presentation for the Welcome app. Fills the empty Invoke-Brand stub (M4 branding). Approved in brainstorming. Next: writing-plans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,6 @@
|
||||
# Brainstorming / design scratch (mockups, companion state) — durable specs live in docs/
|
||||
.superpowers/
|
||||
|
||||
# Build outputs
|
||||
build/output/
|
||||
build/cache/
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user