← ./articles

Tauri Static Updater JSON: Check signature Content, Not Paths

Tauri's static updater JSON is small, but small files can still fail in several quiet ways.

The most common mistake is putting a signature file path into the JSON. Tauri does not want a path to the .sig file. It wants the contents of the generated .sig file.

If the updater sees no release, rejects the manifest, or downloads nothing, check the JSON fields before changing updater code.

The symptom: updater sees no release or rejects the manifest

The application may behave like this:

  • the update check completes without a visible update
  • the update check throws a manifest or signature error
  • the mock server or CDN serves latest.json successfully
  • the installer file exists
  • the app still does not install the update

This does not automatically mean the updater plugin is broken. A static JSON manifest has strict fields, and a single wrong value is enough to stop the update.

Use the required keys for static updater JSON

A minimal static manifest needs a version and a platform entry:

{
  "version": "1.0.1",
  "notes": "Bug fixes and small improvements.",
  "pub_date": "2026-07-03T12:00:00Z",
  "platforms": {
    "windows-x86_64": {
      "signature": "SIGNATURE FILE CONTENT",
      "url": "https://releases.my-app.invalid/my-app_1.0.1_x64-setup.exe"
    }
  }
}

The optional fields are useful, but the updater must at least be able to find:

  • version
  • platforms.<target>.url
  • platforms.<target>.signature

The platform key must match the target that the updater checks. For a normal 64-bit Windows build, that is commonly windows-x86_64.

Paste .sig content, not a .sig path or URL

This is wrong:

{
  "signature": "my-app_1.0.1_x64-setup.exe.sig"
}

This is also wrong:

{
  "signature": "https://releases.my-app.invalid/my-app_1.0.1_x64-setup.exe.sig"
}

The updater expects the content inside the signature file:

{
  "signature": "untrusted comment: signature from minisign secret key\nRWQ..."
}

When you generate the manifest, read the .sig file and trim trailing whitespace:

const fs = require("node:fs");

const signature = fs
  .readFileSync("dist/my-app_1.0.1_x64-setup.exe.sig", "utf8")
  .trim();

Then write that string into the JSON.

Match the platform key to the generated artifact

The JSON platform key is not a label for humans. It is how the updater selects the right asset.

If your manifest contains only this:

{
  "platforms": {
    "linux-x86_64": {
      "signature": "SIGNATURE",
      "url": "https://releases.my-app.invalid/my-app.AppImage"
    }
  }
}

a Windows app will not use it.

For multi-platform releases, keep every listed platform complete. Tauri validates the manifest, so a broken platform entry can still make the file invalid even if the current machine would not install that platform's artifact.

Regenerate the manifest after every signed build

Do not treat .sig files as stable metadata.

A release process should look like this:

1. build the updater artifact
2. verify the .sig file exists
3. generate latest.json from the exact artifact and exact .sig file
4. upload the artifact and latest.json together
5. run a test update against the uploaded files

Avoid this order:

1. edit latest.json by hand
2. rebuild the installer
3. reuse the old signature string
4. upload everything

The manifest should be generated from build output, not copied from a previous release.

Check version rules before debugging downloads

If the manifest is valid but the app still reports no update, compare versions.

The static manifest version must represent a version the app should install. If the installed app is already 1.0.1 and the manifest also says 1.0.1, a normal updater check should not install it as a new update.

For a local test, install an older version first, then serve a higher version from the mock server:

installed app: 1.0.0
manifest version: 1.0.1

If you test 1.0.1 against 1.0.1, you are testing the no-update path.

A small manifest generator is safer than hand editing

Use a script that fails early:

const fs = require("node:fs");

const version = process.argv[2];
const url = process.argv[3];
const sigFile = process.argv[4];

if (!version || !url || !sigFile) {
  throw new Error("Usage: node make-updater-json.js <version> <url> <sig-file>");
}

if (!fs.existsSync(sigFile)) {
  throw new Error(`Signature file not found: ${sigFile}`);
}

const manifest = {
  version,
  notes: `Release ${version}`,
  pub_date: new Date().toISOString(),
  platforms: {
    "windows-x86_64": {
      signature: fs.readFileSync(sigFile, "utf8").trim(),
      url
    }
  }
};

fs.writeFileSync("latest.json", JSON.stringify(manifest, null, 2));

This prevents the two worst mistakes: missing signature files and stale hand-edited JSON.

References

Summary

When a Tauri static updater manifest fails, check the data before rewriting code. The signature must be file content, the platform key must match the target, the URL must point to the real artifact, and the manifest should be regenerated from the exact signed build.