npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tauri-plugin-hotswap-api

v0.0.4

Published

Open-source OTA frontend updates for Tauri v2 apps. Self-hosted, signed bundles, auto-rollback, runtime channels.

Downloads

634

Readme


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 | ✅ |

⚠️ App Store / Google Play note: OTA updates that swap frontend assets (HTML, CSS, JS) within a WebView are generally permitted, but policies can change. Review Apple's App Store Review Guidelines (3.3.2) and Google Play's Device and Network Abuse policy before shipping to ensure your use case complies with the latest rules.

How it works

flowchart TD
    A["Your CDN / S3 / any HTTPS host
    manifest.json
    signed frontend.tar.gz"] -- "download + verify signature" --> B
    B["Tauri App

    HotswapAssets::get(key)
    1. filesystem (cached)
    2. embedded (fallback)"]

🚀 Quickstart

1. Install

# src-tauri/Cargo.toml
[dependencies]
tauri-plugin-hotswap = "0.0.4"
npm install tauri-plugin-hotswap-api

2. Configure

Add to your tauri.conf.json:

{
  "plugins": {
    "hotswap": {
      "endpoint": "https://your-server.com/api/updates/{{current_sequence}}",
      "pubkey": "<YOUR_MINISIGN_PUBKEY>"
    }
  }
}

Config source matters:

  • init(context) reads plugins.hotswap from tauri.conf.json and requires it.
  • init_with_config(context, config) and HotswapBuilder are programmatic paths; plugins.hotswap in JSON is optional for these.

3. Register the plugin

// src-tauri/src/lib.rs
pub fn run() {
    let context = tauri::generate_context!();
    // init() consumes the context to swap the asset provider,
    // then returns the modified context alongside the plugin.
    let (hotswap, context) = tauri_plugin_hotswap::init(context)
        .expect("failed to initialize hotswap");

    tauri::Builder::default()
        .plugin(hotswap)
        .run(context)
        .expect("error running app");
}

Programmatic alternative (no plugins.hotswap required in tauri.conf.json):

let context = tauri::generate_context!();
let config = tauri_plugin_hotswap::HotswapConfig::new("<YOUR_MINISIGN_PUBKEY>")
    .endpoint("https://your-server.com/api/updates/{{current_sequence}}");
let (hotswap, context) = tauri_plugin_hotswap::init_with_config(context, config)
    .expect("failed to initialize hotswap");

4. Add capability

In src-tauri/capabilities/default.json:

{
  "identifier": "default",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "hotswap:default"
  ]
}

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)