Skip to content
365 changes: 360 additions & 5 deletions src/content/docs/features/updater.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,367 @@ title: Updater
description: In-app updates for Tauri applications.
---

import Stub from '@components/Stub.astro';
import PluginLinks from '@components/PluginLinks.astro';
import { Aside, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
import CommandTabs from '@components/CommandTabs.astro';

<PluginLinks plugin="updater" />

<Stub>
Based on
https://github.com/tauri-apps/plugins-workspace/tree/v2/plugins/updater
</Stub>
The Updater plugin offers an update mechanism for Tauri applications distributed via:

- NSIS (Windows)
- MSI (Windows)
- AppImage (Linux)
- App bundle (macOS)

Once your Tauri project is ready, you can configure Tauri's updater to enable auto updating for your users.

<Aside type="note" title="Choose only one of the Windows bundles">
Although we provide both `NSIS` and `MSI` for Windows, you should only choose one for package installation and update. If users have installed the package via `NSIS`, they can't update it using the `MSI`.
</Aside>

## Supported Platforms

- Windows
- Linux
- macOS

## Setup

_This plugin requires a Rust version of at least **1.75**_

Install the updater plugin to get started.

<Tabs>
<TabItem label="Automatic" >
Use your project's package manager to add the dependency:

{ ' ' }

<CommandTabs
npm="npm run tauri add updater"
yarn="yarn run tauri add updater"
pnpm="pnpm tauri add updater"
cargo="cargo tauri add updater"
/>
</TabItem>
<TabItem label = "Manual">
<Steps>
1. Install the updater plugin by adding the following to your `Cargo.toml` file:

```toml title="src-tauri/Cargo.toml"
# you can add the dependencies on the `[dependencies]` section if you do not target mobile
[target."cfg(not(any(target_os = \"android\", target_os = \"ios\")))".dependencies]
tauri-plugin-updater = "2.0.0-beta"
# alternatively with Git:
tauri-plugin-updater = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
```

2. Modify `lib.rs` to initialize the plugin:

```rust title="src-tauri/src/lib.rs" ins={3-7}
fn run() {
tauri::Builder::default()
.setup(|app| {
#[cfg(desktop)]
app.handle().plugin(tauri_plugin_updater::Builder::new().build())?;
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
```

3. Install the JavaScript Guest bindings using your preferred JavaScript package manager:

<CommandTabs
npm = "npm install @tauri-apps/plugin-updater"
yarn = "yarn add @tauri-apps/plugin-updater"
pnpm = "pnpm add @tauri-apps/plugin-updater"
/>
</Steps>
</TabItem>
</Tabs>

## Signing Updates

Tauri's cli has a built-in signature mechanism to ensure that updates are safe to be installed.

To sign your updates you need two things:

1. The _public key_, which will be added to your `tauri.conf.json` file later, to validate update artifacts before the installation.
2. The _private key_, which is used to sign your update artifacts and should NEVER be shared with anyone. Also, if you lose this key, you will NOT be able to publish new updates to your current user base. It is crucial to store it in a safe place where you can always access it.

To generate the keys on Linux and macOS you can use the Tauri CLI:

<CommandTabs
npm="npm run tauri signer generate -w ~/.tauri/myapp.key"
yarn="yarn run tauri signer generate -w ~/.tauri/myapp.key"
pnpm="pnpm tauri signer generate -w ~/.tauri/myapp.key"
cargo="cargo tauri signer generate -w ~/.tauri/myapp.key"
/>

If you are on Windows, you should use `$HOME/.tauri/myapp.key` or a different path of your choice instead:

<CommandTabs
npm="npm run tauri signer generate -w $HOME/.tauri/myapp.key"
yarn="yarn run tauri signer generate -w $HOME/.tauri/myapp.key"
pnpm="pnpm tauri signer generate -w $HOME/.tauri/myapp.key"
cargo="cargo tauri signer generate -w $HOME/.tauri/myapp.key"
/>

## Tauri Configuration

Now you need to configure Tauri's updater. To do this, add this to your Tauri config:

```json title="tauri.conf.json"
{
"plugins": {
"updater": {
"endpoints": [
"https://releases.myapp.com/{{target}}/{{arch}}/{{current_version}}"
],
"pubkey": "YOUR_UPDATER_SIGNATURE_PUBKEY_HERE",
"windows": {
"installMode": "passive",
"installerArgs": []
}
}
}
}
```

The required keys are `"endpoints"` and `"pubkey"` to enable the updater. The `"endpoints"` field can also be set later in Rust.

`"endpoints"`<a id="config-enpoints"/>: must be an array of updater endpoint URLs as strings. TLS is enforced in production mode.<br/>
All endpoints should generally return the same content, Tauri will fall back to the next endpoint URL if it couldn't connect to the first one or if it returned a non-2XX status code.<br/>
Each updater URL can contain the following variables allowing you to determine [server-side](#static-json-file) if an update is available:

- `{{current_version}}`: The version of the app that is requesting the update.
- `{{target}}`: The operating system name (one of `linux`, `windows` or `darwin`).
- `{{arch}}`: The architecture of the machine (one of `x86_64`, `i686`, `aarch64` or `armv7`).

`"pubkey"`: must be a valid public key generated with Tauri's CLI [above](#signing-updates).

`"windows"`: (optional) The [Windows configuration](#windows-configuration) for the updater.

### Windows Configuration

`"installMode"`: The installation mode for the update on Windows. Defaults to `passive`. Can be any **ONE** of the following types:

- `"passive"`: There will be a small window with a progress bar. The update will be installed without requiring any user interaction. Generally recommended and the default mode.
- `"basicUi"`: There will be a basic user interface shown which requires user interaction to finish the installation.
- `"quiet"`: There will be no progress feedback to the user. With this mode the installer cannot request admin privileges by itself so it only works in user-wide installations or when your app itself already runs with admin privileges. Generally not recommended.

`"installerArgs"`: Array of command string. Additional arguments given to the NSIS or WiX installer.

## Update Artifacts

Tauri's bundler will automatically generate and sign update artifacts once the updater is correctly configured and enabled.

Before building your app, you need to set environment variables for the private key and password:

- `TAURI_SIGNING_PRIVATE_KEY`: Path or content of your private key
- `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`: Your private key password (optional)

If you want to set these variables for the current console session you could execute these commands in the console which you will use to build the app later:

<Tabs>
<TabItem label="Bash">
```shell
export TAURI_SIGNING_PRIVATE_KEY="content of the generated key"
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="password"
```
</TabItem>
<TabItem label="PowerShell">
```powershell
$env:TAURI_SIGNING_PRIVATE_KEY="content of the generated key"
$env:TAURI_SIGNING_PRIVATE_KEY_PASSWORD="password"
```
</TabItem>
</Tabs>

After that, you can run `tauri build` as usual and Tauri will generate the update bundle and its signature.

- **Linux**: On Linux, Tauri will create a `.tar.gz` archive from the AppImage inside the `target/release/bundle/appimage/` folder:

- `myapp.AppImage` - the standard app bundle.
- `myapp.AppImage.tar.gz` - the updater bundle.
- `myapp.AppImage.tar.gz.sig` - the signature of the update bundle.

- **macOS**: On macOS, Tauri will create a `.tar.gz` archive from the application bundle inside the `target/release/bundle/macos/` folder:

- `myapp.app` - the standard app bundle.
- `myapp.app.tar.gz` - the updater bundle.
- `myapp.app.tar.gz.sig` - the signature of the update bundle.

- **Windows**: On Windows, Tauri will create `.zip` archives from the MSI and NSIS installers inside the `target/release/bundle/msi/` and `target/release/bundle/nsis` folders:
- `myapp-setup.exe` - the standard app bundle.
- `myapp-setup.nsis.zip` - the updater bundle.
- `myapp-setup.nsis.zip.sig` - the signature of the update bundle.
- `myapp.msi` - the standard app bundle.
- `myapp.msi.zip` - the updater bundle.
- `myapp.msi.zip.sig` - the signature of the update bundle.

The signature can be uploaded and shared safely as long as your private key is secure.

## Server Support

Tauri's updater supports two ways of announcing update data:

- A static JSON file (to use on services like S3 or GitHub gists)
- A dynamic update server

The static JSON file is easier to use while a dynamic update server will give you finer control over the update mechanism.

### Static JSON File

With this approach, Tauri will always request the same JSON file and determine if the app needs to be updated by comparing the version field of the response with the requesting app's current version. Tauri will expect a response in this format:

```json
{
"version": "v1.0.0",
"notes": "Test version",
"pub_date": "2020-06-22T19:25:57Z",
"platforms": {
"darwin-x86_64": {
"signature": "Content of app.tar.gz.sig",
"url": "https://github.com/username/reponame/releases/download/v1.0.0/app-x86_64.app.tar.gz"
},
"darwin-aarch64": {
"signature": "Content of app.tar.gz.sig",
"url": "https://github.com/username/reponame/releases/download/v1.0.0/app-aarch64.app.tar.gz"
},
"linux-x86_64": {
"signature": "Content of app.AppImage.tar.gz.sig",
"url": "https://github.com/username/reponame/releases/download/v1.0.0/app-amd64.AppImage.tar.gz"
},
"windows-x86_64": {
"signature": "Content of app-setup.nsis.sig or app.msi.sig, depending on the chosen format",
"url": "https://github.com/username/reponame/releases/download/v1.0.0/app-x64-setup.nsis.zip"
}
}
}
```

The required keys are `"version"`, `"platforms.[target].url"` and `"platforms.[target].signature"`; the others are optional.

- `"version"` must be a valid semver, with or without a leading `v`, meaning that both `1.0.0` and `v1.0.0` are valid.
- `"platforms"`: Each platform key is in the `OS-ARCH` format, where `OS` is one of `linux`, `darwin` or `windows`, and `ARCH` is one of `x86_64`, `aarch64`, `i686` or `armv7`.
- `"url"` must be a valid url to the update bundle.
- `"signature"` must be the **content** of the generated `.sig` file. The signature may change each time you run `tauri build` so make sure to always update it.
- `"notes"`: Here you can add notes about the update, like release notes.
- `"pub_date"` must be formatted according to [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.8) if present.

Note that Tauri will validate the _whole_ file before checking the version field, so make sure all existing platform configurations are valid and complete.

### Dynamic Update Server

With this approach, Tauri will follow the update server's instructions. To disable the internal version check you can [overwrite Tauri's version comparison](https://docs.rs/tauri-plugin-updater/latest/tauri_plugin_updater/struct.UpdaterBuilder.html#method.version_comparator) to always install the version sent by the server. This could be useful if you need to roll back your app version quickly.

Your server can use variables defined in the [`endpoint`](#config-enpoints) url above to determine if an update is required. If you need more data, you can include additional [request headers in Rust](https://docs.rs/tauri-plugin-updater/latest/tauri_plugin_updater/struct.UpdaterBuilder.html#method.header) to your liking.

Your server should respond with a status code of [`204 No Content`](http://tools.ietf.org/html/rfc2616#section-10.2.5) if there is no update available.

If an update is required, your server should respond with a status code of [`200 OK`](http://tools.ietf.org/html/rfc2616#section-10.2.1) and a JSON response in this format:

```json
{
"version": "0.2.0",
"pub_date": "2020-09-18T12:29:53+01:00",
"url": "https://mycompany.example.com/myapp/releases/myrelease.tar.gz",
"signature": "Content of the relevant .sig file",
"notes": "These are some release notes"
}
```

The required keys are "url", "version" and "signature"; the others are optional.

- `"version"` must be a valid semver, with or without a leading `v`, meaning that both `1.0.0` and `v1.0.0` are valid.
- `"url"` must be a valid url to the update bundle.
- `"signature"` must be the **content** of the generated `.sig` file. The signature may change each time you run `tauri build` so make sure to always update it.
- `"notes"`: Here you can add notes about the update, like release notes.
- `"pub_date"` must be formatted according to [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.8) if present.

## Usage

<Tabs>
<TabItem label="JavaScript" >
```js
import { check } from "@tauri-apps/plugin-updater";

const update = await check();
if (update?.available) {
await update.downloadAndInstall();
}
```
</TabItem>
<TabItem label = "Rust" >
```rust title="src-tauri/src/lib.rs"
use tauri_plugin_updater::UpdaterExt;

fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_updater::Builder::new().build())
.setup(|app| {
let app_handle = app.handle().clone();
tauri::async_runtime::spawn(async move {
let builder = app_handle.updater_builder();
let updater = builder.build().unwrap();

match updater.check().await {
Ok(Some(update)) => {
if let Err(e) = update
.download_and_install(
|| {},
|| {
println!("Download complete.");
},
)
.await
{
println!("Failed to download and install the update: {e}");
} else {
println!("Update complete.");
}
}
Ok(None) => {
println!("No updates available.");
}
Err(e) => {
println!("Failed to check for updates: {e}");
}
}
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
```
</TabItem>
</Tabs>

## Permissions

By default, all plugin commands are blocked and cannot be accessed. You must define a list of permissions in your `capabilities` configuration.

See [Access Control List](/references/v2/acl) for more information.

```json title="src-tauri/capabilities/main.json" ins={6}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": ["updater:default"]
}
```

| Permission | Description |
|--------------------------------------|----------------------------------------------------------------------------|
| `updater:default` | Allows checking for new updates and installing them |
| `updater:allow-check` | Enables the check command without any pre-configured scope. |
| `updater:deny-check` | Denies the check command without any pre-configured scope. |
| `updater:allow-download-and-install` | Enables the download_and_install command without any pre-configured scope. |
| `updater:deny-download-and-install` | Denies the download_and_install command without any pre-configured scope. |