feat: TODO-7 A1/A7 — Product-Full.wxs stub, CI-SIGN-NOTARY, CERT spacing + font fetch example
Commercial installer and CERT-01 layout next slice; no elevated MSI claim.
This commit is contained in:
parent
46fa1aa0d4
commit
8a8db16da1
9 changed files with 674 additions and 30 deletions
|
|
@ -16,6 +16,19 @@ Repo now includes **authoring/docs scaffolds** for the two commercial gaps that
|
|||
|----------|------|------------|----------------|
|
||||
| **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 stub (TODO-7 A1)** | [`platform/windows/wix/Product-Full.wxs`](../platform/windows/wix/Product-Full.wxs) (+ outline, [`SILENT_PROPS.md`](../platform/windows/wix/SILENT_PROPS.md), [`CI-SIGN-NOTARY.md`](../platform/windows/wix/CI-SIGN-NOTARY.md)) | Commented WiX v3 **Product** starter: perMachine/elevated, Feature/ComponentGroup placeholders (bin/scripts/docs/Start Menu), silent public props, distinct UpgradeCode from tray. **candle** should parse; **light** needs `StageFull` binaries. CI sketch: Authenticode + notarytool env names only. | Shippable MSI artifact, deferred printer CAs scheduled, Authenticode in CI, Intune-proven silent install, NSIS replacement. |
|
||||
|
||||
### TODO-7 (A1) — what landed vs still open
|
||||
|
||||
| Done in TODO-7 A1 | Still left (Issue 1 commercial) |
|
||||
|-------------------|----------------------------------|
|
||||
| `Product-Full.wxs` candle-parseable stub from outline | `build-full-msi.ps1` staging + light → real `VeraePrintStamp-windows-*.msi` in dist |
|
||||
| `CI-SIGN-NOTARY.md` GHA/Forgejo draft (secret **names** only) | Live CI: signtool / Trusted Signing + notarytool staple on runners |
|
||||
| `SILENT_PROPS.md` msiexec one-liner + expanded log example | Properties honored by scheduled CAs (config write, skip printers/key) |
|
||||
| This backlog row | Printer deferred CA parity with NSIS; dual-ship validation; thin NSIS |
|
||||
| | macOS menubar automated notarize (checklist exists; script default still unsigned) |
|
||||
| | Version-default drift fix across build-windows / build-pkg / NSIS |
|
||||
| | Clean-VM Gatekeeper + Intune smoke proof |
|
||||
|
||||
**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`. Tray MSI skeleton build: `scripts/build-tray-msi.ps1` (optional WiX).
|
||||
|
|
@ -147,8 +160,8 @@ 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) 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 (NSIS parity) | **No** | **No** |
|
||||
| Windows Authenticode pipeline | **No** (in-repo) | **No** |
|
||||
| Windows **MSI** full product (NSIS parity) | **Stub** (`Product-Full.wxs` TODO-7 A1; candle-oriented) | **No** — no StageFull light artifact / CA / sign |
|
||||
| Windows Authenticode pipeline | **Draft doc** (`wix/CI-SIGN-NOTARY.md`) | **No** — no live signtool job |
|
||||
| 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 |
|
||||
|
|
@ -163,6 +176,9 @@ 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/) + [`build-tray-msi.ps1`](../platform/windows/scripts/build-tray-msi.ps1) — per-user tray MSI skeleton; ProductVersion ← HelperVersion
|
||||
- [`platform/windows/wix/Product-Full.wxs`](../platform/windows/wix/Product-Full.wxs) — elevated full-product MSI stub (TODO-7 A1)
|
||||
- [`platform/windows/wix/CI-SIGN-NOTARY.md`](../platform/windows/wix/CI-SIGN-NOTARY.md) — CI Authenticode + notarytool sketch
|
||||
- [`platform/windows/wix/SILENT_PROPS.md`](../platform/windows/wix/SILENT_PROPS.md) — msiexec `/qn` property contract
|
||||
- [`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
|
||||
|
|
|
|||
|
|
@ -191,10 +191,20 @@ Site page: [`site/printit.verae.ai/certificate.html`](../site/printit.verae.ai/c
|
|||
| Live QR (web) | **Shipped** — final + receipt/digest paints QR via qrcode.js CDN → data-URL; offline/provisional keeps SVG placeholder (print-safe) |
|
||||
| Residual gap | Full **CERT-01.html** pixel match (self-hosted design-kit Fraunces/Cantarell, Substrate extrinsic polish) still **not** claimed — web title ships CDN Fraunces + Georgia/serif offline fallback; issued artifact remains PDF (`internal/pdfcert` + `platform/registration-pdf/build.mjs`) |
|
||||
| Self-host fonts plan | [`site/printit.verae.ai/fonts/README.md`](../site/printit.verae.ai/fonts/README.md) — woff2 placeholder dir (empty + `.gitkeep`); binaries/CDN cutover not started |
|
||||
| Font fetch scaffold (TODO-7 A7) | [`site/printit.verae.ai/fonts/fetch-fonts.example.sh`](../site/printit.verae.ai/fonts/fetch-fonts.example.sh) — curl **placeholders only** (commented URLs, OFL reminder, `--check`); no licensed binaries downloaded or committed |
|
||||
| Layout spacing (TODO-7 A7) | `certificate.html` padding/margins nudged toward CERT-01 mock: sheet **56×64**, column **816px**, logo **40px**, title block **26/6/14**, gold rule **2×120**, banner **18** / check **34** / title **16px**, kv **20**, verify gap **22**, QR **130**; plum **1.5px** + gold **0.5px** inset frame. Still **not** full pixel match |
|
||||
|
||||
Design source of truth for the paper mock: `verae-2026-design/ui-designs/src/html/CERT-01.html`.
|
||||
Cross-product siblings (not owned here): Sign `certificate.html` (SG-P1), Verify ZP-V5, MCP Mac export PDF.
|
||||
|
||||
### TODO-7 residual (Issue 7 — still left)
|
||||
|
||||
| Done this slice | Still left |
|
||||
|-----------------|------------|
|
||||
| `fetch-fonts.example.sh` example + `--check` | Real woff2 + `OFL.txt` committed; `@font-face` cutover; drop Google Fonts CDN links |
|
||||
| CERT sheet spacing closer to CERT-01 | Full CERT-01.html pixel match (Fraunces opsz, Cantarell body if required, Substrate extrinsic polish) |
|
||||
| PACKAGING rows above | PDF Fraunces embed in `internal/pdfcert` / `registration-pdf/build.mjs` (system Times/Helvetica only today) |
|
||||
|
||||
## statuslang notify titles
|
||||
|
||||
Source: [`internal/statuslang`](../internal/statuslang/status.go). Tray,
|
||||
|
|
|
|||
206
platform/windows/wix/CI-SIGN-NOTARY.md
Normal file
206
platform/windows/wix/CI-SIGN-NOTARY.md
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
# CI — Authenticode (Windows) + notarytool (macOS)
|
||||
|
||||
**Status:** draft sketch only. No live pipeline, no secrets in git.
|
||||
**Audience:** release engineers wiring GitHub Actions or Forgejo Actions.
|
||||
**Related:** [`Product-Full.wxs`](Product-Full.wxs), [`SILENT_PROPS.md`](SILENT_PROPS.md),
|
||||
[`docs/COMMERCIAL-INSTALLER-BACKLOG.md`](../../../docs/COMMERCIAL-INSTALLER-BACKLOG.md),
|
||||
[`platform/macos/menubar/NOTARIZE-CHECKLIST.md`](../../macos/menubar/NOTARIZE-CHECKLIST.md),
|
||||
[`platform/macos/pkg/build-pkg.sh`](../../macos/pkg/build-pkg.sh).
|
||||
|
||||
Do **not** commit PFX, passwords, App Store Connect `.p8`, or real Team IDs used as secrets.
|
||||
All credential names below are **environment / secret store keys** only.
|
||||
|
||||
---
|
||||
|
||||
## Goals
|
||||
|
||||
| Artifact | Sign | Notarize / trust |
|
||||
|----------|------|------------------|
|
||||
| `verae-print.exe`, `verae-printhelper.exe` | Authenticode (`signtool`) | SmartScreen reputation over time |
|
||||
| Full / tray **MSI** | Authenticode on the `.msi` | Same |
|
||||
| macOS `.pkg` / menubar `.app` | Developer ID | `notarytool` + `stapler` |
|
||||
|
||||
Primary product install remains **NSIS** until `Product-Full.wxs` lights + signs cleanly.
|
||||
macOS **pkg** path already supports notary when operator `SIGNING_ENV` is set; CI should mirror that with secret injection.
|
||||
|
||||
---
|
||||
|
||||
## Secret / env names (placeholders)
|
||||
|
||||
### Windows Authenticode
|
||||
|
||||
| Env name | Purpose |
|
||||
|----------|---------|
|
||||
| `WINDOWS_CERT_PFX_BASE64` | Base64 of code-signing PFX (CI import only) |
|
||||
| `WINDOWS_CERT_PFX_PASSWORD` | PFX password |
|
||||
| `WINDOWS_CERT_THUMBPRINT` | Optional: thumbprint when cert is pre-installed on runner |
|
||||
| `WINDOWS_SIGN_TIMESTAMP_URL` | RFC 3161 TSA URL (e.g. DigiCert / Sectigo public TSA) |
|
||||
| `WINDOWS_TRUSTED_SIGNING_*` | Optional Azure Trusted Signing — if used, prefer over PFX |
|
||||
|
||||
Never echo these values in logs. Prefer OIDC → cloud KMS/Trusted Signing when available.
|
||||
|
||||
### Apple notary + Developer ID
|
||||
|
||||
| Env name | Purpose |
|
||||
|----------|---------|
|
||||
| `APPLE_API_KEY_P8_BASE64` | App Store Connect API key `.p8` (base64) |
|
||||
| `APPLE_API_KEY_ID` | Key ID |
|
||||
| `APPLE_API_ISSUER_ID` | Issuer UUID |
|
||||
| `APPLE_TEAM_ID` | Team ID for codesign identity selection |
|
||||
| `APPLE_CODESIGN_IDENTITY` | e.g. `Developer ID Application: … (TEAMID)` |
|
||||
| `APPLE_INSTALLER_IDENTITY` | e.g. `Developer ID Installer: … (TEAMID)` |
|
||||
| `NOTARY_KEYCHAIN_PROFILE` | Optional laptop path name (`verae-notary`); CI usually uses API key trio above |
|
||||
|
||||
Match names already used in operator `signing.env` where possible; map in the workflow, do not rename product scripts casually.
|
||||
|
||||
---
|
||||
|
||||
## GitHub Actions sketch (Windows MSI + sign)
|
||||
|
||||
```yaml
|
||||
# .github/workflows/windows-msi-sign.yml — DRAFT, not enabled
|
||||
name: windows-msi-sign
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# push:
|
||||
# tags: ["printstamp-v*"]
|
||||
|
||||
jobs:
|
||||
msi:
|
||||
runs-on: windows-latest
|
||||
permissions:
|
||||
contents: read
|
||||
# id-token: write # if using Azure OIDC Trusted Signing
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: apps/PrintToVeraeTimeStamp/go.mod
|
||||
# adjust path if workflow lives inside the app repo root
|
||||
|
||||
- name: Build Windows binaries
|
||||
shell: pwsh
|
||||
working-directory: apps/PrintToVeraeTimeStamp # or repo root
|
||||
run: |
|
||||
# Prefer existing build-windows.ps1 / cross-build; stage under platform/windows/wix/StageFull
|
||||
# go build → StageFull\bin\verae-print.exe + verae-printhelper.exe
|
||||
# copy scripts + LICENSE + README-WINDOWS.txt into StageFull
|
||||
Write-Host "TODO: wire build-windows.ps1 staging for Product-Full.wxs"
|
||||
|
||||
- name: Install WiX toolset
|
||||
shell: pwsh
|
||||
run: |
|
||||
# choco install wixtoolset --version 3.14.x OR use preinstalled image
|
||||
Write-Host "TODO: ensure candle/light on PATH"
|
||||
|
||||
- name: candle + light Product-Full
|
||||
shell: pwsh
|
||||
run: |
|
||||
# candle -dProductVersion=… -dStageDir=… Product-Full.wxs
|
||||
# light → out\VeraePrintStamp-windows-amd64-<ver>.msi
|
||||
# Expect failure until StageFull complete — see Product-Full.wxs header
|
||||
Write-Host "TODO: invoke candle/light"
|
||||
|
||||
- name: Import code-signing cert
|
||||
shell: pwsh
|
||||
env:
|
||||
WINDOWS_CERT_PFX_BASE64: ${{ secrets.WINDOWS_CERT_PFX_BASE64 }}
|
||||
WINDOWS_CERT_PFX_PASSWORD: ${{ secrets.WINDOWS_CERT_PFX_PASSWORD }}
|
||||
run: |
|
||||
# $bytes = [Convert]::FromBase64String($env:WINDOWS_CERT_PFX_BASE64)
|
||||
# [IO.File]::WriteAllBytes("$env:RUNNER_TEMP\codesign.pfx", $bytes)
|
||||
# Import-PfxCertificate -FilePath ... -CertStoreLocation Cert:\CurrentUser\My -Password ...
|
||||
Write-Host "TODO: import PFX; never Upload-Artifact the PFX"
|
||||
|
||||
- name: signtool Authenticode
|
||||
shell: cmd
|
||||
env:
|
||||
WINDOWS_SIGN_TIMESTAMP_URL: ${{ vars.WINDOWS_SIGN_TIMESTAMP_URL }}
|
||||
run: |
|
||||
REM signtool sign /fd SHA256 /tr %WINDOWS_SIGN_TIMESTAMP_URL% /td SHA256 /a ^
|
||||
REM path\to\verae-print.exe path\to\verae-printhelper.exe path\to\*.msi
|
||||
echo TODO: signtool after binaries + MSI exist
|
||||
|
||||
- name: Checksums
|
||||
shell: pwsh
|
||||
run: |
|
||||
# Get-FileHash … SHA256 | Out-File SHA256SUMS
|
||||
Write-Host "TODO: publish MSI + SHA256SUMS as release assets"
|
||||
```
|
||||
|
||||
**Forgejo Actions:** same steps; replace `${{ secrets.* }}` with Forgejo secret syntax (`secrets.WINDOWS_CERT_PFX_BASE64`, etc.) and `runs-on` with your Windows label. Keep secret **names** identical so docs stay portable.
|
||||
|
||||
---
|
||||
|
||||
## GitHub Actions sketch (macOS notary)
|
||||
|
||||
```yaml
|
||||
# .github/workflows/macos-notarize.yml — DRAFT, not enabled
|
||||
name: macos-notarize
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
notarize:
|
||||
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 ...
|
||||
echo "TODO: write .p8 outside workspace; delete in always() step"
|
||||
|
||||
- name: Codesign + pkgbuild path
|
||||
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_INSTALLER_IDENTITY: ${{ secrets.APPLE_INSTALLER_IDENTITY }}
|
||||
# NOTARY_KEY_PATH / KEY_ID / ISSUER_ID as expected by build-pkg.sh
|
||||
run: |
|
||||
# Prefer platform/macos/pkg/build-pkg.sh with SIGNING_ENV generated from secrets
|
||||
# or menubar NOTARIZE-CHECKLIST.md steps for accessory .app
|
||||
echo "TODO: call build-pkg.sh; notarytool submit --wait; stapler staple"
|
||||
|
||||
- name: Gatekeeper smoke (signed host only)
|
||||
run: |
|
||||
# spctl --assess --type install -vv path/to.pkg || true
|
||||
# xcrun stapler validate path/to.pkg
|
||||
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).
|
||||
|
||||
---
|
||||
|
||||
## Ordering (recommended)
|
||||
|
||||
1. **Build unsigned** artifacts on CI (MSI light needs StageFull; pkg can already build unsigned).
|
||||
2. **Sign** Windows PE + MSI with `signtool` (or Trusted Signing).
|
||||
3. **Notarize** macOS zip/pkg with `notarytool --wait`.
|
||||
4. **Staple** macOS artifacts; `stapler validate`.
|
||||
5. **Checksum** + upload release assets; update `site/printit.verae.ai/DOWNLOADS-MANIFEST.txt` out-of-band or via a docs job.
|
||||
6. Never upload raw PFX / `.p8` as workflow artifacts.
|
||||
|
||||
---
|
||||
|
||||
## What this draft does **not** do
|
||||
|
||||
- Enable a workflow file in-repo (operators copy/adapt).
|
||||
- Provide real certs, Team IDs, or TSA URLs.
|
||||
- Replace NSIS as primary Windows download.
|
||||
- Automate menubar notarization inside `build-menubar-app.sh` (still checklist + backlog).
|
||||
- Claim SmartScreen reputation or Gatekeeper green without clean-VM proof.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
UNLICENSED © Verae — CI sketch only; not a redistributable binary.
|
||||
287
platform/windows/wix/Product-Full.wxs
Normal file
287
platform/windows/wix/Product-Full.wxs
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Product-Full.wxs — elevated full Print Stamp MSI stub (WiX v3).
|
||||
|
||||
Status: candle-parseable starter from Product-Full.wxs.outline.
|
||||
NOT a shipping MSI. light will fail until StageFull binaries exist.
|
||||
Full surface still ships via installer-windows.nsi.
|
||||
|
||||
Scope (target, not all wired yet):
|
||||
- perMachine / elevated → ProgramFiles64\Verae\PrintStamp
|
||||
- bin (verae-print + helper), scripts, LICENSE/README
|
||||
- Start Menu shortcuts (placeholder components)
|
||||
- Public silent props (SILENT_PROPS.md)
|
||||
- Printer CA stubs commented (deferred; need binaries + CA DLL/exe)
|
||||
|
||||
Not this package:
|
||||
- Tray presence → VeraeTrayShortcuts.wxs (per-user, non-elevated)
|
||||
- UpgradeCode MUST stay distinct from tray A7C3E91B-…
|
||||
|
||||
Build (Windows host with WiX v3 candle/light on PATH):
|
||||
|
||||
set WIXDIR=platform\windows\wix
|
||||
set STAGE=%WIXDIR%\StageFull
|
||||
set OUT=%WIXDIR%\out
|
||||
set VER=0.3.6
|
||||
REM Stage bin + scripts first (see outline staging layout), then:
|
||||
candle.exe -nologo -out %OUT%\ ^
|
||||
-dProductVersion=%VER% ^
|
||||
-dStageDir=%CD%\%STAGE% ^
|
||||
%WIXDIR%\Product-Full.wxs
|
||||
light.exe -nologo -out %OUT%\VeraePrintStamp-windows-amd64-%VER%.msi ^
|
||||
%OUT%\Product-Full.wixobj
|
||||
|
||||
candle: should succeed with this file alone (syntax + schema).
|
||||
light: needs StageFull\bin\*.exe and scripts; without them → file-not-found.
|
||||
Do not claim commercial-ready until Authenticode + CA parity land
|
||||
(docs/COMMERCIAL-INSTALLER-BACKLOG.md, CI-SIGN-NOTARY.md).
|
||||
|
||||
ProductVersion: -dProductVersion from future build-full-msi.ps1
|
||||
(Get-VeraePackageVersion → internal/job.HelperVersion). Fallback = 0.3.6.
|
||||
-->
|
||||
<?ifndef ProductVersion ?>
|
||||
<?define ProductVersion = "0.3.6" ?>
|
||||
<?endif?>
|
||||
<?ifndef StageDir ?>
|
||||
<?define StageDir = "StageFull" ?>
|
||||
<?endif?>
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<Product
|
||||
Id="*"
|
||||
Name="Verae Print Stamp"
|
||||
Language="1033"
|
||||
Version="$(var.ProductVersion)"
|
||||
Manufacturer="Verae"
|
||||
UpgradeCode="F8B2A4C6-1E90-4D7B-A3C5-9D0E1F2A3B4C">
|
||||
<!--
|
||||
UpgradeCode is STABLE for the full elevated product only.
|
||||
Do not reuse tray UpgradeCode A7C3E91B-4D2F-4B8A-9E61-0F3C8D2A5B17.
|
||||
Product Id="*" → new ProductCode every build (MajorUpgrade handles replace).
|
||||
-->
|
||||
<Package
|
||||
InstallerVersion="500"
|
||||
Compressed="yes"
|
||||
InstallScope="perMachine"
|
||||
InstallPrivileges="elevated"
|
||||
Platform="x64"
|
||||
Manufacturer="Verae"
|
||||
Description="Elevated Verae Print Stamp (Program Files + printers target). Stub — dual-ship NSIS until validated."
|
||||
Comments="Product-Full.wxs starter. Hold ids are never evidence. See SILENT_PROPS.md for msiexec /qn." />
|
||||
|
||||
<MajorUpgrade
|
||||
DowngradeErrorMessage="A newer version of Verae Print Stamp is already installed."
|
||||
AllowSameVersionUpgrades="yes" />
|
||||
|
||||
<MediaTemplate EmbedCab="yes" />
|
||||
|
||||
<!-- ARP (Add/Remove Programs) -->
|
||||
<Property Id="ARPURLINFOABOUT" Value="https://printit.verae.ai/" />
|
||||
<Property Id="ARPHELPLINK" Value="https://customer.verae.ai/portal/" />
|
||||
<Property Id="ARPCOMMENTS" Value="Verae Print Stamp — elevated MSI stub. Primary setup remains NSIS until this product lights clean." />
|
||||
<Property Id="ARPNOMODIFY" Value="1" />
|
||||
|
||||
<!--
|
||||
Public silent properties — contract in SILENT_PROPS.md.
|
||||
Never put API keys / JWT / passwords here (visible in repair + logs).
|
||||
-->
|
||||
<Property Id="VERAE_SKIP_KEY_SETUP" Value="0" />
|
||||
<Property Id="VERAE_CHANNEL" Value="zapier" />
|
||||
<Property Id="VERAE_INSTALL_API_URL" Value="https://customer.verae.ai" />
|
||||
<Property Id="VERAE_ZAPIER_API_URL" Value="https://zap.verae.ai" />
|
||||
<Property Id="VERAE_SKIP_PRINTERS" Value="0" />
|
||||
<Property Id="VERAE_SKIP_HELPER_TASK" Value="0" />
|
||||
<Property Id="VERAE_INSTALL_TRAY" Value="0" />
|
||||
<Property Id="VERAE_IPP_ADDR" Value="127.0.0.1:8631" />
|
||||
<Property Id="VERAE_OPEN_ON_COMPLETE" Value="folder" />
|
||||
<Property Id="VERAE_PROMPT_FOR_FILENAME" Value="true" />
|
||||
<Property Id="ALLUSERS" Value="1" />
|
||||
|
||||
<!-- Feature tree (minimal placeholders; expand per outline) -->
|
||||
<Feature Id="ProductComplete" Title="Verae Print Stamp" Level="1"
|
||||
Description="Full elevated product (binaries, scripts, docs, Start Menu).">
|
||||
<Feature Id="CoreBinaries" Title="Core binaries" Level="1"
|
||||
Description="verae-print.exe + verae-printhelper.exe under Program Files.">
|
||||
<ComponentGroupRef Id="BinFiles" />
|
||||
</Feature>
|
||||
<Feature Id="Scripts" Title="PowerShell helpers" Level="1"
|
||||
Description="install-printer, user-token, diag scripts.">
|
||||
<ComponentGroupRef Id="ScriptFiles" />
|
||||
</Feature>
|
||||
<Feature Id="Docs" Title="License and readme" Level="1">
|
||||
<ComponentGroupRef Id="DocFiles" />
|
||||
</Feature>
|
||||
<Feature Id="StartMenu" Title="Start Menu shortcuts" Level="1">
|
||||
<ComponentGroupRef Id="StartMenuShortcuts" />
|
||||
</Feature>
|
||||
<!--
|
||||
Feature PrintersAndHelper: Level=1 target — CustomActions only (no File).
|
||||
CA stubs are commented below; enable when StageFull + deferred CA tested.
|
||||
-->
|
||||
<Feature Id="PrintersAndHelper" Title="Printers and print helper" Level="1"
|
||||
Description="Deferred CA runs install-printer.ps1 (not File components). Stub only." />
|
||||
<!--
|
||||
Optional tray Feature Level=2 — prefer separate per-user MSI by default.
|
||||
<Feature Id="TrayPresence" Title="Tray presence" Level="2" ... />
|
||||
-->
|
||||
</Feature>
|
||||
|
||||
<!--
|
||||
CustomAction placeholders (NSIS parity) — NOT scheduled yet.
|
||||
Uncomment + schedule in InstallExecuteSequence when binaries stage cleanly.
|
||||
Conditions: NOT Installed; VERAE_SKIP_PRINTERS<>"1"; REMOVE~="ALL".
|
||||
|
||||
<CustomAction Id="CA_InstallPrinters"
|
||||
Directory="INSTALLFOLDER"
|
||||
ExeCommand="powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File "[INSTALLFOLDER]scripts\install-printer.ps1" -InstallDir "[INSTALLFOLDER]""
|
||||
Execute="deferred" Impersonate="no" Return="check" />
|
||||
<CustomAction Id="CA_UninstallPrinters"
|
||||
Directory="INSTALLFOLDER"
|
||||
ExeCommand="powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File "[INSTALLFOLDER]scripts\uninstall-printer.ps1""
|
||||
Execute="deferred" Impersonate="no" Return="ignore" />
|
||||
-->
|
||||
</Product>
|
||||
|
||||
<!-- Directory tree: ProgramFiles64\Verae\PrintStamp\{bin,scripts} + Start Menu -->
|
||||
<Fragment>
|
||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||
<Directory Id="ProgramFiles64Folder">
|
||||
<Directory Id="VeraePf" Name="Verae">
|
||||
<Directory Id="INSTALLFOLDER" Name="PrintStamp">
|
||||
<Directory Id="BinDir" Name="bin" />
|
||||
<Directory Id="ScriptsDir" Name="scripts" />
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
<Directory Id="ProgramMenuFolder">
|
||||
<Directory Id="VeraeStartMenuDir" Name="Verae" />
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Fragment>
|
||||
|
||||
<!--
|
||||
ComponentGroup BinFiles — StageFull\bin\
|
||||
light fails if Source paths missing. Stage before light:
|
||||
|
||||
StageFull\bin\verae-print.exe
|
||||
StageFull\bin\verae-printhelper.exe
|
||||
-->
|
||||
<Fragment>
|
||||
<ComponentGroup Id="BinFiles" Directory="BinDir">
|
||||
<Component Id="CmpVeraePrintExe" Guid="A11E0001-F001-4B01-9C01-000000000001" Win64="yes">
|
||||
<File Id="FileVeraePrintExe" Name="verae-print.exe"
|
||||
Source="$(var.StageDir)\bin\verae-print.exe" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="CmpVeraePrintHelperExe" Guid="A11E0002-F002-4B02-9C02-000000000002" Win64="yes">
|
||||
<File Id="FileVeraePrintHelperExe" Name="verae-printhelper.exe"
|
||||
Source="$(var.StageDir)\bin\verae-printhelper.exe" KeyPath="yes" />
|
||||
</Component>
|
||||
<!-- Machine InstallDir marker (no secrets) -->
|
||||
<Component Id="CmpInstallDirReg" Guid="A11E0003-F003-4B03-9C03-000000000003" Win64="yes">
|
||||
<RegistryValue
|
||||
Root="HKLM"
|
||||
Key="Software\Verae\PrintStamp"
|
||||
Name="InstallDir"
|
||||
Type="string"
|
||||
Value="[INSTALLFOLDER]"
|
||||
KeyPath="yes" />
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
|
||||
<!-- ComponentGroup ScriptFiles — StageFull\scripts\ -->
|
||||
<Fragment>
|
||||
<ComponentGroup Id="ScriptFiles" Directory="ScriptsDir">
|
||||
<Component Id="CmpCommonPs1" Guid="A11E0011-F011-4B11-9C11-000000000011" Win64="yes">
|
||||
<File Id="FileCommonPs1Full" Name="common.ps1"
|
||||
Source="$(var.StageDir)\scripts\common.ps1" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="CmpUserTokenPs1" Guid="A11E0012-F012-4B12-9C12-000000000012" Win64="yes">
|
||||
<File Id="FileUserTokenPs1" Name="user-token.ps1"
|
||||
Source="$(var.StageDir)\scripts\user-token.ps1" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="CmpInstallPrinterPs1" Guid="A11E0013-F013-4B13-9C13-000000000013" Win64="yes">
|
||||
<File Id="FileInstallPrinterPs1" Name="install-printer.ps1"
|
||||
Source="$(var.StageDir)\scripts\install-printer.ps1" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="CmpUninstallPrinterPs1" Guid="A11E0014-F014-4B14-9C14-000000000014" Win64="yes">
|
||||
<File Id="FileUninstallPrinterPs1" Name="uninstall-printer.ps1"
|
||||
Source="$(var.StageDir)\scripts\uninstall-printer.ps1" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="CmpVeraeDiagPs1" Guid="A11E0015-F015-4B15-9C15-000000000015" Win64="yes">
|
||||
<File Id="FileVeraeDiagPs1" Name="verae-diag.ps1"
|
||||
Source="$(var.StageDir)\scripts\verae-diag.ps1" KeyPath="yes" />
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
|
||||
<!-- ComponentGroup DocFiles — StageFull root docs -->
|
||||
<Fragment>
|
||||
<ComponentGroup Id="DocFiles" Directory="INSTALLFOLDER">
|
||||
<Component Id="CmpLicenseTxt" Guid="A11E0021-F021-4B21-9C21-000000000021" Win64="yes">
|
||||
<File Id="FileLicenseTxt" Name="LICENSE.txt"
|
||||
Source="$(var.StageDir)\LICENSE.txt" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="CmpReadmeWindows" Guid="A11E0022-F022-4B22-9C22-000000000022" Win64="yes">
|
||||
<File Id="FileReadmeWindows" Name="README-WINDOWS.txt"
|
||||
Source="$(var.StageDir)\README-WINDOWS.txt" KeyPath="yes" />
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
|
||||
<!--
|
||||
Start Menu shortcuts — non-advertised + HKLM keypath (elevated package).
|
||||
Targets assume bin\ helpers after InstallFiles. No advertised Feature linkage yet.
|
||||
-->
|
||||
<Fragment>
|
||||
<ComponentGroup Id="StartMenuShortcuts" Directory="VeraeStartMenuDir">
|
||||
<Component Id="CmpSmKeyDialog" Guid="A11E0031-F031-4B31-9C31-000000000031" Win64="yes">
|
||||
<Shortcut
|
||||
Id="ScKeyDialog"
|
||||
Name="Verae - Setup signing key"
|
||||
Description="Open Verae signing-key dialog (verae-printhelper key-dialog)."
|
||||
Target="[BinDir]verae-printhelper.exe"
|
||||
Arguments="key-dialog"
|
||||
WorkingDirectory="BinDir" />
|
||||
<RegistryValue
|
||||
Root="HKLM"
|
||||
Key="Software\Verae\PrintStamp\Shortcuts"
|
||||
Name="KeyDialog"
|
||||
Type="integer"
|
||||
Value="1"
|
||||
KeyPath="yes" />
|
||||
<RemoveFolder Id="RemoveVeraeSmFull" Directory="VeraeStartMenuDir" On="uninstall" />
|
||||
</Component>
|
||||
<Component Id="CmpSmStartHelper" Guid="A11E0032-F032-4B32-9C32-000000000032" Win64="yes">
|
||||
<Shortcut
|
||||
Id="ScStartHelper"
|
||||
Name="Verae - Start print helper"
|
||||
Description="Start verae-printhelper (IPP listen)."
|
||||
Target="[BinDir]verae-printhelper.exe"
|
||||
WorkingDirectory="BinDir" />
|
||||
<RegistryValue
|
||||
Root="HKLM"
|
||||
Key="Software\Verae\PrintStamp\Shortcuts"
|
||||
Name="StartHelper"
|
||||
Type="integer"
|
||||
Value="1"
|
||||
KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="CmpSmDiag" Guid="A11E0033-F033-4B33-9C33-000000000033" Win64="yes">
|
||||
<Shortcut
|
||||
Id="ScDiag"
|
||||
Name="Verae - Collect diagnostics"
|
||||
Description="Run verae-diag.ps1"
|
||||
Target="[System64Folder]WindowsPowerShell\v1.0\powershell.exe"
|
||||
Arguments="-NoProfile -ExecutionPolicy Bypass -File "[ScriptsDir]verae-diag.ps1""
|
||||
WorkingDirectory="ScriptsDir" />
|
||||
<RegistryValue
|
||||
Root="HKLM"
|
||||
Key="Software\Verae\PrintStamp\Shortcuts"
|
||||
Name="Diag"
|
||||
Type="integer"
|
||||
Value="1"
|
||||
KeyPath="yes" />
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# Product-Full.wxs.outline — full elevated Print Stamp MSI (scaffold)
|
||||
|
||||
**Status:** authoring outline only. Not a buildable `.wxs`. Does not produce MSI.
|
||||
**Status:** outline + **TODO-7 A1 stub** [`Product-Full.wxs`](Product-Full.wxs) (candle-oriented; light needs StageFull). Does not produce a shipping MSI yet.
|
||||
**Parity target:** [`../installer-windows.nsi`](../installer-windows.nsi) (elevated full product).
|
||||
**Not this package:** tray-only skeleton [`VeraeTrayShortcuts.wxs`](VeraeTrayShortcuts.wxs) (per-user, non-elevated).
|
||||
**Version:** `ProductVersion` ← `Get-VeraePackageVersion` → `internal/job.HelperVersion` (or `VERSION=`).
|
||||
|
|
@ -204,7 +204,7 @@ Same sources as [`../build-windows.ps1`](../build-windows.ps1) stage (Go build +
|
|||
| Per-user tray-only MSI | `VeraeTrayShortcuts.wxs` + `build-tray-msi.ps1` |
|
||||
| Authenticode / signtool | CI secrets; backlog |
|
||||
| NSIS GUI key wizard pages | Interactive NSIS until MSI UI or always-silent + first-run |
|
||||
| Real `.wxs` Product element / GUIDs locked | Future `Product-Full.wxs` (not this outline) |
|
||||
| Real `.wxs` Product element / GUIDs locked | Starter: [`Product-Full.wxs`](Product-Full.wxs) (TODO-7 A1); lock GUIDs after first light success |
|
||||
| MSIX / Print Support App | Deferred |
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -9,18 +9,28 @@
|
|||
|
||||
## msiexec pattern
|
||||
|
||||
**One-liner (fleet default — skip key UI, PrintIt/zapier channel):**
|
||||
|
||||
```bat
|
||||
msiexec /i VeraePrintStamp-windows-amd64-0.3.6.msi /qn /norestart VERAE_SKIP_KEY_SETUP=1 VERAE_CHANNEL=zapier VERAE_INSTALL_API_URL=https://customer.verae.ai
|
||||
```
|
||||
|
||||
**Expanded (same props + IPP loopback + verbose log):**
|
||||
|
||||
```bat
|
||||
msiexec /i VeraePrintStamp-windows-amd64-0.3.6.msi /qn /norestart ^
|
||||
VERAE_SKIP_KEY_SETUP=1 ^
|
||||
VERAE_CHANNEL=zapier ^
|
||||
VERAE_INSTALL_API_URL=https://customer.verae.ai ^
|
||||
VERAE_IPP_ADDR=127.0.0.1:8631
|
||||
VERAE_IPP_ADDR=127.0.0.1:8631 ^
|
||||
/L*v %TEMP%\verae-msi.log
|
||||
```
|
||||
|
||||
- `/qn` — no UI (required for “silent”).
|
||||
- `/norestart` — printers/helper must not force reboot; log if reboot pending.
|
||||
- Log (ops): `msiexec /i … /qn /L*v %TEMP%\verae-msi.log`.
|
||||
- Uninstall: `msiexec /x {ProductCode} /qn` or ARP.
|
||||
- Full MSI stub authoring: [`Product-Full.wxs`](Product-Full.wxs) (candle OK; light needs StageFull).
|
||||
|
||||
All names below are **public** Windows Installer properties (uppercase) unless marked private.
|
||||
|
||||
|
|
@ -143,7 +153,7 @@ msiexec /i VeraeTray-VERSION-peruser.msi /qn VERAE_IPP_ADDR=127.0.0.1:8631
|
|||
|
||||
## Implementation checklist (Wave 2+ / I1-adjacent)
|
||||
|
||||
- [ ] Declare properties in `Product-Full.wxs` with the defaults above.
|
||||
- [x] Declare properties in `Product-Full.wxs` with the defaults above. (TODO-7 A1 stub)
|
||||
- [ ] Condition key-related CAs on `VERAE_SKIP_KEY_SETUP<>"1"`.
|
||||
- [ ] Condition printer CA on `VERAE_SKIP_PRINTERS<>"1"`.
|
||||
- [ ] Persist channel + portal URL for helper/CLI (config file or HKLM non-secret values only).
|
||||
|
|
|
|||
|
|
@ -21,54 +21,58 @@
|
|||
.btn.secondary{background:var(--card,#fff);color:var(--plum,#531B59);border-color:#CDBFD4}
|
||||
.btn.primary{background:var(--plum,#531B59);color:#fff}
|
||||
.btn.tonal{background:var(--plum-100,#EBD9EE);color:var(--plum-d,#3E1343);border:0}
|
||||
.wrap{max-width:900px;margin:0 auto;padding:24px 16px 48px}
|
||||
/* CERT-01 paper sheet — plum outer + gold inner frame */
|
||||
/* CERT-01 paper ~816px content column (design mock) */
|
||||
.wrap{max-width:816px;margin:0 auto;padding:24px 16px 48px}
|
||||
/* CERT-01 paper sheet — plum 1.5px outer + gold 0.5px inner (inset 22/28 mock) */
|
||||
.sheet{
|
||||
background:#fff;position:relative;border-radius:4px;
|
||||
box-shadow:0 12px 40px rgba(30,10,40,.18),0 2px 8px rgba(30,10,40,.08);
|
||||
padding:48px 56px 56px;min-height:720px;
|
||||
/* content pad matches design 56×64 inside double frame */
|
||||
padding:56px 64px;min-height:720px;
|
||||
box-shadow:
|
||||
0 12px 40px rgba(30,10,40,.18),
|
||||
0 12px 40px rgba(30,10,40,.25),
|
||||
0 2px 8px rgba(30,10,40,.12),
|
||||
inset 0 0 0 1.5px var(--plum,#531B59),
|
||||
inset 0 0 0 6px #fff,
|
||||
inset 0 0 0 6.5px var(--gold,#C4A85A);
|
||||
inset 0 0 0 7.5px #fff,
|
||||
inset 0 0 0 8px var(--gold,#C4A85A);
|
||||
}
|
||||
.head{display:flex;justify-content:space-between;align-items:flex-start;gap:16px}
|
||||
.logo-row{display:flex;align-items:center;gap:10px}
|
||||
.logo-row img{height:36px;width:auto;display:block}
|
||||
.mark{width:36px;height:36px;border-radius:9px;background:linear-gradient(140deg,var(--plum,#531B59),var(--violet,#6B3FD6));display:grid;place-items:center;color:#fff;font-weight:800;font-size:13px;flex:none}
|
||||
.logo-row img{height:40px;width:auto;display:block}
|
||||
.mark{width:40px;height:40px;border-radius:9px;background:linear-gradient(140deg,var(--plum,#531B59),var(--violet,#6B3FD6));display:grid;place-items:center;color:#fff;font-weight:800;font-size:13px;flex:none}
|
||||
.cert-meta{text-align:right;font-size:11px;color:var(--muted,#5E5470);line-height:1.45}
|
||||
/* Title: Fraunces via Google Fonts CDN; Georgia/Times offline-ish fallback */
|
||||
/* Design: font 700 26px serif; title block margin 26px 0 18px */
|
||||
.title{
|
||||
text-align:center;margin:28px 0 0;
|
||||
text-align:center;margin:26px 0 0;
|
||||
font-family:'Fraunces',var(--font-serif,Georgia,serif),Georgia,'Times New Roman',Times,serif;
|
||||
font-weight:700;font-size:clamp(22px,3.2vw,26px);line-height:1.25;letter-spacing:-0.01em;color:var(--ink,#1D1534);
|
||||
}
|
||||
.subtitle{text-align:center;font-size:12.5px;color:var(--muted,#5E5470);margin:8px auto 0;max-width:34rem;line-height:1.45}
|
||||
.rule{height:2px;background:var(--gold,#C4A85A);width:120px;margin:14px auto 0;border:0}
|
||||
.subtitle{text-align:center;font-size:12.5px;color:var(--muted,#5E5470);margin:6px auto 0;max-width:34rem;line-height:1.45}
|
||||
/* Gold rule under title — CERT-01 weight 2px × 120px */
|
||||
.rule{height:2px;background:var(--gold,#C4A85A);width:120px;margin:14px auto 0;border:0;border-radius:1px}
|
||||
.banner{
|
||||
margin-top:20px;border:2px solid var(--ok,#0E6B3A);background:var(--ok-bg,#E3F5EA);
|
||||
margin-top:18px;border:2px solid var(--ok,#0E6B3A);background:var(--ok-bg,#E3F5EA);
|
||||
border-radius:10px;padding:12px 16px;display:flex;gap:12px;align-items:center;
|
||||
}
|
||||
.banner.provisional{border-color:var(--pend-bd,#D5DAE1);background:var(--pend-bg,#EEF0F3)}
|
||||
.banner .check{font-size:28px;line-height:1;color:var(--ok,#0E6B3A);font-weight:700}
|
||||
.banner .check{font-size:34px;line-height:1;color:var(--ok,#0E6B3A);font-weight:700}
|
||||
.banner.provisional .check{color:var(--pend,#475467)}
|
||||
.banner .bt{font:700 15px/1.3 var(--font-sans),system-ui;color:var(--ok,#0E6B3A)}
|
||||
.banner .bt{font:700 16px/1.3 var(--font-sans),system-ui;color:var(--ok,#0E6B3A)}
|
||||
.banner.provisional .bt{color:var(--pend,#475467)}
|
||||
.banner .bs{font-size:12px;color:#1D4D33;margin-top:2px}
|
||||
.banner.provisional .bs{color:var(--muted,#5E5470)}
|
||||
.kv{display:grid;grid-template-columns:150px 1fr;gap:10px 16px;margin-top:22px;font-size:13px}
|
||||
.kv{display:grid;grid-template-columns:150px 1fr;gap:10px 16px;margin-top:20px;font-size:13px}
|
||||
.kv dt{color:var(--muted,#5E5470);font-weight:600;font-size:12px;padding-top:2px}
|
||||
.kv dd{margin:0;word-break:break-all;line-height:1.45}
|
||||
.mono{font-family:var(--font-mono),'JetBrains Mono',ui-monospace,SF Mono,Menlo,monospace;font-size:11px;letter-spacing:-0.01em}
|
||||
.mh{display:inline-flex;align-items:flex-start;gap:4px;flex-wrap:wrap;background:var(--surface-2,#F4F1F8);border:1px solid var(--line,#E3DEF0);border-radius:6px;padding:4px 8px}
|
||||
.mh .pfx{color:var(--plum,#531B59);font-weight:600}
|
||||
.verify-box{
|
||||
display:flex;gap:18px;align-items:flex-start;margin-top:24px;padding:16px;
|
||||
display:flex;gap:22px;align-items:flex-start;margin-top:22px;padding:16px;
|
||||
border:1px solid var(--line,#E3DEF0);border-radius:10px;background:var(--bg,#FBFAFD);
|
||||
}
|
||||
.qr{
|
||||
width:112px;height:112px;flex:none;border:2px solid var(--ink,#1D1534);border-radius:8px;
|
||||
width:130px;height:130px;flex:none;border:1px solid var(--ink,#1D1534);border-radius:6px;padding:4px;box-sizing:border-box;
|
||||
/* Offline / provisional placeholder — print-safe without CDN */
|
||||
background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 29 29'%3E%3Cpath fill='%231D1534' d='M1 1h7v7H1zm2 2v3h3V3zm10 0h7v7h-7zm2 2v3h3V3zM1 13h7v7H1zm2 2v3h3v-3zm18-14h7v7h-7zm2 2v3h3V3zm0 10h3v2h-3zm-4 0h2v2h-2zm4 4h3v3h-3zm-4 2h2v3h-2zm-2-4h2v2h-2zm6 6h3v2h-3zm-8 0h2v2h-2zm4-2h2v4h-2zm-6 0h2v2h-2z'/%3E%3C/svg%3E") center/72% no-repeat;
|
||||
position:relative;overflow:hidden;
|
||||
|
|
@ -94,9 +98,9 @@
|
|||
.wrap{padding:0;max-width:none}
|
||||
.sheet{
|
||||
box-shadow:none!important;
|
||||
border:2px solid #531B59;
|
||||
outline:1px solid #C4A85A;outline-offset:-5px;
|
||||
margin:0;border-radius:0;min-height:0;padding:28px 36px 36px;
|
||||
border:1.5px solid #531B59;
|
||||
outline:0.5px solid #C4A85A;outline-offset:-6px;
|
||||
margin:0;border-radius:0;min-height:0;padding:40px 48px 48px;
|
||||
-webkit-print-color-adjust:exact;print-color-adjust:exact;
|
||||
}
|
||||
/* CDN may be absent offline; keep plum/gold frame + system serif title */
|
||||
|
|
@ -104,8 +108,8 @@
|
|||
.rule,.banner,.mh,.qr,.qr img.qr-bitmap,.qr canvas.qr-bitmap{
|
||||
-webkit-print-color-adjust:exact;print-color-adjust:exact
|
||||
}
|
||||
.rule{background:#C4A85A!important}
|
||||
.qr{width:96px;height:96px;border-color:#1D1534!important}
|
||||
.rule{background:#C4A85A!important;height:2px!important}
|
||||
.qr{width:110px;height:110px;border-color:#1D1534!important;padding:3px}
|
||||
a{color:inherit;text-decoration:none}
|
||||
}
|
||||
@page{size:letter;margin:12mm}
|
||||
|
|
@ -126,7 +130,7 @@
|
|||
<article class="sheet" id="cert" aria-label="Verae Timestamp Certificate">
|
||||
<div class="head">
|
||||
<div class="logo-row">
|
||||
<img src="brand/logo/verae-mark.svg" alt="Verae" width="36" height="36"
|
||||
<img src="brand/logo/verae-mark.svg" alt="Verae" width="40" height="40"
|
||||
onerror="this.style.display='none';this.nextElementSibling.style.display='grid'"/>
|
||||
<span class="mark" style="display:none" aria-hidden="true">V</span>
|
||||
<span style="font-weight:700;font-size:14px;color:var(--plum,#531B59)">Verae</span>
|
||||
|
|
|
|||
|
|
@ -32,12 +32,24 @@ print/PDF-from-browser paths stay consistent offline.
|
|||
fonts/
|
||||
README.md (this file)
|
||||
.gitkeep
|
||||
fetch-fonts.example.sh # curl placeholders; no download until uncommented
|
||||
fraunces-opsz-wght-600.woff2 # variable or static; opsz + weight 600
|
||||
fraunces-opsz-wght-700.woff2 # weight 700 (title)
|
||||
jetbrains-mono-500.woff2 # optional; digests already have mono stack
|
||||
OFL.txt / LICENSE # required license text beside binaries
|
||||
```
|
||||
|
||||
### Fetch scaffold (TODO-7 A7)
|
||||
|
||||
[`fetch-fonts.example.sh`](./fetch-fonts.example.sh) — **example only**. Comment-only
|
||||
upstream URLs (google/fonts OFL trees, gstatic discovery). Does not curl until
|
||||
an operator uncomments a pinned-commit block after license review. Smoke:
|
||||
|
||||
```bash
|
||||
./fetch-fonts.example.sh # prints instructions; no network
|
||||
./fetch-fonts.example.sh --check # exit 1 until woff2 + OFL.txt present
|
||||
```
|
||||
|
||||
Exact filenames may follow upstream release naming; keep them stable once
|
||||
wired so deploys and cache busting stay simple.
|
||||
|
||||
|
|
@ -82,6 +94,8 @@ wired so deploys and cache busting stay simple.
|
|||
| Step | State |
|
||||
|------|-------|
|
||||
| Placeholder dir + this plan | **Done** (L7) |
|
||||
| `fetch-fonts.example.sh` placeholders | **Done** (TODO-7 A7) — no binaries fetched |
|
||||
| Web cert spacing toward CERT-01 | **Partial** (TODO-7 A7) — padding/rule/QR; not full pixel |
|
||||
| woff2 binaries + LICENSE | Not started |
|
||||
| `@font-face` + drop CDN links | Not started |
|
||||
| Pixel match / PDF embed claim | Not started — do not claim |
|
||||
|
|
|
|||
97
site/printit.verae.ai/fonts/fetch-fonts.example.sh
Executable file
97
site/printit.verae.ai/fonts/fetch-fonts.example.sh
Executable file
|
|
@ -0,0 +1,97 @@
|
|||
#!/usr/bin/env bash
|
||||
# fetch-fonts.example.sh — placeholder download steps for CERT self-host fonts
|
||||
#
|
||||
# Does NOT download by default. Review licenses, then uncomment ONE source
|
||||
# block and re-run. Binaries stay uncommitted until OFL/LICENSE is beside them.
|
||||
#
|
||||
# Target dir: this script's directory (site/printit.verae.ai/fonts/).
|
||||
# Wire-up: see README.md in this folder; certificate.html still uses CDN until
|
||||
# local @font-face + woff2 land.
|
||||
#
|
||||
# License reminder:
|
||||
# Fraunces — SIL Open Font License 1.1 (OFL)
|
||||
# JetBrains Mono — SIL Open Font License 1.1 (OFL)
|
||||
# Commit OFL.txt / LICENSE next to any binary. Do not strip copyright notices.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
cd "$DIR"
|
||||
|
||||
echo "CERT fonts dir: $DIR"
|
||||
echo "This is an EXAMPLE script — no network fetch runs unless you uncomment."
|
||||
echo
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Planned local names (keep stable once wired — see README.md)
|
||||
# ---------------------------------------------------------------------------
|
||||
# fraunces-opsz-wght-600.woff2
|
||||
# fraunces-opsz-wght-700.woff2
|
||||
# jetbrains-mono-500.woff2
|
||||
# OFL.txt
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Option A — google/fonts GitHub release trees (OFL sources; pick a tag/commit)
|
||||
# Comment-only reference URLs (do not curl until you pin a commit + verify OFL):
|
||||
#
|
||||
# # Fraunces variable / static under apache/ofl:
|
||||
# # https://github.com/google/fonts/tree/main/ofl/fraunces
|
||||
# # raw example (PIN A COMMIT — main moves):
|
||||
# # https://raw.githubusercontent.com/google/fonts/<COMMIT>/ofl/fraunces/Fraunces%5Bopsz%2CSOFT%2CWONK%2Cwght%5D.ttf
|
||||
# #
|
||||
# # JetBrains Mono:
|
||||
# # https://github.com/google/fonts/tree/main/ofl/jetbrainsmono
|
||||
# # https://raw.githubusercontent.com/google/fonts/<COMMIT>/ofl/jetbrainsmono/JetBrainsMono%5Bwght%5D.ttf
|
||||
# #
|
||||
# # Upstream project pages (also OFL):
|
||||
# # https://github.com/underscript-type/fraunces
|
||||
# # https://github.com/JetBrains/JetBrainsMono
|
||||
#
|
||||
# Example curl skeleton (still commented — replace COMMIT and convert TTF→woff2
|
||||
# with fonttools/woff2_compress; do not commit unlicensed or renamed dumps):
|
||||
#
|
||||
# # COMMIT=________________________________
|
||||
# # curl -fsSL -o /tmp/fraunces.ttf \
|
||||
# # "https://raw.githubusercontent.com/google/fonts/${COMMIT}/ofl/fraunces/Fraunces%5Bopsz%2CSOFT%2CWONK%2Cwght%5D.ttf"
|
||||
# # # subset + woff2, then:
|
||||
# # # cp fraunces-opsz-wght-700.woff2 "$DIR/"
|
||||
# # curl -fsSL -o "$DIR/OFL.txt" \
|
||||
# # "https://raw.githubusercontent.com/google/fonts/${COMMIT}/ofl/fraunces/OFL.txt"
|
||||
#
|
||||
# ---------------------------------------------------------------------------
|
||||
# Option B — fonts.google.com CSS API (CDN helper; not for production self-host
|
||||
# of binaries in-repo — use only to discover gstatic file URLs you already
|
||||
# have rights to cache under OFL):
|
||||
#
|
||||
# # curl -fsSL -A "Mozilla/5.0" \
|
||||
# # "https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,600;9..144,700&family=JetBrains+Mono:wght@500&display=swap"
|
||||
# # # Then curl each https://fonts.gstatic.com/...woff2 URL into this dir
|
||||
# # # with the planned filenames above. Still need OFL.txt from Option A.
|
||||
#
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
if [[ "${1:-}" == "--check" ]]; then
|
||||
missing=0
|
||||
for f in fraunces-opsz-wght-600.woff2 fraunces-opsz-wght-700.woff2 OFL.txt; do
|
||||
if [[ -f "$f" ]]; then
|
||||
echo "OK $f"
|
||||
else
|
||||
echo "MISS $f"
|
||||
missing=1
|
||||
fi
|
||||
done
|
||||
if [[ -f jetbrains-mono-500.woff2 ]]; then
|
||||
echo "OK jetbrains-mono-500.woff2 (optional)"
|
||||
else
|
||||
echo "skip jetbrains-mono-500.woff2 (optional)"
|
||||
fi
|
||||
exit "$missing"
|
||||
fi
|
||||
|
||||
echo "No downloads performed."
|
||||
echo "1. Pin a google/fonts commit or upstream release."
|
||||
echo "2. Uncomment a curl block after license review."
|
||||
echo "3. Place woff2 + OFL.txt here; run: $0 --check"
|
||||
echo "4. Add @font-face in certificate.html; drop Google Fonts <link> tags."
|
||||
echo
|
||||
echo "Status: placeholder only — binaries not started (MULTI-AGENT-TODO-7 A7)."
|
||||
Loading…
Reference in a new issue