PrintToVeraeTimeStamp/platform/windows/wix/DUAL-SHIP.md
George Lambert 376bbe188a
Some checks failed
windows-msi / full-msi-unsigned (push) Has been cancelled
feat: dual-ship-smoke.ps1 offline VM A/B/C runner with optional artifact hashes
Prints checklist steps; -VerifyArtifacts uses fill-manifest-sha256.
2026-10-14 00:39:21 -04:00

16 KiB
Raw Blame History

Dual-ship runbook — NSIS + full MSI (side-by-side)

Status: operator validation while NSIS remains the primary full-product Windows download and the elevated MSI (Product-Full.wxs + build-full-msi.ps1) is unsigned and not yet CA-complete.

Do not thin or deprecate NSIS until the thin-NSIS exit criteria checklist (below) is fully checked — clean-VM gates, live Authenticode, silent/fleet proof, and site cutover (see COMMERCIAL-INSTALLER-BACKLOG.md, CI-SIGN-NOTARY.md).

Related: Product-Full.wxs · SILENT_PROPS.md · VM-DUAL-SHIP-CHECKLIST.md (printable A/B/C) · dual-ship-smoke.ps1 (offline checklist runner + optional local MSI/NSIS hash) · ../installer-windows.nsi · PACKAGING.md · site DOWNLOADS-MANIFEST.md (0.3.6 TBD rows) · fill-manifest-sha256.ps1 (hash built MSI/NSIS → manifest row; no upload).


Why dual-ship

Channel Role today Gap vs commercial
NSIS VeraePrintStamp-windows-*-setup.exe Primary GUI install (printers, helper task, key wizard, ARP) Not MSI; Authenticode still open
Full MSI VeraePrintStamp-windows-*-<ver>.msi Fleet path authoring (msiexec /qn props) Printer deferred CAs commented; unsigned; not Intune-proven
Tray MSI VeraeTray-*-peruser.msi Optional non-elevated presence only Not full product; distinct UpgradeCode

Dual-ship means both NSIS and full MSI may be built from the same HelperVersion tree and validated on separate clean VMs without uninstalling the other channel’s install mid-test (use two VMs or one channel per VM).


Version rule

  • Source of truth: internal/job.HelperVersion (currently 0.3.6).
  • NSIS, build-full-msi.ps1, and site product strings must stamp that value (or explicit VERSION=).
  • Site DOWNLOADS-MANIFEST.txt may still list last-published artifact filenames (historical checksums). Do not rewrite checksum lines until new binaries are published.

Build both artifacts (same tree)

From repo root on a Windows host with Go + NSIS + WiX v3 (candle/light) on PATH:

# 1) Native binaries + NSIS setup.exe
powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\build-windows.ps1

# 2) Elevated full MSI (StageFull → candle/light)
powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\build-full-msi.ps1

Expected outputs (names vary by arch):

Artifact Typical path
NSIS setup dist/ or build output VeraePrintStamp-windows-amd64-<ver>-setup.exe
Full MSI platform/windows/wix/out/VeraePrintStamp-windows-amd64-<ver>.msi

Local SHA-256 for site manifest rows (no upload):

powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\fill-manifest-sha256.ps1 `
  -Path .\dist\VeraePrintStamp-windows-amd64-<ver>-setup.exe,
        .\platform\windows\wix\out\VeraePrintStamp-windows-amd64-<ver>.msi

Paste printed digests over TBD in DOWNLOADS-MANIFEST.txt only after the blob is on the CDN. See DOWNLOADS-MANIFEST.md.

If WiX is missing, build-full-msi.ps1 exits 1 with an install tip — dual-ship fails closed for the MSI leg (do not claim MSI ready).

Cross-host NSIS-only: platform/windows/build-installer.sh (needs prebuilt Windows binaries).

Manifest checksum rows (no upload)

After artifacts exist locally (and only after they are on the CDN when rewriting site TBD):

powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\fill-manifest-sha256.ps1 `
  -Path .\dist\VeraePrintStamp-windows-amd64-<ver>-setup.exe,
        .\platform\windows\wix\out\VeraePrintStamp-windows-amd64-<ver>.msi

