Technical overviewLocal tooling and enforced privacy

Quire

A local PDF toolkit with no hosted version, in a build that cannot reach the network.

Source code

Inspect the implementation, commit history, and project documentation.

Role
Solo developer: React and TypeScript interface, PDF operations, build security policy, tests, CI
Evidence status
CI green on main: lint, typecheck, 47 tests and the no-network build check
Verified
2026-10-23

01

Purpose and scope

A local PDF toolkit: one self-contained HTML file that runs on your own machine in a browser, with no hosted version. Merge, split, organize, images to PDF, page numbers, metadata and passwords all run locally, so a resume or a lease never has to be uploaded to a PDF website. The build carries a Content-Security-Policy with default-src and connect-src set to 'none', and a check in CI fails any change that weakens it.

What it is used for

  • Combine or split private documents without uploading them anywhere.
  • Reorder, turn or drop scanned pages from their thumbnails.
  • Turn phone photos into a PDF, upright from their EXIF orientation.
  • Strip author and software metadata, or add and remove a password, before sending a file.
  • React
  • TypeScript
  • Vite
  • vite-plugin-singlefile
  • @cantoo/pdf-lib
  • pdf.js
  • fflate
  • Vitest
  • GitHub Actions

02

Architecture

On your machine

User interface

Seven tool views

Merge, split, organize, images, numbers, metadata, password

Input

Local files

Picked or dropped, held in memory

File bytes in memory

PDF engine

Application logic

PDF operations

Pure functions over bytes, no DOM

PDF writer

@cantoo/pdf-lib

Pages, labels, metadata, AES-256

Renderer

pdf.js worker

Renders the Organize thumbnails

Browser download

Output

Output

Saved PDF

Written to disk by the browser

Output

Zip of split parts

Built with fflate when there are several

Shipped as

Build and enforcement

Packaging

Single-file build

Scripts, styles and worker inlined

Security policy

Hashed CSP

default-src and connect-src 'none'

CI gate

Build check in CI

Fails the build if the policy weakens

Text equivalent: Each tool is a React view over pure functions in src/lib that take and return PDF bytes. @cantoo/pdf-lib does every write and pdf.js renders the Organize thumbnails in an inline Web Worker. The build inlines everything into one HTML file with a hashed Content-Security-Policy, and a CI script fails the build unless that policy blocks every network request.

03

Engineering decisions

Make the privacy claim checkable

The policy is written after inlining, with a SHA-256 hash for each inline block. check-build.mjs fails the build unless dist holds one file, both directives are 'none', and no src or href points outside a data: URI.

Ship one HTML file

One file opens locally in a browser with nothing to install and no server to host it. With the scripts, styles and pdf.js worker inlined there is nothing left to fetch, so the policy can refuse every request.

Keep PDF operations off the DOM

The functions in src/lib run in Node under Vitest against generated fixtures. Each fixture page has its own width, so a test asserts the exact page order after a merge, split or reorder.

Clean up after decryption

@cantoo/pdf-lib left the old cross-reference stream and the Encrypt dictionary behind, so an unlocked copy reloaded as encrypted. Quire deletes those objects, and a test asserts the saved file has no /Encrypt entry.

04

Verification evidence

  • On October 23, 2026, all 47 Vitest tests passed and the build check confirmed one self-contained dist/index.html of 2.34 MB.
  • GitHub Actions ran lint, typecheck, tests, the build check and a runtime dependency audit, all green on main.
  • In headless Chrome, both served from localhost and opened from disk over file://, Organize rendered thumbnails for a six-page PDF and saved a reordered five-page copy with no console errors.
  • On October 22, 2026, all seven tools were run in a browser with the policy active, and each output was reopened to confirm order, rotation, labels, stripped metadata and the lock and unlock round trip.
verified-2026-10-23
$ npm test
Tests  47 passed (47)
$ npm run build
check-build: dist/index.html is self-contained (2.34 MB) and cannot open network connections.
$ npm audit --omit=dev
found 0 vulnerabilities
✓ CI green on main

05

Demonstrated workflow

06

Limitations