Skip to content

Sign the Windows executables through SignPath - #753

Open
PathGao wants to merge 1 commit into
masterfrom
ci/windows-authenticode-signpath
Open

Sign the Windows executables through SignPath#753
PathGao wants to merge 1 commit into
masterfrom
ci/windows-authenticode-signpath

Conversation

@PathGao

@PathGao PathGao commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

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-windows collects 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.sig is 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 let generate-update-feed read the signatures back off the release.

Scope

Without SIGNPATH_API_TOKEN the 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: no gh release upload of an .exe in the matrix; one signing request per release over four collected executables; the .sig regenerated after the signed executables land and before the upload, with generate-update-feed waiting 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-windows from the feed's needs, and renaming the secret in RELEASING.md each turn exactly one of them red.

Verification

macOS 26.6.2, Node 24.16.0.

npm audit
npm run check
npm test
npx vitest run

0 vulnerabilities, 831 files / 0 errors, 1033 pass (was 1029), 434 pass.

Not verified: the release path itself. build.yml is workflow_dispatch only, 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 test not run; no Rust in the diff.

🤖 Generated with Claude Code

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>
@PathGao

PathGao commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator Author

Before merging: the SIGNPATH_API_TOKEN secret currently in the repository belongs to a SignPath trial organization, which cannot have a trusted build system linked — the connector rejects a submission from it, so the Windows job would fail and upload nothing. Delete the secret first and this merges safely; the job then uploads what the matrix built, unsigned, as today. Details in #562. The four SignPath values here are the trial's and get swapped when the Foundation organization exists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant