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
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
| Step | What it does | Why it matters |
|---|---|---|
| Code signing | Attaches your identity to the app and verifies it has not been altered | Without it, macOS cannot tell who made the app |
| Hardened runtime | Enables extra protections; apps must opt in to risky capabilities with entitlements | Required for notarization |
| Notarization | You upload the app to Apple, which scans it for malicious content and returns a ticket | Gatekeeper trusts notarized apps and opens them without scary warnings |
| Stapling | Attaches the notarization ticket to the app or disk image | Lets 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
- Bump the version in the Tauri configuration and package files.
- Build in a clean continuous integration job on a macOS runner with secrets from the secret store.
- Confirm signing and notarization succeeded and the ticket is stapled.
- Test install on a clean Mac from a downloaded file.
- Test the update path from the previous released version.
- 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.