Security &
Privacy

Technical documentation for security teams evaluating Compressa for enterprise use. Every claim on this page is independently verifiable.

Zero transmission

No APIs, no uploads, no external calls. Your PDFs never leave the browser.

No server runtime

Static HTML/JS/CSS export. No Node.js, no backend, no database.

Fully auditable

Open source. Inspect the network tab yourself to verify.

01

Architecture Overview

Compressa is built with Next.js using output: "export" — this produces a fully static site. The build output is plain HTML, JavaScript, and CSS files. There is no server-side code, no API routes, no database, and no runtime environment.

PDF compression runs entirely in a Web Worker — a separate browser thread that handles PDF parsing, image analysis, recompression, and file rebuilding. The Web Worker communicates with the main thread exclusively through postMessage with transferable ArrayBuffers.

The deployed application consists of:

  • Static HTML, JS bundles, and CSS
  • A PDF.js worker script (pdf.worker.min.mjs, served from same origin)
  • Font files loaded from Google Fonts (DM Sans, Instrument Serif)

No environment variables are used. No secrets or API keys exist anywhere in the codebase.

02

Data Flow

1

File selection

User selects a PDF via drag-drop or file picker. The file is read into an ArrayBuffer in browser memory using the FileReader API.

2

Worker transfer

The ArrayBuffer is transferred (zero-copy) to a dedicated Web Worker thread via postMessage with Transferable semantics. The main thread releases ownership of the data.

3

PDF parsing

The Web Worker uses pdf-lib to parse the PDF structure, enumerating all pages, image XObjects, fonts, and metadata. This is pure JavaScript — no native code or server calls.

4

Image compression

Each embedded image is decoded, optionally downsampled to the target DPI, and re-encoded as JPEG using the browser’s OffscreenCanvas API. Duplicate images are detected and deduplicated. All processing happens in-memory within the Worker thread.

5

PDF rebuild

The Worker rebuilds the PDF with compressed images, optionally stripped metadata, and object stream compression. The final PDF bytes are produced entirely in memory.

6

Download

The compressed PDF ArrayBuffer is transferred back to the main thread. A download is triggered via URL.createObjectURL. The object URL is immediately revoked and all references released for garbage collection.

At no point in this flow does data leave the browser's memory boundary. There is no network transmission of file content.

03

Network Activity

What you will see in the Network tab

On page load: HTML document, JS bundles, CSS, and font files (DM Sans and Instrument Serif from Google Fonts).

On first compression: pdf.worker.min.mjs loaded from same origin (~1 MB). This is the PDF.js rendering worker, a static file bundled with the application.

On subsequent files: Nothing. The worker script is cached by the browser.

What you will NOT see

XHR/fetch to external domains

WebSocket connections

File uploads or POST requests

Tracking pixels or beacons

Google Analytics or any analytics

Sentry, Bugsnag, or error reporting

CDN calls for PDF processing

Any outbound data transmission

04

What Compressa Won't Do

No server-side processing
No file uploads to any server
No data persistence (no localStorage, IndexedDB, or cookies for user data)
No user accounts or authentication
No analytics or telemetry of any kind
No third-party tracking scripts
No external API calls for PDF compression
No error reporting to external services
No A/B testing frameworks
No session recording or heatmaps
No advertising or marketing SDKs
No data collection whatsoever
05

Technical Evidence

Static export configuration

// next.config.ts
const nextConfig = {
  output: "export",  // ← Fully static, no server
  webpack: (config) => {
    config.resolve.fallback = {
      fs: false,       // No filesystem access
      path: false,     // No path module
      crypto: false,   // No crypto module
      canvas: false,   // No native canvas
      // ... all Node.js modules disabled
    };
    return config;
  },
};

No API routes

The src/app/ directory contains only page routes and layout files. There is no api/ subdirectory. No server-side request handlers exist anywhere in the codebase.

Worker isolation

The Web Worker runs in a separate thread within the same browser security context. PDF data is transferred via postMessage using Transferable ArrayBuffers (zero-copy, single-owner semantics). The worker is terminated after each compression completes, and a fresh worker is created for the next file.

In-browser image processing

Image re-encoding uses the browser's native OffscreenCanvas and convertToBlob APIs — no external image processing services. Zlib compression uses the browser's built-in CompressionStream API.

06

Dependency Audit

PackagePurposeNetwork
nextStatic site generation frameworkNone
react / react-domUI renderingNone
pdf-libPDF parsing, modification, and rebuildingNone
pdfjs-distPDF.js worker for page rendering (thumbnails)None

Every runtime dependency operates exclusively on in-memory data. None make network requests, transmit telemetry, or access external resources. Build-time dependencies (tailwindcss, typescript, eslint) are not present in the deployed application.

07

Deployment Options

Hosted

Deploy as static files on any CDN or static hosting platform (Vercel, Netlify, S3 + CloudFront, GitHub Pages).

Self-hosted

Run npm run build and deploy the out/ directory to any internal static file server. Full control over infrastructure.

Air-gapped

Build once, copy the output to an isolated network. No internet connection required after the initial build. All assets including the PDF.js worker are bundled.

Note: The app loads DM Sans and Instrument Serif from Google Fonts on page load. For air-gapped or fully isolated deployments, these fonts can be self-hosted by downloading them and serving from the same origin.

08

Verification Steps

Every claim on this page can be independently verified. Here's how:

1

Open browser DevTools → Network tab

Clear the log, then compress a PDF. You will see only same-origin requests for static assets. Zero external domain requests during compression.

2

Inspect next.config.ts

Confirm output: "export" is set. This guarantees static-only output with no server runtime.

3

Check the src/app/ directory

Confirm there is no api/ subdirectory. No server-side request handlers exist.

4

Review package.json

Confirm no analytics, telemetry, or tracking dependencies are listed.

5

Search for fetch() calls

The codebase contains no fetch() calls to external services. All data processing uses in-memory ArrayBuffers passed between the main thread and Web Worker.

6

For maximum assurance

Clone the repository, build locally, and deploy on an air-gapped network. Compress a PDF and observe: zero network traffic beyond the initial static asset load.