This document is the maintainer-facing runbook for cutting a Markpad release with auto-update enabled. Auto-update is wired through tauri-plugin-updater, which verifies signed update bundles using minisign.
On your local machine, in the Markpad checkout:
npm run tauri signer generate -- -w ~/.tauri/markpad-updater.keyYou'll be prompted for a password. Pick a strong one and store it together with the private key in your password manager. The command produces two files:
~/.tauri/markpad-updater.key— PRIVATE. Never commit. Never share. Back up to a password manager.~/.tauri/markpad-updater.key.pub— PUBLIC. Shared with developers; ends up shipped inside Markpad.
In the GitHub repo settings → Secrets and variables → Actions → New repository secret:
| Name | Value |
|---|---|
TAURI_SIGNING_PRIVATE_KEY |
full content of ~/.tauri/markpad-updater.key |
TAURI_SIGNING_PRIVATE_KEY_PASSWORD |
the password you set in step 1 |
The build workflow reads both at signing time on macOS, Windows, and Linux runners.
Send the single-line content of ~/.tauri/markpad-updater.key.pub (no comments, no header lines) to the developer who'll commit it to src-tauri/tauri.conf.json under plugins.updater.pubkey. Until that placeholder is replaced, auto-update is inert — the app surfaces a clean error state instead of contacting the update server.
Once a release ships with the pubkey embedded, it cannot be rotated without breaking auto-update for every existing user. Rotation means everyone re-installs Markpad manually. Treat the keypair as a long-lived release secret.
If you ever lose the private key:
- Existing users can still use Markpad, but they will not auto-update again.
- A new keypair has to be generated, embedded in a new release, and that release has to be installed manually by every user.
- Communicate this in release notes so users aren't blindsided.
- Bump version in both files (mandatory — Tauri reads runtime version from
Cargo.toml):package.jsonversionsrc-tauri/Cargo.toml[package].version
- Commit, tag, push:
git commit -am "chore: bump version to X.Y.Z" git tag vX.Y.Z git push origin master vX.Y.Z - Trigger the workflow:
- GitHub UI: Actions → "Build and Release" → Run workflow → master
- Or CLI:
gh workflow run build.yml --ref master
- Wait ~30 min for matrix builds to finish, plus ~2 min for
generate-update-feed. - Open the draft release on the Releases page. Verify the assets:
- macOS:
*.dmg,*.app.tar.gz,*.app.tar.gz.sig - Windows x64:
*_x64.exe(portable),*_x64-setup.exe(NSIS installer),*_x64-setup.exe.sig - Windows ARM64:
*_arm64.exe(portable),*_arm64-setup.exe(NSIS installer),*_arm64-setup.exe.sig - Linux:
*.deb,*.rpm,*.AppImage,*.AppImage.sig - Update feed:
latest.json(one entry per successfully built platform)
- macOS:
- Click "Publish release" — this is the gate that activates auto-update for all clients pointing at
releases/latest/download/latest.json.
The first release after auto-update is enabled does not auto-update existing users — older Markpad builds don't have the updater wiring yet. They must download and install this version manually once. From then on, every subsequent release reaches users automatically.
Mention this clearly in the release notes for the first auto-update-capable version, e.g.:
This release activates in-app auto-updates. Install it manually one last time — future releases will update Markpad on their own.
- macOS uses one universal binary (
darwin-aarch64+darwin-x86_64share the same.app.tar.gzand signature). - Windows uses NSIS — the auto-updater downloads
*-setup.exe(verified by*-setup.exe.sig) and runs it inpassiveinstall mode. The existing raw portable.exedistribution path is preserved alongside, so users who download the portable.exedirectly continue to work; only the auto-updater path uses the NSIS installer. - Linux: only
AppImageusers get auto-updates —tauri-plugin-updaterdoesn't support.debor.rpm.apt/rpmusers keep getting updates via their distro package manager (or by downloading a fresh package). - Snap / Chocolatey: independent distribution channels. Their update mechanisms are unaffected.
| Symptom | Likely cause / fix |
|---|---|
Build fails: "missing TAURI_SIGNING_PRIVATE_KEY" |
Step 2 of one-time setup wasn't done, or Secret name doesn't match. |
generate-update-feed succeeds but latest.json lacks a platform |
That platform's matrix build failed silently (or the .sig file wasn't produced). Check the failed build's logs. |
latest.json missing entirely |
The generate-update-feed job didn't run — usually because no *.sig files were uploaded. Check the Upload * Artifacts steps. |
| Users don't see the update | (1) Did you click Publish release? Drafts aren't visible to clients. (2) Is the user on a version older than the first auto-update-capable release? They need a one-time manual reinstall. |
| Update download succeeds but install fails with signature error | Pubkey mismatch — the Secrets and tauri.conf.json pubkey belong to different keypairs. |
- Apple Developer ID code-signing & notarization —
.appbundles are unsigned. macOS may show a Gatekeeper warning on first launch. Minisign verification by the updater is independent of Apple code-signing. - Windows Authenticode signing — neither the portable
.exenor the*-setup.exeNSIS installer is signed with a code-signing certificate. Users may see a SmartScreen warning. Minisign verification by the updater is independent. - Retroactive signing of older releases.