Prints DOWNLOADS-MANIFEST.txt lines (<sha256> <leaf>). -Format md prints Markdown table rows for DOWNLOADS-MANIFEST.md. Does not upload or edit the site files.


Clean-VM matrix (minimum)

Printable tick sheet: VM-DUAL-SHIP-CHECKLIST.md (shared pre-checks + VM A/B/C steps + side-by-side table).

Offline checklist runner (prints the same A/B/C steps; optional local artifact existence + hash via fill-manifest — does not install or drive a VM):

powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\dual-ship-smoke.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\dual-ship-smoke.ps1 -VerifyArtifacts

Use two fresh Windows 10/11 x64 VMs (or reset snapshots). Do not mix channels on one machine until Upgrade/ARP coexistence is explicitly tested.

VM Install Goal
A — NSIS primary setup.exe (GUI) Baseline product parity
B — MSI files-only MSI with VERAE_SKIP_PRINTERS=1 File + ARP path without deferred printer CA
C — MSI printers (when CAs uncommented) MSI default / VERAE_SKIP_PRINTERS=0 Printer + helper parity with NSIS

Until printer CAs are scheduled and tested, treat VM C as blocked; dual-ship gate for “MSI can replace NSIS” stays open.

Shared pre-checks

  1. Snapshot or sysprep-clean OS; admin account available.
  2. Note HelperVersion on both artifacts (NSIS version resource / MSI ProductVersion).
  3. No prior Verae Print Stamp under Program Files or ARP.
  4. Spooler running (Get-Service Spooler).

VM A — NSIS validation

1. Run setup.exe elevated; complete key wizard or skip per test plan.
2. Confirm Program Files\Verae\PrintStamp\bin\verae-print.exe + helper.
3. Confirm printers (IPP and/or Local Port) and scheduled task "Verae Print Helper".
4. Print a one-page PDF → stamp path succeeds (or hold id only — never treat jobId as evidence).
5. Start Menu: Setup signing key, Start print helper, Collect diagnostics.
6. Uninstall via ARP; confirm printers/task removed; reboot optional.

Record: install log path, printer names, task principal, any SmartScreen blocks (unsigned expected).


VM B — MSI files-only (safe today)

Printer deferred CAs are commented in Product-Full.wxs. Validate payload + silent props without expecting queues:

msiexec /i VeraePrintStamp-windows-amd64-0.3.6.msi /qn /norestart ^
  VERAE_SKIP_KEY_SETUP=1 VERAE_SKIP_PRINTERS=1 VERAE_CHANNEL=zapier

Interactive UI install is fine for smoke; /qn proves fleet syntax (SILENT_PROPS.md).

1. msiexec as above (or UI).
2. Confirm InstallDir files: bin\, scripts\ (incl. install-printer.ps1), LICENSE, README.
3. Confirm ARP entry (Publisher Verae, DisplayVersion = HelperVersion).
4. Confirm Start Menu shortcuts resolve to helper / diag (may fail if helper missing — fail the build).
5. Optional: elevated manual run of scripts\install-printer.ps1 -InstallDir "…" to prove script parity outside MSI CA.
6. msiexec /x {ProductCode} /qn — files and Start Menu removed; no orphan HKLM Software\Verae\PrintStamp if components clean.

Pass criteria (files-only): light-built MSI installs/uninstalls cleanly; version matches; no reboot required; props accepted (even if not yet honored by CAs).

Fail: missing StageFull binaries at light time; wrong UpgradeCode collision with tray MSI; elevation refused unexpectedly.


VM C — MSI printer CA (still left)

