Skip to content

Release and Supply Chain

Elman Mohammadi edited this page Oct 6, 2026 · 1 revision

Release and Supply Chain

Release integrity is treated as part of the security boundary because a perfectly implemented encryption engine is not useful if the published code can be replaced without detection.

Release scope

The signing script covers the active source tree:

app/
bin/
config/
public/
bootstrap.php
composer.json
composer.lock (when present)
VERSION

Runtime-generated release and storage material is excluded.

Release manifest

The signing tool builds a canonical JSON manifest containing:

  • product name
  • package format
  • version
  • author
  • scope (active-source)
  • hash algorithm (sha256)
  • SHA-256 digest for each included file

The file list is sorted before canonicalization.

Ed25519 signing

The manifest is signed with an Ed25519 private key.

The public key is written alongside the release metadata, but that embedded key is not automatically an independent trust anchor.

Why external trust matters

Suppose an attacker can replace both:

manifest.json
public-key.hex

If the verifier trusts only the embedded public key, the attacker can generate a new key pair and produce a self-consistent but malicious release.

That is why high-assurance verification supports:

SECURE_PACKAGE_TRUSTED_RELEASE_KEY_HEX

The externally supplied key is the independent trust root.

Release verification process

The verifier checks:

release files present
      |
      v
signature valid
      |
      v
trusted key matches embedded key
      |
      v
manifest identity matches package/version
      |
      v
manifest is canonical
      |
      v
every active-source file hash matches

Failure of any stage terminates verification.

Release command sequence

1. Prepare

Keep the working tree clean. Keep the signing private key outside the repository.

Ensure a real Composer lockfile exists.

2. Run gates

composer validate --strict --no-check-publish
composer install --no-interaction --prefer-dist --no-progress
composer audit --no-interaction
php bin/self-check.php
php bin/source-audit.php
php bin/security-test.php --full
find public/client -type f -name '*.js' -print0 | xargs -0 -n1 node --check

3. Sign

php bin/sign-release.php /secure/location/release-private-key.hex .

4. Verify externally

export SECURE_PACKAGE_TRUSTED_RELEASE_KEY_HEX='<64-hex-public-key>'
php bin/verify-release.php .

5. Package and checksum

Create the final release archive from the clean tree and publish its SHA-256 checksum alongside the independently distributed public trust key.

6. Deploy

Expose only public/. Keep storage, environment secrets, legacy material, and signing keys private.

7. Verify after transfer

Run release verification again on the production host after transfer.

Composer policy

The project sets:

"allow-plugins": false

The rationale is to reduce the ability of dependency installation to execute arbitrary Composer plugin code by default.

CI also validates Composer configuration and runs dependency auditing.

Why a real composer.lock matters

A lockfile pins the dependency graph that was reviewed and tested.

A synthetic or incomplete lockfile should not be created merely to make CI appear reproducible.

The release documentation explicitly requires a real lockfile generated in a normal development environment.

Source audit

The source audit is intentionally limited and targeted. It looks for:

  • legacy project branding leftovers;
  • selected dangerous process execution functions;
  • embedded private-key PEM markers.

Passing the source audit does not replace manual review or dependency analysis.

Release claims

The project should not describe release signing as proof that the code is secure.

Release signing proves something narrower and useful:

"These exact source bytes correspond to this signed manifest and trusted release key."

Security still depends on the source itself, the build environment, dependencies, deployment, and the threat model.

Clone this wiki locally