docs: OPERATOR-READY package for MSI/notary external legs
Some checks failed
windows-msi / full-msi-unsigned (push) Has been cancelled
Some checks failed
windows-msi / full-msi-unsigned (push) Has been cancelled
Consolidate Authenticode, Apple notary, VM C, and digest-paste into platform/windows/wix/OPERATOR-READY.md (runbook + commands + secret names only). Cross-link from CI-SIGN-NOTARY, VM-C-ONE-PAGER, PACKAGING, and COMMERCIAL-INSTALLER-BACKLOG. Live enable remains blocked only on org secret store / clean VM — no secret values invented.
This commit is contained in:
parent
9d17dff73d
commit
778763737e
5 changed files with 232 additions and 2 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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`,
|
||||
|
|
|
|||
224
platform/windows/wix/OPERATOR-READY.md
Normal file
224
platform/windows/wix/OPERATOR-READY.md
Normal file
|
|
@ -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.
|
||||
|
|
@ -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).
|
||||
|
|
|
|||
Loading…
Reference in a new issue