When uncommenting CAs in Product-Full.wxs:

  1. Schedule stubs (see commented InstallExecuteSequence in Product-Full) after InstallFiles.
  2. Conditions: NOT Installed and VERAE_SKIP_PRINTERS<>"1" for install; REMOVE~="ALL" for uninstall.
  3. Deferred elevated: install-printer.ps1 -InstallDir "[INSTALLFOLDER]"; rollback/uninstall → uninstall-printer.ps1.
  4. Re-run dual-ship A vs C parity: same printers, same helper task name, same IPP loopback 127.0.0.1:8631.
  5. Silent: VERAE_SKIP_PRINTERS=1 must skip CA; default must install printers.

Do not mark dual-ship complete until VM C matches VM A on a clean image.


Side-by-side comparison checklist

Check NSIS (A) MSI files (B) MSI+CA (C)
Version = HelperVersion ☐ ☐ ☐
Program Files layout ☐ ☐ ☐
Start Menu shortcuts ☐ ☐ ☐
ARP uninstall clean ☐ ☐ ☐
IPP + Local Port printers ☐ N/A (skip) ☐
Scheduled task helper ☐ N/A (skip) ☐
Key wizard / first-run key ☐ skip prop ☐ / first-run
msiexec /qn N/A ☐ ☐
Authenticode (signtool) ☐ still left ☐ still left ☐ still left

Coexistence / upgrade notes

  • Full MSI UpgradeCode F8B2A4C6-… must stay distinct from tray A7C3E91B-….
  • NSIS uses its own UNINST_KEY; long-term migrate ARP to MSI only after dual-ship pass — avoid dual ARP entries for the “same” product on one machine in production.
  • Do not install tray per-user MSI and full MSI as substitutes for each other; tray is presence-only.
  • MajorUpgrade on full MSI: same-version upgrades allowed (AllowSameVersionUpgrades=yes); test repair after dual-ship CA enablement.

Exit: when to thin NSIS (MSI becomes primary)

Thin or demote NSIS to “key-wizard offline only” (or drop from primary download) only when every box below is checked. Partial passes do not promote MSI. Until then: ship NSIS as primary; MSI is dual-ship candidate only.

Thin-NSIS exit criteria checklist

A — Product parity (clean VMs)

  • VM A NSIS baseline signed off (files, printers, helper task, key path, ARP uninstall).
  • VM B MSI files-only (VERAE_SKIP_PRINTERS=1) install/uninstall clean; version = HelperVersion.
  • VM C MSI with printer/helper deferred CAs uncommented, scheduled, and tested — parity with VM A:
    • Same Program Files layout (bin\, scripts\, docs).
    • IPP loopback 127.0.0.1:8631 and/or Local Port printers match NSIS names/behavior.
    • Scheduled task Verae Print Helper (same principal / trigger class).
    • Start Menu shortcuts resolve; one-page PDF stamp path works (receipt/CID evidence — never jobId alone).
    • VERAE_SKIP_PRINTERS=1 skips CAs; default installs printers.
    • Uninstall (msiexec /x) removes printers, task, files, Start Menu; no orphan HKLM Software\Verae\PrintStamp.
  • Side-by-side comparison table (this doc) fully checked for columns A and C.
  • Repair / same-version upgrade (AllowSameVersionUpgrades) smoke on MSI after CA enablement.

B — Trust / signing

  • Live Authenticode on verae-print.exe, verae-printhelper.exe, and full .msi (CI or release host).
  • CI sign step enabled only with org secrets — never PFX/passwords in git (names in CI-SIGN-NOTARY.md / windows-msi.yml comments).
  • SmartScreen: signed install path validated on a clean VM (warn-only unsigned is not enough to promote).
  • Timestamp counter-signature present (WINDOWS_SIGN_TIMESTAMP_URL / equivalent TSA).

