Skip to content

Releases and verification

A release is a tag push. Everything after that is automated by .github/workflows/release.yml, and every artifact carries evidence you can check yourself without trusting the green check mark.

One download and three checks. If all three checks pass, the binary on your disk is byte-for-byte what the tagged source built on GitHub’s runners.

Terminal window
# 1. fetch the artifact, the checksum list and the provenance
gh release download v3.0.1 \
-p "checksums.txt" \
-p "NetDash-Toolkit-3.0.1-mac-arm64.dmg" \
-p "*.intoto.jsonl"
# 2. checksum: the bytes match what the pipeline published
shasum -a 256 -c checksums.txt --ignore-missing
# 3. github native attestation: signed, and tied to this repository
gh attestation verify NetDash-Toolkit-3.0.1-mac-arm64.dmg --repo sunnypatell/netdash-toolkit
# 4. slsa build l3 provenance: tied to this repository AND this tag
slsa-verifier verify-artifact NetDash-Toolkit-3.0.1-mac-arm64.dmg \
--provenance-path netdash-toolkit-v3.0.1.intoto.jsonl \
--source-uri github.com/sunnypatell/netdash-toolkit \
--source-tag v3.0.1

Each step proves strictly more than the one above it:

Check Proves Does NOT prove
shasum -a 256 -c the file matches the published digest anything about who published the digest
gh attestation verify GitHub signed a statement binding this digest to this repository which commit or tag it came from
slsa-verifier verify-artifact the digest was produced by the trusted builder, from this repository, at this tag that the source itself is free of bugs

The point of the third row is provenance, not quality. SLSA Build L3 means the build ran on a hosted, isolated builder and the provenance was signed in a context the workflow itself cannot forge. It says the bytes came from that tag of that repository. It says nothing about whether the code is any good, and the docs should not imply otherwise.

Provenance is generated by the slsa-framework/slsa-github-generator reusable workflow, pinned to v2.1.0 by tag rather than by SHA. The workflow comment explains why that one exception exists: slsa-verifier validates the trusted builder by its tagged identity, so pinning it to a commit SHA makes verification fail. Every other action in the repository is SHA-pinned.

11 assets ship per release.

Asset What it is Why you might want it
*.dmg, *.zip macOS, x64 and arm64 install
*.exe (NSIS and portable) Windows x64 install, or run without installing
*.AppImage, *.deb Linux x64 install
checksums.txt SHA-256 for every binary integrity
*.intoto.jsonl SLSA provenance, in-toto attestation format provenance
*-sbom.cdx.json CycloneDX SBOM dependency audit

The SBOM is generated by anchore/sbom-action in cyclonedx-json format, and the workflow comment states its scope precisely: it is built from the lockfile only. That gives you an exact, reproducible dependency graph, and it means the SBOM describes what the package manager resolved, not what ended up inside the packaged app.asar. Read it as a dependency inventory, not as a bill of materials for the shipped bundle.

Terminal window
# what shipped, and at what versions
gh release download v3.0.1 -p "*-sbom.cdx.json"
jq -r '.components[] | "\(.name)\t\(.version)"' netdash-toolkit-v3.0.1-sbom.cdx.json | sort
validate -> build (3-os matrix) -> combine-hashes -> provenance -> publish -> homebrew
Stage What it does Gate
validate tag format, format:check, lint, both typechecks fails if the tag does not match package.json version
build 3-OS matrix, hermetic, stages assets, computes SHA-256 subjects codesign --verify --deep --strict on both macOS legs
combine-hashes merges the per-OS digest lists into one SLSA subject list asset sanity check
provenance the SLSA reusable workflow signs the subject list id-token: write, isolated from this workflow
publish creates a draft, attaches all 11 assets, adds native attestations and the SBOM, publishes once draft-first, so publish is atomic
homebrew regenerates Casks/netdash.rb from the publish job’s own hashes skipped for prereleases

Two design decisions in that table are worth calling out because they are not the obvious choice.

The release is created as a draft and published exactly once, with every asset already attached. That keeps the pipeline compatible with GitHub immutable releases if that setting is ever turned on, and it means you never see a release that is missing half its binaries.

The Homebrew cask is regenerated from the publish job’s own hash outputs, not recomputed by re-downloading the artifacts. A cask whose checksum came from a second download could disagree with the one in checksums.txt; deriving both from the same source makes that impossible.

The macOS signature gate has fired for real. electron-builder ad-hoc signs arm64 automatically because Apple Silicon requires a signature, while x64 shipped completely unsigned for every release before v3.0.1. The afterPack hook now seals both, and the CI gate exists so a regression in that hook fails the release instead of shipping.

What is NOT signed, and what that means for you

Section titled “What is NOT signed, and what that means for you”

Being direct about this, because it changes what you should do on first run.

Platform Signing status Consequence
macOS ad-hoc signed, NOT notarized Gatekeeper warns; hardenedRuntime is false and no Apple Developer ID is involved
Windows unsigned SmartScreen warns on first run
Linux unsigned no OS-level prompt

Notarization needs a paid Apple Developer ID, which this project does not have. The ad-hoc signature is what makes Gatekeeper’s “open anyway” path work at all; without it, an unsigned x64 build is a dead end on Intel Macs rather than a warning you can dismiss.

So the OS trust prompts are not the verification story here. The verification story is the four commands at the top of this page, which do not depend on any certificate authority the project could not afford. Run those instead of clicking through a warning on faith.

Terminal window
# the project's own tap
brew tap sunnypatell/netdash https://github.com/sunnypatell/netdash-toolkit
brew install --cask --no-quarantine netdash

--no-quarantine skips the Gatekeeper prompt that ad-hoc signing would otherwise trigger. That is a real trust decision, so verify the release first if you care; the cask’s own checksum comes from the same pipeline output as checksums.txt.

  • Auto-update. electron-builder.json sets "publish": null and there is no electron-updater dependency, so updates are a manual download or brew upgrade. Safe to defer because the verification path above does not rely on an update channel being trustworthy, but it does mean an old install stays old silently.
  • Notarization and Windows code signing. Both need paid certificates. The gap is covered by verifiable provenance rather than by an OS trust prompt, which is a different guarantee, not an equivalent one.
  • Reproducible builds. The build is hermetic in the sense that dependencies come from a frozen lockfile and Electron binaries are checksum-verified by @electron/get, but nobody has shown that two independent runs produce identical bytes. Provenance tells you which builder produced a file; it does not let you rebuild it yourself and compare.