packslip 1.5.0

A vendor-neutral signed release manifest: what shipped, and how to verify it
Documentation

packslip is a signed release manifest for software distributed as archives, installers, or executables. A release publishes one file, packslip.sigstore.json, beside its artifacts. A consumer, such as a package manager or mirror, verifies that file against the project's signer and reads three things from it: which artifact suits the host, where the executables are inside that artifact, and the digest the download must match. The consumer does not have to guess from file names or trust a checksum file served next to the binary.

This repository holds the specification, the packslip CLI and Rust crate that create and verify packslips, and two GitHub Actions: jdx/packslip publishes a packslip from a release job, and jdx/packslip/releases builds a signed release list. The format is stable at version 1; Stability says what that fixes and what a revision may add.

Start here

How packslip fits a release shows how the pieces connect, and the documentation index lists every guide. For exact fields and rules, read the specification, CLI reference, or JSON schemas.

Add it to a GitHub release

In a tag-triggered release job, after your workflow builds the artifacts and uploads them to the GitHub release, add:

permissions:
  contents: write     # Upload the bundle to the release.
  id-token: write     # Sign with the workflow's identity.
  attestations: write # Attest the matched files.

steps:
  # Build the archives and upload them to the release before this step.
  - uses: jdx/packslip@v1
    with:
      artifacts: dist/*.tar.xz dist/*.zip
      bin: mytool

The action attests the matched files, signs a release manifest of their digests, platforms, and executable paths with the workflow's identity, verifies it, and uploads packslip.sigstore.json to the release. It does not upload the artifacts. No long-lived signing key is needed.

This one-job form gives the action contents: write. To keep it away from release write access, run it in a separate read-only job. Publish with GitHub Actions covers every input, how @v1 and packslip-version select the CLI, and how to build the CLI on the runner when packslip publishes no release for it, such as x64 macOS. A project hosted on its own domain also publishes a signed release list with jdx/packslip/releases; see Host releases on your own domain.

Verify a release

packslip signs its own releases. To check one with the packslip CLI, download a release's bundle and one of its archives. packslip verify checks the bundle against the workflow that signs packslip releases, and the archive against the digest in the bundle. This example checks the Linux x64 archive of 1.4.0; packslip show packslip.sigstore.json lists the others:

curl -fLO https://packslip.dev/v1.4.0/packslip.sigstore.json
curl -fLO https://packslip.dev/v1.4.0/packslip-v1.4.0-linux-x64.tar.xz
packslip verify packslip.sigstore.json \
  --identity-prefix https://github.com/jdx/packslip/.github/workflows/release.yml@ \
  --issuer https://token.actions.githubusercontent.com \
  --pin ps1_mcx64bcghek2t4vljgb3lho3ti \
  --artifact packslip-v1.4.0-linux-x64.tar.xz

packslip 1.4.0 and earlier have no --pin flag and reject it as an unexpected argument; with those versions, leave that line out.

The bundle's project is packslip.dev, a domain rather than a GitHub repository name, so verify cannot work out the expected signer from it, and the identity flags are required. --identity-prefix names the repository that signs packslip releases by its current name. --pin adds a check on top of the identity flags: its value is that repository's signer fingerprint, derived from the repository's ID, so a release signed from a different repository that later takes the name jdx/packslip is refused. The fingerprint stays the same if the repository is renamed or moves to another owner, but --identity-prefix would then need the new URL. See Pin a signer with its fingerprint.

Agent skill

The packslip skill helps a coding agent configure a release, declare resources, and diagnose verification failures. packslip releases after 1.4.0 declare the skill for their own version, so mise can link the copy that matches your installed CLI into your agent's skills directory:

mise use packslip
mise skills sync --dir .agents/skills

mise use packslip resolves through the mise registry, which supplies the signer pin for packslip's own releases, published as project packslip.dev. The synced links point into your mise installs, so keep them out of version control. Run the same mise skills sync --dir .agents/skills command again after changing the packslip version. To have mise do that after every mise install and mise use, set skills.dir to .agents/skills and turn on skills.auto_sync. Without skills.dir, mise links into .claude/skills.

Work on packslip

See CONTRIBUTING.md for setup, checks, and documentation sources, and RELEASING.md for the maintainer release process. Bug reports and format feedback belong in issues.

Developed by Jeff Dickey (@jdx), author of mise, and Shunsuke Suzuki (@suzuki-shunsuke), author of aqua. MIT licensed; see LICENSE.