Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Threshold Release Signing with Keep

Keep can act as a general-purpose threshold signer for software releases. A project generates a FROST-Ed25519 group, distributes the n shares to its maintainers, and signs release artifacts with keep sign. Producing a signature requires a threshold t of those maintainers to cooperate, so no single person holds the signing key. The output is minisign-compatible, so downstream users verify with the stock minisign tool or with keep verify.

This is a Keep capability you can adopt for your own project.

Keep’s own releases are not signed this way yet. The workflows are wired for it but dormant: no signing group exists, release-signing.pub is not in this repository, and the Verify release signature check reports NOT VERIFIED on every release. Do not expect a SHA256SUMS.minisig on current releases, and do not treat that check’s green tick as evidence of anything until activation.

Once the group is established, releases will ship release-signing.pub alongside the binaries and the SHA256SUMS manifest, a threshold of maintainers will sign the manifest offline and upload SHA256SUMS.minisig, and the workflow will verify both the signature and that every published artifact matches its signed digest.

Where the trust anchor lives

release-signing.pub is attached to releases for convenience only. Anyone who can publish a release can attach a public key to it, so verifying a release against a key downloaded from that same release proves nothing on its own. The anchor is the copy committed in this repository; fetch it from a source you already trust, pin it, and reuse it across releases.

Establishing a signing group

Done once. Generate the group, distribute the shares, and publish the public key:

keep frost generate --ed25519 --threshold <t> --shares <n> \
  --name release-signing --pubkey-out release-signing.pub

Export each share to its holder with keep frost export (bech32 / QR), then publish release-signing.pub somewhere users can find it (in your repository, on your site, and/or attached to each release).

Signing a release

Sign the checksum manifest rather than every artifact; the manifest covers them all.

# Generate a checksum manifest for the release artifacts.
sha256sum keep-* > SHA256SUMS

# Threshold-sign the manifest (writes SHA256SUMS.minisig).
keep sign SHA256SUMS --group <group-npub-or-hex> -t "release v1.2.3"

--group accepts an npub1... string or a 64-char hex group pubkey. The current minisign signing path is local-threshold: the t shares must be present on the signing machine. See “Distributed signing” below.

Attach SHA256SUMS, SHA256SUMS.minisig, and release-signing.pub to the release.

Verifying a release

Users need the artifact, SHA256SUMS, SHA256SUMS.minisig, and the project public key.

# 1. Confirm the downloaded files match the manifest.
sha256sum --check SHA256SUMS

# 2. Verify the manifest signature against the project public key.
minisign -V -p release-signing.pub -m SHA256SUMS

minisign -V prints Signature and comment signature verified on success. The SHA256SUMS.minisig file must sit next to SHA256SUMS.

With Keep installed, verify without minisign:

keep verify SHA256SUMS SHA256SUMS.minisig --group release-signing.pub

--group accepts the public-key file, a hex group pubkey, or an npub1... string.

GitHub Actions example

Signing should not run in CI: putting the threshold of shares into CI secrets recreates the single point of failure threshold signing exists to remove. Build and publish in CI, then sign offline and upload the signature.

# In your release job, after generating SHA256SUMS:
- name: Attach signing public key
  run: cp release-signing.pub artifacts/
- uses: softprops/action-gh-release@v3
  with:
    files: artifacts/*

After the release publishes, a maintainer signs and uploads the signature:

gh release download <tag> --pattern SHA256SUMS
keep sign SHA256SUMS --group <group-npub-or-hex> -t "release <tag>"
minisign -V -p release-signing.pub -m SHA256SUMS
gh release upload <tag> SHA256SUMS.minisig

Optionally, a non-blocking workflow triggered on release: [published, edited] can run minisign -V to surface the signature status as a check once the .minisig is uploaded.

Distributed signing

The minisign signing path currently reconstructs the threshold from shares held on one machine. Fully distributed signing, where no machine ever holds t shares and signers cooperate over Nostr, reuses Keep’s existing FROST network coordination but is not yet wired to the minisign output format. Tracked in #500.