trustrender 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- trustrender-0.1.0/LICENSE +21 -0
- trustrender-0.1.0/PKG-INFO +262 -0
- trustrender-0.1.0/README.md +227 -0
- trustrender-0.1.0/pyproject.toml +63 -0
- trustrender-0.1.0/setup.cfg +4 -0
- trustrender-0.1.0/src/trustrender/__init__.py +607 -0
- trustrender-0.1.0/src/trustrender/cli.py +783 -0
- trustrender-0.1.0/src/trustrender/contract.py +767 -0
- trustrender-0.1.0/src/trustrender/dashboard.py +371 -0
- trustrender-0.1.0/src/trustrender/doctor.py +363 -0
- trustrender-0.1.0/src/trustrender/engine.py +364 -0
- trustrender-0.1.0/src/trustrender/errors.py +112 -0
- trustrender-0.1.0/src/trustrender/filters.py +57 -0
- trustrender-0.1.0/src/trustrender/fingerprint.py +700 -0
- trustrender-0.1.0/src/trustrender/fonts/Inter/Inter-Bold.ttf +0 -0
- trustrender-0.1.0/src/trustrender/fonts/Inter/Inter-BoldItalic.ttf +0 -0
- trustrender-0.1.0/src/trustrender/fonts/Inter/Inter-Italic.ttf +0 -0
- trustrender-0.1.0/src/trustrender/fonts/Inter/Inter-Regular.ttf +0 -0
- trustrender-0.1.0/src/trustrender/fonts/Inter/LICENSE.txt +92 -0
- trustrender-0.1.0/src/trustrender/playground/assets/index-NINB-pNr.js +31 -0
- trustrender-0.1.0/src/trustrender/playground/assets/index-V6TpJlAv.css +2 -0
- trustrender-0.1.0/src/trustrender/playground/assets/pdf.worker-C1breYqn.mjs +56901 -0
- trustrender-0.1.0/src/trustrender/playground/favicon.svg +14 -0
- trustrender-0.1.0/src/trustrender/playground/icons.svg +24 -0
- trustrender-0.1.0/src/trustrender/playground/index.html +17 -0
- trustrender-0.1.0/src/trustrender/provenance.py +257 -0
- trustrender-0.1.0/src/trustrender/readiness.py +659 -0
- trustrender-0.1.0/src/trustrender/regression.py +580 -0
- trustrender-0.1.0/src/trustrender/semantic.py +688 -0
- trustrender-0.1.0/src/trustrender/server.py +577 -0
- trustrender-0.1.0/src/trustrender/templates.py +149 -0
- trustrender-0.1.0/src/trustrender/trace.py +321 -0
- trustrender-0.1.0/src/trustrender/zugferd.py +565 -0
- trustrender-0.1.0/src/trustrender.egg-info/PKG-INFO +262 -0
- trustrender-0.1.0/src/trustrender.egg-info/SOURCES.txt +62 -0
- trustrender-0.1.0/src/trustrender.egg-info/dependency_links.txt +1 -0
- trustrender-0.1.0/src/trustrender.egg-info/entry_points.txt +2 -0
- trustrender-0.1.0/src/trustrender.egg-info/requires.txt +15 -0
- trustrender-0.1.0/src/trustrender.egg-info/top_level.txt +1 -0
- trustrender-0.1.0/tests/test_audit_e2e.py +399 -0
- trustrender-0.1.0/tests/test_cli.py +104 -0
- trustrender-0.1.0/tests/test_contract.py +585 -0
- trustrender-0.1.0/tests/test_doctor.py +273 -0
- trustrender-0.1.0/tests/test_engine.py +351 -0
- trustrender-0.1.0/tests/test_error_pipeline.py +447 -0
- trustrender-0.1.0/tests/test_errors.py +98 -0
- trustrender-0.1.0/tests/test_filters.py +93 -0
- trustrender-0.1.0/tests/test_fingerprint.py +192 -0
- trustrender-0.1.0/tests/test_fingerprint_stress.py +383 -0
- trustrender-0.1.0/tests/test_fonts.py +327 -0
- trustrender-0.1.0/tests/test_pagination.py +246 -0
- trustrender-0.1.0/tests/test_provenance.py +166 -0
- trustrender-0.1.0/tests/test_readiness.py +364 -0
- trustrender-0.1.0/tests/test_regression.py +310 -0
- trustrender-0.1.0/tests/test_regression_stress.py +429 -0
- trustrender-0.1.0/tests/test_render.py +109 -0
- trustrender-0.1.0/tests/test_semantic.py +838 -0
- trustrender-0.1.0/tests/test_semantic_stress.py +461 -0
- trustrender-0.1.0/tests/test_server.py +556 -0
- trustrender-0.1.0/tests/test_templates.py +247 -0
- trustrender-0.1.0/tests/test_trace.py +196 -0
- trustrender-0.1.0/tests/test_ugly_data.py +525 -0
- trustrender-0.1.0/tests/test_ugly_data_pressure.py +940 -0
- trustrender-0.1.0/tests/test_zugferd.py +790 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Verity Engine
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: trustrender
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Fast, code-first PDF generation from structured data. No browser, no Chromium.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://trustrender.dev
|
|
7
|
+
Project-URL: Documentation, https://trustrender.dev
|
|
8
|
+
Project-URL: Source, https://github.com/verityengine/trustrender
|
|
9
|
+
Project-URL: Issues, https://github.com/verityengine/trustrender/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/verityengine/trustrender/releases
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
17
|
+
Classifier: Topic :: Text Processing :: Markup
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: typst>=0.14
|
|
22
|
+
Requires-Dist: jinja2>=3.1
|
|
23
|
+
Requires-Dist: starlette>=0.40
|
|
24
|
+
Requires-Dist: uvicorn>=0.30
|
|
25
|
+
Requires-Dist: drafthorse>=2024.0
|
|
26
|
+
Requires-Dist: pypdf>=4.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
29
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
30
|
+
Requires-Dist: httpx>=0.27; extra == "dev"
|
|
31
|
+
Requires-Dist: factur-x>=3.0; extra == "dev"
|
|
32
|
+
Provides-Extra: zugferd
|
|
33
|
+
Requires-Dist: factur-x>=3.0; extra == "zugferd"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# TrustRender
|
|
37
|
+
|
|
38
|
+
Structured business PDFs from code. Pre-render validated. No browser. No Chromium.
|
|
39
|
+
|
|
40
|
+
## Why TrustRender
|
|
41
|
+
|
|
42
|
+
- **Pre-render validation** — catches bad payloads before they reach the renderer
|
|
43
|
+
- **Compliance support** — EN 16931 / ZUGFeRD for the supported e-invoice path
|
|
44
|
+
- **Provenance and hashing** — records template, data, and output fingerprints for traceability
|
|
45
|
+
- **Operationally lean** — no browser or Chromium dependency
|
|
46
|
+
|
|
47
|
+
TrustRender renders invoices, statements, receipts, and similar structured documents using [Typst](https://typst.app/) as the layout engine and Jinja2 for data binding. It ships as a Python library, CLI, and HTTP server.
|
|
48
|
+
|
|
49
|
+
### Non-goals
|
|
50
|
+
|
|
51
|
+
TrustRender is not:
|
|
52
|
+
|
|
53
|
+
- arbitrary HTML-to-PDF conversion
|
|
54
|
+
- a browser or headless renderer
|
|
55
|
+
- a visual or WYSIWYG editor
|
|
56
|
+
- a multi-format converter
|
|
57
|
+
|
|
58
|
+
It does one thing: structured business PDFs from code.
|
|
59
|
+
|
|
60
|
+
## Install
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
git clone https://github.com/verityengine/trustrender.git
|
|
64
|
+
cd trustrender
|
|
65
|
+
pip install .
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Requires Python 3.11+ and the Typst CLI binary (`brew install typst` on macOS, or [typst.app](https://typst.app/)).
|
|
69
|
+
|
|
70
|
+
For development: `pip install -e ".[dev]"` or `make dev`.
|
|
71
|
+
|
|
72
|
+
### Verify
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
trustrender doctor --smoke
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Checks Python version, backends, fonts, and runs a real render + server health check.
|
|
79
|
+
|
|
80
|
+
## Security
|
|
81
|
+
|
|
82
|
+
**`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.
|
|
83
|
+
|
|
84
|
+
If you deploy TrustRender as an HTTP server:
|
|
85
|
+
|
|
86
|
+
- Place it behind a reverse proxy (Nginx, Caddy, Traefik, cloud load balancer) that handles TLS termination and authentication.
|
|
87
|
+
- 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.
|
|
88
|
+
- The `/template-source` endpoint returns raw template file contents. Restrict access if templates contain business logic you consider sensitive.
|
|
89
|
+
- Backpressure (503 when at concurrency limit) is the only built-in traffic control. It is not a substitute for rate limiting.
|
|
90
|
+
- 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.
|
|
91
|
+
|
|
92
|
+
TrustRender is a rendering engine, not a security boundary. Treat it like a database: powerful, essential, and never internet-facing without a gateway.
|
|
93
|
+
|
|
94
|
+
## Quick start
|
|
95
|
+
|
|
96
|
+
**Python:**
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from trustrender import render
|
|
100
|
+
|
|
101
|
+
pdf = render("examples/invoice.j2.typ", "examples/invoice_data.json", output="invoice.pdf")
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**CLI:**
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
trustrender render examples/invoice.j2.typ examples/invoice_data.json -o invoice.pdf
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Server:**
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
trustrender serve --templates examples/ --port 8190
|
|
114
|
+
curl -X POST http://localhost:8190/render \
|
|
115
|
+
-H "Content-Type: application/json" \
|
|
116
|
+
--data @examples/request_invoice.json -o invoice.pdf
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Why TrustRender
|
|
120
|
+
|
|
121
|
+
### Validated before render
|
|
122
|
+
|
|
123
|
+
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.
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
TrustRenderError: Data validation failed: 11 field errors in invoice.j2.typ
|
|
127
|
+
sender: missing required field (expected: object)
|
|
128
|
+
items: missing required field (expected: list[object])
|
|
129
|
+
invoice_date: missing required field
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`preflight()` goes further: structural validation, semantic checks, font verification, compliance eligibility, and text safety scanning — all without rendering.
|
|
133
|
+
|
|
134
|
+
### No browser dependency
|
|
135
|
+
|
|
136
|
+
No Chromium, no Puppeteer, no headless browser. Typst compiles directly to PDF. The server runs renders as killable subprocesses with real timeout enforcement.
|
|
137
|
+
|
|
138
|
+
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.
|
|
139
|
+
|
|
140
|
+
### EN 16931 e-invoicing (narrow scope)
|
|
141
|
+
|
|
142
|
+
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.
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
trustrender render einvoice.j2.typ data.json -o invoice.pdf --zugferd en16931
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Supported: DE, EUR, standard VAT (single or mixed rates), invoices and credit notes.
|
|
149
|
+
Not supported (fails loudly): reverse charge, cross-border, allowances/charges, non-EUR, zero/negative tax rates.
|
|
150
|
+
|
|
151
|
+
See [docs/einvoice-scope.md](docs/einvoice-scope.md) for the full scope matrix.
|
|
152
|
+
|
|
153
|
+
### Output provenance
|
|
154
|
+
|
|
155
|
+
Embeds a cryptographic generation proof in the PDF: template hash, data hash, engine version, timestamp, and a combined proof hash. Verifiable without re-rendering.
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from trustrender.provenance import verify_provenance
|
|
159
|
+
result = verify_provenance(pdf_bytes, "invoice.j2.typ", original_data)
|
|
160
|
+
# result.verified → True if hashes match
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Not a digital signature. A generation proof: "was this document produced from this data using this template?"
|
|
164
|
+
|
|
165
|
+
## CLI
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
trustrender render <template> <data.json> -o <output.pdf> [--zugferd en16931] [--provenance] [--no-validate]
|
|
169
|
+
trustrender preflight <template> <data.json> [--semantic] [--strict]
|
|
170
|
+
trustrender check <template> [--data <data.json>]
|
|
171
|
+
trustrender serve --templates <dir> [--port 8190] [--dashboard] [--history <path>]
|
|
172
|
+
trustrender audit <template> <data.json> -o <output.pdf> [--baseline-dir <dir>]
|
|
173
|
+
trustrender doctor [--smoke]
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Full flag reference: `trustrender <command> --help`.
|
|
177
|
+
|
|
178
|
+
## HTTP server
|
|
179
|
+
|
|
180
|
+
| Method | Path | Purpose |
|
|
181
|
+
|--------|------|---------|
|
|
182
|
+
| `POST` | `/render` | Render template to PDF |
|
|
183
|
+
| `POST` | `/preflight` | Pre-render readiness check |
|
|
184
|
+
| `GET` | `/health` | Health check |
|
|
185
|
+
| `GET` | `/template-source?name=` | Raw template source |
|
|
186
|
+
| `GET` | `/history` | Render trace list (requires `--history`) |
|
|
187
|
+
| `GET` | `/dashboard` | Ops dashboard (requires `--dashboard`) |
|
|
188
|
+
|
|
189
|
+
Backpressure: max 8 concurrent renders (configurable), 503 when at capacity.
|
|
190
|
+
Max body: 10 MB (configurable). Timeout: 30s (subprocess killed on expiry).
|
|
191
|
+
|
|
192
|
+
See [docs/server.md](docs/server.md) for full API detail, error model, and configuration.
|
|
193
|
+
|
|
194
|
+
## Bundled templates
|
|
195
|
+
|
|
196
|
+
| Template | File | Description |
|
|
197
|
+
|----------|------|-------------|
|
|
198
|
+
| Invoice | `examples/invoice.j2.typ` | Standard invoice with line items |
|
|
199
|
+
| E-Invoice | `examples/einvoice.j2.typ` | ZUGFeRD EN 16931 compliant |
|
|
200
|
+
| Statement | `examples/statement.j2.typ` | Account/transaction statement |
|
|
201
|
+
| Receipt | `examples/receipt.j2.typ` | Point-of-sale receipt |
|
|
202
|
+
| Letter | `examples/letter.j2.typ` | Business letter |
|
|
203
|
+
| Report | `examples/report.j2.typ` | Executive report with metrics |
|
|
204
|
+
|
|
205
|
+
Each has a matching `_data.json` file in `examples/`.
|
|
206
|
+
|
|
207
|
+
## Docker
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
docker build -t trustrender .
|
|
211
|
+
docker run -p 8190:8190 trustrender
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Mount custom templates or fonts:
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
docker run -p 8190:8190 \
|
|
218
|
+
-v /path/to/templates:/templates -e TRUSTRENDER_TEMPLATES_DIR=/templates \
|
|
219
|
+
-v /path/to/fonts:/fonts -e TRUSTRENDER_FONT_PATH=/fonts \
|
|
220
|
+
trustrender
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Configuration
|
|
224
|
+
|
|
225
|
+
| Variable | Purpose | Default |
|
|
226
|
+
|----------|---------|---------|
|
|
227
|
+
| `TRUSTRENDER_BACKEND` | `typst-py` or `typst-cli` | Auto-detect |
|
|
228
|
+
| `TRUSTRENDER_FONT_PATH` | Font directory | Bundled Inter fonts |
|
|
229
|
+
| `TRUSTRENDER_TEMPLATES_DIR` | Template directory for `serve` | — |
|
|
230
|
+
| `TRUSTRENDER_MAX_BODY_SIZE` | Max request body (bytes) | 10 MB |
|
|
231
|
+
|
|
232
|
+
## Development
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
make dev # editable install + dev deps
|
|
236
|
+
trustrender doctor --smoke # verify environment
|
|
237
|
+
make test # pytest
|
|
238
|
+
make lint # ruff
|
|
239
|
+
make docker # build image
|
|
240
|
+
make help # all targets
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
854 tests (unit, integration, contract, semantic, ZUGFeRD, provenance, ugly-data, font, pagination, text safety, Schematron).
|
|
244
|
+
|
|
245
|
+
## Documentation
|
|
246
|
+
|
|
247
|
+
| Topic | Link |
|
|
248
|
+
|-------|------|
|
|
249
|
+
| Validation & readiness | [docs/validation.md](docs/validation.md) |
|
|
250
|
+
| E-invoice scope matrix | [docs/einvoice-scope.md](docs/einvoice-scope.md) |
|
|
251
|
+
| HTTP server & error model | [docs/server.md](docs/server.md) |
|
|
252
|
+
| Templates & escaping | [docs/templates.md](docs/templates.md) |
|
|
253
|
+
| Fonts | [docs/fonts.md](docs/fonts.md) |
|
|
254
|
+
| Provenance | [docs/provenance.md](docs/provenance.md) |
|
|
255
|
+
| Known limits | [docs/known-limits.md](docs/known-limits.md) |
|
|
256
|
+
|
|
257
|
+
## Caveats
|
|
258
|
+
|
|
259
|
+
- 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
|
|
260
|
+
- Source mapping from generated Typst back to Jinja2 source is limited
|
|
261
|
+
- `typst_markup()` intentionally bypasses escaping — template author's responsibility
|
|
262
|
+
- Code/math mode contexts are not auto-escaped (text-interpolation only)
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# TrustRender
|
|
2
|
+
|
|
3
|
+
Structured business PDFs from code. Pre-render validated. No browser. No Chromium.
|
|
4
|
+
|
|
5
|
+
## Why TrustRender
|
|
6
|
+
|
|
7
|
+
- **Pre-render validation** — catches bad payloads before they reach the renderer
|
|
8
|
+
- **Compliance support** — EN 16931 / ZUGFeRD for the supported e-invoice path
|
|
9
|
+
- **Provenance and hashing** — records template, data, and output fingerprints for traceability
|
|
10
|
+
- **Operationally lean** — no browser or Chromium dependency
|
|
11
|
+
|
|
12
|
+
TrustRender renders invoices, statements, receipts, and similar structured documents using [Typst](https://typst.app/) as the layout engine and Jinja2 for data binding. It ships as a Python library, CLI, and HTTP server.
|
|
13
|
+
|
|
14
|
+
### Non-goals
|
|
15
|
+
|
|
16
|
+
TrustRender is not:
|
|
17
|
+
|
|
18
|
+
- arbitrary HTML-to-PDF conversion
|
|
19
|
+
- a browser or headless renderer
|
|
20
|
+
- a visual or WYSIWYG editor
|
|
21
|
+
- a multi-format converter
|
|
22
|
+
|
|
23
|
+
It does one thing: structured business PDFs from code.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
git clone https://github.com/verityengine/trustrender.git
|
|
29
|
+
cd trustrender
|
|
30
|
+
pip install .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Requires Python 3.11+ and the Typst CLI binary (`brew install typst` on macOS, or [typst.app](https://typst.app/)).
|
|
34
|
+
|
|
35
|
+
For development: `pip install -e ".[dev]"` or `make dev`.
|
|
36
|
+
|
|
37
|
+
### Verify
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
trustrender doctor --smoke
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Checks Python version, backends, fonts, and runs a real render + server health check.
|
|
44
|
+
|
|
45
|
+
## Security
|
|
46
|
+
|
|
47
|
+
**`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.
|
|
48
|
+
|
|
49
|
+
If you deploy TrustRender as an HTTP server:
|
|
50
|
+
|
|
51
|
+
- Place it behind a reverse proxy (Nginx, Caddy, Traefik, cloud load balancer) that handles TLS termination and authentication.
|
|
52
|
+
- 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.
|
|
53
|
+
- The `/template-source` endpoint returns raw template file contents. Restrict access if templates contain business logic you consider sensitive.
|
|
54
|
+
- Backpressure (503 when at concurrency limit) is the only built-in traffic control. It is not a substitute for rate limiting.
|
|
55
|
+
- 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.
|
|
56
|
+
|
|
57
|
+
TrustRender is a rendering engine, not a security boundary. Treat it like a database: powerful, essential, and never internet-facing without a gateway.
|
|
58
|
+
|
|
59
|
+
## Quick start
|
|
60
|
+
|
|
61
|
+
**Python:**
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from trustrender import render
|
|
65
|
+
|
|
66
|
+
pdf = render("examples/invoice.j2.typ", "examples/invoice_data.json", output="invoice.pdf")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**CLI:**
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
trustrender render examples/invoice.j2.typ examples/invoice_data.json -o invoice.pdf
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Server:**
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
trustrender serve --templates examples/ --port 8190
|
|
79
|
+
curl -X POST http://localhost:8190/render \
|
|
80
|
+
-H "Content-Type: application/json" \
|
|
81
|
+
--data @examples/request_invoice.json -o invoice.pdf
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Why TrustRender
|
|
85
|
+
|
|
86
|
+
### Validated before render
|
|
87
|
+
|
|
88
|
+
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.
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
TrustRenderError: Data validation failed: 11 field errors in invoice.j2.typ
|
|
92
|
+
sender: missing required field (expected: object)
|
|
93
|
+
items: missing required field (expected: list[object])
|
|
94
|
+
invoice_date: missing required field
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`preflight()` goes further: structural validation, semantic checks, font verification, compliance eligibility, and text safety scanning — all without rendering.
|
|
98
|
+
|
|
99
|
+
### No browser dependency
|
|
100
|
+
|
|
101
|
+
No Chromium, no Puppeteer, no headless browser. Typst compiles directly to PDF. The server runs renders as killable subprocesses with real timeout enforcement.
|
|
102
|
+
|
|
103
|
+
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.
|
|
104
|
+
|
|
105
|
+
### EN 16931 e-invoicing (narrow scope)
|
|
106
|
+
|
|
107
|
+
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.
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
trustrender render einvoice.j2.typ data.json -o invoice.pdf --zugferd en16931
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Supported: DE, EUR, standard VAT (single or mixed rates), invoices and credit notes.
|
|
114
|
+
Not supported (fails loudly): reverse charge, cross-border, allowances/charges, non-EUR, zero/negative tax rates.
|
|
115
|
+
|
|
116
|
+
See [docs/einvoice-scope.md](docs/einvoice-scope.md) for the full scope matrix.
|
|
117
|
+
|
|
118
|
+
### Output provenance
|
|
119
|
+
|
|
120
|
+
Embeds a cryptographic generation proof in the PDF: template hash, data hash, engine version, timestamp, and a combined proof hash. Verifiable without re-rendering.
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from trustrender.provenance import verify_provenance
|
|
124
|
+
result = verify_provenance(pdf_bytes, "invoice.j2.typ", original_data)
|
|
125
|
+
# result.verified → True if hashes match
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Not a digital signature. A generation proof: "was this document produced from this data using this template?"
|
|
129
|
+
|
|
130
|
+
## CLI
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
trustrender render <template> <data.json> -o <output.pdf> [--zugferd en16931] [--provenance] [--no-validate]
|
|
134
|
+
trustrender preflight <template> <data.json> [--semantic] [--strict]
|
|
135
|
+
trustrender check <template> [--data <data.json>]
|
|
136
|
+
trustrender serve --templates <dir> [--port 8190] [--dashboard] [--history <path>]
|
|
137
|
+
trustrender audit <template> <data.json> -o <output.pdf> [--baseline-dir <dir>]
|
|
138
|
+
trustrender doctor [--smoke]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Full flag reference: `trustrender <command> --help`.
|
|
142
|
+
|
|
143
|
+
## HTTP server
|
|
144
|
+
|
|
145
|
+
| Method | Path | Purpose |
|
|
146
|
+
|--------|------|---------|
|
|
147
|
+
| `POST` | `/render` | Render template to PDF |
|
|
148
|
+
| `POST` | `/preflight` | Pre-render readiness check |
|
|
149
|
+
| `GET` | `/health` | Health check |
|
|
150
|
+
| `GET` | `/template-source?name=` | Raw template source |
|
|
151
|
+
| `GET` | `/history` | Render trace list (requires `--history`) |
|
|
152
|
+
| `GET` | `/dashboard` | Ops dashboard (requires `--dashboard`) |
|
|
153
|
+
|
|
154
|
+
Backpressure: max 8 concurrent renders (configurable), 503 when at capacity.
|
|
155
|
+
Max body: 10 MB (configurable). Timeout: 30s (subprocess killed on expiry).
|
|
156
|
+
|
|
157
|
+
See [docs/server.md](docs/server.md) for full API detail, error model, and configuration.
|
|
158
|
+
|
|
159
|
+
## Bundled templates
|
|
160
|
+
|
|
161
|
+
| Template | File | Description |
|
|
162
|
+
|----------|------|-------------|
|
|
163
|
+
| Invoice | `examples/invoice.j2.typ` | Standard invoice with line items |
|
|
164
|
+
| E-Invoice | `examples/einvoice.j2.typ` | ZUGFeRD EN 16931 compliant |
|
|
165
|
+
| Statement | `examples/statement.j2.typ` | Account/transaction statement |
|
|
166
|
+
| Receipt | `examples/receipt.j2.typ` | Point-of-sale receipt |
|
|
167
|
+
| Letter | `examples/letter.j2.typ` | Business letter |
|
|
168
|
+
| Report | `examples/report.j2.typ` | Executive report with metrics |
|
|
169
|
+
|
|
170
|
+
Each has a matching `_data.json` file in `examples/`.
|
|
171
|
+
|
|
172
|
+
## Docker
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
docker build -t trustrender .
|
|
176
|
+
docker run -p 8190:8190 trustrender
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Mount custom templates or fonts:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
docker run -p 8190:8190 \
|
|
183
|
+
-v /path/to/templates:/templates -e TRUSTRENDER_TEMPLATES_DIR=/templates \
|
|
184
|
+
-v /path/to/fonts:/fonts -e TRUSTRENDER_FONT_PATH=/fonts \
|
|
185
|
+
trustrender
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Configuration
|
|
189
|
+
|
|
190
|
+
| Variable | Purpose | Default |
|
|
191
|
+
|----------|---------|---------|
|
|
192
|
+
| `TRUSTRENDER_BACKEND` | `typst-py` or `typst-cli` | Auto-detect |
|
|
193
|
+
| `TRUSTRENDER_FONT_PATH` | Font directory | Bundled Inter fonts |
|
|
194
|
+
| `TRUSTRENDER_TEMPLATES_DIR` | Template directory for `serve` | — |
|
|
195
|
+
| `TRUSTRENDER_MAX_BODY_SIZE` | Max request body (bytes) | 10 MB |
|
|
196
|
+
|
|
197
|
+
## Development
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
make dev # editable install + dev deps
|
|
201
|
+
trustrender doctor --smoke # verify environment
|
|
202
|
+
make test # pytest
|
|
203
|
+
make lint # ruff
|
|
204
|
+
make docker # build image
|
|
205
|
+
make help # all targets
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
854 tests (unit, integration, contract, semantic, ZUGFeRD, provenance, ugly-data, font, pagination, text safety, Schematron).
|
|
209
|
+
|
|
210
|
+
## Documentation
|
|
211
|
+
|
|
212
|
+
| Topic | Link |
|
|
213
|
+
|-------|------|
|
|
214
|
+
| Validation & readiness | [docs/validation.md](docs/validation.md) |
|
|
215
|
+
| E-invoice scope matrix | [docs/einvoice-scope.md](docs/einvoice-scope.md) |
|
|
216
|
+
| HTTP server & error model | [docs/server.md](docs/server.md) |
|
|
217
|
+
| Templates & escaping | [docs/templates.md](docs/templates.md) |
|
|
218
|
+
| Fonts | [docs/fonts.md](docs/fonts.md) |
|
|
219
|
+
| Provenance | [docs/provenance.md](docs/provenance.md) |
|
|
220
|
+
| Known limits | [docs/known-limits.md](docs/known-limits.md) |
|
|
221
|
+
|
|
222
|
+
## Caveats
|
|
223
|
+
|
|
224
|
+
- 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
|
|
225
|
+
- Source mapping from generated Typst back to Jinja2 source is limited
|
|
226
|
+
- `typst_markup()` intentionally bypasses escaping — template author's responsibility
|
|
227
|
+
- Code/math mode contexts are not auto-escaped (text-interpolation only)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "trustrender"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Fast, code-first PDF generation from structured data. No browser, no Chromium."
|
|
9
|
+
license = "MIT"
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 3 - Alpha",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.11",
|
|
16
|
+
"Programming Language :: Python :: 3.12",
|
|
17
|
+
"Topic :: Office/Business :: Financial",
|
|
18
|
+
"Topic :: Text Processing :: Markup",
|
|
19
|
+
]
|
|
20
|
+
readme = "README.md"
|
|
21
|
+
requires-python = ">=3.11"
|
|
22
|
+
dependencies = [
|
|
23
|
+
"typst>=0.14",
|
|
24
|
+
"jinja2>=3.1",
|
|
25
|
+
"starlette>=0.40",
|
|
26
|
+
"uvicorn>=0.30",
|
|
27
|
+
"drafthorse>=2024.0",
|
|
28
|
+
"pypdf>=4.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://trustrender.dev"
|
|
33
|
+
Documentation = "https://trustrender.dev"
|
|
34
|
+
Source = "https://github.com/verityengine/trustrender"
|
|
35
|
+
Issues = "https://github.com/verityengine/trustrender/issues"
|
|
36
|
+
Changelog = "https://github.com/verityengine/trustrender/releases"
|
|
37
|
+
|
|
38
|
+
[project.scripts]
|
|
39
|
+
trustrender = "trustrender.cli:main"
|
|
40
|
+
|
|
41
|
+
[project.optional-dependencies]
|
|
42
|
+
dev = [
|
|
43
|
+
"pytest>=8.0",
|
|
44
|
+
"ruff>=0.4",
|
|
45
|
+
"httpx>=0.27",
|
|
46
|
+
"factur-x>=3.0",
|
|
47
|
+
]
|
|
48
|
+
zugferd = [
|
|
49
|
+
"factur-x>=3.0",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[tool.setuptools.packages.find]
|
|
53
|
+
where = ["src"]
|
|
54
|
+
|
|
55
|
+
[tool.setuptools.package-data]
|
|
56
|
+
trustrender = ["playground/**/*", "fonts/**/*"]
|
|
57
|
+
|
|
58
|
+
[tool.ruff]
|
|
59
|
+
line-length = 99
|
|
60
|
+
target-version = "py311"
|
|
61
|
+
|
|
62
|
+
[tool.ruff.lint]
|
|
63
|
+
select = ["E", "F", "I", "W"]
|