Code signing
Every Windows artifact — the portable exe, the installer and the binaries inside it — is Authenticode-signed with an RFC 3161 timestamp from DigiCert, so a signature outlives the certificate that made it.
Today: a development certificate
scripts\sign.ps1 -setup creates a self-signed code-signing certificate in the current user’s store (CN=Utly Development Studio (development signing), RSA 3072, SHA-256, non-exportable key, three years) and exports its public half to scripts\utly-dev-signing.cer. scripts\release.ps1 then runs the Tauri build — whose bundle.windows.signCommand signs the exe that goes into the installer, the NSIS plug-ins and the installer itself — and signs the portable exe afterwards, because the CLI restores the unpatched binary once the bundle is made.
A machine that should treat these builds as trusted imports the exported certificate once, elevated:
Import-Certificate -FilePath scripts\utly-dev-signing.cer -CertStoreLocation Cert:\LocalMachine\Root
Import-Certificate -FilePath scripts\utly-dev-signing.cer -CertStoreLocation Cert:\LocalMachine\TrustedPublisher
Without that import the signature is present and intact but its chain ends in an untrusted root, which is what SmartScreen will say about it. That is expected: the point of this stage is that the whole pipeline — signtool, timestamping, the installer, the service — runs for free until the real certificate arrives, and swapping the certificate is one thumbprint.
Later: the real certificate
Azure Artifact Signing issues public-trust certificates only to individuals in the US and Canada and to organizations in a short list of countries that does not include India. The working routes for an India-based publisher are an individually validated certificate with cloud signing (SSL.com eSigner or Certum SimplySign, roughly $100–300 a year, nothing to ship, scriptable), or a Sectigo OV certificate through a reseller with a mailed USB token (roughly $100–200 a year plus the token). The publisher line shows the legal name on an individual certificate; for it to read “Utly” the studio must be a registered business validated as an organization. EV buys nothing any more — its instant SmartScreen reputation is gone.
Order it at the start of M5: validation takes one to six weeks and SmartScreen reputation a few more after the first signed downloads, which have to be in the wild before the M6 beta. After issuance, submit the binaries to Microsoft’s false-positive portal and keep the timestamping.
The update key
Updates are checked against a second signature, separate from Authenticode: scripts\release.ps1 signs the portable exe and the installer with a minisign key (tauri signer, ed25519 over BLAKE2b), and scripts\publish.ps1 puts the base64 signatures into the channel manifest next to the download URLs and signs the manifest itself with the same key (<channel>.json.sig). The client holds the public key (app/src-tauri/src/update.rs), reads no manifest whose signature does not verify, and runs nothing it downloaded unless the artifact’s signature verifies, whatever TLS said.
The private key lives outside the repository at %USERPROFILE%\.utly\updater.key, created once with npx tauri signer generate -w %USERPROFILE%\.utly\updater.key --ci -p "" from app\. Today that is the development key on the release PC, like the development certificate above. Before the public launch the private half moves to a USB key that is plugged in only to run release.ps1, and the public key in the client is replaced by the new one in the same release; every client after that release verifies against the launch key, and the development key is deleted. Losing the private key means every installed client refuses updates until it is reinstalled by hand, so the launch key gets two USB copies in two places.