Electron, Tauri, native C++ — the pattern is the same. The key activates once per machine. Sharing the key does not share the software. No dongles. No cloud dependency for offline users.
Call the validation server from Electron's main process (Node.js side). Never expose your API key to the renderer process or bundle it unprotected in the ASAR archive.
const { app, BrowserWindow, dialog } = require('electron'); const archergate = require('archergate-node'); app.whenReady().then(async () => { const key = await getLicenseKeyFromSecureStorage(); const result = await archergate.validate({ apiKey: process.env.AG_API_KEY, appId: 'com.you.appname', licenseKey: key, }); if (!result.valid) { dialog.showErrorBox('License Required', result.error); app.quit(); return; } createMainWindow(); });
// .env (loaded at build time, not bundled into ASAR) AG_API_KEY=your-server-key // electron-builder config: include .env in extraResources, not asarUnpack
Archergate's Tauri plugin exposes four commands to the frontend: validate_license, activate_license, deactivate_license, and get_license_status. The Rust side handles all server communication and local caching.
[dependencies] tauri-plugin-archergate = "0.1"
fn main() { tauri::Builder::default() .plugin(tauri_plugin_archergate::init()) .run(tauri::generate_context!()) .expect("error running tauri app"); }
import { invoke } from '@tauri-apps/api/core'; export async function checkLicense(key: string) { const result = await invoke<{ valid: boolean; error?: string }>( 'validate_license', { licenseKey: key, appId: 'com.you.appname' } ); if (!result.valid) { throw new Error(result.error ?? 'License invalid'); } }
Link the static library and call three functions. Works on any C-linkable codebase: Win32 apps, Qt, wxWidgets, SDL2 games, command-line tools.
-L/path/to/sdk -larchergate_license -lpthread -ldl
#include "archergate_license.hpp" // C++ RAII wrapper int main() { archergate::License lic("your-api-key", "com.you.appname"); if (!lic.validate(getLicenseKeyFromUser())) { showLicenseDialog(); return 1; } runApp(); return 0; }
Add the crate, call one method. Works in any Rust desktop framework: egui, iced, Dioxus, or straight winit.
[dependencies]
archergate-license = "0.1"
use archergate_license::LicenseClient; fn main() { let client = LicenseClient::new( "your-api-key", "com.you.appname", ); if let Err(e) = client.validate(&get_license_key()) { eprintln!("License check failed: {e}"); show_license_dialog(); std::process::exit(1); } run_app(); }
When a user enters their license key for the first time, the SDK sends the key plus a hardware fingerprint to your validation server. The fingerprint is SHA-256(CPU brand + OS installation ID). The server stores the fingerprint alongside the key. On every subsequent launch, the SDK recomputes the fingerprint and the server verifies it matches.
Moving the app to a different computer generates a different fingerprint. The server rejects it. The user needs to deactivate on the old machine (or contact you for a manual reset) before activating on the new one. Seat count enforcement is handled entirely server-side.
After one successful server check, the SDK writes a signed local receipt. HMAC-SHA256, keyed with a secret that never leaves your server. The receipt is valid for 30 days. Offline-first users, corporate environments with outbound restrictions, and users on planes all get 30 days without any special configuration.
npm package wrapping the native addon. Main-process only. API key never in renderer.
First-party Tauri v2 plugin. Rust commands, TypeScript bindings. Zero boilerplate.
Static .lib/.a, shared .dll/.dylib/.so, C header, C++ RAII wrapper. Pick your ABI.
cargo add archergate-license. Works in egui, iced, Dioxus, winit, anything.