diff --git a/docs/COMMERCIAL-INSTALLER-BACKLOG.md b/docs/COMMERCIAL-INSTALLER-BACKLOG.md index f4f5c07..f59b1db 100644 --- a/docs/COMMERCIAL-INSTALLER-BACKLOG.md +++ b/docs/COMMERCIAL-INSTALLER-BACKLOG.md @@ -22,6 +22,7 @@ Repo now includes **authoring/docs scaffolds** for the two commercial gaps that | **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`. Commented **signtool** + **Trusted Signing** steps (secret **names** only). | No live `signtool`; no certs in git; not a production download channel. | | **CI sign example** | [`.github/workflows/windows-msi-sign.yml.example`](../.github/workflows/windows-msi-sign.yml.example) | GHA/Forgejo **example**: build + Path A PFX/`signtool` + Path B Azure Trusted Signing; all gated `if: false`; secret **names** only. | Not a live workflow; no certs; does not claim SmartScreen. | | **Publish digests runbook** | [`docs/PUBLISH-DIGESTS.md`](PUBLISH-DIGESTS.md) | Operator order: build → sign → upload → `fill-manifest-sha256.ps1` → paste TBD. | Does not upload or enable live sign. | +| **OPERATOR-READY package** | [`wix/OPERATOR-READY.md`](../platform/windows/wix/OPERATOR-READY.md) | Single runbook: secret **names** + copy-paste commands for Authenticode, notary, VM C, digest paste. | Live values / clean VM still org-side; **READY: blocked only on org secret/VM**. | ### Issue 1 — what landed vs still open diff --git a/docs/PACKAGING.md b/docs/PACKAGING.md index d30bba4..908c363 100644 --- a/docs/PACKAGING.md +++ b/docs/PACKAGING.md @@ -290,6 +290,8 @@ CI: unsigned full MSI [`.github/workflows/windows-msi.yml`](../.github/workflows sign example (secret **names** only) [`.github/workflows/windows-msi-sign.yml.example`](../.github/workflows/windows-msi-sign.yml.example). Digest paste after upload: [PUBLISH-DIGESTS.md](PUBLISH-DIGESTS.md). +Operator enable package (secrets/VM only left): +[platform/windows/wix/OPERATOR-READY.md](../platform/windows/wix/OPERATOR-READY.md). ## Related paths diff --git a/platform/windows/wix/CI-SIGN-NOTARY.md b/platform/windows/wix/CI-SIGN-NOTARY.md index 218ad3e..4f2d163 100644 --- a/platform/windows/wix/CI-SIGN-NOTARY.md +++ b/platform/windows/wix/CI-SIGN-NOTARY.md @@ -1,9 +1,10 @@ # CI — Authenticode (Windows) + notarytool (macOS) **Status:** unsigned MSI CI is live (`.github/workflows/windows-msi.yml`); **sign/notary steps remain draft** (commented secret **names** + example workflow). No secrets in git. +**Operator package:** [`OPERATOR-READY.md`](OPERATOR-READY.md) — runbook + commands + secret names; live path **READY: blocked only on org secret/VM**. **Audience:** release engineers wiring GitHub Actions or Forgejo Actions. **Related:** [`Product-Full.wxs`](Product-Full.wxs), [`SILENT_PROPS.md`](SILENT_PROPS.md), -[`DUAL-SHIP.md`](DUAL-SHIP.md) § CI artifact, +[`DUAL-SHIP.md`](DUAL-SHIP.md) § CI artifact, [`VM-C-ONE-PAGER.md`](VM-C-ONE-PAGER.md), [`.github/workflows/windows-msi.yml`](../../../.github/workflows/windows-msi.yml) — unsigned build + commented signtool / Trusted Signing, [`.github/workflows/windows-msi-sign.yml.example`](../../../.github/workflows/windows-msi-sign.yml.example) — full sign sketch (`if: false`; secret **names** only), [`docs/PUBLISH-DIGESTS.md`](../../../docs/PUBLISH-DIGESTS.md) — fill DOWNLOADS-MANIFEST after sign via `fill-manifest-sha256.ps1`, diff --git a/platform/windows/wix/OPERATOR-READY.md b/platform/windows/wix/OPERATOR-READY.md new file mode 100644 index 0000000..ec21eda --- /dev/null +++ b/platform/windows/wix/OPERATOR-READY.md @@ -0,0 +1,224 @@ +# OPERATOR-READY — commercial MSI / notary / dual-ship + +**Status:** software + dry-runs complete. Live sign / notary / VM C / CDN digests are +**READY: blocked only on org secret/VM** (no missing in-repo steps). + +**Audience:** release operator with org secret-store + clean Windows VM access. +**Does not invent secrets.** Names only. Values stay in org CI / keychain / operator home. + +| Item | Blocker | In-repo ready | +|------|---------|---------------| +| Org Authenticode / Trusted Signing | Org secret store values | Yes — names + example workflow + dry-run | +| Live Apple notary submit/staple | Apple API key / keychain profile | Yes — names + checklist + dry-run | +| Clean VM C after `enable-printer-cas` | Physical/clean VM sign-off | Yes — one-pager + dual-ship smoke | +| Real sha256 in DOWNLOADS-MANIFEST | Signed blobs on CDN first | Yes — hash helper + paste runbook | + +**Canonical siblings:** [CI-SIGN-NOTARY.md](CI-SIGN-NOTARY.md) · [VM-C-ONE-PAGER.md](VM-C-ONE-PAGER.md) · +[DUAL-SHIP.md](DUAL-SHIP.md) · [VM-DUAL-SHIP-CHECKLIST.md](VM-DUAL-SHIP-CHECKLIST.md) · +[docs/PUBLISH-DIGESTS.md](../../../docs/PUBLISH-DIGESTS.md) · +[platform/macos/menubar/NOTARIZE-CHECKLIST.md](../../macos/menubar/NOTARIZE-CHECKLIST.md) · +[`.github/workflows/windows-msi-sign.yml.example`](../../../.github/workflows/windows-msi-sign.yml.example) · +[`platform/macos/menubar/ci-notarize.yml.example`](../../macos/menubar/ci-notarize.yml.example) + +--- + +## 0) Preconditions (already in repo) + +```text +# From PrintToVeraeTimeStamp repo root +go test ./internal/pdfcert ./internal/statuslang -count=1 +./platform/smoke-packaging-metadata.sh +./platform/macos/menubar/notary-dry-run.sh # exit 0; no submit +# Windows builder: +powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\sign-dry-run.ps1 +powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\dual-ship-smoke.ps1 +``` + +Unsigned MSI CI is live: `.github/workflows/windows-msi.yml`. +Sign/notary jobs stay gated (`if: false`) until secrets exist. + +--- + +## 1) Org CI secrets — Authenticode / Trusted Signing + +**READY: blocked only on org secret/VM** + +### Secret / var **names** (never commit values) + +| Name | Kind | Purpose | +|------|------|---------| +| `WINDOWS_CERT_PFX_BASE64` | secret | Base64 PFX for Path A (`signtool`) | +| `WINDOWS_CERT_PFX_PASSWORD` | secret | PFX password | +| `WINDOWS_CERT_THUMBPRINT` | secret | Optional `/sha1` after import | +| `WINDOWS_SIGN_TIMESTAMP_URL` | var | RFC 3161 TSA (e.g. `https://timestamp.digicert.com`) | +| `WINDOWS_TRUSTED_SIGNING_TENANT_ID` | secret | Path B Azure Trusted Signing | +| `WINDOWS_TRUSTED_SIGNING_CLIENT_ID` | secret | Path B | +| `WINDOWS_TRUSTED_SIGNING_CLIENT_SECRET` | secret | Omit when OIDC `id-token: write` | +| `WINDOWS_TRUSTED_SIGNING_ENDPOINT` | var | e.g. `https://cus.codesigning.azure.net` | +| `WINDOWS_TRUSTED_SIGNING_ACCOUNT` | var | Trusted Signing account name | +| `WINDOWS_TRUSTED_SIGNING_CERTIFICATE_PROFILE` | var | Certificate profile name | + +### Operator commands + +```text +# A) Add the names above to org GitHub/Forgejo secret store (values out-of-band). +# B) Copy example → live workflow (same secret names on Forgejo): +cp .github/workflows/windows-msi-sign.yml.example .github/workflows/windows-msi-sign.yml +# — or .forgejo/workflows/windows-msi-sign.yml with your Windows runs-on label +# C) Flip job gate: if: false → if: true (only after secrets exist) +# D) workflow_dispatch; download signed MSI artifact +# E) Local verify (Windows SDK): +powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\sign-dry-run.ps1 ` + -Path .\path\to\signed.msi +``` + +**Do not** claim SmartScreen reputation until clean-VM install proof. +**Do not** upload PFX as a workflow artifact. + +--- + +## 2) Live notary submit / staple (macOS) + +**READY: blocked only on org secret/VM** + +### Secret / env **names** + +| Name | Purpose | +|------|---------| +| `APPLE_API_KEY_P8_BASE64` | App Store Connect API key `.p8` (base64; CI materialize only) | +| `APPLE_API_KEY_ID` | Key ID | +| `APPLE_API_ISSUER_ID` | Issuer UUID | +| `APPLE_TEAM_ID` | Team ID for identity selection | +| `APPLE_CODESIGN_IDENTITY` | `Developer ID Application: … (TEAMID)` | +| `APPLE_INSTALLER_IDENTITY` | `Developer ID Installer: … (TEAMID)` (product `.pkg`) | +| `NOTARY_KEYCHAIN_PROFILE` | Laptop profile (default print: `verae-notary`) | +| `NOTARY_KEY_PATH` / `NOTARY_KEY_ID` / `NOTARY_ISSUER_ID` | Checklist aliases after materialize | + +### Operator commands (menubar accessory) + +```sh +# Dry-run first (always safe; no submit): +./platform/macos/menubar/notary-dry-run.sh + +# Build unsigned → codesign → ditto → notarytool → staple +# (full steps: platform/macos/menubar/NOTARIZE-CHECKLIST.md) +./platform/macos/menubar/build-menubar-app.sh "$HOME/Applications/Verae Menubar.app" +# codesign with $APPLE_CODESIGN_IDENTITY + entitlements (see checklist) +ditto -c -k --keepParent "$APP" /tmp/VeraeMenubar.zip +xcrun notarytool submit /tmp/VeraeMenubar.zip \ + --keychain-profile "${NOTARY_KEYCHAIN_PROFILE:-verae-notary}" --wait +# or API key: --key "$NOTARY_KEY_PATH" --key-id "$APPLE_API_KEY_ID" --issuer "$APPLE_API_ISSUER_ID" +xcrun stapler staple "$APP" && xcrun stapler validate "$APP" +spctl --assess --type execute -vv "$APP" +``` + +### Product `.pkg` (already scripted) + +```sh +# When operator signing.env / CI secrets present: +# platform/macos/pkg/build-pkg.sh (codesign + notarytool --wait + stapler) +``` + +### CI enable + +```text +cp platform/macos/menubar/ci-notarize.yml.example .github/workflows/macos-menubar-notarize.yml +# Flip menubar-notarize job if: false → true only after Apple secrets exist. +``` + +**Never** set `NOTARY_SUBMIT=1` in shared CI without secrets + intentional release. + +--- + +## 3) Clean VM C sign-off (`enable-printer-cas`) + +**READY: blocked only on org secret/VM** + +### Host (Windows builder) — unlock + rebuild + +```powershell +# From repo root +powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\enable-printer-cas.ps1 -Status +powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\enable-printer-cas.ps1 +powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\build-full-msi.ps1 +powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\dual-ship-smoke.ps1 -VerifyArtifacts +powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\sign-dry-run.ps1 +# Re-disable files-only tree when done authoring: +# enable-printer-cas.ps1 -Disable +``` + +### Clean VM C (operator physical/VM) + +Printable ticks: [VM-DUAL-SHIP-CHECKLIST.md](VM-DUAL-SHIP-CHECKLIST.md) § VM C. Narrative: [DUAL-SHIP.md](DUAL-SHIP.md). One-pager: [VM-C-ONE-PAGER.md](VM-C-ONE-PAGER.md). + +1. Install **elevated** full MSI (default or `VERAE_SKIP_PRINTERS=0`). +2. Confirm files under Program Files + IPP/`127.0.0.1:8631` + **Verae Print Helper** task. +3. One-page PDF stamp → receipt/CID ≠ jobId (hold is never evidence). +4. Uninstall clean; parity with VM A (NSIS). +5. Sign off checklist; keep NSIS primary until thin-NSIS exit A–D. + +`dual-ship-smoke.ps1` prints steps offline — it does **not** replace VM C sign-off. + +--- + +## 4) Upload signed blobs → real sha256 in DOWNLOADS-MANIFEST + +**READY: blocked only on org secret/VM** + +Order (fail closed) — [docs/PUBLISH-DIGESTS.md](../../../docs/PUBLISH-DIGESTS.md): + +1. **Build** NSIS +/or full MSI (`build-full-msi.ps1` / `windows-msi.yml`). +2. **Sign** (section 1) when shipping Windows trust. +3. **Upload** the **same bytes** to CDN under reserved 0.3.6 leaf names. +4. **Hash** (no upload in script): + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\fill-manifest-sha256.ps1 ` + -Path .\dist\VeraePrintStamp-windows-amd64-0.3.6-setup.exe, + .\platform\windows\wix\out\VeraePrintStamp-windows-amd64-0.3.6.msi ` + -Format both +``` + +POSIX shape if no pwsh: + +```bash +shasum -a 256 VeraePrintStamp-windows-amd64-0.3.6-setup.exe \ + VeraePrintStamp-windows-amd64-0.3.6.msi +# lowercase hex; two spaces; leaf name +``` + +5. **Paste** over `TBD` in `site/printit.verae.ai/DOWNLOADS-MANIFEST.txt` + `.md`. +6. **Verify** re-download from public URL matches pasted digest. + +Do **not** paste digests for unsigned-only CI smoke MSIs into production rows. + +Reserved 0.3.6 leaves: + +| Leaf | Channel | +|------|---------| +| `VeraePrintStamp-windows-amd64-0.3.6-setup.exe` | NSIS primary | +| `VeraePrintStamp-windows-arm64-0.3.6-setup.exe` | NSIS arm64 | +| `VeraePrintStamp-windows-amd64-0.3.6.msi` | Full MSI dual-ship | +| `VeraePrintStamp-windows-arm64-0.3.6.msi` | Full MSI arm64 | +| `verae-print-windows-amd64-0.3.6.exe` | Optional bare helper | + +--- + +## Quick enable checklist (operator) + +- [ ] Org secrets set (names in §1 / §2) — **values never in git** +- [ ] Copy `windows-msi-sign.yml.example` → live; flip `if: false` +- [ ] Copy `ci-notarize.yml.example` → live when Apple secrets exist; flip gate +- [ ] `enable-printer-cas.ps1` + `build-full-msi.ps1` on Windows builder +- [ ] Clean VM C ticks (checklist) signed off +- [ ] CDN upload of signed 0.3.6 blobs +- [ ] `fill-manifest-sha256.ps1` → paste TBD → re-download verify + +Until every box above is done by a human with secrets/VM, commercial claims stay: +software-complete, **READY: blocked only on org secret/VM**. + +--- + +## License + +UNLICENSED © Verae — operator runbook; not a redistributable binary. diff --git a/platform/windows/wix/VM-C-ONE-PAGER.md b/platform/windows/wix/VM-C-ONE-PAGER.md index 36cc7e7..438d25b 100644 --- a/platform/windows/wix/VM-C-ONE-PAGER.md +++ b/platform/windows/wix/VM-C-ONE-PAGER.md @@ -42,4 +42,6 @@ powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\fil - Hold / jobId is never evidence. - Re-disable CAs for files-only tree: `enable-printer-cas.ps1 -Disable`. -**Still left (Issue 1):** org secrets for live CI sign; live notary (macOS); clean-VM **C** sign-off; paste real digests after upload — see [OPEN-ISSUES](../../../../verae-2026-screens/OPEN-ISSUES.md) row 1 · [PUBLISH-DIGESTS.md](../../../docs/PUBLISH-DIGESTS.md) · [CI-SIGN-NOTARY.md](CI-SIGN-NOTARY.md). +**Operator package:** [OPERATOR-READY.md](OPERATOR-READY.md) — all four external legs (sign / notary / VM C / digests) with commands + secret **names**. Status: **READY: blocked only on org secret/VM**. + +**Still left (org-side only):** org secrets for live CI sign; live notary (macOS); clean-VM **C** sign-off; paste real digests after upload — see [OPEN-ISSUES](../../../../verae-2026-screens/OPEN-ISSUES.md) row 1 · [PUBLISH-DIGESTS.md](../../../docs/PUBLISH-DIGESTS.md) · [CI-SIGN-NOTARY.md](CI-SIGN-NOTARY.md).