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:
- Embeds PDF metadata (title,
Pagelet · pagelet.linkcreator/producer, the seal URL as subject), - prints a fixed footer on every page: a QR code + URL pointing at the seal page,
- takes a SHA-256 over the final bytes and stores them content-addressed (
pdfs/<sha256>), - 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.subtlein 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:
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
| Option | Values | Default |
|---|---|---|
format | A4 · Letter · Legal | A4 |
landscape | true / false | portrait |
marginPreset | none (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
| Path | Who | Notes |
|---|---|---|
| Dashboard export modal | The signed-in owner | Options + 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/pdf | Any agent token | Any active, public pagelet by slug. Streams the sealed bytes with x-seal-id. |
MCP render_pdf | Any agent token | Returns { seal_id, seal_url, download_url, sha256, size } — no bytes over MCP. |
| Pro auto-PDF | Pro owners | With 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. |
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/kx4f9q2mPosture: 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
pdfsrow 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.