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.
Verify a download
Section titled “Verify a download”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.
# 1. fetch the artifact, the checksum list and the provenancegh 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 publishedshasum -a 256 -c checksums.txt --ignore-missing
# 3. github native attestation: signed, and tied to this repositorygh 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 tagslsa-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.1Each 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.
What each asset is
Section titled “What each asset is”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.
# what shipped, and at what versionsgh release download v3.0.1 -p "*-sbom.cdx.json"jq -r '.components[] | "\(.name)\t\(.version)"' netdash-toolkit-v3.0.1-sbom.cdx.json | sortThe pipeline, and the gates that fire
Section titled “The pipeline, and the gates that fire”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.
Installing on macOS
Section titled “Installing on macOS”# the project's own tapbrew tap sunnypatell/netdash https://github.com/sunnypatell/netdash-toolkitbrew 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.
Not in scope (yet)
Section titled “Not in scope (yet)”- Auto-update.
electron-builder.jsonsets"publish": nulland there is noelectron-updaterdependency, so updates are a manual download orbrew 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.