DEV Community

Nico
Nico

Posted on

How offline license activation actually works

If you ship a desktop app outside an app store, you eventually hit the same wall: how do you check a license when the user is on a plane, behind a corporate firewall, or just offline? Calling your server on every launch isn't an option. Here's how offline activation actually works, without the hand-waving.

The naive version, and why it breaks

The first thing everyone reaches for is "call home on launch, get back yes/no." It works in the demo and fails in the wild:

  • No network = no app. Fail-closed locks out paying customers. Fail-open means anyone who blocks your domain runs free. Both are bad.
  • A boolean is forgeable. If your app trusts a {"valid": true} response, a proxy or a patched DNS entry returns that for free.

The fix isn't a better endpoint. It's moving the trust off the network and onto cryptography.

The model that works: signed leases

The durable pattern is a cryptographically signed lease (Keygen calls these license files, Keylight calls them leases — same idea):

  1. On first activation, the device talks to the server once.
  2. The server returns a small signed document: the license state, an expiry, the device binding, and any entitlements (which features/tiers are unlocked).
  3. The document is signed with the server's private key (Ed25519 is the modern choice — small, fast, boring in the good way).
  4. Your app ships the matching public key and verifies the signature locally on every launch. No network needed.

Because the app only ever verifies with a public key, there's nothing secret in the binary to steal, and a forged lease fails the signature check. That's the whole trick: the server vouches once, math vouches forever after.

first launch ──► server signs lease (Ed25519, private key) ──► stored on device
every launch ──► app verifies signature (public key) ──► no network
Enter fullscreen mode Exit fullscreen mode

Device binding (so one key isn't infinite installs)

A lease is bound to a device so a single license can't be pasted onto a thousand machines. The lease embeds a device fingerprint, and the SDK checks the running machine matches. The honest engineering note: fingerprints drift. macOS hardware UUIDs are stable; "hostname + user" is not. Pick a stable identifier and give users a deactivate path, or you'll drown in "I reinstalled and now I'm locked out" tickets.

The tradeoff nobody mentions: revocation vs. offline

Here's the tension you have to design around deliberately. A purely offline lease can't be revoked instantly — that's the point, it doesn't phone home. So a refunded or charged-back user keeps a valid lease until it expires.

You resolve it with a max-offline window. The lease carries an expiry (say 7, 14, 30 days). Inside the window, fully offline. Past it, the app must revalidate online once to refresh the lease — which is your chance to revoke. Short window = tighter control, more online checks. Long window = friendlier offline story, slower revocation. There's no universally right number; it depends on your price point and abuse surface.

What this looks like with an SDK

You don't want to hand-roll Ed25519 and lease parsing. Most licensing SDKs hide this behind a couple of calls. With Keylight, for example, the offline path collapses to: activate once, then a local checkOnLaunch() that verifies the lease and hands you a state — licensed, trial, expired, invalid — with no network call. Entitlements ride inside the signed lease, so feature gating is offline too. Keygen, Cryptolens, and LicenseSpring implement the same primitive with different ergonomics; the underlying cryptography is the part that matters and it's the same everywhere.

The takeaway

Offline activation isn't "cache the server's answer." It's: trust the network once, trust signatures forever after, bind to a stable device id, and pick a max-offline window that matches how fast you need to revoke. Get those four right and your app works on a plane and still says no to a refunded license.

If you're building this for a Mac or Tauri/Electron app and don't want to implement the crypto yourself, I wrote up the Keylight offline verification model here.

Top comments (0)