C — Fleet / silent contract

  • Public silent props in SILENT_PROPS.md honored by scheduled CAs (not docs-only):
    • VERAE_SKIP_PRINTERS, VERAE_SKIP_KEY_SETUP, channel / API URL props as authored.
  • msiexec /qn /norestart one-liner documented with real exit codes from clean-VM runs.
  • Intune and/or SCCM sample deployment recorded (success + known failure codes); no reboot required for default path unless documented.
  • UpgradeCode stays distinct from tray MSI; no dual “same product” ARP entries in production guidance.

D — Channel / site cutover

  • Site primary Windows download points at signed full MSI (VeraePrintStamp-windows-*-<ver>.msi).
  • NSIS setup.exe linked as alternate only (or removed from primary CTA) — not the default button.
  • DOWNLOADS-MANIFEST.txt checksums refreshed for the promoted MSI (and remaining NSIS if kept) — use fill-manifest-sha256.ps1 after upload (script does not upload).
  • Release notes / PACKAGING state MSI as primary full-product Windows install.
  • OPEN-ISSUES Issue 1 Still left no longer lists dual-ship gate / thin-NSIS / “NSIS still primary”.

E — Explicit non-goals until checked

Do not thin NSIS if any of these remain true:

  • Printer CAs still commented or untested (VM C blocked).
  • CI only uploads unsigned MSI (VeraePrintStamp-windows-msi-unsigned).
  • Operators still need NSIS for key wizard with no MSI-equivalent first-run path.
  • Tray per-user MSI is being mistaken for full-product MSI.

When all of A–D are checked: demote NSIS (thin installer or archive-only), keep dual-ship docs as historical validation, and treat full MSI as the commercial Windows path.


CI artifact (unsigned full MSI)

GitHub Actions workflow .github/workflows/windows-msi.yml builds the elevated full MSI on windows-latest when WiX v3 (candle/light) is available:

  1. Checkout + Setup Go (go-version-file: go.mod).
  2. Install WiX Toolset v3 (Chocolatey) and put candle/light on PATH.
  3. Run platform/windows/scripts/build-full-msi.ps1 -Arch amd64 (StageFull → candle/light).
  4. Upload unsigned artifact VeraePrintStamp-windows-msi-unsigned (platform/windows/wix/out/VeraePrintStamp-windows-*.msi).
Fact Detail
Trigger workflow_dispatch; push/PR on Windows packaging + Go paths
Signed? No — artifact is dual-ship candidate only; SmartScreen will warn
Use for Clean-VM B (files-only) smoke; CI proof that authoring lights
Not for Production download, Intune primary, or “MSI replaces NSIS” claims

Future sign step (names only — not enabled): org secrets/vars documented in the workflow comments and CI-SIGN-NOTARY.md:

Name Kind
WINDOWS_CERT_PFX_BASE64 secret
WINDOWS_CERT_PFX_PASSWORD secret
WINDOWS_CERT_THUMBPRINT secret (optional)
WINDOWS_SIGN_TIMESTAMP_URL var
WINDOWS_TRUSTED_SIGNING_* secret (optional Azure path)

Do not put PFX bytes or passwords in git. Do not upload the PFX as a workflow artifact. Until sign is live, treat CI MSI like a local build-full-msi.ps1 output for dual-ship gates only.


Doc Path
Full MSI authoring Product-Full.wxs
Stage/light driver ../scripts/build-full-msi.ps1
CI unsigned MSI workflow ../../../.github/workflows/windows-msi.yml
Silent properties SILENT_PROPS.md
Printable VM A/B/C checklist VM-DUAL-SHIP-CHECKLIST.md
Offline dual-ship smoke (print + optional hash) ../scripts/dual-ship-smoke.ps1
Sign/notary draft CI-SIGN-NOTARY.md
Menubar CI notary example ../../macos/menubar/ci-notarize.yml.example
Site 0.3.6 placeholders DOWNLOADS-MANIFEST.md · DOWNLOADS-MANIFEST.txt
Local SHA-256 → manifest rows ../scripts/fill-manifest-sha256.ps1 (hash only; no upload)
Commercial backlog docs/COMMERCIAL-INSTALLER-BACKLOG.md