TenantAtlas/specs/378-management-report-pdf-v1/artifacts/spec378-pdf-renderer-decision-matrix.md
ahmido d43ebcb4ee feat(report): implement management report pdf v1 (#449)
Added PDF generation service for management reports as per Spec 378, including Gotenberg integration in docker-compose and configuration updates.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #449
2026-06-14 18:36:07 +00:00

8.5 KiB

Spec 378 PDF Renderer Decision Matrix

Date: 2026-06-14 Scope: Management Report PDF v1 renderer/runtime governance only. Outcome: approved with controls for Gotenberg 8 Chromium as an internal Docker service.

Repo Safety Baseline

  • Branch: 378-management-report-pdf-v1
  • HEAD: f1eadadf docs: add spec 377 post-productization browser reaudit closeout gate (#448)
  • git status --short --branch: branch 378-management-report-pdf-v1; untracked specs/378-management-report-pdf-v1/
  • git diff --name-only: no tracked-file diff before this governance update
  • Staged files before this governance update: none observed
  • Dirty/untracked files before this governance update: specs/378-management-report-pdf-v1/
  • Spec 378 files already changed before this governance update: present as an untracked active spec directory
  • Other specs affected before this governance update: none observed
  • Existing runtime PDF renderer in apps/platform/composer.json: none
  • Existing PDF runtime in apps/platform/package.json: none; playwright is listed only under devDependencies
  • Docker infrastructure: root docker-compose.yml exists with laravel.test, queue, pgsql, and redis; apps/platform/docker-compose.yml, apps/platform/Dockerfile, and apps/platform/docker/ are not present

Gate Verification

Result: gate can be updated with approved renderer.

  • Spec 378 was correctly blocked at the renderer/package gate.
  • The missing approved production-safe PDF renderer was documented in spec.md, plan.md, tasks.md, and checklists/requirements.md.
  • Downstream implementation tasks remain open.
  • No runtime implementation was started in tracked app/runtime files.
  • Playwright is dev/browser tooling only in apps/platform/package.json.
  • No Composer PDF renderer is present in apps/platform/composer.json.
  • Root Docker Compose already has an internal service network, so an internal renderer service can fit the deployment model, but no Gotenberg service exists yet.

Decision Matrix

Candidate Runtime model Deployment impact Security posture Maintenance posture Layout quality Laravel integration complexity Docker/Dokploy fit License/commercial concern Enterprise suitability Decision Rationale
Gotenberg 8 Chromium internal service Separate Docker HTTP service using Chromium HTML-to-PDF route; Laravel calls it through a narrow PdfRenderingGateway / PdfRendererClient Requires adding a pinned internal service, health check, env config, timeouts, request/output limits, and Dokploy service wiring Strongest option when kept internal-only with no public port, no user-provided URL rendering, outbound restrictions, server-generated HTML, and structured failure mapping Operationally patchable as a service image; avoids embedding Chrome/Node in the app container; image must be pinned to an explicit 8.x Chromium tag or digest High for modern HTML/CSS report layouts, page breaks, headers/footers, and branded management reports Moderate; HTTP client and gateway contract are needed, but no Composer PDF package or browser binary in Laravel Strong; repo is Sail/Docker/Dokploy-oriented and already uses a compose network Open-source service; no per-document commercial licensing identified for report rendering; production image/version governance still required Best fit for enterprise SaaS report PDFs under controls approved with controls Matches Docker-first deployment, isolates browser runtime from Laravel, supports Chromium-quality rendering, and can be governed with network, timeout, egress, and artifact controls.
Spatie Browsershot / Spatie Laravel PDF with Browsershot Laravel package invokes Puppeteer/Chrome through Node Adds Composer package plus Node/Puppeteer/Chrome runtime or custom image changes near the app container Larger app-container attack/ops surface; browser process ownership, temp files, sandboxing, and binary path management move into Laravel runtime Actively used but coupled to Node/Puppeteer/Chrome versions and host dependencies High Chromium quality Low at code level but high at runtime; needs Node 22+, Puppeteer, Chrome, binary paths Weaker for this repo because it pulls browser runtime into app/queue containers MIT-style package ecosystem, but operational dependency surface is larger Good for smaller apps; not preferred here rejected It solves layout quality but violates the preferred isolation boundary: no direct Chromium/Node/browser runtime in Laravel app or queue containers for v1.
dompdf / barryvdh/laravel-dompdf Pure PHP package inside Laravel Simple Composer install and no external service Smaller infrastructure surface, but renderer runs in app process and remote resource/file options require careful hardening Mature PHP package, but HTML/CSS engine is intentionally limited Not sufficient for modern enterprise report layouts; limited CSS, no JavaScript, weak repeating header/footer support Low Good deployment simplicity LGPL/transitive license review required; no commercial blocker Suitable for simple PDFs only rejected Simplicity is outweighed by CSS/layout limitations for branded management reports with reliable page breaks, tables, and headers/footers.
wkhtmltopdf / Snappy Native binary or PHP wrapper around wkhtmltopdf using Qt WebKit Requires binary installation, container image changes, and runtime process management Higher maintenance and security risk due to old browser engine lineage Poor; upstream wkhtmltopdf repository is archived/read-only and organization is marked unmaintained Historically useful but outdated rendering engine Moderate; wrappers exist but depend on external binary Weak for new platform work LGPL-style tooling; main issue is maintenance, not license Not acceptable for new enterprise SaaS renderer rejected The upstream status and Qt WebKit legacy make it a poor default for a new security-sensitive SaaS runtime.
PrinceXML Commercial print/Paged Media engine Requires licensed binary/service integration and procurement Strong if licensed and isolated correctly Strong commercial vendor posture Highest print/Paged Media quality Moderate; custom client/integration required Good if packaged as service or sidecar Commercial/OEM license and cost review required Excellent for premium publishing/compliance documents premium future option Overkill for v1 management reports, but worth revisiting for premium print workflows or legally constrained document classes if licensing is approved.

Selected Renderer

Decision: approved with controls.

Approved renderer family: Gotenberg 8 Chromium internal PDF rendering service.

Production pinning rule: use an explicit Gotenberg 8 Chromium image tag or immutable digest during the runtime config task. Do not use latest. Do not treat the unpinned major tag as sufficient for production promotion unless the deployment governance explicitly accepts that risk.

Approved scope:

  • Internal PDF rendering infrastructure for TenantPilot report-style documents.
  • Server-generated HTML-to-PDF using the Chromium conversion route.
  • Use by Spec 378 Management Report PDF v1 through a Laravel gateway/client abstraction.

Not approved:

  • Legal invoice generation.
  • German B2B e-invoicing.
  • XRechnung, ZUGFeRD, or Factur-X compliance.
  • GoBD archival.
  • Tax calculation.
  • Invoice numbering.
  • Billing compliance.
  • Public renderer exposure.
  • User-supplied URL-to-PDF rendering in v1.
  • Direct Node/Chromium/browser runtime inside the Laravel app or queue containers.

Consulted Primary Sources