Pulp EngineDocument 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

Capability Detail
REST API Fastify on Node 22. Core surface: POST /render/pdf (PDF; POST /render is a deprecated alias), /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 storage PostgreSQL (JSONB), SQL Server (NVARCHAR JSON), or file-based (STORAGE_MODE env var). Versioned. Full CRUD and discovery endpoints.
Field mapping Raw caller payload mapped to adapted data shape via dot-path rules, defaults, and safe expression evaluation (jexl)
Schema validation JSON Schema (Draft-07, Ajv). Structured 422 errors returned before rendering
HTML rendering Full AST walk — 28 content node types including richText, table, pivotTable, conditional, repeater, chart, barcode, signatureField, and stickyHeader
PDF rendering Puppeteer (headless Chrome). Singleton browser, reused across requests
Formatting helpers currency (Intl, locale-configurable), date (locale-configurable, 4 formats), number, upper/lowercase, sum (array field aggregation)
Conditional sections Show/hide blocks based on data values. 8 operators including exists/notExists. Optional conditionExpression jexl string as an escape hatch.
Repeating sections repeater node iterates arrays with per-item scoped data and @index/@first/@last
Tables First-class table node. Headers, footers, alternating rows, empty state, print repeat header
Pagination hints breakBefore, breakAfter, breakInside, keepWithNext — compiled to CSS print rules
Sample templates loan-approval-letter (AUD, conditionals, fee table), sample-invoice (ABN, GST, line items)
Automated tests Broad 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.
CI GitHub 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 management Upload, 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 nodes 24 chart types (bar, line, pie, area, stackedArea, donut, horizontalBar, stackedBar, groupedBar, scatter, gauge, funnel, waterfall, treemap, heatmap, combo, bullet, bubble, radar, pareto, histogram, boxPlot, candlestick, gantt) 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 nodes 6 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 formats POST /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 delivery Cron-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 system Renderers, events, routes, and custom node types via MIT @pulp-engine/plugin-api + @pulp-engine/plugin-testing; see plugin authoring. New npm names are release-ready and await their first publication.
Embeddable editor <pulp-engine-editor> custom element for embedding the template editor into a customer application. Typed vanilla and React wrappers load the element from your own server; new npm names await their first publication. See embed-integration.md.
Render isolation modes RENDER_MODE = in-process / child-process (default) / container / socket.
Template CRUD POST /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 editor apps/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 docs README, 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.

Limitation Notes
Per-request locale override Locale is configured at template level via renderConfig.locale; per-request overrides are not supported
File storage is single-instance, single-tenant STORAGE_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 backend Requires 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 backend Scheduling needs persistent job state; requires STORAGE_MODE=postgres or sqlserver. File mode does not expose the schedule routes.
Output fidelity PPTX is stable, with editable supported charts and table/pivot continuation; charts with advanced features can use images. See the generated format support matrix for current node and feature support.
AI template generation is opt-in POST /templates/generate is only registered when ANTHROPIC_API_KEY is set. No local/offline model path is provided.
SVG image assets are blocked Server-side SVG uploads are rejected (magic-bytes check). Legacy assets are surfaced via GET /assets?legacySvg=true.
Embed CSP requires explicit origin allow-list EMBED_ALLOWED_ORIGINS must be set for the embeddable editor to load in a customer iframe.
Trusted-author deployment model Template 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

Risk Likelihood Mitigation
Puppeteer cold start (~2–3 s) adds latency to first PDF per deployment Low — singleton browser reused Resolved Monitor 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 memory Low for current template sizes PDF 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.

Enabling INLINE_TEMPLATES_ENABLED extends template-author trust to unrestricted render credentials. Keys restricted to saved template keys/labels cannot supply inline definitions. Raw HTML conversion is separately opt-in and always disables JavaScript; isolation and egress policy continue to apply.

SQL connector trust model: any editor can read whatever a shared connection database user can read. Dedicated least-privilege read-only users are mandatory; a SELECT guard and transaction rollback do not replace database grants.