Skip to Content
ElectronWorking offline

Working offline

When a validation can’t get a definitive answer from LicenseManager, the SDK falls back on the stored token, the one it got at the last successful validation. While it’s valid, the app stays ACTIVE, and the customer notices nothing.

It’s the case when:

  • the computer is offline, or LicenseManager can’t be reached,
  • the request takes more than 10 seconds,
  • LicenseManager answers plan_limit_exceeded, validation_error, internal_error, or an error the SDK doesn’t know,
  • the answer isn’t JSON, or its token fails verification.

When LicenseManager can’t be reached, or answers 408, 429 or 5xx, the SDK first retries the request, for up to 30 seconds during refresh() or a startup without a valid stored token, 5 minutes in the background, and only then falls back.

When the customer activates a key, there’s no fallback: setLicenseKey() throws. The fallback only applies to a key already activated.

The fallback

  1. The SDK waits for the first clock check. When the clock was tampered with, the state is CLOCK_TAMPERED, whatever the token.
  2. It reads the stored token, and verifies it like an online one: signature, product, key, fingerprint, exp and nbf. No token, or a token that fails, gives OFFLINE_EXPIRED.
  3. It checks the token’s mv includes the app’s version. When it doesn’t, the state is VERSION_NOT_ALLOWED. See versions offline.
  4. Otherwise, the state is ACTIVE.

The stored token is never deleted by the fallback: only a locking answer from LicenseManager deletes it.

Offline state

getOfflineState() tells whether the last validation reached LicenseManager, and how long the app can keep running without it:

const offline = license.getOfflineState(); if (offline.mode === "offline" && offline.validUntil) { const days = Math.floor(offline.remainingMs / 86_400_000); showBanner(`Offline: connect to the internet within ${days} days to keep using Acme Photo.`); }
ValueWhen
{ mode: "online" }The last validation got a definitive answer: a token, or a locking error. Also during LOADING, and in MISSING_KEY.
{ mode: "offline", validUntil: Date, remainingMs: number }ACTIVE on the stored token. validUntil is its expiration, remainingMs the time left, computed when you call it.
{ mode: "offline", validUntil: null, remainingMs: 0 }Locked offline: OFFLINE_EXPIRED, CLOCK_TAMPERED, or VERSION_NOT_ALLOWED from the token.

It’s not an event: no listener is called when it changes from online to offline while the state stays ACTIVE. Read it when you show your UI, or on a timer, like every hour, to update a banner.

App window with a banner telling it's offline and how many days are left

Most customers never need to know. Only show a banner when little time is left, like 2 days, so the customer has time to get online.

Retries

While offline, the SDK tries again on its own:

  • In ACTIVE, at the renewal point if it’s still ahead. Once it has passed, with a backoff: after 5 minutes, then 10, 20, 40, and every hour after that. Never later than the token’s expiration: the last try happens right when it expires.
  • In OFFLINE_EXPIRED, CLOCK_TAMPERED, and VERSION_NOT_ALLOWED from the token, with the same backoff.

The backoff goes back to 5 minutes after the next successful validation.

SituationNext tries
1 week token, renewal point at day 4 16h, offline since day 2Day 4 16h, then +5 min, +10 min, +20 min, +40 min, then every hour until day 7, when the token expires: OFFLINE_EXPIRED, still every hour.
Launched offline on day 1, with a 1 week tokenNothing until day 4 16h: the token has plenty of time left.
Launched offline on day 6, with a 1 week token+5 min, +10 min, +20 min, +40 min, then every hour.

The SDK doesn’t watch the network: when the computer comes back online, it’s noticed at the next try. To notice it right away, call refresh() when your window sees the online event:

renderer.ts
window.addEventListener("online", () => window.license.refresh());

See renderer and IPC to expose refresh() to your window.

Versions offline

Online, LicenseManager decides which versions the license includes. Offline, the SDK checks the token’s mv claim, the version hard-coded in the license when it was issued:

mvAllows
null, or no mvEvery version.
"2"Every 2.x.x, and every older version.
"2.3"Every 2.3.x, and every older version.

Only the major and minor numbers are compared: patch numbers and suffixes are ignored, so 2.3.9 and 2.3.0-beta.1 are both 2.3. A leading v is accepted. A version that can’t be parsed, like nightly, is allowed: LicenseManager will tell at the next validation.

When the version isn’t allowed, the state is VERSION_NOT_ALLOWED, and getLatestAllowedVersion() returns undefined: the token knows the max version, not which releases exist. Unlike a refusal from LicenseManager:

  • the stored token is kept: going back to an allowed version works offline right away,
  • the SDK keeps retrying, since LicenseManager has the final word.

A license with time-limited updates has mv: null until its lock date. A token issued before that date allows every version offline, even ones released after it. The next online validation decides.

Edge cases

An expired license, launched offline. If the app was EXPIRED at its last session, its token was deleted: the state is OFFLINE_EXPIRED. If the license expired while the app was offline, the stored token expired with it: OFFLINE_EXPIRED too.

A license deactivated or expired from the dashboard, used offline. The stored token doesn’t know about it, and the app keeps running until it expires: at most the license’s max offline duration. See what a token doesn’t know.

A token copied from another computer. Its fp doesn’t match this computer’s fingerprint: it fails verification, and the state is OFFLINE_EXPIRED.

The fingerprint changed, like when the stored install id was deleted. The stored token holds the old fingerprint and fails: OFFLINE_EXPIRED offline. Online, the computer is validated as a new machine.

LicenseManager is down. Every running app falls back on its token. Apps with a valid token keep working; only first activations fail.

Your organization is over its plan. Validations answer plan_limit_exceeded, handled like LicenseManager being down: apps keep running until their tokens expire, then become OFFLINE_EXPIRED. See plans and billing.