Licensing for desktop apps that keeps working offline

LicenseKit issues signed leases that your Electron app verifies without a network call. Each lease is bound to one device, seats are counted on the server, and customers can move to a new machine themselves. It is a license server, an SDK and an admin console.

No sign-up. The public console resets every hour, and trial keys last 7 days.

Try it

In your browser

This uses the real license server with a sandbox key: 2 devices, valid for 2 days, check-in every hour, 1 day offline. The devices are simulated in this page, and the page checks each lease's signature itself.

How the sandbox handles your data

In a desktop app

A small Electron app that uses the SDK. The trial key works for 7 days on 2 devices. No sign-up.

  1. Download the app.

    Loading downloads…

    The app is not code-signed. How to open it

    macOS: the first time, macOS says it cannot check the app for malicious software. Choose Done, open System Settings, go to Privacy & Security, and choose Open Anyway next to the app's name. Confirm with Open. Apple's instructions.

    Windows: SmartScreen shows "Windows protected your PC". Choose Run anyway to continue. On computers managed by an organization, policy can block this. Microsoft's description.

  2. Get a trial key.

    How the trial handles your data

  3. Paste the key into the app, then turn off Wi-Fi. The app keeps working from its signed lease until the check-in deadline plus the offline grace period.

In a terminal

The same SDK in plain Node.js, with the trial key from the steps above. Needs Node.js 22.13 or later.

  1. Activate this computer.

    npx @carchalabs/licensekit-quickstart activate LK-XXXX-XXXX-XXXX-XXXX
  2. Turn off the network and check the license. It is verified from the signed lease on your computer.

    npx @carchalabs/licensekit-quickstart status
  3. Release the seat.

    npx @carchalabs/licensekit-quickstart deactivate

How the quickstart handles your data

How it works

The app sends its device hash and the license key to the license server. The server returns a signed lease. The app verifies the lease offline with the public key. Your app SDK, main process signed lease verified offline License server policies, seats, devices signs with a private key key + device hash Ed25519-signed lease Offline, the app trusts the lease until its check-in deadline plus the grace period. The app sends its device hash and the license key to the license server. The server returns a signed lease. The app verifies the lease offline with the public key. Your app SDK, main process signed lease verified offline key + device hash Ed25519-signed lease License server policies, seats, devices signs with a private key Offline, the app trusts the lease until its check-in deadline plus the grace period.
Signed, not secret
A lease is a JSON Web Signature with the Ed25519 algorithm. Apps hold only public keys, so they can check a lease but not create one.
Bound to one device
The lease carries a per-product SHA-256 hash of the operating system's machine ID. The raw ID never leaves the computer, and a copied license file fails on another one.
Rules come from a policy
Each product has policies for seats, duration, how long updates are included, how often the app checks in and how long it works offline.
Clock changes are noticed
If the computer's clock moves back more than a day, the SDK reports clock_suspect and paid features stop until an online check.
Customers move machines themselves
Deactivating frees the seat. Reinstalling on the same computer reuses its seat.

Add it to an Electron app

const { LicenseKit } = require('@carchalabs/licensekit-electron');

const kit = new LicenseKit({
  serverUrl: 'https://licenses.example.com',
  product: 'example-studio',
  publicKey: [currentKeyPem, nextKeyPem],
  storageDir: app.getPath('userData'),
  secureStore: {
    encrypt: (s) => safeStorage.encryptString(s),
    decrypt: (b) => safeStorage.decryptString(b),
  },
});

const state = await kit.check();
if (state.status === 'unlicensed') await kit.activate(userEnteredKey);
if (LicenseKit.hasFeature(state, 'export-pdf')) exportPdf();

Security model and limits

  • Revocation is not instant offline

    A revoked or suspended license keeps working on a computer that stays offline until its check-in deadline plus the offline grace period. With the default policy that is up to 72 hours plus 7 days after the last successful check-in.

  • The check runs inside your app

    Licensing code in JavaScript can be patched by someone who controls the computer. LicenseKit raises the cost of misuse; it does not prevent it. The demo app turns off Electron's RunAsNode, NODE_OPTIONS and inspector fuses, and on macOS loads only an app.asar whose integrity it has verified.

  • Local storage can be unprotected

    On Linux without a system keyring, Electron stores encrypted data with a fixed password. The SDK reports this as storageProtected: false.

  • The demo installers are not code-signed

    macOS and Windows warn before opening them. The steps above show how to continue.

  • Keys can be rotated

    Each lease names its signing key. Apps ship with the current and the next public key, so the server can switch keys without an app update.