Licensing for desktop apps that keeps working offline

Carcha Seal is a license server, an Electron SDK and an admin console. It issues Ed25519-signed leases that your 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.

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

Carcha Seal was previously named LicenseKit. It is not related to licensekit.dev or to Kankoda's LicenseKit.

Watch your app react

A running copy of the Carcha Seal Demo app, and you as its vendor. Every vendor button changes a real license on this server, and the app picks the change up at its next check-in.

Creates a sandbox license for 2 devices that lasts 2 days. No sign-up. How the demo handles your data

Try it

In a terminal

The SDK in plain Node.js, on your own computer. The trial key works for 7 days on 2 devices. No sign-up. Needs Node.js 22.13 or later.

  1. Get a trial key.

  2. Activate this computer with it.

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

    npx @carchalabs/seal-quickstart status
  4. Release the seat.

    npx @carchalabs/seal-quickstart deactivate

How the trial and the quickstart handle 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 { Seal } = require('@carchalabs/seal-electron');

const kit = new Seal({
  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 (Seal.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. Carcha Seal raises the cost of misuse; it does not prevent it. Turn off Electron's RunAsNode, NODE_OPTIONS and inspector fuses, and load only an app.asar whose integrity Electron 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.

  • 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.