feat(macos): PDF Services stamp helper with dual-channel Verae auth

Implements macOS v0: verae-print stamp + install-macos PDF menu items.
Saves {name}.pdf, {name}-verae-timestamp-{RECEIPTID}.pdf, and .verae.json.
Channels: verae (api.veraetime.net JWT) and zapier (zap.verae.ai xVerae- + attachPdf).
This commit is contained in:
George Lambert 2026-10-03 03:56:27 -04:00
commit 46f2bcd43e

103
internal/pdfcert/pdfcert.go Normal file
View file

@ -0,0 +1,103 @@
package pdfcert
import (
"fmt"
"strings"
"time"
)
// Input for a simple one-page Verae registration certificate PDF.
type Input struct {
DocumentName string
SHA256 string
ReceiptID string
CreatedAt string
Merkle string
VerifyURL string
Channel string
}
// Build returns a minimal PDF (no external deps) describing the stamp.
func Build(in Input) ([]byte, error) {
created := in.CreatedAt
if created == "" {
created = time.Now().UTC().Format(time.RFC3339)
}
local := ""
if t, err := time.Parse(time.RFC3339, created); err == nil {
local = t.Local().Format(time.RFC1123)
}
verify := in.VerifyURL
if verify == "" && in.SHA256 != "" {
verify = fmt.Sprintf("https://mcp.verae.ai/register.html?sha256=%s&receipt=%s", in.SHA256, in.ReceiptID)
}
lines := []string{
"Verae Timestamp Registration",
"",
"Document: " + sanitize(in.DocumentName),
"SHA-256: " + in.SHA256,
"Receipt ID: " + in.ReceiptID,
"UTC: " + created,
}
if local != "" {
lines = append(lines, "Local: "+local)
}
if in.Merkle != "" {
lines = append(lines, "Merkle/block: "+in.Merkle)
}
if in.Channel != "" {
lines = append(lines, "Channel: "+in.Channel)
}
lines = append(lines, "Verify: "+verify)
lines = append(lines, "", "Digests only — document bytes were not uploaded.")
return buildTextPDF(lines)
}
func sanitize(s string) string {
s = strings.ReplaceAll(s, "(", "\\(")
s = strings.ReplaceAll(s, ")", "\\)")
s = strings.ReplaceAll(s, "\\", "\\\\")
return s
}
// Minimal PDF 1.4 with Helvetica text lines.
func buildTextPDF(lines []string) ([]byte, error) {
var content strings.Builder
content.WriteString("BT /F1 12 Tf 50 750 Td 14 TL\n")
for i, line := range lines {
if i > 0 {
content.WriteString("T*\n")
}
content.WriteString("(")
content.WriteString(sanitize(line))
content.WriteString(") Tj\n")
}
content.WriteString("ET\n")
stream := content.String()
objects := []string{
"<< /Type /Catalog /Pages 2 0 R >>",
"<< /Type /Pages /Kids [3 0 R] /Count 1 >>",
"<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >>",
fmt.Sprintf("<< /Length %d >>\nstream\n%s\nendstream", len(stream), stream),
"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>",
}
var body strings.Builder
body.WriteString("%PDF-1.4\n")
offsets := make([]int, len(objects)+1)
for i, obj := range objects {
offsets[i+1] = body.Len()
fmt.Fprintf(&body, "%d 0 obj\n%s\nendobj\n", i+1, obj)
}
xrefStart := body.Len()
fmt.Fprintf(&body, "xref\n0 %d\n", len(objects)+1)
body.WriteString("0000000000 65535 f \n")
for i := 1; i <= len(objects); i++ {
fmt.Fprintf(&body, "%010d 00000 n \n", offsets[i])
}
fmt.Fprintf(&body, "trailer << /Size %d /Root 1 0 R >>\nstartxref\n%d\n%%%%EOF\n", len(objects)+1, xrefStart)
return []byte(body.String()), nil
}