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

84 lines
8.5 KiB
Markdown

# 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
- Gotenberg installation and image variants: https://gotenberg.dev/docs/getting-started/installation
- Gotenberg Chromium HTML-to-PDF route: https://gotenberg.dev/docs/convert-with-chromium/convert-html-to-pdf
- Gotenberg configuration flags: https://gotenberg.dev/docs/configuration
- Gotenberg outbound URL filtering: https://gotenberg.dev/docs/outbound-url-filtering
- Gotenberg health check: https://gotenberg.dev/docs/system/get-health-check
- Spatie Browsershot requirements: https://spatie.be/docs/browsershot/v4/requirements
- Dompdf README: https://github.com/dompdf/dompdf
- Spatie Laravel PDF DOMPDF driver limitations: https://spatie.be/docs/laravel-pdf/v2/drivers/using-the-dompdf-driver
- wkhtmltopdf status: https://wkhtmltopdf.org/status.html
- wkhtmltopdf archived repository evidence: https://github.com/wkhtmltopdf/wkhtmltopdf/issues/5160
- Snappy wrapper dependency: https://github.com/KnpLabs/snappy/
- PrinceXML product and licensing: https://www.princexml.com/ and https://www.princexml.com/purchase/