stacklok / stacklok/toolhive-studio
[Task] Migrate Windows code signing from DigiCert KeyLocker to Azure Trusted Signing
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 167
- Forks
- 24
- Avg merge
- 11h 32m
- Merged PRs (30d)
- 91
Description
Description
Migrate the Windows code signing pipeline from DigiCert KeyLocker to Azure Trusted Signing, authenticated via GitHub OIDC workload identity federation, and decommission the DigiCert path entirely.
Today Windows artifacts are signed by shelling out to DigiCert's smctl.exe from a custom @electron/windows-sign hook, which requires storing a client certificate (.p12), its password, an API key, a host URL, and a SHA-1 fingerprint as long-lived repository secrets. The same flow is duplicated in both pr-build-test.yml (opt-in via /build-test --sign-windows) and on-release.yml (stable + prerelease tags).
Azure Trusted Signing replaces all of that with OIDC (no client secret stored anywhere), uses the standard signtool.exe + Azure Code Signing DLib, and is the path Electron Forge now documents as canonical for Windows signing. The migration also lets us scope signing credentials to a dedicated artifact-signing GitHub environment so unsigned PR builds and non-Windows matrix rows can never request them.
Technical Details
Target architecture
- Signing backend: Azure Trusted Signing account + Certificate Profile, with a federated credential trusting
repo:stacklok/toolhive-studio:environment:artifact-signing. - Auth:
azure/login@v2withid-token: write; no client secret. - Signer:
signtool.exe(from the Windows SDK on the runner) +Azure.CodeSigning.Dlib(installed vianuget) + a generatedmetadata.json— wired through@electron/windows-sign'ssignWithParamsoption, following the official Electron Forge Trusted Signing guide. - GitHub environment:
artifact-signing, holding sixAZURE_ARTIFACT_SIGNING_*secrets (tenant id, client id, subscription id, endpoint, account name, certificate profile name). Activated only for the Windows matrix row when signing is requested. - Reusable composite action:
.github/actions/setup-azure-trusted-signingencapsulatesazure/login,nuget install, locating the latest x64signtool.exe, writingmetadata.json, and exportingAZURE_CODE_SIGNING_DLIB/AZURE_METADATA_JSON/SIGNTOOL_PATHtoGITHUB_ENV. - Forge integration: a single
getWindowsSignConfig()helper inforge.config.tsreturns the Azure config when env vars are present; bothpackagerConfig.windowsSignandmaker-squirrel'swindowsSignconsume it.
Rollout plan (staged to keep releases shipping)
The migration is broken into three PRs so we can burn in the new flow progressively without risking a broken stable release:
- PR builds (opt-in): migrate
pr-build-test.yml//build-test --sign-windowsto Azure Trusted Signing. Keep DigiCert as a runtime fallback inforge.config.tssoon-release.ymlis unaffected. Addutils/windows-sign-azure.ts, the composite action, and theartifact-signingenvironment. - Prerelease tags: extend Azure signing to
*-alpha,*-beta,*-rctags inon-release.ymlvia a conditional step. Stable releases still sign via DigiCert until prereleases are proven. - Stable releases + cleanup: flip stable Windows releases to Azure, then remove the entire DigiCert path —
.github/actions/setup-windows-codesign,utils/digicert-hook.js, the DigiCert fallback inforge.config.ts, theeslint.config.mjsignore entry, the DigiCert section indocs/README.md, and theSM_*repository/environment secrets.
AWS OIDC compatibility
The build-and-release job also assumes an AWS IAM role via OIDC (for S3 sync / CloudFront invalidation). Entering the artifact-signing environment changes the token subject from repo:.../ref:refs/tags/vX.Y.Z to repo:.../environment:artifact-signing. The existing AWS trust policy uses the wildcard repo:stacklok/toolhive-studio:*, so both subjects are accepted and no AWS-side changes are required.
Relevant files
.github/workflows/pr-build-test.yml.github/workflows/on-release.yml.github/actions/setup-azure-trusted-signing/action.yml(new).github/actions/setup-windows-codesign/action.yml(to be removed at the end)utils/windows-sign-azure.ts(new)utils/digicert-hook.js(to be removed at the end)forge.config.ts(getWindowsSignConfig())eslint.config.mjsdocs/README.md
Acceptance Criteria
- A dedicated
artifact-signingGitHub environment holds the sixAZURE_ARTIFACT_SIGNING_*secrets and has a federated credential trusting the repo's environment subject -
.github/actions/setup-azure-trusted-signingcomposite action is in place and used by bothpr-build-test.ymlandon-release.yml -
utils/windows-sign-azure.tsproduces a valid@electron/windows-signconfig and is consumed byforge.config.tsfor bothpackagerConfig.windowsSignandmaker-squirrel'swindowsSign -
/build-test --sign-windowsproduces Azure-signed artifacts on PRs - Prerelease tags (
*-alpha,*-beta,*-rc) are signed via Azure Trusted Signing fromon-release.yml - Stable release tags are signed via Azure Trusted Signing from
on-release.yml - Signed artifacts pass
signtool verify /pa /v, install cleanly on a fresh Windows VM without SmartScreen blocking after reputation builds, and the Squirrel auto-updater handshake succeeds on an existing installed build - AWS S3 sync + CloudFront invalidation on the Windows release runner continue to work after switching the OIDC token subject to
environment:artifact-signing -
.github/actions/setup-windows-codesign/,utils/digicert-hook.js, the DigiCert fallback branch inforge.config.ts, and theutils/digicert-hook.jsentry ineslint.config.mjsare removed -
docs/README.mddocuments Azure Trusted Signing as the sole Windows signing path - The
SM_HOST,SM_API_KEY,SM_CLIENT_CERT_FILE_B64,SM_CLIENT_CERT_PASSWORD, andSM_CODE_SIGNING_CERT_SHA1_HASHsecrets are removed from the repo and any environments - Lint, type-check, and unit tests pass
Out of Scope
- macOS / Flatpak signing (unchanged — Apple certificates and Flatpak remain as-is)
- Reworking the AWS OIDC trust policy (the existing wildcard already covers both tag and environment subjects)
- Changes to the auto-updater feed itself — only the signing identity on the artifacts changes
- Cancelling the DigiCert KeyLocker subscription is tracked under this task but handled outside the repo
Priority
High — removes long-lived client certs + API keys from repository secrets, aligns with the Electron Forge recommended path, and unblocks decommissioning DigiCert KeyLocker.
Task Type
Chore / Infrastructure
References
- Electron Forge — Azure Trusted Signing guide
- electron/windows-sign#45 (install
Azure.CodeSigning.Dlibinto a no-space path, e.g.C:\azsign\)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading .github/workflows/pr-build-test.yml, .github/workflows/on-release.yml, and forge.config.ts, then review the Electron Forge Trusted Signing guide and the existing DigiCert action. Implement the staged workflow and Forge integration across the listed files, validate signing and release behavior on Windows, and finish with lint, type-check, and unit tests passing and the DigiCert path removed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- azure, electron, github-actions, typescript
- Domain
- build-system, ci-cd, devops, release, security
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100