Installation and setup of the Tauri SDK
Install
Add the crate to your Rust app, in src-tauri:
cargo add tauri-plugin-licensemanagerAnd the JavaScript bindings to your frontend:
npm
npm install @licenseman/tauri@tauri-apps/api 2 is a peer dependency, already in every Tauri app. Keep both packages on the same version: they’re released together.
Register the plugin
Copy your product’s id and public key
Open Security in the sidebar: copy the public key, and the id of the product, like x7Kp2QaZ.
Add the plugin to your app
const PRODUCT_ID: &str = "x7Kp2QaZ";
const PUBLIC_KEY: &str = "-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA…
-----END PUBLIC KEY-----";
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_licensemanager::Builder::new(PRODUCT_ID, PUBLIC_KEY).build())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}The plugin creates one LicenseManager for your app, and disposes it when the app exits. Reach it from Rust with app.license().
Allow it in your capability
Your windows can only call the plugin when a capability allows it:
{
"identifier": "default",
"windows": ["main"],
"permissions": ["core:default", "licensemanager:default"]
}licensemanager:default allows everything an activation screen and locked screens need, but not the full key or the fingerprint. See frontend and permissions.
Hard-code the public key and the product id in your source code. Never load them from a file next to your app, or fetch them at runtime: whoever replaces the public key can sign their own tokens, and your app would trust them. That’s why the plugin has no tauri.conf.json configuration. See the public key.
What happens on setup
The plugin’s setup runs while Tauri starts your app, and returns right away. Then, in the background:
- It reads the stored key. No key: the state becomes
MISSING_KEYduring setup, andready()resolves with it. - With a key and a valid stored token, the state becomes
ACTIVE, andready()resolves with it right away. The online validation runs in the background: when it refuses the license, the state changes, and an event reaches your windows. - With a key but no valid stored token, it validates the key online, retrying for up to 30 seconds, and
ready()resolves once it’s done, withACTIVEor the reason the app is locked. - It starts the clock check, now and every hour.
The setup fails, and your app with it, only when the product id is empty, the public key isn’t a valid Ed25519 public key, or the app runs on iOS or Android. You’ll see it the first time you run the app. Everything else, a missing keychain, no network, an unreadable machine id, is handled without failing.
Your windows load after the setup, so they may miss the first changes. Render them with watchLicense, which reads the current license and then follows every change, instead of only listening to changes.
Options
Most of the time, you only need the product id and the public key. The other options have defaults that fit most apps.
use std::time::Duration;
use tauri_plugin_licensemanager::{Builder, MachineFingerprint};
Builder::new(PRODUCT_ID, PUBLIC_KEY) // both required
.version("2.3.1") // default: the app's version
.machine_name("Jane's MacBook") // default: the hostname
.platform("macOS") // default: macOS, Windows or Linux
.fingerprint(MachineFingerprint::from_sync_fn(read_my_id)) // default: the system's machine id
.min_offline_duration(Duration::from_secs(3 * 86_400)) // default: zero
.build()| Option | Default | What it does |
|---|---|---|
product_id | Required | The id of your product. Checked in every token’s aud, and used to keep each product’s stored data apart. Shown on the Security page. |
public_key | Required | Your organization’s Ed25519 public key, as the PEM shown on the Security page. Verifies every token. |
version | The app’s version | The version of your app, sent with every validation and checked offline against the token’s mv. |
machine_name | The hostname | The name of the machine, shown to you and in the customer portal. |
platform | macOS, Windows or Linux | The machine’s platform. |
fingerprint | The system’s machine id | The machine part of the fingerprint: a string, or a function. See machine fingerprint. |
min_offline_duration | Zero | How long, at least, your app must keep working offline after a renewal. Makes renewals happen earlier. See renewal. |
api_base_url | https://api.licensemanager.dev | For tests only: another API origin. Trailing slashes are removed. |
version
By default, it’s your app’s version from tauri.conf.json, or from src-tauri/Cargo.toml when tauri.conf.json has none: the same in development and in release builds.
machine_name and platform
Both are sent with every validation, and replace what LicenseManager knows about the machine. The hostname often holds the customer’s name, like Janes-MacBook-Pro.local, which is what makes machines easy to recognize in the customer portal. Pass your own value if you’d rather not send it.
One plugin per app
The plugin manages one product. An app selling several products, like an app and a paid plugin with their own licenses, can’t register the plugin twice: write to us if you need it.
Development and release builds
Point your development builds at a test product, and your release builds at the live one. Both share the same public key, which is one per organization:
const PRODUCT_ID: &str = if cfg!(debug_assertions) { "t3Hw9LmB" } else { "x7Kp2QaZ" };A test key validated against the live product answers not_found, and the opposite too: the SDK shows INVALID_KEY.
Since both products share the public key, the product id is what stops a test key from unlocking your release build: the SDK checks it in every token. Make sure release builds can’t end up with the test product’s id, like from an environment variable a customer could set.
Packaging
There’s nothing to do. The keychain access is compiled into the plugin, for each system, with no native module to unpack and no system package to install: on Linux, D-Bus is built in.
To check it works, activate a key in your release build, then look for items of the service dev.licensemanager.sdk in the Keychain Access app on macOS, or the Credential Manager on Windows.
On macOS, each development build is signed differently, so the Keychain may ask for permission when a new build reads an entry written by a previous one. Allow it, or use a test product. Release builds signed with the same certificate don’t ask.
Network access
The SDK makes two kinds of requests, from Rust, not from your webview:
| Request | When | Timeout |
|---|---|---|
POST https://api.licensemanager.dev/v1/products/{productId}/validate-key | Each validation | 10 seconds |
HEAD https://google.com, to read the time from its Date header | On launch, then every hour, for the clock check | 5 seconds |
They don’t go through your webview’s Content Security Policy, or need the http plugin. On a network that blocks them, the SDK handles it like being offline.