Released docs. You are viewing the documentation published with v0.46.0. Development docs are available at Latest.
Registry Render promises that fixed inputs produce identical PDF bytes, on one machine and across machines.
What do you define?
Section titled “What do you define?”A bundle. Its manifest.yaml declares each document type with a version, a Typst entry file, a
JSON Schema for the document’s data, and the label locales it uses. Label tables hold the words a
template prints in each language, and every table carries the same keys. Fonts ship in the bundle.
The configuration reference lists every key.
A template reads one input: an envelope holding the request’s data, its decoded image assets,
the label tables, the locale when one is sent, the issuance time, and the document’s id and
version. Nothing else reaches it: no host fonts, no environment, no clock, no network.
What fixes the bytes?
Section titled “What fixes the bytes?”Render canonicalizes the envelope with RFC 8785 (JSON Canonicalization Scheme) and hands the
template that exact string. The dataDigest a caller stores is the sha256 of those bytes, so the
hash and the template’s view of the request are the same thing.
issuedAt is the document’s issuance claim and its only clock: datetime.today() in a template
and the PDF creation date both come from it. Render normalizes it to UTC, so 10:32Z and
13:32+03:00 render identically and the caller’s offset is not kept. A template that prints a
local date shifts the instant itself with datetime.today(offset: ...).
Each render starts a fresh Typst world, so no state or cached layout passes between requests. The
world offers three roots: the bundle, the request’s assets under a virtual assets/ path, and the
vendored packages. A path outside them is refused even when request data supplies it, and error
messages name virtual paths, never the host’s. Fonts load in a fixed order, the binary’s baseline
set and then the bundle’s sorted by path, because order decides fallback.
The PDF names a fixed creator with no version, so an upgrade that changes no layout changes no bytes. Below Typst, the workspace lockfile pins the compression libraries, because a patch release there can re-encode a stream and move every byte after it. The same inputs give the same bytes on Linux and macOS.
What exists at runtime?
Section titled “What exists at runtime?”registry-render serve serves one package. registry-render package copies validated source into
a new directory and writes a sorted SHA256SUMS file covering every file, plus an optional
REVISION; the package digest is the sha256 of that checksum file. The package writer refuses an
existing directory, so a digest never gains different bytes.
At startup serve verifies the checksums and, when configured, package.expectedDigest. Each render
runs in a worker process that verifies the package again, and the parent requires the worker’s
digest to equal the startup digest, so a file changed after startup is refused by name. A worker
that passes its timeout is killed, and on Linux its address space is capped.
The service keeps no database and no rendered document. Its one file is the audit log: a request
entry before each render starts and a response entry, with both hashes, before the PDF leaves.
If either write fails, the call fails and no document is returned.
Who owns the result?
Section titled “Who owns the result?”The caller does. Render returns the PDF and forgets it; the record the document was made from
should keep pdfDigest for the file and dataDigest for the exact input. The audit log holds
hashes, versions, and a fingerprint of the caller’s key, never data values or image bytes. What a
template prints is a disclosure decision that belongs to its author: once on paper, it has left
every access profile behind.
How do you check a document later?
Section titled “How do you check a document later?”Re-render it. The same package, document type, locale, data, assets, and issuedAt reproduce the
stored pdfDigest byte for byte, which is what makes a replay after a failure safe and lets an
auditor match paper to the log. GET /health names the live package digest as bundleHash, and
every audit entry carries the bundle hash and renderer version.
When a hash moved, one of three things changed: an input, the package, or the renderer. A Typst or compression upgrade can change the bytes, so check a new binary before you switch to it.
registry-render check prints a hash for a source directory too, but for unpackaged source it
covers manifest.yaml only. Compare packages, not source directories.
Cookies on Registry Stack
We use optional cookies to understand how you use the main Registry Stack website and this documentation. If you contact us, we may link this activity to your enquiry. You can change your choice at any time. Read the privacy policy.
Your browser asks sites not to track you, so optional tracking stays off.
We could not use or save that choice. Optional tracking remains off.