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, and frontend/editor/src‑taur​i/src/lib.rs for 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 under app/*/build.gradle and build.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 is engine/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.json that 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:

  • Secretsengine/.env holds STIRLING_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.ttf 16.9 MB, NotoSansKR-Regular.ttf 9.9 MB, opencv.js 8.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 is engine/.env.example (empty values).
  • The PostHog key (STIRLING_POSTHOG_API_KEY) lives in engine/.env; replace with your own key or set it in the runtime environment.
  • SaaS‑specific config is read from app/.env.saas (tracked though .gitignore excludes 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 (contains STIRLING_POSTHOG_API_KEY with generated values).
  • HIGH – Untrack app/.env.saas although .gitignore excludes 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 .env files and rotate what they hold: app/.env.saas, engine/.env, frontend/editor/.env, frontend/editor/.env.desktop; add a .env.example with empty values.
  • HIGH – Make CI invoke the test suite it has – add a test step to the existing .github/workflows rather than a new workflow.
  • MEDIUM – Gate pull requests on a dependency vulnerability scan – add dependency-review-action on pull_request or osv-scanner on 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 with USER.
  • LOW – Set timeout-minutes on the workflow job aur-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.