Backport builder security and operations
Build tooling — NOT a runtime dependency. This page explains how to operate and trust the build pipeline. The security posture of the Portable Document Format (PDF) engine itself is documented with the engine, not here.
At a glance
Section titled “At a glance”The builder transforms source it does not author and emits a distribution it does not develop against. Its security model follows from that boundary. The trust boundary is the source checkout and the toolchain. The emitted artifact is read-only and machine-produced. The licensing split between the public and Pro packages is fixed in code.
The trust boundary
Section titled “The trust boundary”The builder takes source repositories and the pinned toolchain as inputs. It produces a derived artifact. Three properties hold:
- The generated distribution is read-only and machine-produced. It is published as version tags, not as branches of this repository. Development, bug reports, and feature requests go to the original
nextpdf/*source repositories, never to the generated tree. Verified against the projectREADME.mdcaution block and.github/workflows/build.yml(the release commits and tags the generated tree from scratch). - The release token scope is narrow.
secrets.BACKPORT_TRIGGER_TOKENauthorizes cloning the source repositories at the release tag. It is referenced only in the source-checkout steps. Verified againstbuild.yml(GH_TOKENusage). - The continuous integration (CI) runner split is deliberate. Trusted events run on self-hosted PHP runners; fork pull requests and Dependabot runs are forced onto GitHub-hosted runners. Untrusted code never executes on the trusted runner pool. Verified against
.github/workflows/0-ci.yml(theruns-onconditional).
Supply-chain posture
Section titled “Supply-chain posture”- The toolchain is constrained in
composer.json. It uses Rector^2.0, PHPStan^2.1, PHPUnit^13.0, and thesymfony/polyfill-*set. The builder has no NextPDF runtime dependency, so a compromised NextPDF runtime package cannot reach the builder. Verified againstcomposer.json. - Dependency updates are bot-driven and gated. Dependabot configuration and an auto-merge workflow are present. Dependabot runs are pinned to GitHub-hosted runners and still pass through the full CI gate (PHPStan, tests, dry-run) before merge. Verified against
.github/dependabot.ymland.github/workflows/0-ci.yml,9-dependabot-auto-merge.yml. - The output excludes build state. The release archive is zipped with
vendor/and.git/excluded. The published artifact carries source and the generated manifest, not the builder’s own dependency tree. Verified againstbuild.yml(zip ... -x '*/vendor/*' '*/.git/*'). - Static analysis gates the build code.
composer analyseruns PHPStan at level 10 overrector/rulesandscriptson every push and pull request to either permanent branch. It runs before any dry-run. Verified againstcomposer.jsonand0-ci.yml.
The syntax-validation gate
Section titled “The syntax-validation gate”The build script does not syntax-check the output locally because the build host runs a newer PHP than the target. The authoritative gate lives in the release workflow. After the build, the runner switches to the target PHP and runs php -l across output/src, failing the release on any parse or fatal error. The artifact is then installed and exercised across the validation matrix — PHP 8.1 through 8.4 for the PHP 8.1 lane, and PHP 7.4 and 8.0 for the PHP 7.4 lane. A distribution that a target runtime would reject does not reach a release. Verified against scripts/build.php (validateOutput()) and .github/workflows/build.yml (the syntax-check and validate-* jobs).
This is an observed-behavior guarantee scoped to the runtimes the pipeline exercises. It is not a conformance certification. It does not assert correctness of the transformed program beyond syntax acceptance and the project’s own test suite.
Licensing split
Section titled “Licensing split”The two produced packages carry different licences, fixed in scripts/adjust-composer.php:
| Package | Licence field | Set by |
|---|---|---|
nextpdf/backport | Apache-2.0 | generatePublicComposer() |
nextpdf/backport-pro | proprietary | generateProComposer() |
The builder repository itself is Apache-2.0 (composer.jsonlicense). Its CHANGELOG.md records a prior relicensing from LGPL-3.0-or-later to Apache-2.0. Versions published before that change remain under the older license and stay retrievable, while every new version is Apache-2.0. The Pro distribution is proprietary, is produced only for the PHP 8.1 target, and requires phpseclib/phpseclib ^3.0. Verified against CHANGELOG.md, composer.json, and scripts/adjust-composer.php.
Operational guarantees and their limits
Section titled “Operational guarantees and their limits”What the pipeline guarantees, scoped to what the code enforces:
- Stop-on-first-failure. The build aborts at the first failing stage with a named error. A partial distribution is never released because the release jobs depend on a successful build and validation. Verified against
scripts/build.php(step()) andbuild.ymljobneeds. - Serialized builds. The
backport-buildconcurrency group withcancel-in-progress: falseprevents two source releases from racing the same release tag. Verified againstbuild.yml. - Reproducible inputs. The pipeline clones every source repository at the exact release tag before building. Verified against
build.yml(--branch "${TAG}").
What it does not claim:
- It does not certify standards compliance, full PHP-version compatibility, or correctness of the transformed program beyond syntax acceptance and the test suite.
- The downgrade removes runtime-enforced immutability (
readonlystripping). Code that relied on the runtime to reject a write to a readonly property loses that defense in the downgraded output. This is a documented, intentional trade for clone-with safety — see /integrations/backport/troubleshooting/.
Reporting an issue
Section titled “Reporting an issue”Security issues in the builder follow the repository SECURITY.md: report them through a GitHub Security Advisory or the security contact, not a public issue. File issues in the generated code against the original source repositories, because the generated tree is machine-produced and not developed against. Verified against SECURITY.md and the project README.md.
- /integrations/backport/overview/ — what the builder is and what it produces.
- /integrations/backport/production-usage/ — how to operate the release pipeline.