v0.3.0 — artefact integrity bundle
Artefact integrity bundle. Two new pre-publish gates and a rewired publish path that lets downstream consumers verify the registry tarball byte-for-byte against what CI built.
What's new
record-tarballpacks the release artefact once into a known location, parsesnpm pack --jsonfor the sha512 integrity, hashes the tarball with sha256, and writestarball.metafor the rest of the pipeline to consume.publish-npmnow uploads the exact tarball recorded above rather than re-packing. The bytes the registry receives are byte-identical to the bytes the integrity block reports. On a clean re-run of an already-published release, the registry'sdist.integrityis compared to the recorded value: a match exits silently, a mismatch fails the workflow loudly. That scenario is registry tarball substitution and you want to know about it on the next CI run.update-releaseappends anArtefact integrityblock to the GitHub Release body containing filename, size, sha256, sha512 (npm format), and a one-linecurl | shasumrecipe. Anyone can fetch the registry tarball and confirm byte-equivalence with what CI built.verify-action-pinswalks.github/workflows/*.ymlin your repo and warns on anyuses: owner/repo@refline whose ref is not a 40-char hex SHA. Warn-only by default. Newstrict-action-pins: trueinput promotes warnings to a hard failure.forgesworn/release-actionis exempt by name; seeTHREAT-MODEL.mdfor the rationale.
Migration
None required. The new gates are additive and verify-action-pins is warn-only by default. Existing v0.2.x consumers can bump their pin to v0.3.0 (or stay on @v0) without changing any caller-workflow input.
If you want the strict pin gate:
with:
vector-test-command: npm run test:vectors
strict-action-pins: trueKnown limits
- The recorded sha256 is a single-runner integrity anchor, not a reproducibility proof. Two runners building the same commit may still produce two different hashes today, due to embedded timestamps and path leakage. Cross-runner reproducible-build is a planned
v0.4theme. The integrity block defends against registry tarball substitution, which does not require build-level determinism to detect. verify-action-pinscannot statically resolve dynamicuses: ${{ matrix.action }}lines and silently skips them. Audit those templates separately.
Verify a release tarball
Every release body now ends with a verify recipe like:
curl -sLO https://registry.npmjs.org/<pkg>/-/<pkg>-1.2.3.tgz
shasum -a 256 <pkg>-1.2.3.tgzCompare the result to the sha256: line in the release body. They should match exactly.
Stats
- 2 new step scripts (
record-tarball.sh,verify-action-pins.sh) - 13 new bats tests (43 total, all passing)
- shellcheck clean
- Zero new dependencies; still pure
bash+jq+gh+npm - ~1000 lines of bash across all step scripts (still inside the 30-minute audit budget)