Released docs. You are viewing the documentation published with v0.46.0. Development docs are available at Latest.
You will render one letter with Registry Render twice, once at a terminal and once over HTTP, and prove the two PDFs are the same bytes.
Build registry-render
Section titled “Build registry-render”Releases publish a Linux amd64 binary, which Deploy Registry Render installs. This tutorial builds from the checkout so it also works on macOS. From the checkout’s root:
. scripts/cargo-runtime-library-path.shregistry_cargo_build "$PWD" --release --locked -p registry-renderexport PATH="$PWD/target/release:$PATH"The helper also sets the library path the binary needs on macOS, so run every registry-render
command in this terminal. Change to an empty directory in it, then:
registry-render --versionregistry-render <version> (typst 0.15.1)Render offline
Section titled “Render offline”Scaffold a bundle:
registry-render init render-work/bundleThe bundle holds manifest.yaml, which declares one document type, letter; its Typst template;
a JSON Schema for its data; an English label table; fonts; and a fixture. init also prints a
first-render command; this one gives paths relative to this directory and the issuance time the
rest of the tutorial uses:
registry-render compile \ --bundle render-work/bundle \ --type letter \ --data render-work/bundle/fixtures/data.json \ --issued-at 2026-09-16T10:32:00Z \ --output render-work/letter.pdfwrote render-work/letter.pdf (13292 bytes, pdf sha256 1e2dc9c981d19ef62549c3100395a106bc8fb30e01bef21c26844e45a0c29f9f, data sha256 685d9fbcaac74fea3e7ea7839e79da9f0ceb56e9cf218f9ae1fe9d0d0d09e9fa)--issued-at is the document’s issuance time and the only clock the template sees, so your hashes
match these. The pdf sha256 identifies the file; the data sha256 identifies the exact input it
was rendered from.
Package the bundle
Section titled “Package the bundle”The service serves only a package: a copy of the bundle with a checksum file. Build one and check it:
registry-render package \ --bundle render-work/bundle \ --output render-work/package \ --revision tutorial-1registry-render check --bundle render-work/packagepackaged render-work/package as sha256:<package-digest>document letter v1 entry templates/letter.typ labels [en] pdf plainbundle render-work/package v1 hash <digest-prefix> (19 fonts, 1 documents, 9 governed files)0 errors, 0 warnings in 3 filesThe sha256: digest names this package. A later change to any file in it is refused by name.
Serve the package
Section titled “Serve the package”Create the API key callers present, and a runtime file that names the package, the key, and an audit file. Its paths are absolute:
mkdir -p render-work/deployopenssl rand -hex 32 > render-work/deploy/api.keychmod 600 render-work/deploy/api.keyhere="$(pwd -P)/render-work"cat > render-work/deploy/runtime.yaml <<EOFapiVersion: id.registrystack.org/formats/render/runtime/v1alpha1kind: RenderRuntimeConfiglistener: bind: 127.0.0.1:64123package: root: $here/packagesecretProviders: file: root: $here/deployauthentication: apiKeyRef: secret:file/api.keyaudit: path: $here/audit/render.jsonlEOFregistry-render check --runtime-config "$here/deploy/runtime.yaml"0 errors, 0 warnings in 1 fileStart the service and leave it running in this terminal:
registry-render serve --runtime-config "$here/deploy/runtime.yaml"It logs JSON lines to standard error. Open a second terminal in the same directory and run the rest of the tutorial there:
curl -s http://127.0.0.1:64123/health{"bundleHash":"<package-digest>","bundleVersion":1,"rendererVersion":"<version> (typst 0.15.1)","status":"ok","typstPin":"0.15.1"}bundleHash is the package digest without sha256:, so you can tell which package is live.
Render over HTTP
Section titled “Render over HTTP”Send the fixture data with the same issuance time. The key goes in a header file, which keeps it
out of curl’s command line, where ps would show it to other local users:
(umask 077; printf 'Authorization: Bearer %s\n' "$(cat render-work/deploy/api.key)" \ > render-work/deploy/auth.header)jq -c '{issuedAt: "2026-09-16T10:32:00Z", data: .}' render-work/bundle/fixtures/data.json \ > render-work/request.jsoncurl -s -X POST http://127.0.0.1:64123/v1/render/letter \ -H @render-work/deploy/auth.header \ -H "Content-Type: application/json" \ -d @render-work/request.json \ -o render-work/served.pdf -D render-work/headers.txtgrep -i '^x-registry' render-work/headers.txtx-registry-pdf-digest: sha256:1e2dc9c981d19ef62549c3100395a106bc8fb30e01bef21c26844e45a0c29f9fx-registry-data-digest: sha256:685d9fbcaac74fea3e7ea7839e79da9f0ceb56e9cf218f9ae1fe9d0d0d09e9fax-registry-pdf-sha256: 1e2dc9c981d19ef62549c3100395a106bc8fb30e01bef21c26844e45a0c29f9fx-registry-data-sha256: 685d9fbcaac74fea3e7ea7839e79da9f0ceb56e9cf218f9ae1fe9d0d0d09e9fax-registry-warning-count: 0x-registry-document-version: letter v1The headers carry the two hashes compile printed, as labelled digests and as the deprecated bare hex, and the warning count. Compare the files themselves:
cmp render-work/letter.pdf render-work/served.pdf && echo identicalidenticalTwo processes rendered the letter, and the bytes are the same.
See two refusals
Section titled “See two refusals”A wrong key, then a request without an issuance time:
curl -s -o /dev/null -w '%{http_code}\n' -X POST http://127.0.0.1:64123/v1/render/letter \ -H "Authorization: Bearer wrong-key-0000000000000000000000000000" \ -H "Content-Type: application/json" \ -d @render-work/request.jsoncurl -s -X POST http://127.0.0.1:64123/v1/render/letter \ -H @render-work/deploy/auth.header \ -H "Content-Type: application/json" \ -d '{"data":{}}'401{"code":"render.issued-at-missing","detail":"The request was refused; use the problem code and the operator diagnostics to correct it.","status":400,"title":"issued-at-missing","traceId":"<trace-id>","type":"https://render.registrystack.org/problems/issued-at-missing"}Render never supplies the issuance time for you. Use traceId to find the refusal in the
service logs.
Read the audit file
Section titled “Read the audit file”jq -c '{phase, outcome: .record.outcome, problem: .record.problem}' render-work/audit/render.jsonl{"phase":"request","outcome":null,"problem":null}{"phase":"response","outcome":"rendered","problem":null}{"phase":"response","outcome":"refused","problem":"unauthorized"}{"phase":"response","outcome":"refused","problem":"issued-at-missing"}The render wrote a request entry before it started and a response entry with both hashes
before the PDF left. Each refusal wrote one entry. Entries carry hashes, versions, and a
fingerprint of the caller’s key, never the letter’s data.
When you are done, press Ctrl+C in the first terminal and delete render-work.
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.