Back to Turso

Windows Code Signing

docs/code-signing.md

0.7.26.2 KB
Original Source

Windows Code Signing

Turso's Windows build artifacts are Authenticode-signed with Azure Artifact Signing (formerly Azure Trusted Signing). Signing is what lets Windows SmartScreen and the OS verify that a binary genuinely comes from Turso and hasn't been tampered with.

Two workflows sign on the Windows runners where the binaries are already built:

WorkflowArtifact signed
.github/workflows/dotnet-publish.ymlturso_sdk_kit.dll (win-x64, win-arm64) inside the NuGet package
.github/workflows/release.ymltursodb.exe inside turso_cli-x86_64-pc-windows-msvc.zip

Signing is a no-op unless the AZURE_SIGNING_ENABLED repository variable is true, so forks and pull requests without access to the secrets build normally without signing.

[!NOTE] release.yml is generated by cargo-dist (dist generate). cargo-dist has no native Azure signing support yet (axodotdev/cargo-dist#2395), so the signing steps are hand-added. Re-running dist generate will drop them — re-apply from git history if you regenerate the file.

GitHub Actions configuration

Both workflows authenticate to Azure with OpenID Connect (OIDC), so there is no long-lived certificate or client secret stored in the repo.

Repository secrets

Configure under Settings → Secrets and variables → Actions → Secrets (gh secret set <NAME>):

SecretDescription
AZURE_CLIENT_IDApplication (client) ID of the Entra app registration used for signing
AZURE_TENANT_IDMicrosoft Entra (Azure AD) tenant ID
AZURE_SUBSCRIPTION_IDAzure subscription ID containing the signing account

Repository variables

Configure under Settings → Secrets and variables → Actions → Variables (gh variable set <NAME> --body <VALUE>):

VariableDescriptionExample
AZURE_SIGNING_ENABLEDMaster switch; signing runs only when this is truetrue
AZURE_SIGNING_ENDPOINTSigning account endpoint for your regionhttps://eus.codesigning.azure.net
AZURE_SIGNING_ACCOUNTArtifact Signing account nameturso-signing
AZURE_SIGNING_CERT_PROFILECertificate profile nameturso-public-trust

These are non-secret, so variables (not secrets) are intentional — it keeps them visible and easy to audit.

Example: setting everything with the gh CLI

bash
gh secret set AZURE_CLIENT_ID --body "<app-client-id>"
gh secret set AZURE_TENANT_ID --body "<tenant-id>"
gh secret set AZURE_SUBSCRIPTION_ID --body "<subscription-id>"

gh variable set AZURE_SIGNING_ENABLED --body "true"
gh variable set AZURE_SIGNING_ENDPOINT --body "https://eus.codesigning.azure.net"
gh variable set AZURE_SIGNING_ACCOUNT --body "<signing-account-name>"
gh variable set AZURE_SIGNING_CERT_PROFILE --body "<certificate-profile-name>"

One-time Azure setup

This is done in the Azure portal / Entra and only needs to happen once. See the Artifact Signing quickstart for full detail.

  1. Paid subscription. Artifact Signing does not work on free, trial, or sponsored subscriptions.

  2. Create the signing account and a Public Trust certificate profile. This requires a completed identity validation request. The legal name and address on your Azure billing account must exactly match what you want on the certificate, and the billing account type (Individual vs Organization) must match the validation type.

  3. Register an Entra app for CI and add a federated credential for GitHub OIDC. Classic federated credentials match the subject exactly — a literal * in the subject never wildcard-matches, so a credential like refs/heads/* silently fails with AADSTS7002131. The subjects must match the refs the workflows sign on:

    • dotnet-publish.yml signs only on main pushes (and manual dispatch from main) → repo:tursodatabase/turso:ref:refs/heads/main
    • release.yml runs on tag pushes, where the ref differs per release, so an exact-match subject is impossible. Use a flexible federated credential with a claims-matching expression (e.g. claims['sub'] matches 'repo:tursodatabase/turso:ref:refs/tags/*'), or scope the job to a GitHub Environment and match repo:tursodatabase/turso:environment:<name> instead.

    Add a federated credential for each (or scope them to a GitHub Environment). See Configuring OpenID Connect in Azure.

  4. Assign roles on the signing account to that app:

    • Trusted Signing Certificate Profile Signer — required for the app to request signing certificates.
    • You also need Trusted Signing Identity Verifier to complete step 2.

    See Artifact Signing resources and roles.

Verifying a signature

Download a release archive and check the binary on Windows:

powershell
signtool verify /pa /v tursodb.exe

Or inspect the Digital Signatures tab in the file's Properties dialog.

Reference