diff --git a/docs/COMMERCIAL-INSTALLER-BACKLOG.md b/docs/COMMERCIAL-INSTALLER-BACKLOG.md index f32fece..46620d5 100644 --- a/docs/COMMERCIAL-INSTALLER-BACKLOG.md +++ b/docs/COMMERCIAL-INSTALLER-BACKLOG.md @@ -15,8 +15,9 @@ Repo now includes **authoring/docs scaffolds** for the two commercial gaps that | Scaffold | Path | What it is | What it is not | |----------|------|------------|----------------| | **WiX tray presence MSI skeleton** | [`platform/windows/wix/`](../platform/windows/wix/) — `VeraeTrayShortcuts.wxs`, `VeraeTrayShortcutsFragment.wxs`, [`README.md`](../platform/windows/wix/README.md); driver [`scripts/build-tray-msi.ps1`](../platform/windows/scripts/build-tray-msi.ps1) | Non-elevated **per-user** WiX v3 product + shortcut fragment: stages `verae-tray.ps1`, Start Menu `Programs\Verae\Verae Tray.lnk`, Startup `Verae Tray.lnk`. **ProductVersion** wired from `Get-VeraePackageVersion` → `internal/job.HelperVersion` (or `VERSION=` / `-Version`). `build-tray-msi.ps1` runs **candle/light** when present; otherwise prints WiX install tip (exit 0). | Full elevated commercial MSI (printers, Program Files, Authenticode, NSIS parity, CI-signed MSI in `dist/`). No MSI binary without the WiX toolset on the build host. | -| **Menubar notarize checklist** | [`platform/macos/menubar/NOTARIZE-CHECKLIST.md`](../platform/macos/menubar/NOTARIZE-CHECKLIST.md) | Operator step list: unsigned build → codesign hardened runtime → `ditto` zip → `notarytool` (keychain profile **or** API key placeholders) → staple → `spctl`. No secrets. | Automated sign/notarize in `build-menubar-app.sh`, stapled `.app.zip`/DMG release, or CI keychain import. | -| **Full elevated MSI (StageFull + light)** | [`Product-Full.wxs`](../platform/windows/wix/Product-Full.wxs) + [`scripts/build-full-msi.ps1`](../platform/windows/scripts/build-full-msi.ps1) (+ outline, [`SILENT_PROPS.md`](../platform/windows/wix/SILENT_PROPS.md), [`CI-SIGN-NOTARY.md`](../platform/windows/wix/CI-SIGN-NOTARY.md), [`DUAL-SHIP.md`](../platform/windows/wix/DUAL-SHIP.md)) | Elevated WiX v3 product: perMachine, Feature/ComponentGroups for **bin** (`verae-print.exe` required), **scripts**, **docs**, Start Menu; silent public props; distinct UpgradeCode. `build-full-msi.ps1` creates `StageFull/{bin,scripts}`, copies or Go-builds EXEs, copies scripts/LICENSE/README, stamps **ProductVersion** from `Get-VeraePackageVersion`, runs candle/light → `VeraePrintStamp-windows-*-.msi`. WiX missing → **exit 1** + clear tip. Version defaults on build-windows / build-pkg / NSIS / build-installer aligned to HelperVersion. Commented deferred CA + `InstallExecuteSequence` stubs for `install-printer.ps1` (no binaries required). Dual-ship runbook documents NSIS vs MSI clean-VM gates. | **Not** Authenticode-signed; printer CAs still **commented** (not scheduled/tested); properties not yet honored by live CAs; not Intune-proven; NSIS still primary full install; CI builds **unsigned** MSI only (sign step not live). | +| **Menubar notarize checklist** | [`platform/macos/menubar/NOTARIZE-CHECKLIST.md`](../platform/macos/menubar/NOTARIZE-CHECKLIST.md) | Operator step list: unsigned build → codesign hardened runtime → `ditto` zip → `notarytool` (keychain profile **or** API key placeholders) → staple → `spctl`. No secrets. | Automated sign/notarize in `build-menubar-app.sh`, stapled `.app.zip`/DMG release, or live CI keychain import. | +| **Menubar CI notary sketch** | [`platform/macos/menubar/ci-notarize.yml.example`](../platform/macos/menubar/ci-notarize.yml.example) | GHA/Forgejo **example** workflow: default **unsigned** `build-menubar-app.sh` + artifact; commented notarytool job gated `if: false`; secret **names** only (`APPLE_API_KEY_*`, `APPLE_CODESIGN_IDENTITY`, …). | Not a live workflow under `.github/`; no staple; not Gatekeeper-proven. | +| **Full elevated MSI (StageFull + light)** | [`Product-Full.wxs`](../platform/windows/wix/Product-Full.wxs) + [`scripts/build-full-msi.ps1`](../platform/windows/scripts/build-full-msi.ps1) (+ outline, [`SILENT_PROPS.md`](../platform/windows/wix/SILENT_PROPS.md), [`CI-SIGN-NOTARY.md`](../platform/windows/wix/CI-SIGN-NOTARY.md), [`DUAL-SHIP.md`](../platform/windows/wix/DUAL-SHIP.md)) | Elevated WiX v3 product: perMachine, Feature/ComponentGroups for **bin** (`verae-print.exe` required), **scripts**, **docs**, Start Menu; silent public props; distinct UpgradeCode. `build-full-msi.ps1` creates `StageFull/{bin,scripts}`, copies or Go-builds EXEs, copies scripts/LICENSE/README, stamps **ProductVersion** from `Get-VeraePackageVersion`, runs candle/light → `VeraePrintStamp-windows-*-.msi`. WiX missing → **exit 1** + clear tip. Version defaults on build-windows / build-pkg / NSIS / build-installer aligned to HelperVersion. Commented deferred CA + `InstallExecuteSequence` stubs for `install-printer.ps1` (no binaries required). Dual-ship runbook documents NSIS vs MSI clean-VM gates + **thin-NSIS exit checklist**. | **Not** Authenticode-signed; printer CAs still **commented** (not scheduled/tested); properties not yet honored by live CAs; not Intune-proven; NSIS still primary full install; CI builds **unsigned** MSI only (sign step not live). | | **CI unsigned full MSI** | [`.github/workflows/windows-msi.yml`](../.github/workflows/windows-msi.yml) + DUAL-SHIP § CI artifact | `windows-latest`: install WiX v3 → `build-full-msi.ps1` → upload artifact `VeraePrintStamp-windows-msi-unsigned`. Future Authenticode secret **names** only in workflow comments / CI-SIGN-NOTARY. | No live `signtool`; no certs in git; not a production download channel. | ### Issue 1 — what landed vs still open @@ -27,11 +28,12 @@ Repo now includes **authoring/docs scaffolds** for the two commercial gaps that | `build-full-msi.ps1` StageFull + ProductVersion + exit codes | Live CI: **signtool / Trusted Signing** on MSI + EXEs (workflow sign step commented; secret names only) | | `CI-SIGN-NOTARY.md` GHA/Forgejo draft (secret **names** only) | Live notarytool staple on macOS runners; clean-VM Gatekeeper proof | | `SILENT_PROPS.md` msiexec one-liner + exit codes for full MSI | Properties honored by **scheduled** CAs (config write, skip printers/key) | -| HelperVersion path on build-windows / build-pkg / NSIS / build-installer / full MSI | Dual-ship **clean-VM validation** then thin NSIS; Intune silent smoke | -| Commented CA + `InstallExecuteSequence` stubs (`install-printer` schedule) | Publish 0.3.6 download blobs + refresh `DOWNLOADS-MANIFEST` checksums | -| [`DUAL-SHIP.md`](../platform/windows/wix/DUAL-SHIP.md) NSIS+MSI side-by-side + **CI artifact** section | macOS menubar automated notarize (checklist exists; script default still unsigned) | -| Site product string → **0.3.6**; manifest header notes HelperVersion vs last-published 0.3.2 | Do **not** claim Authenticode / notary / dual-ship complete | +| HelperVersion path on build-windows / build-pkg / NSIS / build-installer / full MSI | Dual-ship **clean-VM validation** (A/B/C); then execute thin-NSIS exit checklist | +| Commented CA + `InstallExecuteSequence` stubs (`install-printer` schedule) | Intune/SCCM silent smoke with real exit codes; publish 0.3.6 signed blobs + manifest checksums | +| [`DUAL-SHIP.md`](../platform/windows/wix/DUAL-SHIP.md) NSIS+MSI + CI artifact + **thin-NSIS exit checklist** | Site primary download still NSIS; thin/demote NSIS only after exit checklist A–D | +| Site product string → **0.3.6**; manifest header notes HelperVersion vs last-published 0.3.2 | Do **not** claim Authenticode / notary / dual-ship / thin-NSIS complete | | [`.github/workflows/windows-msi.yml`](../.github/workflows/windows-msi.yml) unsigned MSI on `windows-latest` + artifact upload | Wire org secrets and enable sign step; publish signed assets to site | +| [`ci-notarize.yml.example`](../platform/macos/menubar/ci-notarize.yml.example) menubar GHA/Forgejo sketch (unsigned default; secret names) | Promote example → live workflow; automate `build-menubar-app.sh` sign when `SIGNING_ENV` set | **Still use for day-to-day presence (no WiX / no Apple certs):** Windows `scripts/build-tray-startmenu.ps1` + `install-tray-startup.ps1`; macOS `verae-print menubar --install` / unsigned `build-menubar-app.sh`. Full product install remains NSIS (`installer-windows.nsi`) and macOS `pkg/build-pkg.sh`. Optional: `build-tray-msi.ps1` (WiX tip exit 0); `build-full-msi.ps1` (needs WiX; exit 1 if missing). @@ -66,7 +68,7 @@ Related but out of scope for “done” above (still backlog-adjacent): Linux `. | **WiX tray presence skeleton** (per-user, non-elevated) | [`platform/windows/wix/`](../platform/windows/wix/) + [`scripts/build-tray-msi.ps1`](../platform/windows/scripts/build-tray-msi.ps1) | **Scaffold shipped** — ProductVersion ← HelperVersion; candle/light when present, else WiX tip; not full product MSI | | **WiX full elevated MSI** (StageFull driver) | [`Product-Full.wxs`](../platform/windows/wix/Product-Full.wxs) + [`scripts/build-full-msi.ps1`](../platform/windows/scripts/build-full-msi.ps1) | **Authoring + stage/light path** — MSI when WiX present; unsigned; printer CAs commented stubs; dual-ship NSIS | | **CI unsigned full MSI** | [`.github/workflows/windows-msi.yml`](../.github/workflows/windows-msi.yml) | **Live workflow** — WiX install → `build-full-msi.ps1` → unsigned artifact; sign step commented (secret names only) | -| **Dual-ship runbook** | [`wix/DUAL-SHIP.md`](../platform/windows/wix/DUAL-SHIP.md) | **Docs** — NSIS + MSI clean-VM matrix; CI artifact section; thin-NSIS exit criteria | +| **Dual-ship runbook** | [`wix/DUAL-SHIP.md`](../platform/windows/wix/DUAL-SHIP.md) | **Docs** — NSIS + MSI clean-VM matrix; CI artifact section; **thin-NSIS exit checklist** (A–E) | | Shared version helper | [`platform/windows/scripts/common.ps1`](../platform/windows/scripts/common.ps1) `Get-VeraePackageVersion` | Aligns tray/full MSI/NSIS drivers with `HelperVersion` | | End-user Windows readme | [`platform/windows/README-WINDOWS.txt`](../platform/windows/README-WINDOWS.txt), [`README.txt`](../platform/windows/README.txt) | Documents NSIS flow; states **no elevated MSI** for tray | | Packaging policy doc | [PACKAGING.md](PACKAGING.md) | “**No elevated MSI**” for tray presence by design | @@ -99,7 +101,7 @@ NSIS product already covers much of the *install experience* (files under Progra 5. Authenticode on binaries + MSI (**not shipped** — enable commented sign step; secret names in workflow + `CI-SIGN-NOTARY.md`). 6. Honor silent props in live CAs + Intune sample command line proof. 7. Publish 0.3.6 artifacts; refresh site `DOWNLOADS-MANIFEST` checksums; keep smoke on HelperVersion drift. -8. Deprecate or thin NSIS once MSI is the primary download (exit criteria in DUAL-SHIP.md). +8. Deprecate or thin NSIS once MSI is the primary download (**thin-NSIS exit checklist** A–D in DUAL-SHIP.md — all boxes). --- @@ -143,7 +145,7 @@ NSIS product already covers much of the *install experience* (files under Progra 2. Decide distribution primary: **`.pkg` only** (already closest to done) vs **self-contained notarized `.app`** (+ optional DMG). 3. If `.app` primary: bundle helper + resources inside the app; first-run equals today’s postinstall responsibilities without root where possible. 4. Automate menubar sign/notarize as accessory artifact (or embed menubar in product app) — **not shipped** as automated path. -5. CI: notarize + staple + `spctl` + checksums; never commit `signing.env`. +5. CI: promote [`ci-notarize.yml.example`](../platform/macos/menubar/ci-notarize.yml.example) → live notarize + staple + `spctl` + checksums; never commit `signing.env`. 6. Optional: DMG, Sparkle. --- @@ -169,7 +171,8 @@ NSIS product already covers much of the *install experience* (files under Progra | Windows NSIS `setup.exe` | Yes | **Partial** — full GUI install; needs Authenticode + version align; not MSI | | Windows **MSI** (WiX) tray presence skeleton | **Yes** (`.wxs` + `build-tray-msi.ps1`; ProductVersion ← HelperVersion) | **No** — scaffold only; MSI only when candle/light installed; not full product MSI | | Windows **MSI** full product (files + ARP) | **Yes authoring** (`Product-Full.wxs` + `build-full-msi.ps1` StageFull → light; CA schedule stubs commented) | **No commercial** — unsigned; printer CAs not live; NSIS still primary | -| Dual-ship NSIS↔MSI runbook | **Yes** ([`DUAL-SHIP.md`](../platform/windows/wix/DUAL-SHIP.md)) | **Docs only** — clean-VM validation + thin-NSIS still left | +| Dual-ship NSIS↔MSI runbook | **Yes** ([`DUAL-SHIP.md`](../platform/windows/wix/DUAL-SHIP.md)) | **Docs only** — clean-VM validation + thin-NSIS exit checklist still unchecked | +| Menubar CI notary example | **Yes** ([`ci-notarize.yml.example`](../platform/macos/menubar/ci-notarize.yml.example)) | **Example only** — unsigned default; notary job `if: false` | | Windows Authenticode pipeline | **Draft doc** (`wix/CI-SIGN-NOTARY.md`) | **No** — no live signtool job (**do not claim shipped**) | | macOS menu bar LaunchAgent | Yes | Yes (dev/ops) | | macOS unsigned menubar `.app` | Yes | **No** (Gatekeeper) | @@ -192,6 +195,7 @@ NSIS product already covers much of the *install experience* (files under Progra - [`platform/macos/pkg/`](../platform/macos/pkg/) — commercial `.pkg` build - [`platform/macos/menubar/`](../platform/macos/menubar/) — menu bar + unsigned `.app` + notarize docs/checklist - [`platform/macos/menubar/NOTARIZE-CHECKLIST.md`](../platform/macos/menubar/NOTARIZE-CHECKLIST.md) — menubar notarytool step list +- [`platform/macos/menubar/ci-notarize.yml.example`](../platform/macos/menubar/ci-notarize.yml.example) — GHA/Forgejo menubar CI sketch (unsigned default; secret names only) - [`docs/installer-registration-api.md`](installer-registration-api.md) — installer ↔ portal registration API - [`README.md`](../README.md) — top-level packaging pointers diff --git a/platform/macos/menubar/NOTARIZE-CHECKLIST.md b/platform/macos/menubar/NOTARIZE-CHECKLIST.md index 114bc5e..fe3e994 100644 --- a/platform/macos/menubar/NOTARIZE-CHECKLIST.md +++ b/platform/macos/menubar/NOTARIZE-CHECKLIST.md @@ -149,12 +149,15 @@ spctl --assess --type execute -vv "$APP" ## Automation gap (still backlog) +- [x] CI workflow **example** (unsigned default; notary secret **names** only, gated off): + [`ci-notarize.yml.example`](ci-notarize.yml.example) — copy into `.github/workflows/` or + `.forgejo/workflows/` when enabling; do not treat the example as a live release job - [ ] Wire `build-menubar-app.sh` (or shared `sign-and-notarize.sh`) to run the above when `SIGNING_ENV` / CI secrets exist; keep **unsigned default** for dev -- [ ] CI macOS runner: import Developer ID + notary key → staple → upload checksums +- [ ] Promote example → live CI macOS runner: import Developer ID + notary key → staple → checksums - [ ] Smoke: `spctl` + `stapler validate` on signed hosts only -Until those land, this checklist is the operator runbook. +Until live CI + script automation land, this checklist is the operator runbook. --- diff --git a/platform/macos/menubar/README.txt b/platform/macos/menubar/README.txt index 04616b5..95f61ce 100644 --- a/platform/macos/menubar/README.txt +++ b/platform/macos/menubar/README.txt @@ -49,6 +49,10 @@ It does not apply codesign itself and never embeds secrets. Codesign + notarization (document only — needs Apple certs) ----------------------------------------------------------- +Operator steps: NOTARIZE-CHECKLIST.md +CI sketch (unsigned default; notary secret names only): ci-notarize.yml.example + Copy into .github/workflows or .forgejo/workflows when enabling; not live. + You need an Apple Developer Program membership and a Developer ID Application certificate in the keychain. This project does not ship secrets or signing.env values. When certs are available: diff --git a/platform/macos/menubar/ci-notarize.yml.example b/platform/macos/menubar/ci-notarize.yml.example new file mode 100644 index 0000000..1a51d11 --- /dev/null +++ b/platform/macos/menubar/ci-notarize.yml.example @@ -0,0 +1,109 @@ +# EXAMPLE ONLY — not a live workflow. +# Copy into .github/workflows/ or .forgejo/workflows/ and enable when Apple certs exist. +# Default job path is UNSIGNED (build-menubar-app.sh). Sign/notary steps stay gated off. +# +# Secret / var NAMES only — never commit .p8, .p12, passwords, or real Team IDs as values. +# See NOTARIZE-CHECKLIST.md and platform/windows/wix/CI-SIGN-NOTARY.md. +# +# GitHub Actions: secrets.* / vars.* as below. +# Forgejo Actions: same secret names; map runs-on to your macOS label; keep names identical. +# +# name: macos-menubar-notarize +# on: +# workflow_dispatch: +# # push: +# # tags: ["printstamp-v*"] + +name: macos-menubar-ci-example + +on: + workflow_dispatch: + +permissions: + contents: read + +jobs: + menubar-unsigned-default: + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build unsigned menubar .app + working-directory: platform/macos/menubar + run: | + set -euo pipefail + # Default product path: no codesign, no notarytool, no secrets. + ./build-menubar-app.sh "${RUNNER_TEMP}/Verae Menubar.app" + test -d "${RUNNER_TEMP}/Verae Menubar.app" + echo "Unsigned app at ${RUNNER_TEMP}/Verae Menubar.app" + cat "${RUNNER_TEMP}/Verae Menubar.app/Contents/Resources/VERSION" || true + + - name: Upload unsigned .app zip (dev/CI smoke only) + if: ${{ true }} + run: | + set -euo pipefail + APP="${RUNNER_TEMP}/Verae Menubar.app" + ZIP="${RUNNER_TEMP}/VeraeMenubar-unsigned.zip" + ditto -c -k --keepParent "$APP" "$ZIP" + echo "UNSIGNED_ZIP=$ZIP" >> "$GITHUB_ENV" + # Forgejo: write GITHUB_ENV-equivalent or upload path directly. + + - name: Upload artifact (unsigned) + uses: actions/upload-artifact@v4 + with: + name: VeraeMenubar-macos-unsigned + path: ${{ env.UNSIGNED_ZIP }} + if-no-files-found: warn + retention-days: 14 + + # --- Future: Developer ID + notarytool (NOT enabled) --- + # Enable only when org secret store holds the names below. Keep unsigned job as default. + # Secret / var names (placeholders — values never in git): + # secrets.APPLE_API_KEY_P8_BASE64 # base64 of AuthKey_*.p8 + # secrets.APPLE_API_KEY_ID + # secrets.APPLE_API_ISSUER_ID + # secrets.APPLE_TEAM_ID + # secrets.APPLE_CODESIGN_IDENTITY # "Developer ID Application: … (TEAMID)" + # secrets.APPLE_INSTALLER_IDENTITY # optional; product .pkg path, not menubar + # vars.NOTARY_KEYCHAIN_PROFILE # optional laptop path name; CI prefers API key trio + # + # menubar-notarize: + # if: ${{ false }} # flip only after secrets exist; do not ship Gatekeeper claims until green + # runs-on: macos-latest + # steps: + # - uses: actions/checkout@v4 + # - name: Materialize notary API key + # env: + # APPLE_API_KEY_P8_BASE64: ${{ secrets.APPLE_API_KEY_P8_BASE64 }} + # APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} + # run: | + # # mkdir -p "$RUNNER_TEMP/private_keys" + # # echo "$APPLE_API_KEY_P8_BASE64" | base64 -d > \ + # # "$RUNNER_TEMP/private_keys/AuthKey_${APPLE_API_KEY_ID}.p8" + # # chmod 600 "$RUNNER_TEMP/private_keys/AuthKey_${APPLE_API_KEY_ID}.p8" + # # Never upload .p8 as an artifact; delete in always() step. + # echo "TODO: materialize .p8 outside workspace" + # - name: Build + codesign + notarytool + staple + # env: + # APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} + # APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }} + # APPLE_CODESIGN_IDENTITY: ${{ secrets.APPLE_CODESIGN_IDENTITY }} + # APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + # NOTARY_KEY_PATH: ${{ runner.temp }}/private_keys/AuthKey_${{ secrets.APPLE_API_KEY_ID }}.p8 + # run: | + # set -euo pipefail + # # 1) ./build-menubar-app.sh "$RUNNER_TEMP/Verae Menubar.app" + # # 2) codesign --force --deep --options runtime --timestamp \ + # # --entitlements … --sign "$APPLE_CODESIGN_IDENTITY" "$APP" + # # 3) ditto -c -k --keepParent "$APP" /tmp/VeraeMenubar.zip + # # 4) xcrun notarytool submit /tmp/VeraeMenubar.zip \ + # # --key "$NOTARY_KEY_PATH" --key-id "$APPLE_API_KEY_ID" \ + # # --issuer "$APPLE_API_ISSUER_ID" --wait + # # 5) xcrun stapler staple "$APP" && xcrun stapler validate "$APP" + # # 6) spctl --assess --type execute -vv "$APP" + # # Operator detail: NOTARIZE-CHECKLIST.md + # echo "TODO: notary path not enabled — unsigned default only" + # - name: Scrub API key material + # if: always() + # run: rm -rf "${RUNNER_TEMP}/private_keys" || true diff --git a/platform/windows/wix/CI-SIGN-NOTARY.md b/platform/windows/wix/CI-SIGN-NOTARY.md index 147e77a..848a001 100644 --- a/platform/windows/wix/CI-SIGN-NOTARY.md +++ b/platform/windows/wix/CI-SIGN-NOTARY.md @@ -178,7 +178,8 @@ jobs: echo "TODO: fail job on assess failure once pipeline is real" ``` -Menubar-only accessory: follow [`NOTARIZE-CHECKLIST.md`](../../macos/menubar/NOTARIZE-CHECKLIST.md) (`ditto` zip → `notarytool` → staple app). +Menubar-only accessory: follow [`NOTARIZE-CHECKLIST.md`](../../macos/menubar/NOTARIZE-CHECKLIST.md) (`ditto` zip → `notarytool` → staple app). +GHA/Forgejo copy-paste sketch (unsigned default, notary job gated off): [`ci-notarize.yml.example`](../../macos/menubar/ci-notarize.yml.example). --- diff --git a/platform/windows/wix/DUAL-SHIP.md b/platform/windows/wix/DUAL-SHIP.md index ac9cd6f..b8f387b 100644 --- a/platform/windows/wix/DUAL-SHIP.md +++ b/platform/windows/wix/DUAL-SHIP.md @@ -2,7 +2,7 @@ **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 this runbook’s clean-VM gates pass and Authenticode is live (see [COMMERCIAL-INSTALLER-BACKLOG.md](../../../docs/COMMERCIAL-INSTALLER-BACKLOG.md), [CI-SIGN-NOTARY.md](CI-SIGN-NOTARY.md)). +**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](../../../docs/COMMERCIAL-INSTALLER-BACKLOG.md), [CI-SIGN-NOTARY.md](CI-SIGN-NOTARY.md)). **Related:** [Product-Full.wxs](Product-Full.wxs) · [SILENT_PROPS.md](SILENT_PROPS.md) · [../installer-windows.nsi](../installer-windows.nsi) · [PACKAGING.md](../../../docs/PACKAGING.md). @@ -154,17 +154,59 @@ Do **not** mark dual-ship complete until VM C matches VM A on a clean image. --- -## Exit: when to thin NSIS +## Exit: when to thin NSIS (MSI becomes primary) -Thin or demote NSIS to “key-wizard offline only” only when **all** are true: +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. -1. VM C printer/helper parity signed off. -2. Live Authenticode on EXE + MSI (CI secrets; never in git). -3. Silent props honored by scheduled CAs; Intune/SCCM one-liner documented with real exit codes. -4. Site primary Windows download points at MSI; NSIS linked as alternate if kept. -5. OPEN-ISSUES Issue 1 “Still left” no longer lists dual-ship/thin NSIS. +### Thin-NSIS exit criteria checklist -Until then: **ship NSIS as primary**; MSI is dual-ship candidate only. +#### 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](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](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-*-.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). +- [ ] 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. --- @@ -207,4 +249,5 @@ Do **not** put PFX bytes or passwords in git. Do not upload the PFX as a workflo | CI unsigned MSI workflow | [../../../.github/workflows/windows-msi.yml](../../../.github/workflows/windows-msi.yml) | | Silent properties | [SILENT_PROPS.md](SILENT_PROPS.md) | | Sign/notary draft | [CI-SIGN-NOTARY.md](CI-SIGN-NOTARY.md) | +| Menubar CI notary example | [../../macos/menubar/ci-notarize.yml.example](../../macos/menubar/ci-notarize.yml.example) | | Commercial backlog | [docs/COMMERCIAL-INSTALLER-BACKLOG.md](../../../docs/COMMERCIAL-INSTALLER-BACKLOG.md) |