<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: eddie_mate</title>
    <description>The latest articles on DEV Community by eddie_mate (@eddie_mate).</description>
    <link>https://dev.to/eddie_mate</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4056288%2F8693336f-ba1c-4685-abfb-f25a7c1b5978.jpg</url>
      <title>DEV Community: eddie_mate</title>
      <link>https://dev.to/eddie_mate</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eddie_mate"/>
    <language>en</language>
    <item>
      <title>Tauri v2 auto-updates without maintaining latest.json</title>
      <dc:creator>eddie_mate</dc:creator>
      <pubDate>Fri, 31 Jul 2026 08:50:23 +0000</pubDate>
      <link>https://dev.to/eddie_mate/tauri-v2-auto-updates-without-maintaining-latestjson-14de</link>
      <guid>https://dev.to/eddie_mate/tauri-v2-auto-updates-without-maintaining-latestjson-14de</guid>
      <description>&lt;p&gt;Tauri’s updater is deliberately simple: your app asks an endpoint whether a newer version exists, downloads the returned artifact, and verifies its signature.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;I’m building &lt;a href="https://releases.mateality.com" rel="noopener noreferrer"&gt;Mateality Releases&lt;/a&gt; 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.&lt;/p&gt;

&lt;p&gt;This walkthrough shows the complete path.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;p&gt;The release flow has four parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;GitHub Actions builds and signs the desktop app.&lt;/li&gt;
&lt;li&gt;The workflow uploads the artifact and its adjacent &lt;code&gt;.sig&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Mateality publishes that version to a release channel such as &lt;code&gt;stable&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The installed app checks a dynamic endpoint. It receives JSON when an update is available and HTTP &lt;code&gt;204&lt;/code&gt; when it is already current.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The private signing key never needs to be uploaded to Mateality. It remains in your CI secrets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a Tauri v2 app that already produces signed updater artifacts;&lt;/li&gt;
&lt;li&gt;a Mateality organization and app;&lt;/li&gt;
&lt;li&gt;a publish-only API key stored as the GitHub Actions secret &lt;code&gt;MATEALITY_RELEASES_KEY&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Hobby plan supports one app, so the setup can be tested without committing to a paid plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Configure the Tauri updater
&lt;/h2&gt;

&lt;p&gt;Enable updater artifacts and point the updater plugin at your app’s endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bundle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"createUpdaterArtifacts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"updater"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"pubkey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR_TAURI_PUBLIC_KEY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"endpoints"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"https://YOUR-PUBLIC-SLUG.mateality.app/releases/stable/{{target}}/{{arch}}/{{current_version}}"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the public key in the app configuration. Keep its corresponding private key in CI.&lt;/p&gt;

&lt;p&gt;Tauri replaces &lt;code&gt;{{target}}&lt;/code&gt;, &lt;code&gt;{{arch}}&lt;/code&gt;, and &lt;code&gt;{{current_version}}&lt;/code&gt; at runtime. That lets one endpoint resolve the correct artifact for macOS, Windows, or Linux.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Build and sign as usual
&lt;/h2&gt;

&lt;p&gt;Your existing Tauri workflow remains responsible for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;compiling the app;&lt;/li&gt;
&lt;li&gt;code signing and notarization where required;&lt;/li&gt;
&lt;li&gt;producing the updater artifact;&lt;/li&gt;
&lt;li&gt;producing the Tauri &lt;code&gt;.sig&lt;/code&gt; signature.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a macOS updater artifact may look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MyApp.app.tar.gz
MyApp.app.tar.gz.sig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the signature beside the artifact. The upload action detects that adjacent file automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Upload from GitHub Actions
&lt;/h2&gt;

&lt;p&gt;Add the upload step after the build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish desktop update&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mateality/releases-upload@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.MATEALITY_RELEASES_KEY }}&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-desktop-app&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.ref_name }}&lt;/span&gt;
    &lt;span class="na"&gt;artifact&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src-tauri/target/release/bundle/macos/MyApp.app.tar.gz&lt;/span&gt;
    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;darwin-aarch64&lt;/span&gt;
    &lt;span class="na"&gt;channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stable&lt;/span&gt;
    &lt;span class="na"&gt;notes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;See&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;changelog&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;details."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;artifact&lt;/code&gt; glob must resolve to exactly one file. For a multi-platform matrix, run one upload step per target:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;darwin-aarch64
windows-x86_64
linux-x86_64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  4. What the installed app receives
&lt;/h2&gt;

&lt;p&gt;When a newer compatible release exists, the endpoint returns the structure Tauri expects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.4.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"notes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"See the changelog for details."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pub_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-31T12:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"signature"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When there is no update, it returns HTTP &lt;code&gt;204 No Content&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common problems
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The artifact glob matches nothing—or several files
&lt;/h3&gt;

&lt;p&gt;Use a precise path for each matrix target. The action intentionally refuses an ambiguous glob.&lt;/p&gt;

&lt;h3&gt;
  
  
  The update downloads but verification fails
&lt;/h3&gt;

&lt;p&gt;Check that the &lt;code&gt;.sig&lt;/code&gt; file was generated for that exact artifact and uploaded beside it. Also confirm that the app contains the matching public key.&lt;/p&gt;

&lt;h3&gt;
  
  
  The endpoint always returns 204
&lt;/h3&gt;

&lt;p&gt;Confirm that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the release is published, not left as a draft;&lt;/li&gt;
&lt;li&gt;its channel matches the endpoint, such as &lt;code&gt;stable&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;the uploaded target matches the requesting platform;&lt;/li&gt;
&lt;li&gt;the published version is newer than the installed version.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why use a dynamic endpoint?
&lt;/h2&gt;

&lt;p&gt;A static &lt;code&gt;latest.json&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;That also leaves room for stable, beta, or internal channels without shipping different updater logic in the app.&lt;/p&gt;

&lt;p&gt;If you want to try this flow, start with the &lt;a href="https://releases.mateality.com/docs/tauri-v2" rel="noopener noreferrer"&gt;Tauri v2 guide&lt;/a&gt; or the &lt;a href="https://releases.mateality.com/docs/github-action" rel="noopener noreferrer"&gt;GitHub Action quickstart&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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?&lt;/p&gt;

</description>
      <category>rust</category>
      <category>tauri</category>
      <category>githubactions</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
