How to Add License Keys to a Tauri App
Tauri apps get a Rust core and a web frontend, which is exactly the right shape for licensing: keep the trust-sensitive work — signature verification, storage, state resolution — in the Rust core where it is hard to tamper with, and let the frontend just ask yes/no questions. The Keylight Tauri plugin is built that way. This guide adds it to a Tauri v2 app end to end: the Rust registration, the capability grant Tauri v2 requires, and the frontend calls that drive your UI.
Install the plugin: a Rust crate and a JS package
A Tauri plugin has two halves, and Keylight is no exception. The Rust crate runs in your src-tauri core; the npm package gives your frontend typed functions to call it. Install both:
# In src-tauri/ — the Rust core
cargo add keylight tauri-plugin-keylight
# In your project root — the frontend bindings
npm install tauri-plugin-keylight-api
You add the keylight crate alongside the plugin because you build the configuration object with it. The plugin (tauri-plugin-keylight) and core crate (keylight) are published on crates.io; the frontend bindings (tauri-plugin-keylight-api) are on npm. This plugin targets Tauri v2.11+ — it will not work on a v1 project.
Register the plugin in your Rust entry point
Configuration lives in Rust, in the run() function Tauri v2 scaffolds into src-tauri/src/lib.rs. Build a KeylightConfig and hand it to the plugin’s init:
// src-tauri/src/lib.rs
use keylight::KeylightConfig;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
let cfg = KeylightConfig::builder("your-tenant", "your-product", "sdk_live_…")
.max_offline_days(7) // bound how long a device can stay offline
.build();
tauri::Builder::default()
.plugin(tauri_plugin_keylight::init(cfg))
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
The builder takes three required values — your tenant ID, product ID, and SDK key (the sdk_live_… value from your dashboard) — and a set of optional tuning methods: .max_offline_days(...) for the offline grace window, .trial_duration_days(...) (default 14), .free_tier_enabled(...), .key_prefix(...) for a client-side key-format check, and .trusted_key(kid, pub_b64) to pin the Ed25519 verification keys. The base_url defaults to https://api.keylight.dev, so you do not set it unless you have a reason to.
That is the entire Rust-side setup. There is no server to stand up and no webhook to wire here — the plugin talks to Keylight directly.
Grant the plugin’s permissions in your capabilities file
Tauri v2 replaced v1’s allowlist with a capability system: a frontend command is unreachable until a capability explicitly permits it. The Keylight plugin ships a default permission set covering its three commands, so you add one entry to your capabilities file:
// src-tauri/capabilities/default.json
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"windows": ["main"],
"permissions": [
"core:default",
"keylight:default"
]
}
keylight:default enables activate, validate, and hasEntitlement in one line. If you want finer control you can grant the individual permissions instead — keylight:allow-activate, keylight:allow-validate, and keylight:allow-has-entitlement — but the default set is the right starting point. Skip this step and your frontend calls will fail with a permission error, which is the most common first-run snag.
Activate and gate features from your frontend
With the plugin registered and permitted, the frontend bindings are three functions, each returning a Promise<boolean>:
import { activate, validate, hasEntitlement } from "tauri-plugin-keylight-api";
// On your license-entry form submit:
const ok = await activate(userEnteredKey);
if (!ok) {
showError("That key could not be activated.");
}
// On app launch, re-validate the stored license online:
await validate();
// Gate a feature — resolves from the cached, signature-verified lease,
// so it works offline once the device has activated:
if (await hasEntitlement("pro")) {
enableProFeatures();
}
The deliberately small surface is the point. activate claims a device slot and verifies the returned lease in Rust before persisting it; validate refreshes that lease online; hasEntitlement answers from the cached, already-verified lease without a network call. Everything trust-sensitive — the Ed25519 check, where the lease is stored, how the license state is resolved — stays in the Rust core, out of reach of the DevTools console. The frontend only ever sees booleans.
That division is also why the plugin keeps working when the network drops. The Rust core resolves a full license state internally — Licensed, Trial { days_left }, Limited, FreeTier, Expired, or Invalid — and hasEntitlement reads straight from the verified lease. For the field-by-field anatomy of that signed lease, see what is inside a Keylight lease, and for the offline model overall, how offline license validation works.
One control plane across your stack
The reason to verify in Rust and gate in the frontend is the same reason Keylight exists: licensing should be infrastructure you configure, not a backend you build and babysit. The Tauri plugin reads from the same Keylight tenant as the Swift, Rust, and JavaScript SDKs, so if you also ship a native Mac build or an Electron app, they all share one set of customers, keys, and device limits. There is more on the framework-specific side in the licensing for Tauri apps and licensing for Rust apps guides.
That is a full Tauri v2 licensing integration: install the crate and the bindings, register the plugin with your tenant config in Rust, grant keylight:default, and call activate, validate, and hasEntitlement from your frontend. If your Tauri setup hits something this post does not cover, send us your feedback and we’ll extend it.
Frequently asked
Does the Keylight Tauri plugin work with Tauri v1 or v2?+
Tauri v2 (2.11 and up). It uses the v2 plugin system and capability-based permissions, so you grant it access through a capabilities file rather than an allowlist.
Where does license verification actually happen in a Tauri app?+
In the Rust core. The plugin verifies the Ed25519-signed lease, stores it, and resolves the license state natively. The frontend only calls three thin commands — activate, validate, and hasEntitlement — and gets back booleans.
Do I need a backend to license a Tauri app?+
No. The Keylight plugin talks to api.keylight.dev directly and verifies leases locally with the tenant's public keys. You configure your tenant and product IDs in Rust and ship — there is no server for you to run.
Ready to ship?
Create your account and start licensing your apps in under a minute. Free forever tier included.
Start Free