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.mdfilename 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), /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 — 20 content node types including richText, table, pivotTable, conditional, repeater, chart, barcode, and signatureField |
| 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 | 15 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 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 @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 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. |
| PPTX rendering | pivotTable 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-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 | 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. |