docs/code-signing.md
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:
| Workflow | Artifact signed |
|---|---|
.github/workflows/dotnet-publish.yml | turso_sdk_kit.dll (win-x64, win-arm64) inside the NuGet package |
.github/workflows/release.yml | tursodb.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.ymlis 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-runningdist generatewill drop them — re-apply from git history if you regenerate the file.
Both workflows authenticate to Azure with OpenID Connect (OIDC), so there is no long-lived certificate or client secret stored in the repo.
Configure under Settings → Secrets and variables → Actions → Secrets
(gh secret set <NAME>):
| Secret | Description |
|---|---|
AZURE_CLIENT_ID | Application (client) ID of the Entra app registration used for signing |
AZURE_TENANT_ID | Microsoft Entra (Azure AD) tenant ID |
AZURE_SUBSCRIPTION_ID | Azure subscription ID containing the signing account |
Configure under Settings → Secrets and variables → Actions → Variables
(gh variable set <NAME> --body <VALUE>):
| Variable | Description | Example |
|---|---|---|
AZURE_SIGNING_ENABLED | Master switch; signing runs only when this is true | true |
AZURE_SIGNING_ENDPOINT | Signing account endpoint for your region | https://eus.codesigning.azure.net |
AZURE_SIGNING_ACCOUNT | Artifact Signing account name | turso-signing |
AZURE_SIGNING_CERT_PROFILE | Certificate profile name | turso-public-trust |
These are non-secret, so variables (not secrets) are intentional — it keeps them visible and easy to audit.
gh CLIgh 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>"
This is done in the Azure portal / Entra and only needs to happen once. See the Artifact Signing quickstart for full detail.
Paid subscription. Artifact Signing does not work on free, trial, or sponsored subscriptions.
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.
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/mainrelease.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.
Assign roles on the signing account to that app:
Download a release archive and check the binary on Windows:
signtool verify /pa /v tursodb.exe
Or inspect the Digital Signatures tab in the file's Properties dialog.