Skip to content

Scanner releases and verification

Scanner binaries use scanner-vX.Y.Z tags so their experimental product version does not collide with versions of the checklist corpus. A release is built only from the exact tagged commit. The workflow reruns the source, catalog, schema, security, benchmark, and pack gates before publishing anything.

Release contents

Each scanner release contains:

  • Linux, macOS, and Windows archives for AMD64 and ARM64;
  • one dependency-free @marinjursic/prc npm launcher tarball and six exact native npm platform tarballs for the same systems;
  • a binary with the scanner version, source revision, source timestamp, and Go toolchain embedded in prc version --format json;
  • the exact compatible catalog/, packs/, and schemas/ trees in every archive, together with the catalog's human-readable objective sources and the packs' benchmark fixtures;
  • a versioned release manifest binding artifact digests to the catalog and pack validation digests;
  • a timestamp-free CycloneDX 1.6 module SBOM;
  • a canonical self-scan of the exact tagged source, executed by the packaged binary against its bundled catalog without hiding blocked or manual results;
  • SHA256SUMS; and
  • GitHub-hosted Sigstore attestations for SLSA build provenance and the CycloneDX SBOM predicate.

The SBOM describes the scanner's Go module and resolved Go dependencies. It does not claim that the bundled catalog data, schemas, host operating system, or future adapter images are Go components; those files are integrity-bound by the archive checksum and release manifest instead. GitHub documents what its artifact attestations prove and how consumers should verify them.

The builder fixes archive order, ownership, modes, paths, timestamps, binary metadata, and source identity. CI builds the complete release twice and requires a byte-for-byte directory comparison. This is a reproducibility check under the same declared CI toolchain; it is not a claim that provenance or reproducibility proves the artifact is secure.

The npm platform packages use a narrower runtime allowlist than the standalone archives. They retain all catalog files, all 10,042 source controls, schemas, adapter manifests, packs, and benchmark evidence. They exclude website assets, demo video, and contributor-only documentation. The builder caps runtime support at 512 files and 24 MiB, caps each compressed platform package at 24 MiB, and caps the launcher package at 128 KiB. A size-budget failure blocks publication; the builder never drops controls to make a package fit.

The two largest machine-readable control indexes use deterministic gzip only in the npm runtime package. The native scanner expands them in memory with strict compressed and expanded byte limits, then applies the same schemas, source binding, per-control digests, and registry-to-contract checks as the plain JSON files. Standalone archives and repository source keep the plain JSON form.

Verify before running

Download the archive, SHA256SUMS, release manifest, and SBOM from the same GitHub release. Verify the checksum from the directory containing those files:

sha256sum --check SHA256SUMS

On macOS, verify one downloaded archive against the matching line instead:

shasum -a 256 prc_0.1.0_darwin_arm64.tar.gz
grep 'prc_0.1.0_darwin_arm64.tar.gz' SHA256SUMS

Then verify that GitHub's signed provenance binds the archive to this repository and release workflow:

gh attestation verify prc_0.1.0_linux_amd64.tar.gz \
  --repo MarinJursic/production-readiness-checklist

Verify the separate CycloneDX SBOM attestation using its recognized predicate:

gh attestation verify prc_0.1.0_linux_amd64.tar.gz \
  --repo MarinJursic/production-readiness-checklist \
  --predicate-type https://cyclonedx.org/bom

Finally, inspect prc_X.Y.Z_release-manifest.json and compare its source_commit, catalog digest, pack digests, and artifact digest with the assessment scope you intend to use. Inspect prc_X.Y.Z_self-scan.json as a normal prc.run/v0.13 report: a valid signed self-assessment may still be environment_blocked because organizational, production, or adapter evidence is deliberately unavailable in the release job. After extraction:

./prc_X.Y.Z_linux_amd64/prc version --format json

The release manifest also binds every npm tarball. Before the packages are published to npm, they can be tested directly from one release directory. On Linux x64, for example:

mkdir npm-smoke && cd npm-smoke
npm install --ignore-scripts --offline --no-audit --no-fund --package-lock=false \
  ../marinjursic-prc-linux-x64-X.Y.Z.tgz \
  ../marinjursic-prc-X.Y.Z.tgz
./node_modules/.bin/prc version --format json
./node_modules/.bin/prc scan /path/to/project

The platform package contains the native binary and its exact runtime catalog. The launcher checks the platform manifest, binary SHA-256, and the size and SHA-256 of every runtime support file on each start. It fails closed on a missing, changed, oversized, or unbound file and never downloads a fallback or starts a binary found on PATH.

The scanner is declared as security-related dual-use software with npm's contentPolicy metadata and the same plain-text DISCLOSURE in the repository, launcher, platform packages, and standalone archives. The disclosure describes the defensive purpose, opt-in analyzers, and prohibited unauthorized use. See npm's dual-use policy.

