Sign and notarize the macOS binary - #5
Open
las7 wants to merge 1 commit into
Open
Conversation
A browser download of the macOS tarball is refused by Gatekeeper: "the developer cannot be verified". Building on a macos-14 runner already gets an ad-hoc signature from the linker, which is why `curl | sh` works -- curl sets no quarantine attribute, so Gatekeeper never runs -- but an ad-hoc signature is not a Developer ID, and the same artifact fetched from the releases page is quarantined and refused. The binary is now signed with a Developer ID certificate under the hardened runtime and notarized before the release is cut. Notarization rejects a submission without `--options runtime`, with an error that does not say so. Signing runs BEFORE packaging, and has to: it rewrites the binary, so a tarball built first would ship the unsigned one and SHA256SUMS would still verify -- install.sh would confirm the checksum of the wrong artifact and the failure would name the download. A bare executable cannot be stapled, so the ticket is resolved online. A first run with no route to Apple fails the way an unnotarized binary does; a stapled .pkg is the fix if that is ever a real complaint. Recorded in the README rather than left for someone to rediscover. Six secrets, documented in the README, and a missing one fails the release loudly rather than shipping something Gatekeeper will refuse. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V1dcKuVdxyArHpXvHLkiGx
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.
Fixes the Gatekeeper refusal on a browser download of the macOS tarball.
Why it happens
The macOS build already runs on a
macos-14runner, so the linker applies an ad-hoc signature. That is whycurl | shworks — curl sets nocom.apple.quarantineattribute, so Gatekeeper never runs on that path.A browser download does set it, and an ad-hoc signature is not a Developer ID. The same artifact fetched from the releases page is refused with "the developer cannot be verified".
The change
The macOS binary is signed with a Developer ID Application certificate under the hardened runtime, then notarized with
--wait, so a rejection fails the release rather than being discovered by whoever downloads it.--options runtimeis required — notarization rejects a submission without it, with an error that does not say so.Signing runs before packaging, and has to. Signing rewrites the binary, so a tarball built first would ship the unsigned one and
SHA256SUMSwould still verify:install.shwould confirm the checksum of the wrong artifact, and the failure would name the download rather than the ordering.Only the
aarch64-apple-darwinmatrix leg is touched. The Linux musl legs need none of this and are unchanged.Two things recorded in the README rather than left to be rediscovered
notarytoolwill not take a.tar.gz(only.zip,.pkg,.dmg), so the binary is zipped purely to be submitted. What ships is still the tarball containing that same signed binary — the ticket binds to the code signature, not to the container it was submitted in..dmgor.pkg), so Gatekeeper resolves the ticket online. A first run on a machine with no route to Apple fails the way an unnotarized binary does. A stapled.pkgalongside the tarball is the fix if that ever becomes a real complaint; it did not seem worth the second artifact today.Before this can run — six secrets, none of which I can create
Documented in README → Signed macOS builds. A missing one fails the release loudly rather than shipping something Gatekeeper will refuse, matching how
RELEASE_REPO_TOKENalready behaves.APPLE_CERT_P12_BASE64,APPLE_CERT_PASSWORD,APPLE_SIGNING_IDENTITY,APPLE_API_KEY_P8_BASE64,APPLE_API_KEY_ID,APPLE_API_ISSUER_ID.Two notes on obtaining them: the certificate must be Developer ID Application, not "Mac App Distribution" (App Store only, will not satisfy Gatekeeper here); and prefer an App Store Connect API key over an Apple ID with an app-specific password, which breaks whenever someone's password or 2FA changes.
Verification
The workflow YAML parses. Nothing here has been executed — it cannot be until the secrets exist, and it only runs on a
cli-v*tag. The first tagged release after the secrets land is the real test; the check to make afterwards is a browser download ofreachpad-aarch64-apple-darwin.tar.gz, extracted and run on a Mac that has never seen the binary, plusspctl -a -vv -t installagainst the extracted binary.🤖 Generated with Claude Code
https://claude.ai/code/session_01V1dcKuVdxyArHpXvHLkiGx