Skip to main content

Fast, code-first PDF generation from structured data. No browser, no Chromium.

Project description

TrustRender

Structured business PDFs from code. Pre-render validated. No browser. No Chromium.

Website: trustrender.dev PyPI: pypi.org/project/trustrender

Why TrustRender

  • Pre-render validation — catches bad payloads before they reach the renderer
  • Compliance support — EN 16931 / ZUGFeRD for the supported e-invoice path
  • Provenance and hashing — records template, data, and output fingerprints for traceability
  • Operationally lean — no browser or Chromium dependency

TrustRender renders invoices, statements, receipts, and similar structured documents using Typst as the layout engine and Jinja2 for data binding. It ships as a Python library, CLI, and HTTP server.

Non-goals

TrustRender is not:

  • arbitrary HTML-to-PDF conversion
  • a browser or headless renderer
  • a visual or WYSIWYG editor
  • a multi-format converter

It does one thing: structured business PDFs from code.

Install

pip install trustrender

Requires Python 3.11+ and the Typst CLI binary (brew install typst on macOS, or typst.app).

For e-invoice support: pip install "trustrender[zugferd]"

Development

git clone https://github.com/verityengine/trustrender.git
cd trustrender
pip install -e ".[dev]"

Verify

trustrender doctor --smoke

Checks Python version, backends, fonts, and runs a real render + server health check.

Security

trustrender serve has no built-in authentication, authorization, TLS, or rate limiting. It is designed to run as a backend service behind a reverse proxy. Do not expose the server port to the public internet.

If you deploy TrustRender as an HTTP server:

  • Place it behind a reverse proxy (Nginx, Caddy, Traefik, cloud load balancer) that handles TLS termination and authentication.
  • The /render endpoint accepts template source code via the template_source field. Without authentication, any client that can reach the server can submit arbitrary templates for rendering.
  • The /template-source endpoint returns raw template file contents. Restrict access if templates contain business logic you consider sensitive.
  • Backpressure (503 when at concurrency limit) is the only built-in traffic control. It is not a substitute for rate limiting.
  • The server binds to 127.0.0.1 by default. Passing --host 0.0.0.0 opens it to all interfaces — do this only behind a proxy.

TrustRender is a rendering engine, not a security boundary. Treat it like a database: powerful, essential, and never internet-facing without a gateway.

Quick start

pip install trustrender
trustrender quickstart

This creates a sample invoice template and starts the server. Open http://localhost:8190 to render your first PDF.

Python:

from trustrender import render

pdf = render("invoice.j2.typ", "invoice_data.json", output="invoice.pdf")

CLI:

trustrender render invoice.j2.typ invoice_data.json -o invoice.pdf

Server:

trustrender serve --templates . --dashboard --port 8190

Why TrustRender

Validated before render

Every render() call on a .j2.typ template validates data against the template's inferred contract by default. Missing fields, null values, and wrong structural types are rejected with specific field-level errors before Typst compilation starts.

TrustRenderError: Data validation failed: 11 field errors in invoice.j2.typ
  sender: missing required field (expected: object)
  items: missing required field (expected: list[object])
  invoice_date: missing required field

preflight() goes further: structural validation, semantic checks, font verification, compliance eligibility, and text safety scanning — all without rendering.

No browser dependency

No Chromium, no Puppeteer, no headless browser. Typst compiles directly to PDF. The server runs renders as killable subprocesses with real timeout enforcement.

Measured on Apple Silicon (macOS, Python 3.12, Typst 0.14): 1,000-row invoice renders in 211ms (33 pages). Server throughput: 53.8 RPS. Peak RSS: 69.5 MB.

EN 16931 e-invoicing (narrow scope)

Supports a narrow subset of EN 16931 e-invoicing: domestic German B2B invoices with standard VAT, in EUR, via SEPA payment only. Reverse charge, cross-border, allowances/discounts, and non-EUR currencies are not supported. This is not full German e-invoicing mandate coverage. PDF/A-3b output with embedded CII XML. When the optional facturx library is installed (pip install "trustrender[zugferd]"), XSD and Schematron validation run before embedding; without it, field-level and arithmetic consistency validation still run but schema validation is skipped.

trustrender render einvoice.j2.typ data.json -o invoice.pdf --zugferd en16931

Supported: DE, EUR, standard VAT (single or mixed rates), invoices and credit notes. Not supported (fails loudly): reverse charge, cross-border, allowances/charges, non-EUR, zero/negative tax rates.

See docs/einvoice-scope.md for the full scope matrix.

Output provenance

Embeds a cryptographic generation proof in the PDF: template hash, data hash, engine version, timestamp, and a combined proof hash. Verifiable without re-rendering.

from trustrender.provenance import verify_provenance
result = verify_provenance(pdf_bytes, "invoice.j2.typ", original_data)
# result.verified → True if hashes match