Public npm release is deliberately split into two workflows. The tagged release workflow uses npm trusted publishing from the exact pinned workflow identity, refuses NPM_TOKEN and NODE_AUTH_TOKEN, creates a draft GitHub release, and uses npm stage publish for the six native packages before the launcher. A trusted-publisher relationship is allowed to stage but not directly publish. The maintainer then reviews and approves each staged package with npm 2FA. The manual finalizer downloads the draft assets again, checks every SHA-256 and release-manifest binding, waits for npm's publish-time malware scan, verifies the public SHA-512 and npm SLSA provenance for all seven exact packages, and only then makes the GitHub release public. See npm's staged-publishing guide and publish-time scanning announcement.

Because npm versions are immutable, any byte mismatch stops the release. The stager safely skips only an already-public version with exactly matching bytes. It runs npm in a new empty working directory with user, global, and environment npm configuration removed, so a saved .npmrc token cannot silently replace OIDC. It also independently requires the exact repository, workflow file, release tag, commit, and GitHub OIDC request variables. Running the staging script locally or from another workflow fails before contacting npm. Release-time Python validation installs only the seven exact CPython 3.12 Linux wheels in requirements-release.lock.txt, with required SHA-256 hashes and source builds disabled. The publication job uses the publisher's standard- library --verify-only path, so it does not add an unpinned Python dependency before attesting or publishing the tested bytes.

The release job builds once, uploads those exact bytes, then runs the matching native archive and npm launcher on Linux x64, Linux ARM64, macOS x64, macOS ARM64, Windows x64, and Windows ARM64. Publication starts only after every host has completed a real 10,042-control smoke scan.

One-time npm owner setup

npm requires a package to exist before a trusted publisher can be configured. The owner must therefore bootstrap each of the seven package names once from the exact verified release tarballs, with npm's required human authentication. Then configure the same trusted-publisher identity on every package:

  • repository owner: MarinJursic;
  • repository: production-readiness-checklist;
  • workflow filename: release-scanner.yml; and
  • no GitHub environment unless the workflow is later changed to use one.

For every package, allow npm stage publish and disable direct npm publish in the trusted-publisher relationship. At package level, require two-factor authentication and disallow token publishing. Remove any bypass-capable legacy publishing token. These settings are state held by npm and must be checked in the npm package settings; repository files cannot enforce them.

After that one-time setup, scanner-vX.Y.Z tags use OIDC and no long-lived npm publishing secret. The staging workflow requires Node.js 22.14 or newer and the exact hash-bound npm 12.0.2 client. npm provenance links a package to its build source; it does not prove the package has no unsafe code.

For a normal release:

  1. push one new immutable scanner-vX.Y.Z tag from a commit already on main;
  2. wait for all builds, security gates, native scans, and package smoke tests;
  3. open npm's staged-package list, inspect all seven matching versions, and approve each one with 2FA;
  4. run Finalize scanner release with that exact tag; and
  5. require the finalizer to verify checksums, public bytes, and npm provenance before it publishes the draft GitHub release.

Staged publication is per package rather than transactional. If staging stops after only some packages, do not move or recreate the tag. Use an authenticated owner session to list the staged records, compare their names, versions, tags, and downloaded tarball hashes with the draft release, approve only exact matches, and rerun the same tagged workflow. It will verify already-public exact packages and stage only the missing versions. Rejecting a staged package is a separate destructive 2FA action and is not part of automated recovery.

Version 0.1.0 was the one-time human bootstrap. npm registry signatures for its launcher and selected platform package can be verified, but that bootstrap version has no npm provenance attestation. Do not describe it as provenance- verified. All later releases must be published by the trusted workflow with provenance or fail closed.

Protect the scanner-v* tag pattern with an active GitHub tag ruleset that restricts creation, update, and deletion to the release maintainer. Require the main-branch validation, CodeQL, dependency-review, and secret-scan checks before the tagged commit can enter main. These hosted controls cannot be truthfully claimed from repository files alone and must be verified in GitHub settings.

Do not substitute a successful signature check for vulnerability review or a production-readiness decision. An attestation proves the signed claim's origin and integrity, not the absence of defects.

Release failure and revocation

Publishing is fail closed: an invalid tag, test failure, benchmark regression, schema failure, vulnerability finding, non-reproducible output, checksum error, or attestation failure prevents release publication. A failed workflow is not a release and its temporary artifacts are not supported.

If a signing identity, workflow dependency, tool, release asset, catalog, pack, or scanner version is compromised or materially incorrect, maintainers will:

  1. publish a private-to-public security advisory as coordination permits;
  2. mark the affected release and version as revoked, with the exact artifact digests and reason;
  3. remove or revoke affected attestations where the platform supports it;
  4. never reuse the affected version or tag and never replace an asset under the same name;
  5. publish a new patched version from a reviewed commit with new checksums, SBOM, provenance, and compatibility manifest; and
  6. update bundled trust stores or registry revocations when a pack, adapter, or publisher identity is affected.

Consumers should stop using a revoked artifact even if its historical signature still verifies: verification establishes who produced it, not whether it remains approved.