@@ -202,9 +202,80 @@ Dependabot handles that on its weekly schedule.
202202one-time setup. The token fallback remains in place until you remove it
203203so the transition is risk-free.
204204
205+ ## Build attestations (Sigstore)
206+
207+ Every ` publish-*.yml ` workflow generates a ** Sigstore build provenance
208+ attestation** for the artifact it ships, using
209+ [ ` actions/attest-build-provenance@v3 ` ] ( https://github.com/actions/attest-build-provenance )
210+ (SHA-pinned). The attestation is a signed statement, recorded in the
211+ GitHub attestations API and Sigstore's public transparency log, that
212+ says: "this exact byte-for-byte artifact was built by this exact
213+ workflow run on this commit". It's how the OpenSSF Scorecard
214+ ` Signed-Releases ` check verifies our releases.
215+
216+ | Workflow | Subject attested | When |
217+ | ---| ---| ---|
218+ | ` publish-cli.yml ` | ` cli/*.tgz ` (output of ` npm pack ` ) | after pack, before ` npm publish ` |
219+ | ` publish-mcp.yml ` | ` mcp/*.tgz ` (output of ` npm pack ` ) | after pack, before ` npm publish ` |
220+ | ` publish-pysdk.yml ` | ` python-sdk/dist/* ` (sdist + wheel) | after ` python -m build ` , before PyPI upload |
221+
222+ For the npm packages the attestation is ** complementary** to npm's own
223+ ` --provenance ` flag — that one is recorded inside the npm registry, the
224+ Sigstore attestation is recorded on GitHub. Both verify, neither
225+ replaces the other.
226+
227+ ### Required permissions
228+
229+ Each publish job carries:
230+
231+ ``` yaml
232+ permissions :
233+ contents : read # (or write for pysdk to attach assets)
234+ id-token : write # OIDC token for Sigstore signing
235+ attestations : write # write to GitHub's attestations API
236+ ` ` `
237+
238+ ### Verifying a release
239+
240+ Given a downloaded artifact (tarball, sdist, wheel):
241+
242+ ` ` ` bash
243+ # npm package (CLI or MCP)
244+ npm pack @looptech-ai/understand-quickly-cli # downloads .tgz
245+ gh attestation verify ./looptech-ai-understand-quickly-cli-*.tgz \
246+ --owner looptech-ai
247+
248+ # PyPI artifact
249+ pip download --no-deps understand-quickly # downloads .whl + sdist
250+ gh attestation verify ./understand_quickly-*.whl \
251+ --owner looptech-ai
252+ gh attestation verify ./understand-quickly-*.tar.gz \
253+ --owner looptech-ai
254+ ```
255+
256+ A successful verify proves: artifact digest matches a signed statement
257+ on Sigstore's Rekor log, the signing identity is a workflow in the
258+ ` looptech-ai/understand-quickly ` repo, and the workflow file matches one
259+ of our ` publish-*.yml ` .
260+
261+ ### When attestation fails the build
262+
263+ The action fails the job (and therefore the publish) if:
264+
265+ - The ` subject-path ` glob matches ** zero** files. Cause: the prior ` pack `
266+ / ` build ` step changed its output location or filename. Fix the glob.
267+ - The ` id-token: write ` permission is missing. Fix: re-add to the job
268+ permissions block.
269+ - Sigstore is unreachable. Rare; the action retries. If persistently
270+ failing, re-run the workflow.
271+
272+ There's no "skip on failure" — by design, a release that can't be
273+ attested doesn't ship.
274+
205275## See also
206276
207277- [ ` npm-org-setup.md ` ] ( npm-org-setup.md ) — one-time npm org + token setup.
208278- [ ` pypi-trusted-publishing.md ` ] ( pypi-trusted-publishing.md ) — OIDC setup for PyPI.
209279- [ ` ../../CHANGELOG.md ` ] ( ../../CHANGELOG.md ) — human-curated changelog (release-please appends).
210280- [ release-please action docs] ( https://github.com/googleapis/release-please-action ) .
281+ - [ GitHub artifact attestations] ( https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds ) .
0 commit comments