Not a digital signature. A generation proof: "was this document produced from this data using this template?"

CLI

trustrender render <template> <data.json> -o <output.pdf> [--zugferd en16931] [--provenance] [--no-validate]
trustrender preflight <template> <data.json> [--semantic] [--strict]
trustrender check <template> [--data <data.json>]
trustrender serve --templates <dir> [--port 8190] [--dashboard] [--history <path>]
trustrender audit <template> <data.json> -o <output.pdf> [--baseline-dir <dir>]
trustrender doctor [--smoke]

Full flag reference: trustrender <command> --help.

HTTP server

Method Path Purpose
POST /render Render template to PDF
POST /preflight Pre-render readiness check
GET /health Health check
GET /template-source?name= Raw template source
GET /history Render trace list (requires --history)
GET /dashboard Ops dashboard (requires --dashboard)

Backpressure: max 8 concurrent renders (configurable), 503 when at capacity. Max body: 10 MB (configurable). Timeout: 30s (subprocess killed on expiry).

See docs/server.md for full API detail, error model, and configuration.

Bundled templates

Template File Description
Invoice examples/invoice.j2.typ Standard invoice with line items
E-Invoice examples/einvoice.j2.typ ZUGFeRD EN 16931 compliant
Statement examples/statement.j2.typ Account/transaction statement
Receipt examples/receipt.j2.typ Point-of-sale receipt
Letter examples/letter.j2.typ Business letter
Report examples/report.j2.typ Executive report with metrics

Each has a matching _data.json file in examples/.

Docker

docker build -t trustrender .
docker run -p 8190:8190 trustrender

Mount custom templates or fonts:

docker run -p 8190:8190 \
  -v /path/to/templates:/templates -e TRUSTRENDER_TEMPLATES_DIR=/templates \
  -v /path/to/fonts:/fonts -e TRUSTRENDER_FONT_PATH=/fonts \
  trustrender

Configuration

Variable Purpose Default
TRUSTRENDER_BACKEND typst-py or typst-cli Auto-detect
TRUSTRENDER_FONT_PATH Font directory Bundled Inter fonts
TRUSTRENDER_TEMPLATES_DIR Template directory for serve
TRUSTRENDER_MAX_BODY_SIZE Max request body (bytes) 10 MB

Development

make dev                    # editable install + dev deps
trustrender doctor --smoke  # verify environment
make test                   # pytest
make lint                   # ruff
make docker                 # build image
make help                   # all targets

854 tests (unit, integration, contract, semantic, ZUGFeRD, provenance, ugly-data, font, pagination, text safety, Schematron).

Documentation

Topic Link
Validation & readiness docs/validation.md
E-invoice scope matrix docs/einvoice-scope.md
HTTP server & error model docs/server.md
Templates & escaping docs/templates.md
Fonts docs/fonts.md
Provenance docs/provenance.md
Known limits docs/known-limits.md

Caveats

  • Typst silently substitutes fonts when a declared font is missing — preflight and doctor catch this for configured font paths, but the render path itself does not error
  • Source mapping from generated Typst back to Jinja2 source is limited
  • typst_markup() intentionally bypasses escaping — template author's responsibility
  • Code/math mode contexts are not auto-escaped (text-interpolation only)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

trustrender-0.1.5.tar.gz (1.8 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

trustrender-0.1.5-py3-none-any.whl (1.7 MB view details)

Uploaded Python 3

File details

Details for the file trustrender-0.1.5.tar.gz.

File metadata

  • Download URL: trustrender-0.1.5.tar.gz
  • Upload date:
  • Size: 1.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for trustrender-0.1.5.tar.gz
Algorithm Hash digest
SHA256 75a0e4a7e873e52acf420061046070b92a976447eed19626dbb1b18d0f6ffe79
MD5 6adec82456aecd134e1e1822cb40cd33
BLAKE2b-256 915322edb4f649fa552a3c008d73c049e4085f5360312d8a5356728b631951f1

See more details on using hashes here.

Provenance

The following attestation bundles were made for trustrender-0.1.5.tar.gz:

Publisher: publish.yml on verityengine/trustrender

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file trustrender-0.1.5-py3-none-any.whl.

File metadata

  • Download URL: trustrender-0.1.5-py3-none-any.whl
  • Upload date:
  • Size: 1.7 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for trustrender-0.1.5-py3-none-any.whl
Algorithm Hash digest
SHA256 741a0528970f7eea0cf82e27d3c840967c89b6fe4905bf10fe7b4dbe333b499d
MD5 ad4285581f953506fb01f6fb6b6cf7b2
BLAKE2b-256 5fb571b2c4ffd09652e7a4b7e3d90c91667d2bae4ce1c1ec1ba7b2f966da88ac

See more details on using hashes here.

Provenance

The following attestation bundles were made for trustrender-0.1.5-py3-none-any.whl:

Publisher: publish.yml on verityengine/trustrender

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page