Publish sha256 alongside the release binary and verify on install (closes #12, #1) - #25
Merged
Conversation
Closes #12. Closes #1. The release step previously uploaded a single file — the ussher binary — with no checksum, no signature, and no SLSA / sigstore attestation. The documented install path then fetched those bytes and `chmod +x`'d them with no verification. For a binary that becomes every adopting host's `AuthorizedKeysCommand`, that's the highest-impact gap in the release pipeline: any compromise of the release artifact reaches every adopter silently. The release workflow now generates `ussher.sha256` after the build and includes it in the release upload alongside the binary. install.sh downloads both files with `curl -fLO` (also fixes the long-standing bug from #1 where `-o` was missing its filename and the URL was being consumed as the output path) and runs `sha256sum -c ussher.sha256` before doing anything with the bytes — `set -e` at the top of the script aborts the install on any mismatch. `--fail` ensures a 4xx/5xx response from GitHub doesn't silently produce an HTML error page that gets installed. Verification only runs when ussher.sh just downloaded the binary; if a local `ussher` already exists in cwd (the build-from-source flow), the script trusts it and skips both downloads. That keeps `./build.sh && ./install.sh` working without a sha256 sidecar file. The README quickstart grows a parallel block showing the curl + verify steps for adopters who skip install.sh and follow the documentation manually. Don't-proceed-on-mismatch is called out explicitly there. Local dry run: `./build.sh` produces `ussher`; `sha256sum ussher > ussher.sha256` followed by `sha256sum -c ussher.sha256` round-trips cleanly. shellcheck is green on install.sh. Out of scope, deferred to a fuller follow-up: replacing the bespoke build/release with `.goreleaser.yml` for cross-compiled binaries (#15) and Cosign keyless signatures for SLSA-style provenance. Tier A here is a strict improvement and doesn't preclude that work — goreleaser will replace these lines wholesale anyway.
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #12.
Closes #1.
Summary
Publishes a
sha256of the release binary as a release asset, and hasinstall.sh(and the README quickstart) verify the binary against that checksum before doing anything with the bytes. Also fixes the long-standingcurlbug from #1 —curl -L -o https://...was missing its filename, so the documented install path didn't actually work.This is Tier A from the issue body. Tier B (
.goreleaser.ymlfor cross-compile + Cosign signatures) is a strictly bigger scope conversation that pairs with #15; left as a follow-up.Changes
.github/workflows/go.ymlNew step before the release upload, gated on the same tag predicate, plus the new file in
files::install.shDownload block rewritten end-to-end:
This single block fixes three things:
-osyntax (-Oderives the filename from the URL — same effect, no foot-gun).--fail: a 404 / 5xx from GitHub no longer silently writes an HTML error page that then getschmod +x'd.set -eat the top ofinstall.shmeans a mismatch aborts beforesudo installruns.The verification only runs when
install.shjust downloaded the binary — if a localussheralready exists (the./build.sh && ./install.shbuild-from-source flow), the script trusts it and skips both downloads. That keeps the local-build flow working without a sidecar.sha256file. The trust model there is "you built it yourself", which is fine.README.mdThe "Recommended installation & usage" step 1 now shows the parallel curl +
sha256sum -ccommands for adopters who skipinstall.shand follow the README manually. The "don't proceed past this step on a mismatch" instruction is called out explicitly.Test plan
shellcheck install.sh build.shpasses (the existing CI job from Add shellcheck to CI (refs #1) #6/Simplify shellcheck discovery to .sh only (review feedback from #6) #7 will also catch this on the PR)../build.shproducesussher;sha256sum ussher > ussher.sha256followed bysha256sum -c ussher.sha256round-trips cleanly../build.shstill green; tests pass; coverage unchanged at 39.5%.ussherandussher.sha256as release assets; the README quickstart steps run end-to-end against that release.Out of scope (follow-ups)
.goreleaser.ymlfor cross-compiled binaries and Cosign signing — pairs with Release only ships a single linux-amd64 binary — no arm64, no darwin #15 and is a strictly bigger scope. The lines this PR adds will be replaced wholesale if/when goreleaser lands; this PR is a strict improvement on the current state in the meantime.install.sh's download block (the test plan I sketched on install.sh: curl -o is missing its filename argument, breaking the binary download #1's comment thread). Worth adding but widens scope; happy to do it as its own PR if you want.https://claude.ai/code/session_013HnepY8MhhxrJJjE5ysW47
Generated by Claude Code