What is this?
An open-source Tauri v2 plugin that pushes OTA frontend updates to users instantly — without rebuilding the native binary, without app store review, and without requiring a cloud service. Self-hosted, bring your own CDN.
It works by swapping Tauri's embedded asset provider at startup. The WebView keeps loading from tauri://localhost — the swap is invisible. Your keys, your server, your infrastructure. If anything goes wrong, the app rolls back to embedded assets on next launch.
Platform Support
| Platform | Supported |
|---|---|
| macOS | ✅ |
| Windows | ✅ |
| Linux | ✅ |
| Android | ✅ |
| iOS | 🔜 |
How it works
Your CDN / S3 / any HTTPS host
┌─────────────────────────────┐
│ manifest.json │
│ signed frontend.tar.gz │
└────────────┬────────────────┘
│
download + verify signature
│
┌────────────▼────────────────┐
│ Tauri App │
│ │
│ HotswapAssets::get(key) │
│ 1. filesystem (cached) │
│ 2. embedded (fallback) │
└─────────────────────────────┘
🚀 Quickstart
1. Install
# src-tauri/Cargo.toml
[]
= "0.0.1"
2. Configure
Add to your tauri.conf.json:
3. Register the plugin
// src-tauri/src/lib.rs
4. Add capability
In src-tauri/capabilities/default.json:
5. Use from the frontend
import { checkUpdate, applyUpdate, notifyReady } from 'tauri-plugin-hotswap-api';
// ✅ Confirm current version works (call on every startup)
await notifyReady();
// 🔍 Check for updates
const result = await checkUpdate();
if (result.available) {
// ⬇️ Download, verify, and activate
await applyUpdate();
// 🔄 Reload to serve new assets
window.location.reload();
}
That's it. A few lines to add OTA updates to your Tauri app.
You can also change configuration at runtime — for example, to switch channels without restarting:
import { configure } from 'tauri-plugin-hotswap-api';
// Switch to a beta channel at runtime
await configure({ channel: 'beta' });
✨ Features
| Feature | Description |
|---|---|
| 🔐 Signed bundles | Every download is verified with minisign before extraction |
| ↩️ Auto-rollback | If notifyReady() isn't called, the next launch rolls back automatically |
| 📡 Channels | Route users to production, staging, beta — switchable at runtime via configure() |
| 🔑 Custom headers | Auth tokens, API keys — sent on every check and download request |
| 🔄 Retry with backoff | Failed downloads retry automatically (1s → 2s → 4s → 8s) |
| 🔀 Download/activate split | Download now, apply later — you control the timing |
| 📊 Lifecycle events | hotswap://lifecycle events for telemetry (Sentry, PostHog, etc.) |
| 📏 Bundle size + mandatory flag | Warn users on mobile data, force security patches |
| 🌍 Platform-aware | Sends platform, arch, channel on every check request |
| 🛡️ Size limits | Configurable max bundle size prevents memory exhaustion |
| 🔒 HTTPS enforced | Non-HTTPS URLs rejected by default |
| ⚡ Atomic operations | Temp dir extraction + rename; temp file pointer writes |
| 🤖 Custom resolvers | HotswapResolver trait — bring your own update source |
| 📦 Zip support | Enable with features = ["zip"] |
📖 Documentation
| Document | Description |
|---|---|
| Design Philosophy | Opinionated defaults, extensible when you need it |
| Configuration | All config options, builder API, tauri.conf.json reference |
| API Reference | Full JS and Rust API with examples |
| Server Contract | What your update endpoint needs to return |
| Security | Threat model, mitigations, signing guide |
| Architecture | How the plugin works internally |
| Creating Bundles | Build, sign, upload your frontend bundles |
| CONTRIBUTING | How to contribute to this project |
| CHANGELOG | Version history |
🛡️ Security
Every update is cryptographically signed with minisign and verified before extraction. The plugin is designed to fail safely — if anything goes wrong, the app falls back to embedded assets.
See the full Security documentation for the threat model and all mitigations.
License
MIT OR Apache-2.0 (same as Tauri)