Sign the Windows executables through SignPath - #753
Open
PathGao wants to merge 1 commit into
Open
Conversation
One signing request covers all four executables, so it lives in a job of its own rather than in the two Windows matrix entries. The matrix hands its build over as an artifact; nothing Windows reaches the release page until the request comes back approved. Authenticode rewrites the installer, so its `.sig` -- the minisign signature the updater checks -- is regenerated from the signed file before anything is uploaded. Without SIGNPATH_API_TOKEN the job uploads what the matrix built, unsigned, and the release notes keep saying so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 4, 2026
Collaborator
Author
|
Before merging: the |
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.
What this is
Windows Authenticode signing through SignPath Foundation, using the four strings @alecdotdev posted in #562. Ref #562, #334, #466, #722.
Mechanism
One signing request covers all four executables — SignPath signs the contents of one Actions artifact and an approver releases it by hand — but x64 and arm64 are two matrix entries, and each uploaded straight to the release page. So the signing is a job of its own: the matrix hands its build over as an artifact,
sign-windowscollects both, submits one request, and is the only place a Windows file reaches the release. An approval that never comes leaves the draft without Windows assets rather than with unsigned ones.Authenticode rewrites the installer's bytes, and
*-setup.exe.sigis a minisign signature over the pre-signing ones. Shipping that makes every Windows install reject its next update — the same defect the repacked AppImage had, fixed the same way: re-sign from the signed file, then letgenerate-update-feedread the signatures back off the release.Scope
Without
SIGNPATH_API_TOKENthe job uploads what the matrix built, unsigned, and the release notes keep the SmartScreen note — both keyed on that one secret, so a release cannot claim to be signed when it is not. Same shape as the macOS certificate, for the reason #294 was closed: a release must not be blocked on credentials nobody has configured yet.The four non-secret values are in the job rather than in secrets, and RELEASING.md gets a one-time setup step 8 saying so, plus what the SignPath side needs — the GitHub App, the trusted build system, the artifact configuration, an approver.
Not here: signing the copy of Markpad an NSIS installer unpacks. Nothing can reach inside an installer after it is built, so that would be a second build pass and a second approval, and what people report — SmartScreen on the download, the heuristic on the portable
.exe— is on the files this signs.Tests
Four in
scripts/releaseWorkflow.test.ts, all matching workflow text, which is what a workflow makes checkable: nogh release uploadof an.exein the matrix; one signing request per release over four collected executables; the.sigregenerated after the signed executables land and before the upload, withgenerate-update-feedwaiting on the job; and the upload not gated on the token while every SignPath step is. The existing "RELEASING.md names the signing secrets the workflow reads" now reads the new job too.Each was falsified: putting a release upload back in the matrix, asking for two files instead of four, moving the re-sign ahead of the signed copy, gating the upload on the token, dropping
sign-windowsfrom the feed'sneeds, and renaming the secret in RELEASING.md each turn exactly one of them red.Verification
macOS 26.6.2, Node 24.16.0.
0 vulnerabilities, 831 files / 0 errors, 1033 pass (was 1029), 434 pass.
Not verified: the release path itself.
build.ymlisworkflow_dispatchonly, so nothing here has run — the first proof is 2.7.7, and it is also the first time the SignPath side is exercised end to end.cargo testnot run; no Rust in the diff.🤖 Generated with Claude Code