Tauri v2 auto-updates without maintaining latest.json

rust dev.to

Tauri’s updater is deliberately simple: your app asks an endpoint whether a newer version exists, downloads the returned artifact, and verifies its signature.

The awkward part begins after the build. Someone still has to collect each platform’s artifact and signature, publish them, keep release metadata current, and return the right response for the requesting target and architecture.

I’m building Mateality Releases to handle that delivery layer. Signing and notarization stay in your CI; the service stores the already-signed output and serves Tauri’s updater response.

This walkthrough shows the complete path.

The architecture

The release flow has four parts:

  1. GitHub Actions builds and signs the desktop app.
  2. The workflow uploads the artifact and its adjacent .sig file.
  3. Mateality publishes that version to a release channel such as stable.
  4. The installed app checks a dynamic endpoint. It receives JSON when an update is available and HTTP 204 when it is already current.

The private signing key never needs to be uploaded to Mateality. It remains in your CI secrets.

Prerequisites

You need:

  • a Tauri v2 app that already produces signed updater artifacts;
  • a Mateality organization and app;
  • a publish-only API key stored as the GitHub Actions secret MATEALITY_RELEASES_KEY.

The Hobby plan supports one app, so the setup can be tested without committing to a paid plan.

1. Configure the Tauri updater

Enable updater artifacts and point the updater plugin at your app’s endpoint:

{"bundle":{"createUpdaterArtifacts":true},"plugins":{"updater":{"pubkey":"YOUR_TAURI_PUBLIC_KEY","endpoints":["https://YOUR-PUBLIC-SLUG.mateality.app/releases/stable/{{target}}/{{arch}}/{{current_version}}"]}}}
Enter fullscreen mode Exit fullscreen mode

Keep the public key in the app configuration. Keep its corresponding private key in CI.

Tauri replaces {{target}}, {{arch}}, and {{current_version}} at runtime. That lets one endpoint resolve the correct artifact for macOS, Windows, or Linux.

2. Build and sign as usual

Your existing Tauri workflow remains responsible for:

  • compiling the app;
  • code signing and notarization where required;
  • producing the updater artifact;
  • producing the Tauri .sig signature.

For example, a macOS updater artifact may look like:

MyApp.app.tar.gz
MyApp.app.tar.gz.sig
Enter fullscreen mode Exit fullscreen mode

Keep the signature beside the artifact. The upload action detects that adjacent file automatically.

3. Upload from GitHub Actions

Add the upload step after the build:

- name: Publish desktop update
  uses: mateality/releases-upload@v1
  with:
    api-key: ${{ secrets.MATEALITY_RELEASES_KEY }}
    app: my-desktop-app
    version: ${{ github.ref_name }}
    artifact: src-tauri/target/release/bundle/macos/MyApp.app.tar.gz
    target: darwin-aarch64
    channel: stable
    notes: "Seethechangelogfordetails."
Enter fullscreen mode Exit fullscreen mode

The artifact glob must resolve to exactly one file. For a multi-platform matrix, run one upload step per target:

darwin-aarch64
windows-x86_64
linux-x86_64
Enter fullscreen mode Exit fullscreen mode

The action uploads the artifact directly, computes its SHA-256 digest, creates a draft release, and publishes it to the selected channel.

4. What the installed app receives

When a newer compatible release exists, the endpoint returns the structure Tauri expects:

{"version":"1.4.0","notes":"See the changelog for details.","pub_date":"2026-07-31T12:00:00Z","url":"https://...","signature":"..."}
Enter fullscreen mode Exit fullscreen mode

When there is no update, it returns HTTP 204 No Content.

The installed app still verifies the signature with the public key embedded in its configuration. The hosting layer cannot turn an unsigned artifact into a trusted update.

Common problems

The artifact glob matches nothing—or several files

Use a precise path for each matrix target. The action intentionally refuses an ambiguous glob.

The update downloads but verification fails

Check that the .sig file was generated for that exact artifact and uploaded beside it. Also confirm that the app contains the matching public key.

The endpoint always returns 204

Confirm that:

  • the release is published, not left as a draft;
  • its channel matches the endpoint, such as stable;
  • the uploaded target matches the requesting platform;
  • the published version is newer than the installed version.

Why use a dynamic endpoint?

A static latest.json file is workable for a small release process, but it becomes another deployment artifact that must stay synchronized with every binary and signature. A dynamic endpoint resolves releases from the channel, target, architecture, and installed version at request time.

That also leaves room for stable, beta, or internal channels without shipping different updater logic in the app.

If you want to try this flow, start with the Tauri v2 guide or the GitHub Action quickstart.

I’d especially value feedback from Tauri maintainers already shipping on more than one operating system: which part of the release pipeline still causes the most friction for you?

Source: dev.to

arrow_back Back to Tutorials