# Stirling PDF v2.14.2 — Windows desktop local run

Run date: 2026-07-20
Evidence status: hands-on, synthetic files only

## Setup

- Owner MSI downloaded from Release v2.14.2.
- Downloaded SHA-256 exactly matched the digest published by GitHub Release API.
- Authenticode status was `Valid`; signer was Stirling PDF Inc.
- MSI declares `ALLUSERS=1`. It was not installed into `C:\Program Files`; an administrative image was created under the experiment directory, then launched with isolated `APPDATA` and analytics environment flags set to false.
- The app launched its bundled Java 25.0.3 backend on `127.0.0.1:1441`.
- One process-tree snapshot showed only the loopback listener for the Stirling process tree. This is a bounded observation, not a proof that the application never makes outbound requests.
- Windows displayed a firewall permission prompt for the bundled OpenJDK process. The prompt was not accepted. UI automation stopped at that boundary; functional tests continued through the same local backend API.

## Real startup boundaries

Backend startup logs reported these missing desktop dependencies:

- `tesseract` and `ocrmypdf`: OCR disabled;
- `qpdf`: some repair/compression paths disabled;
- Ghostscript, ImageMagick, LibreOffice, Calibre and other optional converters missing.

The backend then listed `/api/v1/misc/ocr-pdf` among disabled endpoints.

## Test 1 — Merge

Inputs:

- `inputs/merge-a-two-pages.pdf`: two pages, markers `MERGE-A-1`, `MERGE-A-2`;
- `inputs/merge-b-one-page.pdf`: one page, marker `MERGE-B-1`.

Observed:

- Local API returned HTTP 200 and a 72,612-byte PDF in 1,691 ms.
- Independent `pypdf` reading found three pages in the exact expected order: A1, A2, B1.
- Result: passed.

## Test 2 — Redaction

Input: `inputs/redaction-fictional-data.pdf`, containing a fictional phone number and an invalid example email domain.

First path, legacy `/auto-redact`:

- HTTP 200 and a file named `*_redacted.pdf` were returned.
- The rendered preview was pixel-identical to the input preview.
- Independent text extraction still found both target strings.
- Result: failed. HTTP 200 was not treated as successful redaction.

Second path, unified `/redact-execute` with `IMAGE_FINALIZE` and `convertToImage=true`:

- HTTP 200 returned a 318,876-byte PDF.
- Visual review showed black boxes over the phone and email while the name, test ID and public note remained visible.
- Independent extraction returned zero text characters; neither target was recoverable through text extraction.
- Result: passed for this synthetic single-page fixture.

This does not prove every complex PDF can be safely redacted. An open owner issue reports a bank PDF for which manual redaction fails unless split into pages first.

## Test 3 — Desktop local OCR

Input: a 300 DPI image-only Chinese PDF with zero extractable text characters.

Observed:

- Local OCR request returned HTTP 403 JSON: `This endpoint is disabled`.
- Startup logs independently reported missing Tesseract and OCRmyPDF dependencies.
- Official OCR documentation says the desktop app needs Stirling Cloud or a self-hosted Tesseract backend for OCR.
- Result: not available in desktop local mode. No OCR quality score was invented.

## Cleanup

- The Stirling desktop and bundled Java processes were stopped after the run.
- Port 1441 no longer had a listener; only temporary `TIME_WAIT` loopback entries remained.
- Heavy MSI, extracted binaries, runtime profile and raw runtime logs are excluded from Git. Synthetic fixtures, outputs, hashes and sanitized verification results remain.

## Re-run

1. Re-download v2.14.2 from the owner Release and verify `supply-chain.json` values.
2. Run `create_fixtures.py` with the bundled workspace Python.
3. Launch the signed desktop build with an isolated configuration directory and analytics disabled.
4. Send the fixtures to the loopback API using the request fields recorded in the OpenAPI schema.
5. Run `verify_outputs.py`.

`verification-results.json` is the machine-readable acceptance record.
