Pulp Engine Document Rendering
Get started

Pulp Engine — Scope Summary

Canonical reference for what’s shipped and which limitations still apply. Other docs link here for the authoritative current-limitations list.

File retains its mvp-scope.md filename for link stability; the content has grown well beyond the original MVP scope and is now the living product-scope reference.


What Pulp Engine does

Pulp Engine is a document rendering service. Callers POST a template key and JSON data over HTTP and receive a PDF. The system handles field mapping, schema validation, HTML generation, and PDF rendering internally. The caller’s technology stack is irrelevant — any system that can make an HTTP request can use it.

Calling systems tested: C#, JavaScript/Node.js, PHP, PowerShell, curl, VB.NET.


What’s shipped

CapabilityDetail
REST APIFastify on Node 22. Core surface: POST /render (PDF), /render/html, /render/docx, /render/pptx, /render/csv, /render/xlsx, /render/batch, /render/validate, /render/pdf/* (merge/watermark/insert), /render/preview/*, /templates/*, /assets/*, /schedules/*, /admin/*, /capabilities, /usage, /audit-events. OpenAPI 3.0 at /docs.
Template storagePostgreSQL (JSONB), SQL Server (NVARCHAR JSON), or file-based (STORAGE_MODE env var). Versioned. Full CRUD and discovery endpoints.
Field mappingRaw caller payload mapped to adapted data shape via dot-path rules, defaults, and safe expression evaluation (jexl)
Schema validationJSON Schema (Draft-07, Ajv). Structured 422 errors returned before rendering
HTML renderingFull AST walk — 20 content node types including richText, table, pivotTable, conditional, repeater, chart, barcode, and signatureField
PDF renderingPuppeteer (headless Chrome). Singleton browser, reused across requests
Formatting helperscurrency (Intl, locale-configurable), date (locale-configurable, 4 formats), number, upper/lowercase, sum (array field aggregation)
Conditional sectionsShow/hide blocks based on data values. 8 operators including exists/notExists. Optional conditionExpression jexl string as an escape hatch.
Repeating sectionsrepeater node iterates arrays with per-item scoped data and @index/@first/@last
TablesFirst-class table node. Headers, footers, alternating rows, empty state, print repeat header
Pagination hintsbreakBefore, breakAfter, breakInside, keepWithNext — compiled to CSS print rules
Sample templatesloan-approval-letter (AUD, conditionals, fee table), sample-invoice (ABN, GST, line items)
Automated testsBroad unit coverage across every workspace (template-model, data-adapter, schema-validator, html/chart/docx/pptx renderers, pdf-renderer, editor, API). Two Playwright E2E jobs (auth-disabled and auth-enabled) covering login, publish, version restore, asset upload/delete. Postgres integration tests for the API. Run pnpm test to see current counts.
CIGitHub Actions — install, build, test on every push/PR to main. PostgreSQL service container. Separate test-e2e job (auth-disabled Playwright, Chromium, uploads report on failure) and test-e2e-auth job (auth-enabled Playwright, login/publish/restore/asset flows).
Image asset managementUpload, list, and delete image files via POST /assets/upload, GET /assets, DELETE /assets/:id. Files stored in ASSETS_DIR on the API host and served statically at /assets/*. Editor validates image node src references against the loaded asset list and warns on broken refs.
Chart / graph nodes15 chart types (bar, line, pie, area, donut, horizontalBar, stackedBar, groupedBar, scatter, gauge, funnel, waterfall, treemap, heatmap, combo) rendered as server-side inline SVG. Data bound via dataPath, categoryField, valueField, and (for multi-series) seriesFields; combo also accepts lineSeriesFields and an optional secondaryAxis. Bar-family charts support showDataLabels + dataLabelPosition. Optional title, legend, and empty-state message.
Barcode / QR nodes6 formats (QR, Code 128, Code 39, EAN-13, UPC, Data Matrix) via @pulp-engine/barcode-renderer. Handlebars-capable value. Rendered as inline SVG in HTML/PDF/DOCX/PPTX.
Additional output formatsPOST /render/docx, /render/pptx, /render/csv, /render/xlsx, and the /render/preview/* family for editor previews. Batch variants: /render/batch, /batch/pptx, /batch/docx.
Scheduled deliveryCron-driven render jobs with delivery to email, S3, or webhook targets (SCHEDULE_ENABLED, Postgres or SQL Server). DLQ inspection via /admin/schedule-dlq.
AI template generation (opt-in)POST /templates/generate — schema-constrained Claude tool-use. Opt-in via ANTHROPIC_API_KEY.
Multi-tenant mode (opt-in)MULTI_TENANT_ENABLED (Postgres or SQL Server) — per-tenant isolation of templates, assets, credentials, and audit events. Super-admin and /admin/tenants CRUD. See tenant-isolation-guarantees.md.
OIDC / SSO (opt-in)PKCE auth-code flow with auto-provisioning, silent refresh, and embed exchange. See oidc-guide.md.
Plugin systemRenderers, events, routes, and custom node types via @pulp-engine/plugin-api + @pulp-engine/plugin-testing.
Embeddable editor<pulp-engine-editor> custom element for embedding the template editor into a customer application. See embed-integration.md.
Render isolation modesRENDER_MODE = in-process / child-process (default) / container / socket.
Template CRUDPOST /templates (create), PUT /templates/:key (update with auto version bump), DELETE /templates/:key (soft delete), POST /templates/:key/versions/:version/restore (restore historical version to current).
Visual editorapps/editor — browser-based template builder. Drag-and-drop reordering and cross-container moves, keyboard drag/reorder (Space+Arrow with live-region announcements), inline Handlebars and jexl expression feedback, version history with visual compare and restore, version labels (promote stable / draft / A/B variants), save/state management, unsaved-change protection. Multi-template tabs, duplicate/Save As, command menu (Ctrl+K), find/replace, schema explorer, autosave drafts. AI template generation (opt-in — ✨ Generate with AI card in the new-template dialog). OIDC/SSO login alongside key-based auth. Scheduled-delivery UI (cron → email/S3/webhook). Per-node comments, node templates/favorites, template linting hints with dismissals, visual JEXL expression builder, Handlebars playground, collapsible property groups. Document tab: paper size, orientation, page margins, header/footer HTML authoring. Navigable validation panel; preview panel distinguishes HTML and PDF output. richText nodes support inline canvas editing and a Tiptap-based Properties panel (WCAG-compliant toolbar, Visual + JSON modes). Embed mode (<pulp-engine-editor> custom element) and plugin-registered custom node types. Browser-level Playwright E2E coverage. In internal pilot.
Developer docsREADME, API guide, technical spec, demo guide, editor guide, release checklist

Current limitations

Consolidated list of limitations that still apply on the current release. This is the canonical source — other docs should link here rather than duplicate.

LimitationNotes
Per-request locale overrideLocale is configured at template level via renderConfig.locale; per-request overrides are not supported
File storage is single-instance, single-tenantSTORAGE_MODE=file is intended for evaluation and single-node deployments. Multi-instance deployments require postgres (or sqlserver). MULTI_TENANT_ENABLED=true requires postgres and rejects file mode at startup.
Multi-tenant mode requires a database backendRequires STORAGE_MODE=postgres or sqlserver. File mode is single-tenant by construction and rejects MULTI_TENANT_ENABLED=true at startup.
Schedule delivery requires a database backendScheduling needs persistent job state; requires STORAGE_MODE=postgres or sqlserver. File mode does not expose the schedule routes.
PPTX renderingpivotTable nodes are not rendered in PPTX; pageBreak inside columns is not honoured; header/footer totalPages marker is not substituted. All other node types render normally.
AI template generation is opt-inPOST /templates/generate is only registered when ANTHROPIC_API_KEY is set. No local/offline model path is provided.
SVG image assets are blockedServer-side SVG uploads are rejected (magic-bytes check). Legacy assets are surfaced via GET /assets?legacySvg=true.
Embed CSP requires explicit origin allow-listEMBED_ALLOWED_ORIGINS must be set for the embeddable editor to load in a customer iframe.
Trusted-author deployment modelTemplate authors are assumed to be credentialled internal team members. Handlebars helpers and templates run inside a trusted Chromium render context; Pulp Engine is not designed for untrusted-template environments. This is a design boundary, not a roadmap item.

Risk register

RiskLikelihoodMitigation
Puppeteer cold start (~2–3 s) adds latency to first PDF per deploymentLow — singleton browser reused ResolvedMonitor response times; consider warm-up request on startup Browser warmup runs at startup: warmBrowser() populates the singleton (in-process) or the child-process worker’s browser before the server accepts requests. Cold-start penalty eliminated for all persistent render modes.
Large documents exhaust memoryLow for current template sizesPDF responses are streamed via createPDFStream() — no Node.js PDF buffer. Concurrent pages capped at 5 (excess requests queue). In RENDER_MODE=container / socket, each render runs in an ephemeral worker, insulating API process memory from Chrome render memory entirely. Chrome render-time and HTML rendering memory still apply within the worker; monitor worker RSS on very large documents.