The Problem
Stirling‑PDF is promoted as a single PDF platform, but the code base is a loose collection of five independent projects (frontend, app, engine, scripts, testing) with separate tech stacks, build systems and deployable artefacts. Teams that treat it as one monolith risk mismatching versions, duplicate dependencies, and hidden credentials that are committed alongside the source.
What This Does
- frontend (2481 files, 2019 code files) – a React + Tailwind UI that can run as a desktop app (Tauri) or in a browser. Entry points:
frontend/.storybook/main.ts,frontend/editor/index.html, andfrontend/editor/src‑tauri/src/lib.rsfor the Tauri provisioner. - app (2076 files, 1406 code files) – three Gradle‑based Java modules (
app/core,app/proprietary,app/saas) that provide the server‑side processing API and SaaS‑specific features. Build files live underapp/*/build.gradleandbuild.gradle. - engine (130 files, 120 code files) – a Python package under
engine/src/stirling/that contains the core PDF‑processing pipeline. The concrete entry point isengine/src/stirling/api/app.py, a Flask‑style app that receives HTTP requests, invokes OpenCV, vector‑store and other libs, and returns processed PDFs. - scripts (45 files, 27 code files) – automation helpers, CI‑related scripts and the Task runner configuration.
- testing (101 files, 9 code files) – unit‑ and integration‑test suites, plus a
testing/compose/mcp-client-check/package.jsonthat lacks a lockfile.
The five projects share no single source‑root; each has its own package.json, requirements*.txt, or build.gradle. Deployments therefore require pulling the correct Docker image (the official docker.stirlingpdf.com/stirlingtools/stirling-pdf) or building individual components.
How It Is Wired
Execution starts at the engine HTTP server (engine/src/stirling/api/app.py). Incoming requests are routed through a small set of Flask routes that call PDF‑processing functions (OCR, merge, sign, etc.) which in turn import OpenCV, vector‑store clients and other language‑specific libraries. The frontend contacts the engine via the private API (URL configurable through environment variables). The app Java modules expose additional enterprise‑grade features (SSO, auditing) through Spring‑style controllers; they are built with Gradle and packaged into separate Docker images referenced from docker/backend/Dockerfile.
External effects:
- Secrets –
engine/.envholdsSTIRLING_POSTHOG_API_KEY; the value is loaded at boot and used for analytics. - Database / storage – not explicitly mapped in the static analysis, but the engine code references a configurable data store (likely PostgreSQL) via connection strings supplied at runtime.
- Filesystem – large font binaries (
NotoSansSC-Regular.ttf16.9 MB,NotoSansKR-Regular.ttf9.9 MB,opencv.js8.6 MB) are checked into the repo, inflating clone size and CI bandwidth.
No single architecture document ties the modules together; the wiring is inferred from the entry points and the environment‑variable conventions present in the five projects.
How To Use It
Setup
# Official Docker quick‑start (from README)
docker run -p 8080:8080 docker.stirlingpdf.com/stirlingtools/stirling-pdf
# Then open http://localhost:8080
For local development the repo ships a Taskfile: task dev launches the frontend editor and the engine together. Individual projects can be built with their respective managers (npm install && task dev for frontend, ./gradlew bootRun for the Java app modules, pip install -e engine for the Python engine).
Configuration
- Environment files that must not be committed:
engine/.env,app/.env.saas,frontend/editor/.env,frontend/editor/.env.desktop. An example skeleton isengine/.env.example(empty values). - The PostHog key (
STIRLING_POSTHOG_API_KEY) lives inengine/.env; replace with your own key or set it in the runtime environment. - SaaS‑specific config is read from
app/.env.saas(tracked though.gitignoreexcludes it – see hygiene findings below).
Running it
# Full‑stack dev (Task runner)
task dev
# Or start components independently:
# Engine (Python)
cd engine && uvicorn stirling.api.app:app --host 0.0.0.0 --port 8080
# Frontend (Tauri desktop)
cd frontend/editor && npm run tauri dev
Real‑World Use
A marketing team needs to automatically redact social‑security numbers from incoming invoices and store the cleaned PDFs in a private bucket. They point the Stirling‑PDF engine at the invoice bucket, invoke the /redact endpoint with a regex pattern, and receive a sanitized PDF back via the API. The frontend UI lets non‑technical staff schedule the same workflow via a no‑code pipeline, while developers integrate the REST API into their CI pipeline for batch processing.
Code Health & Issues
The static analysis produced 9 findings (1 critical, 4 high, 3 medium, 1 low). Reported verbatim:
- CRITICAL – Rotate the credentials in the committed environment file
engine/.env(containsSTIRLING_POSTHOG_API_KEYwith generated values). - HIGH – Untrack
app/.env.saasalthough.gitignoreexcludes it; rotate its credentials and remove from git. - HIGH – Commit a lockfile beside the manifest
testing/compose/mcp-client-check/package.json– run the package manager once and commit the generated lockfile. - HIGH – Remove the committed
.envfiles and rotate what they hold:app/.env.saas,engine/.env,frontend/editor/.env,frontend/editor/.env.desktop; add a.env.examplewith empty values. - HIGH – Make CI invoke the test suite it has – add a test step to the existing
.github/workflowsrather than a new workflow. - MEDIUM – Gate pull requests on a dependency vulnerability scan – add
dependency-review-actiononpull_requestorosv-scanneron push/schedule. - MEDIUM – Move large binaries to Git LFS or out of the repository:
app/core/src/main/resources/static/fonts/NotoSansSC-Regular.ttf(16.9 MB),NotoSansKR-Regular.ttf(9.9 MB),opencv.js(8.6 MB). - MEDIUM – Add a non‑root USER to the Docker image
docker/backend/Dockerfile; create an unprivileged user, chown needed paths, and end the Dockerfile withUSER. - LOW – Set
timeout-minuteson the workflow jobaur-publish.yml(currently no job timeout).
The Bottom Line
Stirling‑PDF delivers a wide‑range of PDF‑editing capabilities across desktop, browser and self‑hosted server, but its code base is a portfolio of five separate projects rather than a unified monolith. The engineering effort required to keep dependencies, secrets and large binaries in sync is non‑trivial. It is well‑suited for teams that want a customizable PDF platform and are prepared to manage multiple build pipelines and credential rotation. Organizations that need a single‑source‑of‑truth deployment may find the fragmented structure a liability unless they adopt the provided Docker image and enforce the hygiene fixes above.