PrintToVeraeTimeStamp/docs/PACKAGING.md
George Lambert da4a0dc4e2
Some checks failed
windows-msi / full-msi-unsigned (push) Has been cancelled
feat: fill-manifest-sha256.ps1 + CERT local font-face first
Hash helper for DOWNLOADS-MANIFEST; certificate.html prefers fonts/*.woff2 when present.
2026-10-14 00:27:11 -04:00

14 KiB
Raw Blame History

PrintIt packaging (tray, menubar, CERT)

Desktop packaging for PrintToVeraeTimeStamp: version stamps, Windows tray / Start Menu presence (no elevated MSI), macOS menu bar / LaunchAgent / optional unsigned .app, CERT-01 digests, statuslang notify titles, and the split between pure GET status routes and the IPP POST print path.

Version source of truth

Item Value / location
Constant internal/job.HelperVersion = 0.3.6
File internal/job/job.go
Sidecar field "helper_version" on every .verae.json

All packaging metadata must resolve from that constant (or fall back to the same string when job.go is not on disk):

  • Windows: platform/windows/scripts/common.ps1 → Get-VeraePackageVersion (fallback "0.3.6"). Used by verae-tray.ps1, install-tray-startup.ps1, build-tray-startmenu.ps1.
  • macOS: platform/macos/menubar/build-menubar-app.sh reads job.go when VERSION is unset; stamps CFBundleShortVersionString / CFBundleVersion, Contents/Resources/VERSION, and packaging-metadata.txt.
  • Binary build: scripts/build-win-linux.sh greps HelperVersion from job.go.
  • Smoke: platform/smoke-packaging-metadata.sh fails if tray/menubar scripts drift from HelperVersion.

Override for a one-off stamp: VERSION=x.y.z (env) on the build/install scripts. Do not invent a second version constant.

Windows tray (no elevated MSI)

User-level presence only. No elevated MSI, no admin rights, no WiX requirement for the tray stub.

Script Role
platform/windows/scripts/verae-tray.ps1 STA NotifyIcon; polls GET /status; menu opens GET /queue
platform/windows/scripts/install-tray-startup.ps1 Startup-folder shortcut (Verae Tray.lnk) that runs verae-tray.ps1
platform/windows/scripts/build-tray-startmenu.ps1 WiX-free Start Menu folder stub; also installs Startup unless -NoStartup

CLI entry points (same behavior):

verae-print menubar
verae-print menubar --install
verae-print menubar --uninstall

Or invoke the scripts directly:

powershell -NoProfile -ExecutionPolicy Bypass -STA -File platform\windows\scripts\verae-tray.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\install-tray-startup.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\build-tray-startmenu.ps1

Notes:

  • Package version in shortcut description / tooltip = HelperVersion.
  • Full product installer (NSIS) is separate: see Windows README / installer-windows.nsi when present. Tray packaging does not depend on it.
  • Hold count in the tray is a local queue handle, never evidence (statuslang.EvidenceNote).

More detail: platform/windows/README.txt.

macOS menu bar

NSStatusItem that polls helper GET /status and opens GET /queue. Does not touch IPP POST.

Asset Role
platform/macos/menubar/verae-menubar.swift NSStatusItem source
platform/macos/menubar/run-menubar.sh Compile-or-interpret entry
platform/macos/menubar/com.verae.timestamping.menubar.plist LaunchAgent template
platform/macos/menubar/build-menubar-app.sh Optional unsigned .app builder
platform/macos/menubar/README.txt Runbook + notarization steps (docs only)

Run / install

verae-print menubar
verae-print menubar --foreground
verae-print menubar --install    # LaunchAgent at login
verae-print menubar --uninstall
./platform/macos/menubar/run-menubar.sh

--install copies durable assets to ~/Library/Application Support/Verae/menubar/ and writes ~/Library/LaunchAgents/com.verae.timestamping.menubar.plist (launchctl bootstrap, fallback load -w). Prefer LaunchAgent over Login Items for headless reliability.

Unsigned .app

./platform/macos/menubar/build-menubar-app.sh
./platform/macos/menubar/build-menubar-app.sh ~/Applications/Verae\ Menubar.app
VERSION=0.3.6 ./platform/macos/menubar/build-menubar-app.sh
  • LSUIElement=true (menu bar only, no Dock).
  • Bundle id: ai.verae.timestamping.menubar.
  • Not codesigned or notarized by default — local/dev use.
  • Builder copies platform/macos/pkg/entitlements.plist into Contents/Resources/ for optional signing; it does not apply codesign and never embeds secrets.

Codesign + notarization (document only)

Requires Apple Developer Program + Developer ID Application cert in the keychain. This repo does not ship secrets or signing.env values. When certs are available (steps also in platform/macos/menubar/README.txt):

  1. Build unsigned app: ./build-menubar-app.sh ~/Applications/Verae\ Menubar.app
  2. Codesign hardened runtime + entitlements:
    APP="$HOME/Applications/Verae Menubar.app"
    ENT="$APP/Contents/Resources/entitlements.plist"
    codesign --force --deep --options runtime --timestamp \
      --entitlements "$ENT" \
      --sign "Developer ID Application: YOUR ORG (TEAMID)" \
      "$APP"
    codesign --verify --deep --strict --verbose=2 "$APP"
    
  3. Zip: ditto -c -k --keepParent "$APP" /tmp/VeraeMenubar.zip
  4. Notarize: xcrun notarytool submit /tmp/VeraeMenubar.zip --keychain-profile "verae-notary" --wait
  5. Staple: xcrun stapler staple "$APP" then xcrun stapler validate "$APP"
  6. Optional signed .pkg: platform/macos/pkg/build-pkg.sh (Developer ID Installer + productbuild) — separate from the menu-bar stub.

Gatekeeper: unsigned app needs right-click → Open once, or xattr -dr com.apple.quarantine "Verae Menubar.app". Do not paste API keys, app-specific passwords, or .p12 material into the repo.

CLI surface: pending / tray-status / menubar

Command Purpose
verae-print serve IPP helper + loopback GET /queue, GET /status, GET / (health)
verae-print tray-status Offline/online held · key · helper snapshot (statuslang headlines)
verae-print tray-status --open Open live http://127.0.0.1:8631/queue (or temp HTML if helper down)
verae-print menubar PT-W3 / PT-06: Windows tray or macOS menu bar
verae-print menubar --install / --uninstall Login presence (Startup / LaunchAgent)
verae-print pending --html Offline queue page (same HTML family as live /queue)

Default loopback: 127.0.0.1:8631 (VERAE_IPP_ADDR overrides).

CERT-01: multihash / blake3 when present

Branded certificate path: internal/pdfcert (CERT-01 plum/gold, final-only wording). Job path always prefers local CERT-01 over upstream attach PDF.

Digest When shown Multihash form
SHA-256 Always when stamp hash is known f1220 + 64 hex (pdfcert.ContentMultihash)
BLAKE3 Only when Options.Blake3 / upstream provides it f1e20 + 64 hex; omit fields when empty

Rules:

  • Never invent digests. Empty blake3 → omit blake3 / blake3_multihash on sidecar and CERT.
  • Job does not hash file bytes for blake3; it only passes through an upstream digest.
  • Sidecar always stamps "helper_version": HelperVersion.
  • Hold ≠ evidence. Local hold / pending ids are queue handles only. Certificates, “Download proof”, and legal claims are allowed only for final / already-registered states (statuslang.IsEvidence). Standing copy: statuslang.EvidenceNote.

Web certificate viewer (printit.verae.ai)

Site page: site/printit.verae.ai/certificate.html

Item Status
CERT-01 layout Shipped — plum/gold double frame, Fraunces title (local woff2 else CDN), gold rule, final banner, mono digests, digests-only footer
Brand kit Uses brand/tokens.css, verae-ui-kit.css, brand/logo/verae-mark.svg
Multihash / BLAKE3 Shown only when present (query, sidecar, or sample); never invented
Load paths Query string (?sha256=&receipt=&…), optional .verae.json sidecar drop, “Sample data” button
Print CSS Letter page, toolbar hidden, frame colors preserved
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 prefers local fonts/*.woff2 else 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 — drop-in woff2 dir (empty + .gitkeep); local-first probe in certificate.html
Local-first fonts (Issue 7) certificate.html probes planned fonts/*.woff2 (magic wOF2); any hit → @font-face only; none → Google Fonts CDN. No proprietary binaries downloaded or committed
Font fetch scaffold (TODO-7 A7) 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
Local-first @font-face else CDN; fonts drop-in README Real OFL woff2 + OFL.txt committed (operator drop-in only today)
fetch-fonts.example.sh example + --check Full CERT-01.html pixel match (Fraunces opsz, Cantarell body if required, Substrate extrinsic polish)
CERT sheet spacing closer to CERT-01 PDF Fraunces embed in internal/pdfcert / registration-pdf/build.mjs (system Times/Helvetica only today)

statuslang notify titles

Source: internal/statuslang. Tray, menu bar, and helper balloons must use these short titles (not free-form strings):

State key Notify title
held / signin Sign in to finish timestamp
final / completed / ok Final receipt ready
(ready path) Printer ready
pending Pending receipt
queued Queued offline
hashing Hashing on device…
provisional Stamped, awaiting blockchain
failed / action failed / action chip strings
existing Already registered

Helpers:

  • NotifyTitle(state), NotifyBody(state, title, extra) — for held/pending, extra is a local hold id (never a receipt).
  • NotifyHeldJobs(n) — cancel / still-waiting banner.
  • TrayHeadline / TrayDetail — persistent tray line (held count, key OK, helper up, package version on Windows scripts).

Windows verae-tray.ps1 mirrors the same brand strings inline; keep them in sync with statuslang when labels change.

IPP: GET status pure; POST print separate

Same loopback port hosts two concerns. They must not couple.

Method / path Role Consumers
GET /status JSON helper snapshot (held, key, health) tray, menubar, tray-status
GET /queue HTML job queue (PT-04/W4) tray “Open queue”, browser
GET / Health probes
POST (IPP Print-Job / send-document) Actual print capture → stamp pipeline OS print spooler only

Constraints:

  • Tray / menubar / install scripts GET only. No IPP POST, no Print-Job, no document send from presence UI.
  • serve wires ServeQueue / ServeStatus as extra handlers beside the IPP server; POST remains pure IPP so Windows/macOS print clients are unaffected (cmd/verae-print/serve.go).
  • Smoke platform/smoke-packaging-metadata.sh checks tray scripts for accidental IPP POST coupling.

Verification

# From apps/PrintToVeraeTimeStamp
./platform/smoke-packaging-metadata.sh
go test ./cmd/verae-print -count=1 -run 'Menubar|Packaging|TrayStartmenu|FindRepo'
go test ./internal/job ./internal/pdfcert ./internal/statuslang -count=1

Expect: HelperVersion match, no elevated MSI path required, IPP pure on tray/menubar assets.

Path Notes
internal/job/job.go HelperVersion, sidecar digests
internal/pdfcert/ CERT-01 build + multihash helpers
internal/statuslang/ Brand notify titles + evidence rules
cmd/verae-print/serve.go IPP + GET /queue /status
cmd/verae-print/traystatus.go tray-status CLI + HTTP handlers
cmd/verae-print/menubar.go cross-platform menubar install
cmd/verae-print/pending.go pending queue HTML
platform/windows/scripts/ tray / Startup / Start Menu
platform/macos/menubar/ Swift menubar + LaunchAgent + .app
platform/macos/pkg/build-pkg.sh optional signed .pkg (certs required)
platform/smoke-packaging-metadata.sh A4 packaging smoke
site/printit.verae.ai/certificate.html CERT-01 web cert viewer (brand kit)