# PDF2HTML SaaS Development Readiness Review

Date: 2026-08-23

## Verdict

The product specification and acceptance plan are strong enough to define the commercial workflow, but the repository is not ready for unrestricted feature development.

Development can begin with Phase 0: converter hardening, converter contract definition, and deployment preflight. Full SaaS implementation should start only after the Phase 0 exit gates are green.

## Evidence reviewed

- `docs/specs/pdf2html-saas-mvp.md`
- `docs/specs/pdf-to-html-visual-parity-poc.md`
- `docs/plans/pdf2html-saas-mvp-test-plan.md`
- generated five-page pdf2htmlEX demo
- live `../platform/` registry and platform money/authz standards
- live `facebook/astryx` repository and release state

## Confirmed product decisions

- MVP delivery is a deployable ZIP only; no public HTML hosting.
- Source PDFs and generated ZIPs expire after 48 hours.
- No customer-facing file-size or page-count limit is currently advertised.
- Internal safety ceilings and abuse controls remain mandatory.
- Stripe Tax is required for launch.
- One accepted conversion costs one $5 credit.

## Blocking gaps

### B1. The current converter does not yet satisfy the SEO contract

The current five-page output contains:

- 0 heading elements
- 0 paragraph elements
- 123 generic `div` elements
- 5 page-sized background images
- 2 external runtime scripts plus inline initialization
- 0 anchors in the sampled pages

It demonstrates selectable positioned text and manually observed close resemblance on this five-page sample, but no reproducible parity score exists. It does not yet satisfy the specification's semantic-heading, meaningful-reading-order, or no-rasterized-page-text requirements.

Required closure:

- define the converter output contract
- add semantic alignment or a deterministic semantic postprocessor
- decide which page-wide background techniques are acceptable for non-text artwork
- validate actual links, reading order, metadata, and no-JavaScript rendering
- measure against a representative corpus rather than one catalog

### B2. Generated HTML is an untrusted output surface

A hostile PDF can attempt to produce unsafe links, filenames, active content, oversized assets, malformed fonts, or pathological DOM/CSS.

Required closure:

- allowlist output files and URL schemes
- reject path traversal, absolute paths, symlinks, device files, and archive escapes
- remove or replace unapproved active content
- sanitize links and metadata
- generate a restrictive default CSP suitable for the package
- validate total output bytes, file count, individual asset size, and ZIP structure
- serve security fixtures in an isolated browser during testing

### B3. The worker boundary is under-specified

The spec requires isolation but does not define an executable contract.

Required closure:

- non-root container user
- no network
- read-only root filesystem
- dropped capabilities and no-new-privileges
- CPU, memory, PID, temporary-storage, output-size, and wall-time ceilings
- per-job work directory
- deterministic exit codes and public-safe failure codes
- guaranteed process-tree termination and cleanup
- converter version pin, image provenance, SBOM, and vulnerability policy

The current pdf2htmlEX image is a 2020 POC baseline and must not silently become the production image.

### B4. Visual-quality acceptance has no measured threshold

The POC says quality will be measured but defines no representative corpus or pass threshold.

Required closure:

- corpus covering text, magazines/catalogs, forms, tables, links, vectors, transparency, RTL, CJK, scanned pages, rotated pages, and malformed PDFs
- page-level screenshot comparison
- text-recall and Unicode correctness measurements
- link and reading-order checks
- browser matrix
- explicit launch threshold and known-unsupported policy

### B5. There was no implementation plan

The existing plan is an excellent acceptance plan, but not a build sequence. A phased development plan is now added at `docs/plans/pdf2html-saas-development-plan.md`.

### B6. Platform integration has prerequisites

Live platform registry findings:

- `db`, `jobs`, `auth`, `ledger`, `uploads`, and `billing` are shipped and verified.
- `auth-react`, `uploads-react`, and `billing-react` are built but not marked shipped/verified.
- testing doubles for `auth`, `billing`, `uploads`, and `jobs` are still listed as pending.

Required closure:

- verify the React packages in a consumer harness before relying on them
- build only the missing general-purpose platform testing seams required by this app, in the platform repository and under its specs/standards
- do not recreate platform primitives in this repository

### B7. Split-origin deployment needs preflight verification

The deployment contract is now fixed:

- frontend: `https://tools.press.zone`
- API: `https://api.press.zone`
- same-site secure cookies where the platform auth adapter supports them
- exact Origin allowlist
- CSRF protection on cookie-authenticated mutations
- no wildcard credentialed CORS

Live verification on 2026-08-23 confirms that `api.press.zone` resolves through Cloudflare and its HTTPS certificate validates. The root path currently returns HTTP 404; this proves the public DNS/TLS route exists, not that the CloudPanel application route or origin mapping is ready. Verify those remaining layers during deployment preflight.

### B8. Billing lifecycle design is specified but unimplemented

The product spec and development plan now define Stripe Tax jurisdiction gating, idempotent payment-reversal entries, deficit projection/blocking, later-grant application, and audited operator resolution. Phase 4 still must implement and verify them against platform money invariants.

Required closure:

- Stripe Tax enabled and tested in configured launch jurisdictions
- provider events atomically claimed by stable event/object identities
- refunds, disputes, and chargebacks append one linked reversal entry without rewriting history
- positive deficit blocks new work and later grants reduce deficit before creating spendable balance
- payment, grant, reversal, debit, and compensation entries remain auditable

### B9. Privacy and lifecycle surfaces are incomplete

The 48-hour timestamp rules and lifecycle behavior are now specified, and Phase 5/6 own the customer/legal and operational surfaces. Launch still requires implementation and verification of:

- lifecycle deletion plus reconciliation
- visible expiry timestamps
- privacy policy and terms links during registration/payment
- account deletion/export policy
- log redaction and retention policy
- no source PDF content in normal logs, analytics, or error traces

## Non-blocking observations

- ASTRYX is available under MIT and has a current stable release (`v0.4.6` at review time). Pin an exact release and use published packages rather than an unpinned Git branch.
- ZIP-only delivery is compatible with SEO because the output is deployable HTML, but the service itself does not provide discoverability until the customer deploys it.
- Absolute positioning is acceptable for visual parity, but it does not by itself provide semantic reading order or accessibility.
- A flat one-credit price with no advertised document limit is commercially exposed. Internal measured safety ceilings and abuse/rate controls are launch requirements even if they are not marketed as product limits.

## Readiness gates

Full SaaS development may begin when:

1. converter input/output/exit contract is committed
2. production converter image strategy is committed
3. representative corpus and parity/semantic thresholds are committed
4. output sanitizer/package validator is specified
5. worker resource and cleanup contract is specified
6. CloudPanel runtime, API hostname, database, and private object store are verified
7. required platform React/test seams are verified or planned in the platform repo
8. Stripe Tax plus refund/dispute behavior is specified
9. implementation plan Phase 0 exit checks pass

Until then, work should remain in Phase 0 and must not build customer UI around an unstable converter contract.
