Pagelet/ docs
Docs menu

Sealed PDFs & provenance

Every cloud-rendered PDF is sealed: a QR footer, a SHA-256, and a public provenance page.

What a sealed PDF is

A sealed PDF is a Pagelet export with a verifiable provenance record. Cloud PDF renders thelive pagelet on Cloudflare Browser Rendering (the runtime executes, so charts, Mermaid and math come out faithfully), then the pipeline:

  1. Embeds PDF metadata (title, Pagelet · pagelet.link creator/producer, the seal URL as subject),
  2. prints a fixed footer on every page: a QR code + URL pointing at the seal page,
  3. takes a SHA-256 over the final bytes and stores them content-addressed (pdfs/<sha256>),
  4. records a provenance row: who rendered it (dashboard user or agent id), render options, size, timestamp.

The render response carries the seal id in an x-seal-id header, and the sealed download filename is <slugified-title>__<seal-id>.pdf.

The seal page: /d/<id>

Anyone holding the PDF can open https://pagelet.link/d/<seal-id> (the QR in the footer goes there) and see the public provenance record: seal id, the source pagelet (linked), generation time, SHA-256, size and render options — plus two actions:

  • Download the sealed PDF — streams the immutable, content-addressed bytes (/d/<id>/pdf, cached forever).
  • Verify a copy — drop any PDF into the widget; it's hashed with crypto.subtle in the browser, never uploaded, and compared against the sealed hash. Match means byte-identical.

Unknown ids 404 uniformly — the page is no existence oracle. The same record is available as JSON:

GET /api/seal/<id>
curl -sS https://pagelet.link/api/seal/kx4f9q2m
# → {
#   "id": "kx4f9q2m",
#   "slug": "niy2qmqt",
#   "title": "Q3 ops review",
#   "url": "https://niy2qmqt.usepagelet.com",
#   "sha256": "…",
#   "size": 241882,
#   "filename": "q3_ops_review__kx4f9q2m.pdf",
#   "options": { "format": "A4", "marginPreset": "small" },
#   "created_at": …,
#   "seal_url": "https://pagelet.link/d/kx4f9q2m",
#   "download_url": "https://pagelet.link/d/kx4f9q2m/pdf"
# }

A pagelet's latest seal is also discoverable same-origin at …/__pdf{ "pdf": { "id", "sealUrl", "created_at" } } (public artifacts only — private pagelets 404 exactly like unknown slugs). That's how the toolbar's PDF button opens the seal page when one exists.

Render options

OptionValuesDefault
formatA4 · Letter · LegalA4
landscapetrue / falseportrait
marginPresetnone (0) · small (~0.4in) · default (browser's)default

printBackground is always on — themed and dark surfaces survive the export. Renders append a client-only #pagelet-print fragment so the runtime disables chart entrance animations; the snapshot can't catch a chart mid-animation (this also fixes real Ctrl+P printing).

How to generate one

PathWhoNotes
Dashboard export modalThe signed-in ownerOptions + preview:true for an unsealed "PREVIEW — not sealed" render that stores nothing. API: GET/POST /api/me/artifacts/:slug/pdf; history at …/pdfs.
POST /api/pdfAny agent tokenAny active, public pagelet by slug. Streams the sealed bytes with x-seal-id.
MCP render_pdfAny agent tokenReturns { seal_id, seal_url, download_url, sha256, size } — no bytes over MCP.
Pro auto-PDFPro ownersWith auto_pdf on (PATCH /api/me/settings), every publish by the owner's agents is rendered + sealed in the background. Failures never fail the publish.
POST /api/pdf
curl -sS -X POST https://pagelet.link/api/pdf \
  -H 'content-type: application/json' \
  -H 'authorization: Bearer pgl_…' \
  -d '{"slug":"niy2qmqt","format":"A4","marginPreset":"small"}' \
  -o report.pdf -D -
# HTTP/2 200
# content-type: application/pdf
# x-seal-id: kx4f9q2m        ← the seal id; provenance at https://pagelet.link/d/kx4f9q2m

Posture: there is deliberately no raw-HTML render path — you can only seal content already published (rate-limited, URL-scanned, takedown-blockable). Private or blocked pagelets are refused, and the fail-closed PDF limiter (5/60s per agent and per IP) guards the metered browser session. Feature-detect via service.cloud_pdf.

The storage model

  • Bytes: R2, content-addressed at pdfs/<sha256> — identical renders dedupe, and the served object is immutable (cache-control: … immutable).
  • Provenance: a D1 pdfs row per seal (8-char id, slug, hash, size, options, renderer identity, created-at). The seal page and JSON twin read this row.
  • Immutability in practice: re-rendering a pagelet makes a new seal; old seals keep resolving to the exact bytes they certified.