Paperplain is a small, independent PDF-to-Markdown integration demo. Its entire product boundary is three generated fictional PDFs, one server-side transform, and fresh inspectable Markdown. There is no upload route and no path for visitor documents.
Live demonstration: paperplain.peterkassel.com
Paperplain does not implement the PDF conversion engine. It uses
@opendataloader/pdf from
the OpenDataLoader PDF project.
The pinned 2.5.1 package and upstream repository identify that dependency as
Apache-2.0 licensed. Paperplain is not affiliated with, sponsored by, or
endorsed by OpenDataLoader PDF or its maintainers. See
THIRD_PARTY_NOTICES.md for attribution.
- The page opens with no selected PDF and no Markdown result.
- A visitor chooses one of three fictional fixtures and inspects its source.
- One explicit action requests a real server conversion.
- A CSS scan communicates waiting without claiming measured progress.
- Fresh Markdown, a run receipt, and the copy action appear only after success.
- Failure reveals no captured or substitute output.
The public server route accepts only one of these IDs:
field-briefstudio-invoiceblock-bulletin
It accepts no body, PDF, URL, filename, or arbitrary visitor content. There is no persistence, database, storage bucket, account system, OCR service, hybrid processing, home-lab connection, tunnel, proxy, or direct browser-to-Render request.
presentation/contains the public Sites portfolio and server-side signer.server.mjsprovides separate localhost and hosted converter modes.lib/samples.mjsis the converter's fixed-corpus allowlist.lib/converter.mjsruns the packaged OpenDataLoader PDF Java engine with a hard deadline and removes temporary output.lib/request-auth.mjsverifies the hosted HMAC, timestamp, and nonce.Dockerfileandrender.yamldescribe the bounded Render converter.public/contains the dependency-free localhost UI and generated fixtures.
The Sites route keeps the signing secret server-side, wakes the Render service, and signs an empty fixed-sample request. Render verifies the signature and returns a sanitized result. Browser assets contain neither the secret nor the Render origin.
The Sites action is intentionally anonymous so a potential client can run the
demo without an account. Same-origin checking is defense in depth, not user
authentication: a non-browser caller can forge an Origin header.
The capability is safe because it is narrow. A public caller can request only conversion of the three committed fictional fixtures. Render permits one active conversion and six admitted conversions per minute per instance, rejects nonce replay, uses ephemeral temporary files, and returns generic errors. The HMAC prevents direct callers from authorizing work at Render.
This single-instance design is not authority for horizontal scaling. Scaling would require a separately designed shared replay and rate-limit store.
OpenDataLoader PDF's published extraction table reports 0.015 s/page for its
fast local mode—more than 1,000 pages per minute under those benchmark
conditions. This is an upstream benchmark, not an independently measured
Paperplain result. The hosted portfolio demo runs one fixed sample at a time and
can take longer while its managed service wakes. Upstream also recommends
batching because each conversion invocation starts a JVM process.
See the upstream extraction benchmark.
Requirements:
- Node.js 22.13 or newer
- Java 11 or newer
Install dependencies once:
npm installThen double-click Start Paperplain.command in Finder. It starts the service at
http://127.0.0.1:4173 and opens the default browser. Keep the Terminal window
open; press Control-C to stop it. Opening public/index.html directly cannot
start Node or Java and is not a working substitute.
The equivalent Terminal command is:
npm startRender requires:
PAPERPLAIN_ALLOWED_ORIGIN— the exact public Sites HTTPS origin, with no path or trailing slash.PAPERPLAIN_REQUEST_SECRET— exactly 64 lowercase hexadecimal characters encoding 32 random bytes.
Sites requires:
PAPERPLAIN_RENDER_ORIGIN— the exacthttps://…onrender.comservice origin, with no path or trailing slash.PAPERPLAIN_REQUEST_SECRET— the exact same secret stored in Render.
Secret values must remain in the two server environments. Do not put them in a URL, browser code, client storage, logs, or source control.
The converter and Sites presentation are intentionally tested separately:
npm test
cd presentation && npm testThe presentation suite is limited to the contract's high-value boundaries: empty initial state and clean client assets, anonymous HMAC interoperability, and refusal of off-contract requests. A green build is not proof of hosted operation; that claim requires a real Sites-to-Render conversion.
The included PDFs and previews are generated from local vector shapes and fictional copy:
python3 -m pip install -r requirements-dev.txt
python3 scripts/generate_samples.pyPreview rendering also requires Poppler's pdftoppm command.
No software license has been selected for Paperplain. The Apache-2.0 license identified above applies to OpenDataLoader PDF; it does not automatically license the independent Paperplain source.