feat: MSI full outline, silent props, HelperVersion NSIS sync, CERT fonts plan
TODO-6 L1/I1/L7/I7 — commercial + CERT next-level scaffolds.
This commit is contained in:
parent
f766e537ba
commit
46fa1aa0d4
13 changed files with 600 additions and 6 deletions
|
|
@ -190,6 +190,7 @@ Site page: [`site/printit.verae.ai/certificate.html`](../site/printit.verae.ai/c
|
|||
| Live verify | Link to `explorer.verae.ai/time?receipt=&sha256=` (same as `pdfcert.LookupURL`) |
|
||||
| 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 |
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ const (
|
|||
)
|
||||
|
||||
// Minimal fallback when Node/jsPDF is unavailable.
|
||||
//
|
||||
// Fonts: PDF core / library faces only (Times title, Helvetica body).
|
||||
// Do not claim Fraunces embed — no AddFont/TTF path unless a licensed
|
||||
// Fraunces file is wired in later (OPEN-ISSUES #7 / TODO-6 I7).
|
||||
func buildFallbackGofpdf(in Input, docName, created, scope, merkle, lookup string) ([]byte, error) {
|
||||
pdf := gofpdf.New("P", "pt", "Letter", "")
|
||||
pdf.SetCompression(false)
|
||||
|
|
@ -51,13 +55,13 @@ func buildFallbackGofpdf(in Input, docName, created, scope, merkle, lookup strin
|
|||
}
|
||||
}
|
||||
|
||||
// Gold-d eyebrow (BrandingBook §12)
|
||||
// Gold-d eyebrow (BrandingBook §12) — Helvetica core font (not Fraunces)
|
||||
pdf.SetTextColor(goldDR, goldDG, goldDB)
|
||||
pdf.SetFont("Helvetica", "B", 9)
|
||||
pdf.SetXY(margin, 92)
|
||||
pdf.CellFormat(pageW-margin*2, 11, "TIMESTAMP CERTIFICATE", "", 0, "C", false, 0, "")
|
||||
|
||||
// Title — CERT-01 serif / ink
|
||||
// Title — Times core serif stand-in for CERT-01 Fraunces (no embed unless present)
|
||||
pdf.SetTextColor(inkR, inkG, inkB)
|
||||
pdf.SetFont("Times", "B", 22)
|
||||
pdf.SetXY(margin, 106)
|
||||
|
|
|
|||
|
|
@ -103,6 +103,10 @@ func LookupURL(sha256, receiptID string) string {
|
|||
// Build returns the branded CERT-01 certificate PDF (plum/gold, final-only
|
||||
// evidence copy). Prefers platform/registration-pdf/build.mjs when Node is
|
||||
// available; otherwise gofpdf fallback.
|
||||
//
|
||||
// Typography: both paths use PDF standard fonts (Times title, Helvetica body).
|
||||
// Fraunces is the brand serif on web CERT shells only — the issued PDF does
|
||||
// not embed Fraunces unless a licensed face is explicitly added later.
|
||||
func Build(in Input) ([]byte, error) {
|
||||
created := in.CreatedAt
|
||||
if created == "" {
|
||||
|
|
|
|||
|
|
@ -259,3 +259,69 @@ func TestFindBuildScriptNoResearchHardcode(t *testing.T) {
|
|||
t.Fatal("build.mjs must keep hold≠evidence disclaimer")
|
||||
}
|
||||
}
|
||||
|
||||
// TestBuildUsesSystemFontsNotFraunces: issued PDF uses core Times/Helvetica;
|
||||
// do not claim Fraunces embed until a licensed face is explicitly added (I7).
|
||||
func TestBuildUsesSystemFontsNotFraunces(t *testing.T) {
|
||||
sha := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
in := Input{
|
||||
DocumentName: "FontCheck.pdf",
|
||||
SHA256: sha,
|
||||
ReceiptID: "QmFontCheck",
|
||||
CreatedAt: "2026-10-03T12:00:00Z",
|
||||
Merkle: "99",
|
||||
}
|
||||
// gofpdf path — deterministic, no Node.
|
||||
pdf, err := buildFallbackGofpdf(in, "FontCheck.pdf", in.CreatedAt, "GLOBAL", in.Merkle, LookupURL(sha, in.ReceiptID))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s := string(pdf)
|
||||
if strings.Contains(s, "Fraunces") || strings.Contains(s, "fraunces") {
|
||||
t.Fatal("PDF must not embed or name Fraunces unless a licensed face is wired")
|
||||
}
|
||||
// Standard 14 base fonts (gofpdf): Times title + Helvetica body.
|
||||
if !strings.Contains(s, "/BaseFont /Times") && !strings.Contains(s, "/BaseFont/Times") {
|
||||
if !strings.Contains(s, "Times-Bold") && !strings.Contains(s, "Times-Roman") {
|
||||
t.Fatal("expected PDF core Times base font for title serif stand-in")
|
||||
}
|
||||
}
|
||||
if !strings.Contains(s, "/BaseFont /Helvetica") && !strings.Contains(s, "/BaseFont/Helvetica") {
|
||||
if !strings.Contains(s, "Helvetica") {
|
||||
t.Fatal("expected PDF core Helvetica for body text")
|
||||
}
|
||||
}
|
||||
// Full Build() path must also stay honest (node or gofpdf).
|
||||
b, err := Build(in)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
full := string(b)
|
||||
if strings.Contains(full, "Fraunces") || strings.Contains(full, "fraunces") {
|
||||
t.Fatal("Build() PDF must not claim Fraunces embed")
|
||||
}
|
||||
// build.mjs must keep standard faces; no real addFont(...) call for Fraunces.
|
||||
script := findBuildScript()
|
||||
if script == "" {
|
||||
t.Fatal("expected build.mjs")
|
||||
}
|
||||
src, err := os.ReadFile(script)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
body := string(src)
|
||||
lower := strings.ToLower(body)
|
||||
// Call-site only: addFont(…fraunces…) — prose notes may mention both words.
|
||||
if strings.Contains(lower, `addfont(`) && strings.Contains(lower, "fraunces") {
|
||||
// Narrow: same line or adjacent token would be an embed; bare prose is OK.
|
||||
for _, line := range strings.Split(body, "\n") {
|
||||
ll := strings.ToLower(line)
|
||||
if strings.Contains(ll, "addfont(") && strings.Contains(ll, "fraunces") {
|
||||
t.Fatal("build.mjs must not addFont(Fraunces) until licensed binary lands")
|
||||
}
|
||||
}
|
||||
}
|
||||
if !strings.Contains(body, `setFont("times"`) || !strings.Contains(body, `setFont("helvetica"`) {
|
||||
t.Fatal("build.mjs should keep times/helvetica standard faces")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@
|
|||
* - no mcp.verae.ai URLs
|
||||
* - footer: Verae LLC · verae.ai · explorer.verae.ai
|
||||
*
|
||||
* Fonts: jsPDF standard faces only (times title, helvetica body). Do not claim
|
||||
* Fraunces embed — no addFont/TTF unless a licensed Fraunces file is wired later.
|
||||
*
|
||||
* Usage: node build.mjs <input.json> <output.pdf>
|
||||
* input.json: { file, size_bytes, sha256, multihash?, blake3?, blake3_multihash?,
|
||||
* scope, receipt_id, created_at, merkle, lookup_url? }
|
||||
|
|
@ -143,7 +146,7 @@ async function buildPdf(row) {
|
|||
doc.setTextColor(...goldD);
|
||||
doc.text("TIMESTAMP CERTIFICATE", pageW / 2, 98, { align: "center" });
|
||||
|
||||
// Title — CERT-01 serif / ink
|
||||
// Title — Times core serif stand-in for CERT-01 Fraunces (no embed unless present)
|
||||
doc.setFont("times", "bold");
|
||||
doc.setFontSize(22);
|
||||
doc.setTextColor(...ink);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,34 @@
|
|||
# Build the Windows installer natively on Windows (Go + NSIS).
|
||||
# Usage: powershell -ExecutionPolicy Bypass -File platform\windows\build-windows.ps1 [-Arch amd64|arm64]
|
||||
param([string]$Arch = "amd64", [string]$Version = "0.3.2")
|
||||
# Usage: powershell -ExecutionPolicy Bypass -File platform\windows\build-windows.ps1 [-Arch amd64|arm64] [-Version x.y.z]
|
||||
# Version default: Get-VeraePackageVersion → internal/job.HelperVersion (or $env:VERSION).
|
||||
param([string]$Arch = "amd64", [string]$Version = "")
|
||||
$ErrorActionPreference = "Stop"
|
||||
$Root = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path
|
||||
$Out = Join-Path $Root "dist\windows"
|
||||
$Stage = Join-Path $Out "stage-$Arch"
|
||||
$Go = (Get-Command go -ErrorAction SilentlyContinue).Source; if (-not $Go) { $Go = "C:\Program Files\Go\bin\go.exe" }
|
||||
$Nsis = (Get-Command makensis -ErrorAction SilentlyContinue).Source; if (-not $Nsis) { $Nsis = "${env:ProgramFiles(x86)}\NSIS\makensis.exe" }
|
||||
|
||||
# Package version = internal/job.HelperVersion (single source of truth).
|
||||
$common = Join-Path $PSScriptRoot "scripts\common.ps1"
|
||||
if (Test-Path -LiteralPath $common) { . $common }
|
||||
if (-not $Version) {
|
||||
if (Get-Command Get-VeraePackageVersion -ErrorAction SilentlyContinue) {
|
||||
$Version = Get-VeraePackageVersion
|
||||
} elseif ($env:VERSION -and $env:VERSION.Trim()) {
|
||||
$Version = $env:VERSION.Trim()
|
||||
} else {
|
||||
$jobGo = Join-Path $Root "internal\job\job.go"
|
||||
if (Test-Path -LiteralPath $jobGo) {
|
||||
$line = Select-String -Path $jobGo -Pattern 'HelperVersion\s*=\s*"([^"]+)"' -ErrorAction SilentlyContinue |
|
||||
Select-Object -First 1
|
||||
if ($line -and $line.Matches.Count -gt 0) {
|
||||
$Version = $line.Matches[0].Groups[1].Value
|
||||
}
|
||||
}
|
||||
}
|
||||
if (-not $Version) { $Version = "0.3.6" } # must match HelperVersion fallback
|
||||
}
|
||||
Remove-Item -Recurse -Force $Stage -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Force -Path $Stage, (Join-Path $Stage "scripts") | Out-Null
|
||||
Push-Location $Root
|
||||
|
|
|
|||
|
|
@ -6,8 +6,12 @@
|
|||
; only through files in $PLUGINSDIR that are deleted immediately and are never logged.
|
||||
; Logging: one concise %ProgramData%\Verae\logs\installer-<time>.log with errors and
|
||||
; the per-step result summary. (Start > Verae > Collect diagnostics for troubleshooting.)
|
||||
;
|
||||
; VERSION: prefer /DVERSION= from build-windows.ps1 (Get-VeraePackageVersion →
|
||||
; internal/job.HelperVersion, or $env:VERSION / -Version). Bare makensis fallback
|
||||
; below must match current HelperVersion when the driver is not used.
|
||||
!ifndef VERSION
|
||||
!define VERSION "0.3.2"
|
||||
!define VERSION "0.3.6"
|
||||
!endif
|
||||
!ifndef ARCH
|
||||
!define ARCH "amd64"
|
||||
|
|
|
|||
|
|
@ -13,6 +13,11 @@
|
|||
|
||||
Scope: non-elevated per-user tray shortcuts only — not the full commercial MSI.
|
||||
|
||||
.EXITCODES
|
||||
0 Success: MSI built, or WiX missing and tip printed (default; day-to-day OK).
|
||||
1 Failure: -SkipTip with WiX missing; missing .wxs/tray payload; candle/light
|
||||
non-zero; or Write-Error / unhandled terminating error ($ErrorActionPreference Stop).
|
||||
|
||||
.PARAMETER Version
|
||||
Override package version (default: Get-VeraePackageVersion / HelperVersion).
|
||||
|
||||
|
|
|
|||
232
platform/windows/wix/Product-Full.wxs.outline
Normal file
232
platform/windows/wix/Product-Full.wxs.outline
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
# Product-Full.wxs.outline — full elevated Print Stamp MSI (scaffold)
|
||||
|
||||
**Status:** authoring outline only. Not a buildable `.wxs`. Does not produce MSI.
|
||||
**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=`).
|
||||
**Backlog:** [`docs/COMMERCIAL-INSTALLER-BACKLOG.md`](../../../docs/COMMERCIAL-INSTALLER-BACKLOG.md).
|
||||
**Silent props:** [`SILENT_PROPS.md`](SILENT_PROPS.md).
|
||||
|
||||
---
|
||||
|
||||
## Product identity (proposed)
|
||||
|
||||
| Field | Value | Notes |
|
||||
|-------|--------|------|
|
||||
| Name | Verae Print Stamp | ARP display name |
|
||||
| Manufacturer | Verae | |
|
||||
| InstallScope | `perMachine` | Requires elevation (`InstallPrivileges=elevated`) |
|
||||
| InstallDir default | `ProgramFiles64Folder\Verae\PrintStamp` | Same as NSIS `$PROGRAMFILES64\Verae\PrintStamp` |
|
||||
| UpgradeCode | **TBD stable GUID** (full product; distinct from tray `A7C3E91B-…`) | MajorUpgrade; never share tray UpgradeCode |
|
||||
| ProductCode | `*` per build | |
|
||||
| Package code | auto | |
|
||||
| Arch | x64 (amd64); arm64 later | NSIS rejects non-x64 today |
|
||||
| Output name | `VeraePrintStamp-windows-<arch>-<version>.msi` | |
|
||||
|
||||
ARP: `ARPURLINFOABOUT=https://printit.verae.ai/`, `ARPHELPLINK=https://customer.verae.ai/portal/`, Publisher Verae, DisplayVersion=`ProductVersion`.
|
||||
|
||||
---
|
||||
|
||||
## Directory tree
|
||||
|
||||
```
|
||||
TARGETDIR
|
||||
└── ProgramFiles64Folder
|
||||
└── Verae (Id=VeraePf)
|
||||
└── PrintStamp (Id=INSTALLFOLDER) ← [INSTALLDIR]
|
||||
├── bin\ ← binaries
|
||||
└── scripts\ ← PowerShell helpers
|
||||
ProgramMenuFolder
|
||||
└── Verae (Id=VeraeStartMenuDir) ← Start Menu shortcuts
|
||||
```
|
||||
|
||||
Optional later: `CommonAppDataFolder\Verae\logs` created by CA (NSIS uses `%ProgramData%\Verae\logs` at runtime, not as a permanent component).
|
||||
|
||||
---
|
||||
|
||||
## Component groups (file payload)
|
||||
|
||||
Mirror NSIS `Section` install surface (`installer-windows.nsi` ~613–624).
|
||||
|
||||
### ComponentGroup `BinFiles` → `INSTALLFOLDER\bin`
|
||||
|
||||
| Component Id | File | Source (stage) | KeyPath | Notes |
|
||||
|--------------|------|----------------|---------|-------|
|
||||
| `CmpVeraePrintExe` | `verae-print.exe` | `Stage\bin\verae-print.exe` | yes | CLI + installer-account |
|
||||
| `CmpVeraePrintHelperExe` | `verae-printhelper.exe` | `Stage\bin\verae-printhelper.exe` | yes | GUI subsystem (`-H windowsgui`); same main package |
|
||||
|
||||
### ComponentGroup `ScriptFiles` → `INSTALLFOLDER\scripts`
|
||||
|
||||
| Component Id | File | Source | KeyPath | Used by |
|
||||
|--------------|------|--------|---------|---------|
|
||||
| `CmpCommonPs1` | `common.ps1` | `Stage\scripts\common.ps1` | yes | version helper, console user |
|
||||
| `CmpUserTokenPs1` | `user-token.ps1` | `Stage\scripts\user-token.ps1` | yes | key status/save |
|
||||
| `CmpInstallPrinterPs1` | `install-printer.ps1` | `Stage\scripts\install-printer.ps1` | yes | deferred CA (elevated) |
|
||||
| `CmpUninstallPrinterPs1` | `uninstall-printer.ps1` | `Stage\scripts\uninstall-printer.ps1` | yes | uninstall CA |
|
||||
| `CmpVeraeDiagPs1` | `verae-diag.ps1` | `Stage\scripts\verae-diag.ps1` | yes | Start Menu diagnostics |
|
||||
|
||||
**Optional (not in NSIS product tree today; include if tray ships with full MSI):**
|
||||
|
||||
| Component Id | File | Notes |
|
||||
|--------------|------|-------|
|
||||
| `CmpVeraeTrayPs1` | `verae-tray.ps1` | Presence UI; may stay in separate per-user MSI |
|
||||
| `CmpInstallTrayStartupPs1` | `install-tray-startup.ps1` | User-level Startup; prefer impersonated CA |
|
||||
|
||||
### ComponentGroup `DocFiles` → `INSTALLFOLDER`
|
||||
|
||||
| Component Id | File | Source | KeyPath |
|
||||
|--------------|------|--------|---------|
|
||||
| `CmpLicenseTxt` | `LICENSE.txt` | `Stage\LICENSE.txt` | yes |
|
||||
| `CmpReadmeWindows` | `README-WINDOWS.txt` | `Stage\README-WINDOWS.txt` | yes |
|
||||
|
||||
### ComponentGroup `StartMenuShortcuts` → `VeraeStartMenuDir`
|
||||
|
||||
| Component Id | Shortcut name | Target | Arguments |
|
||||
|--------------|---------------|--------|-----------|
|
||||
| `CmpSmKeyDialog` | Verae - Setup signing key | `[INSTALLFOLDER]bin\verae-printhelper.exe` | `key-dialog` |
|
||||
| `CmpSmStartHelper` | Verae - Start print helper | `[INSTALLFOLDER]bin\verae-printhelper.exe` | (none) |
|
||||
| `CmpSmLogs` | Verae - Logs folder | `explorer.exe` | `%ProgramData%\Verae\logs` |
|
||||
| `CmpSmDiag` | Verae - Collect diagnostics | `powershell.exe` | `-NoProfile -ExecutionPolicy Bypass -File "[INSTALLFOLDER]scripts\verae-diag.ps1"` |
|
||||
| `CmpSmUninstall` | Uninstall Verae Print Stamp | ARP / `msiexec /x [ProductCode]` | (WiX standard) |
|
||||
|
||||
Each shortcut component: `RegistryValue` HKCU/HKLM keypath under `Software\Verae\PrintStamp\Shortcuts` **or** Advertise shortcuts with Feature linkage. Prefer non-advertised + HKLM keypath for elevated package.
|
||||
|
||||
---
|
||||
|
||||
## Feature tree
|
||||
|
||||
```
|
||||
Feature ProductComplete (Level=1, typical default)
|
||||
├── Feature CoreBinaries → BinFiles
|
||||
├── Feature Scripts → ScriptFiles
|
||||
├── Feature Docs → DocFiles
|
||||
├── Feature StartMenu → StartMenuShortcuts
|
||||
├── Feature PrintersAndHelper (Level=1) → CustomActions only (no files)
|
||||
└── Feature TrayPresence (Level=2 optional) → tray scripts + user shortcuts
|
||||
OR leave tray to VeraeTray-*-peruser.msi
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Registry (machine)
|
||||
|
||||
| Key | Name | Value | Component |
|
||||
|-----|------|-------|-----------|
|
||||
| `HKLM\Software\Verae\PrintStamp` | `InstallDir` | `[INSTALLFOLDER]` | with install-printer / core |
|
||||
| `HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\…` | ARP via WiX | — | WiX generates; do not dual-write NSIS `UNINST_KEY` long-term |
|
||||
| Remove on uninstall | `HKLM\Software\Verae\PrintStamp` | — | match NSIS uninstall |
|
||||
|
||||
Do **not** store API keys or tokens in machine registry. Keys stay per-user (`.verae` / user-token.ps1 paths).
|
||||
|
||||
---
|
||||
|
||||
## Custom actions (NSIS parity) — placeholders
|
||||
|
||||
Ordered for install (after `InstallFiles`):
|
||||
|
||||
| Id | Type | Impersonate | When | Action |
|
||||
|----|------|-------------|------|--------|
|
||||
| `CA_StopHelper` | exe/schtasks | no | before overwrite | `schtasks /End /TN "Verae Print Helper"`; `taskkill /F /IM verae-printhelper.exe` |
|
||||
| `CA_SaveKey` | PowerShell | **yes** (user) | if not skip | `user-token.ps1 -Mode save` only when property supplies token file path (silent rarely) |
|
||||
| `CA_WriteUserConfig` | exe | yes | if profile known | `verae-print.exe config --open-on-complete …` |
|
||||
| `CA_InstallPrinters` | PowerShell | **no** (elevated) | always unless `VERAE_SKIP_PRINTERS=1` | `install-printer.ps1 -InstallDir "[INSTALLFOLDER]"` |
|
||||
| `CA_InstallTray` | optional | yes | if Feature Tray | Startup + Start Menu tray lnk |
|
||||
|
||||
Uninstall:
|
||||
|
||||
| Id | Action |
|
||||
|----|--------|
|
||||
| `CA_UninstallPrinters` | `uninstall-printer.ps1` |
|
||||
| `CA_KillHelper` | taskkill helper |
|
||||
|
||||
**Printer CA placeholders (authoring stubs — not implemented here):**
|
||||
|
||||
```
|
||||
<!-- PLACEHOLDER: deferred elevated -->
|
||||
CustomAction Id="CA_InstallPrinters"
|
||||
ExeCommand="powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File "[INSTALLFOLDER]scripts\install-printer.ps1" -InstallDir "[INSTALLFOLDER]""
|
||||
Execute="deferred" Impersonate="no" Return="check" />
|
||||
|
||||
<!-- PLACEHOLDER: rollback -->
|
||||
CustomAction Id="CA_InstallPrintersRollback"
|
||||
… uninstall-printer.ps1 … Execute="rollback" Impersonate="no" />
|
||||
|
||||
<!-- PLACEHOLDER: uninstall sequence -->
|
||||
CustomAction Id="CA_UninstallPrinters"
|
||||
… uninstall-printer.ps1 … Execute="deferred" Impersonate="no" />
|
||||
```
|
||||
|
||||
Schedule: `InstallExecuteSequence` — `CA_StopHelper` before `RemoveFiles`/`InstallFiles`; `CA_InstallPrinters` after `InstallFiles`; uninstall CA before `RemoveFiles`.
|
||||
|
||||
Condition examples: `NOT Installed`, `REMOVE~="ALL"`, `VERAE_SKIP_PRINTERS<>"1"`.
|
||||
|
||||
---
|
||||
|
||||
## Printer / helper runtime surface (created by CA, not File components)
|
||||
|
||||
Documented so WiX authors do not try to harvest these as static files:
|
||||
|
||||
| Item | Created by | Notes |
|
||||
|------|------------|-------|
|
||||
| Scheduled task `Verae Print Helper` | `install-printer.ps1` | Runs helper as console user |
|
||||
| Printer `Verae TimeStamp PDF` | IPP class driver → `http://127.0.0.1:8631/ipp/print` | |
|
||||
| Printer Local Port fallback | Microsoft Print To PDF + local port | |
|
||||
| Port / driver prerequisites | same script | Spooler must run |
|
||||
| HKLM `Software\Verae\PrintStamp\InstallDir` | install-printer.ps1 | |
|
||||
|
||||
---
|
||||
|
||||
## Staging layout (build host)
|
||||
|
||||
Before `candle`/`light` (future `build-full-msi.ps1`):
|
||||
|
||||
```
|
||||
wix/StageFull/
|
||||
bin/verae-print.exe
|
||||
bin/verae-printhelper.exe
|
||||
scripts/common.ps1
|
||||
scripts/user-token.ps1
|
||||
scripts/install-printer.ps1
|
||||
scripts/uninstall-printer.ps1
|
||||
scripts/verae-diag.ps1
|
||||
LICENSE.txt
|
||||
README-WINDOWS.txt
|
||||
```
|
||||
|
||||
Same sources as [`../build-windows.ps1`](../build-windows.ps1) stage (Go build + copy scripts + LICENSE).
|
||||
|
||||
---
|
||||
|
||||
## Explicitly out of this outline
|
||||
|
||||
| Item | Where it lives instead |
|
||||
|------|------------------------|
|
||||
| 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) |
|
||||
| MSIX / Print Support App | Deferred |
|
||||
|
||||
---
|
||||
|
||||
## Suggested WiX file split (when implementing)
|
||||
|
||||
| File | Contents |
|
||||
|------|----------|
|
||||
| `Product-Full.wxs` | Product, Package, MajorUpgrade, Properties, Feature refs, MediaTemplate |
|
||||
| `Product-Full-Files.wxs` | Directory + Bin/Script/Doc ComponentGroups |
|
||||
| `Product-Full-Shortcuts.wxs` | Start Menu components |
|
||||
| `Product-Full-CA.wxs` | CustomAction defs + InstallExecuteSequence |
|
||||
| `SILENT_PROPS.md` | Public property contract (done) |
|
||||
| `Product-Full.wxs.outline` | This checklist |
|
||||
|
||||
---
|
||||
|
||||
## Definition of done for a real Product-Full.wxs (not L1)
|
||||
|
||||
1. candle/light builds MSI with HelperVersion stamped.
|
||||
2. Elevated install places bin + scripts + LICENSE under Program Files.
|
||||
3. CA runs install-printer.ps1; IPP + fallback printers + helper task.
|
||||
4. Silent props from SILENT_PROPS.md honored (`/qn`).
|
||||
5. Uninstall removes printers/task/files/ARP; leaves user keys + logs.
|
||||
6. Dual-ship NSIS until validated; then thin NSIS.
|
||||
|
|
@ -71,6 +71,15 @@ The script:
|
|||
5. If candle/light are missing, prints a **WiX install tip** and exits 0
|
||||
(day-to-day tray path stays `build-tray-startmenu.ps1`).
|
||||
|
||||
#### `build-tray-msi.ps1` exit codes
|
||||
|
||||
| Code | Meaning |
|
||||
|------|---------|
|
||||
| **0** | MSI built successfully, **or** WiX (candle/light) not found and install tip printed (default; does not fail the non-WiX tray path). |
|
||||
| **1** | Failure: `-SkipTip` when WiX is missing; missing product/fragment `.wxs` or `verae-tray.ps1`; candle or light returned non-zero; or a terminating PowerShell error. |
|
||||
|
||||
CI that requires an MSI artifact should pass `-SkipTip` so a missing toolset fails the job.
|
||||
|
||||
### Build (manual WiX v3 candle / light)
|
||||
|
||||
From repo root on Windows:
|
||||
|
|
|
|||
157
platform/windows/wix/SILENT_PROPS.md
Normal file
157
platform/windows/wix/SILENT_PROPS.md
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
# SILENT_PROPS — full Print Stamp MSI public properties
|
||||
|
||||
**Status:** contract scaffold for the future elevated full-product MSI (`Product-Full.wxs`).
|
||||
**Not implemented** in a shippable MSI yet. Tray skeleton only exposes `VERAE_IPP_ADDR` today.
|
||||
**Parity intent:** fleet / Intune / SCCM `msiexec /i … /qn` without the NSIS key wizard UI.
|
||||
**Related:** [`Product-Full.wxs.outline`](Product-Full.wxs.outline), [`docs/COMMERCIAL-INSTALLER-BACKLOG.md`](../../../docs/COMMERCIAL-INSTALLER-BACKLOG.md), [`docs/installer-registration-api.md`](../../../docs/installer-registration-api.md).
|
||||
|
||||
---
|
||||
|
||||
## msiexec pattern
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
- `/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.
|
||||
|
||||
All names below are **public** Windows Installer properties (uppercase) unless marked private.
|
||||
|
||||
---
|
||||
|
||||
## Property table
|
||||
|
||||
### Required for silent enterprise (skip interactive key UI)
|
||||
|
||||
| Property | Type | Default | Meaning |
|
||||
|----------|------|---------|---------|
|
||||
| `VERAE_SKIP_KEY_SETUP` | `0` \| `1` | `0` (UI builds) / **`1` recommended for `/qn`** | Skip NSIS-equivalent key wizard (login / create / paste). No portal prompts during install. User supplies key later (first print, Start Menu “Setup signing key”, or pre-placed token). |
|
||||
| `VERAE_CHANNEL` | string | `zapier` | Billing/API channel for stamp path. `zapier` → zap.verae.ai + xVerae- + `x-billing-channel=printit`. `verae` → api.veraetime.net JWT. Written into machine or first-run config when MSI implements config CA. |
|
||||
| `VERAE_INSTALL_API_URL` | URL | `https://customer.verae.ai` | Portal **origin** (no trailing path). Client uses `{URL}/portal/api/…` for installer registration (`VERAE_PORTAL_BASE` equivalent). HTTPS only in production. |
|
||||
|
||||
### API / endpoint overrides
|
||||
|
||||
| Property | Type | Default | Meaning |
|
||||
|----------|------|---------|---------|
|
||||
| `VERAE_INSTALL_API_URL` | URL | `https://customer.verae.ai` | See above. Alias concept: env `VERAE_PORTAL_BASE`. |
|
||||
| `VERAE_ZAPIER_API_URL` | URL | `https://zap.verae.ai` | Zapier/edge stamp base when `VERAE_CHANNEL=zapier`. |
|
||||
| `VERAE_VERAE_API_URL` | URL | (product default host for `verae` channel) | Optional override for non-zapier channel. |
|
||||
|
||||
Do not put API **keys** or passwords in MSI properties (show in ARP repair, logs, process list). Use `VERAE_SKIP_KEY_SETUP=1` and deploy tokens out-of-band, or a secure first-run.
|
||||
|
||||
### Install surface toggles
|
||||
|
||||
| Property | Type | Default | Meaning |
|
||||
|----------|------|---------|---------|
|
||||
| `VERAE_SKIP_PRINTERS` | `0` \| `1` | `0` | Skip `install-printer.ps1` custom action (no IPP / Local Port queues, no helper task). Files still install. |
|
||||
| `VERAE_SKIP_HELPER_TASK` | `0` \| `1` | `0` | Install printers but do not register scheduled task (rare; usually leave `0`). |
|
||||
| `VERAE_INSTALL_TRAY` | `0` \| `1` | `0` | If full MSI gains optional tray Feature: install per-user Startup tray. Default off — tray remains separate per-user MSI. |
|
||||
| `VERAE_IPP_ADDR` | `host:port` | `127.0.0.1:8631` | Helper loopback address for tray shortcuts / docs. Does not change IPP purity (presence = GET only). |
|
||||
|
||||
### Optional UX prefs (map to `verae-print config`)
|
||||
|
||||
| Property | Type | Default | Meaning |
|
||||
|----------|------|---------|---------|
|
||||
| `VERAE_OPEN_ON_COMPLETE` | `folder` \| `pdf` \| `none` | `folder` | After each stamp; same as NSIS Options page. |
|
||||
| `VERAE_PROMPT_FOR_FILENAME` | `true` \| `false` | `true` | Prompt for output name. |
|
||||
|
||||
Only applied when a target user profile is known (impersonated CA). Silent machine install may defer until first interactive run.
|
||||
|
||||
### Standard WiX / Windows properties (not Verae-specific)
|
||||
|
||||
| Property | Notes |
|
||||
|----------|--------|
|
||||
| `INSTALLDIR` / `INSTALLFOLDER` | Override Program Files target. |
|
||||
| `ALLUSERS=1` | per-machine (full MSI default). |
|
||||
| `REBOOT=ReallySuppress` | Prefer with `/norestart`. |
|
||||
| `ARPNOMODIFY` | Optional `1` until modify UI exists. |
|
||||
|
||||
---
|
||||
|
||||
## Mapping: property → runtime
|
||||
|
||||
| MSI property | Runtime / env / config |
|
||||
|--------------|------------------------|
|
||||
| `VERAE_INSTALL_API_URL` | `VERAE_PORTAL_BASE` for `verae-print installer-account` / portal client |
|
||||
| `VERAE_CHANNEL` | stamp `--channel=` / config channel default (`zapier` \| `verae`) |
|
||||
| `VERAE_ZAPIER_API_URL` | zap client base (if configurable) |
|
||||
| `VERAE_SKIP_KEY_SETUP=1` | No `user-token.ps1 -Mode save` during install; Finish = “key SKIPPED” |
|
||||
| `VERAE_IPP_ADDR` | tray `-Addr`, `$env:VERAE_IPP_ADDR` |
|
||||
| `VERAE_OPEN_ON_COMPLETE` | `config --open-on-complete` |
|
||||
| `VERAE_PROMPT_FOR_FILENAME` | `config --prompt-for-filename` |
|
||||
|
||||
---
|
||||
|
||||
## Intune / SCCM sample lines
|
||||
|
||||
**Minimum silent (files + printers, skip key UI):**
|
||||
|
||||
```bat
|
||||
msiexec /i VeraePrintStamp-windows-amd64-VERSION.msi /qn /norestart VERAE_SKIP_KEY_SETUP=1
|
||||
```
|
||||
|
||||
**Explicit channel + portal (PrintIt default channel):**
|
||||
|
||||
```bat
|
||||
msiexec /i VeraePrintStamp-windows-amd64-VERSION.msi /qn /norestart ^
|
||||
VERAE_SKIP_KEY_SETUP=1 ^
|
||||
VERAE_CHANNEL=zapier ^
|
||||
VERAE_INSTALL_API_URL=https://customer.verae.ai
|
||||
```
|
||||
|
||||
**Files only (lab / golden image before printer step):**
|
||||
|
||||
```bat
|
||||
msiexec /i VeraePrintStamp-windows-amd64-VERSION.msi /qn VERAE_SKIP_KEY_SETUP=1 VERAE_SKIP_PRINTERS=1
|
||||
```
|
||||
|
||||
**Tray-only package (existing skeleton — not full product):**
|
||||
|
||||
```bat
|
||||
msiexec /i VeraeTray-VERSION-peruser.msi /qn VERAE_IPP_ADDR=127.0.0.1:8631
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security rules
|
||||
|
||||
1. **Never** pass `xVerae-…`, JWT, password, or TOTP as public MSI properties.
|
||||
2. Portal base must be **https://** unless a documented insecure lab flag exists (`VERAE_ALLOW_INSECURE_PORTAL` is process env today — do not expose as default MSI prop).
|
||||
3. Log property **names** in verbose msiexec logs; do not add custom actions that echo secret-bearing properties.
|
||||
4. `VERAE_SKIP_KEY_SETUP=1` is the supported fleet path; interactive key flows stay NSIS or first-run helper UI.
|
||||
|
||||
---
|
||||
|
||||
## Tray skeleton vs full product
|
||||
|
||||
| Property | Tray MSI (`VeraeTrayShortcuts.wxs`) | Full MSI (this table) |
|
||||
|----------|-------------------------------------|------------------------|
|
||||
| `VERAE_IPP_ADDR` | Yes | Yes |
|
||||
| `VERAE_SKIP_KEY_SETUP` | N/A (no key UI) | Yes |
|
||||
| `VERAE_CHANNEL` | N/A | Yes |
|
||||
| `VERAE_INSTALL_API_URL` | N/A | Yes |
|
||||
| `VERAE_SKIP_PRINTERS` | N/A | Yes |
|
||||
|
||||
---
|
||||
|
||||
## Implementation checklist (Wave 2+ / I1-adjacent)
|
||||
|
||||
- [ ] Declare properties in `Product-Full.wxs` with the defaults above.
|
||||
- [ ] 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).
|
||||
- [ ] Document exit codes from future `build-full-msi.ps1` alongside `build-tray-msi.ps1`.
|
||||
- [ ] Keep NSIS silent story separate until MSI is primary download.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
UNLICENSED © Verae — packaging contract only; not a redistributable binary.
|
||||
0
site/printit.verae.ai/fonts/.gitkeep
Normal file
0
site/printit.verae.ai/fonts/.gitkeep
Normal file
87
site/printit.verae.ai/fonts/README.md
Normal file
87
site/printit.verae.ai/fonts/README.md
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
# CERT self-host fonts (printit.verae.ai)
|
||||
|
||||
Placeholder directory for **self-hosted woff2** used by the CERT-01 web
|
||||
viewer (`../certificate.html`). Binaries are **not** committed yet — only
|
||||
`.gitkeep` keeps the path in git.
|
||||
|
||||
## Current state
|
||||
|
||||
| Surface | Fonts today | Offline fallback |
|
||||
|---------|-------------|------------------|
|
||||
| `certificate.html` title | **Fraunces** 600/700 via Google Fonts CDN | Georgia / Times New Roman / serif |
|
||||
| Mono digests | **JetBrains Mono** 500 via same CDN | ui-monospace / SF Mono / Menlo |
|
||||
| UI kit (`verae-ui-kit.css`) | Inter + Material Symbols Rounded CDN | system stacks in `brand/tokens.css` |
|
||||
| Issued PDF (`internal/pdfcert`) | System / library serif — **not** Fraunces embed | N/A (separate from web) |
|
||||
|
||||
Design source of truth: `verae-2026-design/ui-designs/src/html/CERT-01.html`
|
||||
(tokens `--font-serif: 'Fraunces'`; design-kit `src/fonts/` currently ships
|
||||
Cantarell + Material Symbols TTF only — **no Fraunces binary in-repo yet**).
|
||||
|
||||
**Not claimed:** full CERT-01.html pixel match, PDF Fraunces embed, or
|
||||
self-hosted production fonts.
|
||||
|
||||
## Goal
|
||||
|
||||
Serve brand serif (and optional mono) from this origin so the certificate
|
||||
viewer works without `fonts.googleapis.com` / `fonts.gstatic.com`, and
|
||||
print/PDF-from-browser paths stay consistent offline.
|
||||
|
||||
## Planned files (when licensed binaries land)
|
||||
|
||||
```
|
||||
fonts/
|
||||
README.md (this file)
|
||||
.gitkeep
|
||||
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
|
||||
```
|
||||
|
||||
Exact filenames may follow upstream release naming; keep them stable once
|
||||
wired so deploys and cache busting stay simple.
|
||||
|
||||
## How to wire (later — do not claim done until files exist)
|
||||
|
||||
1. Obtain **OFL-licensed** Fraunces (and optional JetBrains Mono) woff2 from
|
||||
the upstream release or a subsetted build (title glyphs only is enough
|
||||
for CERT).
|
||||
2. Drop files into this directory; commit license text next to them.
|
||||
3. Add local `@font-face` in `certificate.html` (or a small
|
||||
`fonts/cert-fonts.css`):
|
||||
|
||||
```css
|
||||
@font-face {
|
||||
font-family: 'Fraunces';
|
||||
src: url('/fonts/fraunces-opsz-wght-700.woff2') format('woff2');
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
```
|
||||
|
||||
4. Remove the three Google Fonts `<link>` tags from `certificate.html`
|
||||
head once local faces load cleanly.
|
||||
5. Keep the Georgia/serif stack as final fallback.
|
||||
6. Deploy with the rest of the static site (`rsync` to
|
||||
`/var/www/printit.verae.ai/` — see parent `README.md`).
|
||||
7. Smoke: open `/certificate.html` with network blocked; title must still
|
||||
render serif (local woff2 or Georgia).
|
||||
|
||||
## Out of scope here
|
||||
|
||||
| Item | Owner / later |
|
||||
|------|----------------|
|
||||
| PDF CERT Fraunces embed | **I7 done:** pdfcert/build.mjs note + test — Times/Helvetica only until embed lands |
|
||||
| Inter / Material Symbols self-host for whole site | optional; CERT title is the residual gap called out in OPEN-ISSUES #7 |
|
||||
| Cantarell from design-kit TTFs | GNOME mock only; not required for CERT-01 web title |
|
||||
| Pixel-perfect CERT-01.html match | Still later (OPEN-ISSUES Advanced) |
|
||||
|
||||
## Status
|
||||
|
||||
| Step | State |
|
||||
|------|-------|
|
||||
| Placeholder dir + this plan | **Done** (L7) |
|
||||
| woff2 binaries + LICENSE | Not started |
|
||||
| `@font-face` + drop CDN links | Not started |
|
||||
| Pixel match / PDF embed claim | Not started — do not claim |
|
||||
Loading…
Reference in a new issue