diff --git a/docs/COMMERCIAL-INSTALLER-BACKLOG.md b/docs/COMMERCIAL-INSTALLER-BACKLOG.md index 14ddb40..59d2fee 100644 --- a/docs/COMMERCIAL-INSTALLER-BACKLOG.md +++ b/docs/COMMERCIAL-INSTALLER-BACKLOG.md @@ -8,6 +8,20 @@ Day-to-day packaging (tray, menu bar, version stamps, CERT-01, IPP GET vs POST) --- +## Scaffold shipped (this leftover) + +Repo now includes **authoring/docs scaffolds** for the two commercial gaps that were still empty trees. These are **not** shipping-grade release artifacts. + +| 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) | Non-elevated **per-user** WiX v3 product + shortcut fragment: stages `verae-tray.ps1`, Start Menu `Programs\Verae\Verae Tray.lnk`, Startup `Verae Tray.lnk`. Build when **candle/light** (or adapted WiX v4) are present. | Full elevated commercial MSI (printers, Program Files, Authenticode, NSIS parity, CI-signed MSI in `dist/`). No MSI binary is produced in-repo without the WiX toolset. | +| **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. | + +**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`. + +--- + ## Goals (definition of done) | Platform | Ship artifact | Done when | @@ -33,6 +47,7 @@ Related but out of scope for “done” above (still backlog-adjacent): Linux `. | Tray NotifyIcon + GET `/status` | [`platform/windows/scripts/verae-tray.ps1`](../platform/windows/scripts/verae-tray.ps1) | User-level; see [PACKAGING.md](PACKAGING.md) § Windows tray | | Startup-folder tray install | [`platform/windows/scripts/install-tray-startup.ps1`](../platform/windows/scripts/install-tray-startup.ps1) | **Not** MSI; no elevation | | WiX-free Start Menu stub | [`platform/windows/scripts/build-tray-startmenu.ps1`](../platform/windows/scripts/build-tray-startmenu.ps1) | Explicitly “future MSI can supersede” | +| **WiX tray presence skeleton** (per-user, non-elevated) | [`platform/windows/wix/`](../platform/windows/wix/) (`VeraeTrayShortcuts.wxs` + fragment + README) | **Scaffold shipped** — Start Menu + Startup → `verae-tray.ps1` only; needs candle/light to emit MSI | | Shared version helper | [`platform/windows/scripts/common.ps1`](../platform/windows/scripts/common.ps1) `Get-VeraePackageVersion` | Aligns tray scripts 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 | @@ -45,7 +60,7 @@ NSIS product already covers much of the *install experience* (files under Progra | Gap | Why it matters | Suggested work | |-----|----------------|----------------| -| **No WiX project** (no `.wxs` / `.wixproj`, no `candle`/`light` or WiX v4 `wix build`) | Enterprise wants MSI, not only `setup.exe` | Author WiX that installs the same payload NSIS stages: `verae-print.exe`, helper, scripts, LICENSE, printers custom action | +| **No full-product WiX project** (tray skeleton only under `platform/windows/wix/`) | Enterprise wants MSI with same payload as NSIS, not only `setup.exe` or tray shortcuts | Extend or replace tray skeleton with WiX that installs NSIS surface: `verae-print.exe`, helper, scripts, LICENSE, printers custom action | | **No MSI output artifact** in `dist/` or CI | Nothing to hand Intune/SCCM | Produce `VeraePrintStamp-windows--.msi` (+ SHA256) | | **No Authenticode signing pipeline** in-repo | SmartScreen / enterprise trust | `signtool` (or Azure Trusted Signing) for EXE/DLL **and** MSI; store certs in CI secrets only — never in git | | **Silent / unattended install contract** | Fleet deploy | Document and implement `msiexec /i … /qn` properties (e.g. skip key UI, `VERAE_SKIP_KEY_SETUP`, install API URL, channel) | @@ -82,6 +97,7 @@ NSIS product already covers much of the *install experience* (files under Progra | Menu bar Swift + LaunchAgent | [`platform/macos/menubar/`](../platform/macos/menubar/) | Runtime presence; see [PACKAGING.md](PACKAGING.md) § macOS menu bar | | **Unsigned** menu-bar `.app` builder | [`platform/macos/menubar/build-menubar-app.sh`](../platform/macos/menubar/build-menubar-app.sh) | Dev/local only; copies entitlements but **does not** codesign | | Notarization **documentation only** (menubar) | [`platform/macos/menubar/README.txt`](../platform/macos/menubar/README.txt) + [PACKAGING.md](PACKAGING.md) § Codesign + notarization | Manual steps; no automated menubar notarize | +| **Notarize checklist** (menubar) | [`platform/macos/menubar/NOTARIZE-CHECKLIST.md`](../platform/macos/menubar/NOTARIZE-CHECKLIST.md) | **Scaffold shipped** — notarytool placeholders, staple, `spctl`; no secrets; automation still open | | Packaging smoke | [`platform/smoke-packaging-metadata.sh`](../platform/smoke-packaging-metadata.sh) | Version + “no elevated MSI” + IPP purity — not Gatekeeper | ### Missing for commercial notarized `.app` @@ -130,13 +146,15 @@ NSIS product already covers much of the *install experience* (files under Progra |-------------|--------|------------------| | Windows tray / Startup / Start Menu (user) | Yes | Yes for **presence**; not enterprise MSI | | Windows NSIS `setup.exe` | Yes | **Partial** — full GUI install; needs Authenticode + version align; not MSI | -| Windows **MSI** (WiX) | **No** | **No** | +| Windows **MSI** (WiX) tray presence skeleton | **Yes** (`.wxs` + README under `platform/windows/wix/`) | **No** — scaffold only; needs toolset build + is not full product MSI | +| Windows **MSI** full product (NSIS parity) | **No** | **No** | | Windows Authenticode pipeline | **No** (in-repo) | **No** | | macOS menu bar LaunchAgent | Yes | Yes (dev/ops) | | macOS unsigned menubar `.app` | Yes | **No** (Gatekeeper) | | macOS **signed+notarized `.pkg`** script | Yes | **Near** — needs version sync, CI, clean-VM proof | | macOS **standalone notarized `.app`** zip/DMG | **Partial** (app built inside pkg only) | **No** as primary download | -| Menubar automated notarization | Docs only | **No** | +| Menubar notarize checklist | **Yes** (`NOTARIZE-CHECKLIST.md`) | Docs only — not automated | +| Menubar automated notarization | Docs + checklist only | **No** | --- @@ -144,8 +162,10 @@ NSIS product already covers much of the *install experience* (files under Progra - **[PACKAGING.md](PACKAGING.md)** — tray, menubar, unsigned `.app`, notarization steps (docs), CERT-01, statuslang, IPP split, smoke tests - [`platform/windows/`](../platform/windows/) — NSIS + tray scripts +- [`platform/windows/wix/`](../platform/windows/wix/) — per-user tray shortcut MSI **skeleton** (candle/light) - [`platform/macos/pkg/`](../platform/macos/pkg/) — commercial `.pkg` build -- [`platform/macos/menubar/`](../platform/macos/menubar/) — menu bar + unsigned `.app` + notarize docs +- [`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 - [`docs/installer-registration-api.md`](installer-registration-api.md) — installer ↔ portal registration API - [`README.md`](../README.md) — top-level packaging pointers @@ -153,4 +173,4 @@ NSIS product already covers much of the *install experience* (files under Progra ## Maintenance -When closing a row above, update the **Quick status matrix** and add a one-line note under the relevant “Exists” table (path + date). Do not move day-to-day tray/menubar how-to out of [PACKAGING.md](PACKAGING.md) into this backlog. +When closing a row above, update the **Quick status matrix** and add a one-line note under the relevant “Exists” table (path + date). When shipping a new **scaffold** (authoring only), add/refresh the **Scaffold shipped** section rather than marking commercial-ready. Do not move day-to-day tray/menubar how-to out of [PACKAGING.md](PACKAGING.md) into this backlog. diff --git a/platform/macos/menubar/NOTARIZE-CHECKLIST.md b/platform/macos/menubar/NOTARIZE-CHECKLIST.md new file mode 100644 index 0000000..114bc5e --- /dev/null +++ b/platform/macos/menubar/NOTARIZE-CHECKLIST.md @@ -0,0 +1,166 @@ +# Menu bar `.app` notarization checklist + +Step list for **Developer ID** codesign + **notarytool** + **stapler** of the +optional Verae menu bar app (`ai.verae.timestamping.menubar`). + +This is a **checklist only**. It does **not** embed API keys, `.p12` material, +app-specific passwords, or `signing.env` values. Keep credentials in the +keychain, CI secrets, or an out-of-repo env file (e.g. the pkg path’s +`$HOME/fie/installers/macos/signing.env`). + +Related runbooks: [`README.txt`](README.txt), [`docs/PACKAGING.md`](../../../docs/PACKAGING.md) +§ Codesign + notarization, [`platform/macos/pkg/build-pkg.sh`](../pkg/build-pkg.sh) +(product `.pkg`, separate from this menubar stub). + +--- + +## Prerequisites (once per machine / CI) + +- [ ] Apple Developer Program membership; team id known (`TEAM_ID=…` placeholder) +- [ ] **Developer ID Application** certificate installed in the signing keychain +- [ ] Notary credentials stored **outside git**, one of: + - [ ] Keychain profile via `xcrun notarytool store-credentials` + (profile name placeholder: `verae-notary`) + - [ ] App Store Connect API key: `AuthKey_XXXXXXXXXX.p8` path + Key ID + Issuer ID + (paths/ids as env placeholders only — never commit the `.p8`) +- [ ] Xcode CLT with `codesign`, `notarytool`, `stapler`, `spctl`, `ditto` +- [ ] Entitlements file available (builder copies pkg entitlements into the app): + `platform/macos/pkg/entitlements.plist` or + `$APP/Contents/Resources/entitlements.plist` after build + +**Do not** paste secrets into this checklist or the repo. + +--- + +## Build unsigned app + +- [ ] From repo root or `platform/macos/menubar/`: + + ```sh + ./build-menubar-app.sh ~/Applications/Verae\ Menubar.app + # optional version pin (default = internal/job.HelperVersion): + # VERSION=x.y.z ./build-menubar-app.sh ~/Applications/Verae\ Menubar.app + ``` + +- [ ] Confirm bundle id `ai.verae.timestamping.menubar`, `LSUIElement=true` +- [ ] Confirm `Contents/Resources/VERSION` and `packaging-metadata.txt` match + `HelperVersion` (or your `VERSION=` override) + +--- + +## Codesign (hardened runtime) + +Placeholders: `YOUR ORG`, `TEAMID`, `APP`. + +```sh +APP="${APP:-$HOME/Applications/Verae Menubar.app}" +ENT="$APP/Contents/Resources/entitlements.plist" +# fallback: ENT="../../pkg/entitlements.plist" from menubar dir +IDENTITY="Developer ID Application: YOUR ORG (TEAMID)" + +codesign --force --deep --options runtime --timestamp \ + --entitlements "$ENT" \ + --sign "$IDENTITY" \ + "$APP" + +codesign --verify --deep --strict --verbose=2 "$APP" +codesign -dvv "$APP" # inspect hardened runtime + team +``` + +- [ ] `codesign --verify` succeeds +- [ ] Hardened runtime present (`flags=0x10000(runtime)` or equivalent) +- [ ] No unexpected nested Mach-O left unsigned (menubar is a thin Swift binary) + +--- + +## Zip for notarytool + +```sh +ZIP="${ZIP:-/tmp/VeraeMenubar.zip}" +rm -f "$ZIP" +ditto -c -k --keepParent "$APP" "$ZIP" +``` + +- [ ] Zip created with `--keepParent` (bundle layout preserved) + +--- + +## Submit notarization (`notarytool`) + +### Option A — keychain profile (preferred on operator laptops) + +```sh +# One-time (interactive; stores in keychain — not in git): +# xcrun notarytool store-credentials "verae-notary" \ +# --apple-id "YOUR_APPLE_ID@example.com" \ +# --team-id "TEAMID" \ +# --password "@keychain:AC_PASSWORD" # or app-specific password prompt + +xcrun notarytool submit "$ZIP" \ + --keychain-profile "verae-notary" \ + --wait +``` + +### Option B — App Store Connect API key (CI-friendly) + +```sh +# Env placeholders only — inject from CI secrets / signing.env: +# NOTARY_KEY_PATH=/path/to/AuthKey_XXXXXXXXXX.p8 +# NOTARY_KEY_ID=XXXXXXXXXX +# NOTARY_ISSUER_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + +xcrun notarytool submit "$ZIP" \ + --key "$NOTARY_KEY_PATH" \ + --key-id "$NOTARY_KEY_ID" \ + --issuer "$NOTARY_ISSUER_ID" \ + --wait +``` + +- [ ] Submission status **Accepted** +- [ ] On **Invalid**: `xcrun notarytool log --keychain-profile "verae-notary"` + (or same key flags as submit); fix entitlements / signing; do not commit logs with tokens + +--- + +## Staple + Gatekeeper verify + +```sh +xcrun stapler staple "$APP" +xcrun stapler validate "$APP" +spctl --assess --type execute -vv "$APP" +``` + +- [ ] `stapler validate` OK +- [ ] `spctl --assess` accepted (source = Notarized Developer ID) +- [ ] Optional: copy app to a **clean Mac / fresh user**, confirm no right-click Open workaround + +--- + +## Distribute (optional) + +- [ ] Publish stapled `.app` as zip or DMG + SHA256 (naming example: + `VeraeMenubar-.app.zip`) +- [ ] If DMG: notarize/staple the **DMG** as well when it is the download vessel +- [ ] Do **not** ship unstapled builds as the primary customer download +- [ ] Product `.pkg` path remains separate: `../pkg/build-pkg.sh` (already has + notarytool + stapler when `SIGNING_ENV` is set) + +--- + +## Automation gap (still backlog) + +- [ ] 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 +- [ ] Smoke: `spctl` + `stapler validate` on signed hosts only + +Until those land, this checklist is the operator runbook. + +--- + +## Secrets hygiene (standing rules) + +- Never commit `signing.env`, `.p8`, `.p12`, notary passwords, or keychain exports +- Never put real Apple IDs, Team IDs used as production secrets, or API key IDs + into sample commands in PRs if policy forbids — placeholders only +- Rotate credentials out-of-band if leaked; revoke via App Store Connect diff --git a/platform/windows/wix/README.md b/platform/windows/wix/README.md new file mode 100644 index 0000000..e6ad6e9 --- /dev/null +++ b/platform/windows/wix/README.md @@ -0,0 +1,132 @@ +# Windows WiX tray presence (skeleton) + +[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) + +Non-elevated per-user WiX MSI skeleton for Start Menu + Startup → verae-tray.ps1. + +This is **not** the full commercial Print Stamp MSI (printers, Program Files, +Authenticode, elevated custom actions). Day-to-day tray presence without WiX +remains [`../scripts/build-tray-startmenu.ps1`](../scripts/build-tray-startmenu.ps1). +Full product install remains NSIS (`../installer-windows.nsi`). Commercial gaps: +[`docs/COMMERCIAL-INSTALLER-BACKLOG.md`](../../../docs/COMMERCIAL-INSTALLER-BACKLOG.md). + +## Install + +Requires **WiX Toolset** on a Windows host (or CI Windows runner). Without +`candle`/`light` (or WiX v4 `wix build`), no MSI is produced — use the +PowerShell Start Menu stub instead. + +**WiX v3** (this skeleton’s XML): + +```bat +REM Add candle.exe + light.exe to PATH, e.g.: +REM set PATH=%PATH%;%ProgramFiles(x86)%\WiX Toolset v3.14\bin +``` + +**WiX v4+** (`dotnet tool install --global wix`): adapt namespaces / `Package` +element; authoring here is WiX v3. + +Stage tray scripts from this repo before building: + +```bat +set WIXDIR=platform\windows\wix +set STAGE=%WIXDIR%\Stage +mkdir %STAGE% 2>nul +copy /Y platform\windows\scripts\verae-tray.ps1 %STAGE%\ +copy /Y platform\windows\scripts\common.ps1 %STAGE%\ +``` + +## Usage + +### What the package does + +| Item | Behavior | +|------|----------| +| Install scope | **perUser**, `InstallPrivileges=limited` (no UAC) | +| Files | `%LocalAppData%\Verae\Tray\verae-tray.ps1` (+ `common.ps1`) | +| Start Menu | `Programs\Verae\Verae Tray.lnk` → PowerShell STA + tray script | +| Startup | `Startup\Verae Tray.lnk` (same idea as `install-tray-startup.ps1`) | +| IPP | Unchanged — tray only uses **GET `/status`** / **GET `/queue`** | + +### What it does not do + +Elevated Program Files install, printers, scheduled helper, Authenticode, +NSIS key wizard parity, machine-wide Start Menu under ProgramData. + +### Build (WiX v3 candle / light) + +From repo root on Windows: + +```bat +set WIXDIR=platform\windows\wix +set STAGE=%WIXDIR%\Stage +set OUT=%WIXDIR%\out +set VER=0.3.6 + +mkdir %STAGE% 2>nul +mkdir %OUT% 2>nul +copy /Y platform\windows\scripts\verae-tray.ps1 %STAGE%\ +copy /Y platform\windows\scripts\common.ps1 %STAGE%\ + +candle.exe -nologo -out %OUT%\ ^ + -dProductVersion=%VER% ^ + -dStageDir=%CD%\%STAGE% ^ + %WIXDIR%\VeraeTrayShortcuts.wxs ^ + %WIXDIR%\VeraeTrayShortcutsFragment.wxs + +light.exe -nologo -out %OUT%\VeraeTray-%VER%-peruser.msi ^ + %OUT%\VeraeTrayShortcuts.wixobj ^ + %OUT%\VeraeTrayShortcutsFragment.wixobj +``` + +Notes: + +- Set `VER` from `internal/job.HelperVersion` (see `docs/PACKAGING.md`), or + pass `-dProductVersion=` explicitly. The `.wxs` defaults to `0.3.6` when + unset (HelperVersion at scaffold time). +- `Product Id="*"` generates a new ProductCode per build; `UpgradeCode` is + fixed for major-upgrade of this **tray presence** package only. +- Skeleton is **UI-less** (silent-friendly). For WixUI_Minimal, uncomment + `UIRef` / `WixUILicenseRtf` in `VeraeTrayShortcuts.wxs`, add `License.rtf`, + and pass `-ext WixUIExtension` to `light`. + +Silent install (per-user, no elevation): + +```bat +msiexec /i VeraeTray-0.3.6-peruser.msi /qn VERAE_IPP_ADDR=127.0.0.1:8631 +``` + +Uninstall via ARP (**Verae Tray (presence)**) or `msiexec /x {ProductCode} /qn`. + +### Files in this folder + +| File | Role | +|------|------| +| `VeraeTrayShortcuts.wxs` | Per-user Product + file components | +| `VeraeTrayShortcutsFragment.wxs` | Start Menu + Startup shortcut fragment | +| `README.md` | This file | + +### Relation to other Windows paths + +| Path | Role | +|------|------| +| `../scripts/verae-tray.ps1` | Runtime tray (source staged into MSI) | +| `../scripts/install-tray-startup.ps1` | WiX-free Startup shortcut | +| `../scripts/build-tray-startmenu.ps1` | WiX-free Start Menu + Startup | +| `../installer-windows.nsi` | Elevated **full product** setup.exe | +| **`wix/` (this folder)** | Per-user tray shortcut **MSI skeleton** | + +A shipping-grade elevated MSI still needs a separate WiX product that mirrors +NSIS: `verae-print.exe`, helper, printers, ARP, Authenticode, silent +properties, and CI. + +## Contributing + +Questions and packaging bugs: open an issue or PR against the PrintToVeraeTimeStamp +tree. PRs accepted for WiX authoring that keep **per-user / non-elevated** tray +scope separate from the full elevated product MSI. Do not commit certs, +passwords, or signed binaries. + +## License + +UNLICENSED © Verae diff --git a/platform/windows/wix/VeraeTrayShortcuts.wxs b/platform/windows/wix/VeraeTrayShortcuts.wxs new file mode 100644 index 0000000..0caf7b3 --- /dev/null +++ b/platform/windows/wix/VeraeTrayShortcuts.wxs @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platform/windows/wix/VeraeTrayShortcutsFragment.wxs b/platform/windows/wix/VeraeTrayShortcutsFragment.wxs new file mode 100644 index 0000000..64079b5 --- /dev/null +++ b/platform/windows/wix/VeraeTrayShortcutsFragment.wxs @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + +