Desktop

Shipping a Tauri App on macOS: Code Signing, Notarization and Updates Explained

Building the app is the easy half. If you skip signing and notarization, macOS warns users or refuses to open your app. Here is the overview and a checklist.

By Prolificrew Engineering · · 4 min read

In this article
  1. What you need first
  2. Signing, hardened runtime and notarization in plain language
  3. How Tauri fits in
  4. Entitlements: only ask for what you use
  5. Automatic updates
  6. Common problems
  7. A release checklist
  8. Build once, release often

You have built a desktop app with Tauri and it runs on your machine. When you send it to someone else, macOS may say the app is damaged, cannot be verified, or refuse to open it. That is Gatekeeper, the macOS security feature that checks where software comes from. To distribute an app outside the Mac App Store without warnings, you need to sign it with an Apple Developer ID certificate and have Apple notarize it. This guide explains the concepts and a sensible order of work. Always confirm exact variable names and commands against the current Tauri distribution documentation and Apple's documentation, because tooling changes.

What you need first

  • A paid Apple Developer Program membership. Free accounts cannot create Developer ID certificates.
  • A Developer ID Application certificate in your keychain. This is the certificate for apps distributed outside the App Store.
  • A way for notarization to authenticate: either an Apple ID with an app-specific password and your team ID, or an App Store Connect API key.
  • A Mac to build on. Signing and notarizing macOS apps requires Apple's tools, so plan a macOS machine or a macOS runner on your continuous integration service.

Signing, hardened runtime and notarization in plain language

StepWhat it doesWhy it matters
Code signingAttaches your identity to the app and verifies it has not been alteredWithout it, macOS cannot tell who made the app
Hardened runtimeEnables extra protections; apps must opt in to risky capabilities with entitlementsRequired for notarization
NotarizationYou upload the app to Apple, which scans it for malicious content and returns a ticketGatekeeper trusts notarized apps and opens them without scary warnings
StaplingAttaches the notarization ticket to the app or disk imageLets the app pass checks even when the user is offline

How Tauri fits in

The Tauri build tool can sign and notarize during tauri build when it finds the right configuration. In general you give it your signing identity and your notarization credentials through environment variables, so secrets never live in the repository. The documentation lists the current variables for the certificate, the signing identity and the Apple ID or API key route. In a continuous integration job, store these as encrypted secrets and import the certificate into a temporary keychain on the runner.

# Build for both Apple silicon and Intel Macs in one app bundle
rustup target add aarch64-apple-darwin x86_64-apple-darwin
pnpm tauri build --target universal-apple-darwin

# Verify the result on a Mac
codesign --verify --deep --strict --verbose=2 "src-tauri/target/universal-apple-darwin/release/bundle/macos/YourApp.app"
spctl --assess --type execute --verbose "src-tauri/target/universal-apple-darwin/release/bundle/macos/YourApp.app"

Test on a different Mac than the one you built on, ideally one that has never run your app, and download the installer through a browser rather than copying it. Downloaded files carry a quarantine flag, and that is the situation your real users are in.

Entitlements: only ask for what you use

With the hardened runtime, capabilities such as microphone access, camera access, or running certain kinds of code need entitlements declared in a plist file, and usage description strings in your app's Info.plist so macOS can explain the permission prompt. If your app bundles other executables, such as FFmpeg as a sidecar, those executables must be signed with the same identity and hardened runtime too. A single unsigned helper binary is a very common reason for failed notarization.

Automatic updates

Tauri's updater plugin downloads new versions and verifies them with its own signature. This is separate from Apple's signing. You generate an updater key pair, embed the public key in the app configuration and use the private key when you build releases. Treat the private key like a production credential: back it up securely and limit who can use it. If you lose it, existing installs cannot trust updates signed with a new key, and you would need users to install a fresh build manually.

Common problems

  • 'App is damaged and can't be opened': usually unsigned, not notarized, or modified after signing.
  • Notarization rejected: read the log Apple returns. Typical causes are unsigned nested binaries, a missing hardened runtime flag or a missing secure timestamp.
  • Works on the build machine only: the build machine trusts your own certificate. Test elsewhere.
  • Updater rejects a new version: the version number must be higher, and the bundle must be signed with the matching private key.
  • Intel users cannot run the app: you built only for Apple silicon. Build a universal binary or ship separate builds.

A release checklist

  1. Bump the version in the Tauri configuration and package files.
  2. Build in a clean continuous integration job on a macOS runner with secrets from the secret store.
  3. Confirm signing and notarization succeeded and the ticket is stapled.
  4. Test install on a clean Mac from a downloaded file.
  5. Test the update path from the previous released version.
  6. Publish release notes and the download, then watch crash reports.

Build once, release often

The first signed release always takes the longest. Once the pipeline works, releases become routine. Our own macOS product, Cut Video Gaps, is a Tauri and Rust app, and we set up this kind of release pipeline for clients too. For help with a desktop product, see Tauri vs Electron for the framework decision or contact us.