0041: Windows signing with Artifact Signing

On this page

Status: accepted

Context

The Windows installer was unsigned, so SmartScreen showed Windows protected your PC and people had to choose More info, then Run anyway. A signature from a certificate that chains to Microsoft's trusted roots, issued to a verified publisher, is what lets Windows show the publisher's name and build reputation.

The usual ways to get one are an EV or OV code signing certificate on a hardware token or in a cloud HSM, or Azure Artifact Signing (called Trusted Signing until 2025). A token can't be used from a hosted CI runner, and a certificate exported to a file would be a long-lived secret in the repository's settings. Artifact Signing issues a certificate valid for three days, signs on Microsoft's side, and gives the publisher's identity only after Microsoft verifies the organization (Frank The Programmer LLC, validated in October 2026).

Decision

  • The release workflow signs with Azure Artifact Signing: an account in East US and a Public Trust certificate profile on it, bound to the validated organization. The workflow reads their names from the GitHub variables AZURE_SIGNING_ACCOUNT and AZURE_SIGNING_PROFILE.
  • Tauri runs apps/desktop/scripts/sign-windows.ps1 as its signCommand for the installer and Short Leash's executables. The workflow writes that setting into a config file for the run (apps/desktop/scripts/windows-sign-config.ps1), so local, CI and pull request builds stay unsigned. Tauri signs first and makes the updater signature after, so the update signature covers the signed installer.
  • The script runs signtool from the Windows SDK with Microsoft's Artifact Signing client library (a NuGet package) and a timestamp from timestamp.acs.microsoft.com, so signatures keep validating after the three-day certificate expires. We don't use a third-party signing CLI.
  • The client library is pinned to a version and SHA-512 that nuget.org publishes, and its DLL is used only with a valid Authenticode signature from Microsoft, the same rule as the Visual C++ Redistributable (ADR 0032, invariant 7 in CLAUDE.md).
  • The workflow signs in as a service principal that holds only the Artifact Signing Certificate Profile Signer role on that account. Its tenant ID, client ID and client secret are the repository secrets AZURE_TENANT_ID, AZURE_CLIENT_ID and AZURE_CLIENT_SECRET. The secret expires, so signing stops until it's replaced; the release skill says when and how.
  • After the build, apps/desktop/scripts/check-windows-signature.ps1 fails the release unless the installer, the app and the two programs it carries (leashd and leash) each have a valid, timestamped signature issued to Frank The Programmer LLC.
  • The Preview build workflow has a sign_windows option that does the same signing for a hand-run build from any branch, to try changes to signing before a release.

Consequences

  • No certificate or private key is stored anywhere, and a leaked signing credential can only sign as Short Leash until its role assignment or secret is removed.
  • Windows shows Short Leash's publisher name. The SmartScreen warning should fade as the signed installer builds reputation, but that isn't immediate, so the docs keep the Run anyway step until a signed release has been tried on a clean PC.
  • Releases now depend on the Artifact Signing service. If it's down or the secret has expired, the Windows job fails before anything is published.
  • The Mac build is unchanged (Developer ID and notarization).