Offline validation & grace

Your app verifies its lease on the device, so it keeps working through an outage, a flight or a broken connection. Licensing fails in favor of the paying customer: only a definite refusal or the end of the lease stops it.

At a glance

The app runs offline for
Its lease: the license model's Offline days, 1 to 90
status() goes online
In seven cases only
A revocation reaches a device
At its next check-in

A missing network, a timeout, a server error, a rate limit or a response the SDK cannot use never takes away a valid lease.

How long the app runs offline

Your app runs offline for as long as its lease lasts: the license model's Offline days. When the model sets none, a lease lasts 30 days for a perpetual key and 7 days for a subscription, trial or timed key.

  • A lease never runs past the key's end, which is its expiry plus its grace period (see Lease format).
  • Offline days is also how long a revoked or refunded key keeps working on a device that does not check in: the SDK does not contact KeyGrant on every launch.

When the SDK goes online

status() works from what is stored on the device, and contacts KeyGrant only in these cases:

  1. The lease is near its end: less than 7 days left, or less than half its life when that is shorter. A 7-day lease renews in its last 3.5 days, a 1-day lease in its last 12 hours.
  2. A newer build: the lease covers every version (an open range, or upgrade protection running), and this build is a newer major than the last one this device reported. The SDK checks in once, so that upgrade protection can keep the version.
  3. The lease no longer licenses the app: it has run out, does not verify, or does not cover this build or this device.
  4. KeyGrant refused the key earlier, and the refusal still applies to this build. Checking in again lets a renewed or reinstated key work again.
  5. A device trial's lease no longer verifies while the trial still has time left.
  6. A device trial is running: the SDK reports the launch to KeyGrant at most once a day, in the background (see Device trials).
  7. Metered uses not yet reported: the device spent uses KeyGrant has not heard of (while offline, for example), and status() reports them (see Unsynced spends).

A device with no key and no trial stored never goes online from status().

How long it waits for a response

CaseWaits
The first two cases (near the end, a newer build) At most 5 seconds; without a response, it returns licensed from the valid lease
A device trial's requestAt most 5 seconds
A launch reportstatus() never waits for it
The other cases Up to the request timeout, httpTimeoutMs (15 seconds by default)

After a check-in that fails

After a check-in that brings back no lease the SDK accepts, status() waits before contacting KeyGrant again on its own, and works from what the device holds in the meantime:

  • 1 minute after a request that got no response at all (no network, a timeout).
  • 1 hour after any other response: a refusal, a server error, a rate limit, or a response or lease the SDK could not use.
  • A lease the SDK accepts ends the wait.
  • refresh() contacts KeyGrant immediately, regardless of the wait. Call it when the customer says they have paid, so a renewal or reinstatement takes effect at once rather than within the hour.
  • activate(), startTrial() and deactivate() also ignore the wait.

What a refusal means

A definite refusal from KeyGrant overrides the lease the device still holds: every later status() call returns the refusal, offline too, until a new lease arrives. An ambiguous response (a proxy's error page, a server error, a rate limit) never counts as a refusal.

StatusMeansTell the customer
invalid · revoked The key was revoked, is past its expiry and grace period, or this device's seat was released. The lease is dropped. Renew, or enter another key. Call refresh() once they have paid.
invalid · upgrade This build is newer than the key covers. The lease is kept for builds it does cover. Buy the upgrade, or go back to a covered version.
invalid · outdated This build is older than the key covers. Update the app.
invalid · device The license was activated on another machine: this machine's ID changed (see Device management), or the license file was copied. Activate again: activate(key) takes a seat for this machine. At the device limit, release a seat first, or more than one if the key's limit was lowered below the seats in use.
invalid · tampered The stored lease does not verify, or is for another product, key or activation. Connect to the internet so the app can renew its lease. If it is still refused, activate again.
invalid · unknown-key The stored lease was signed by a key this build does not carry. Connect to the internet so the app can renew its lease. If it is still refused, update the app.
expired · offline The lease ran out and could not be renewed. Connect to the internet.
expired · trial The device trial is over. Buy a key.
unknown · storage The license file could not be read just now. Nothing: keep running and check again shortly.
  • An upgrade or outdated refusal applies to that build and to any build further outside the key's range. Going back to a build the key covers works offline.
  • A renamed machine, or one whose machine ID cannot be read, is never refused with device.

The grace period

The grace period is how long a key keeps working past its end. Only subscription and timed models have the setting, Grace after expiry, days:

  • Subscription: how long its keys keep working past their expiry, the same for a lapse, a failed renewal and a cancellation.
  • Timed: how long its keys keep working past the end of their term.
  • A key with no grace period stops at its expiry, and a test license never has one.

During the grace period, check-ins succeed: the response carries inGrace: true and the lease grace: true, and the SDK reports licensed.

When the grace period runs out:

  • KeyGrant refuses the key, and the SDK reports invalid with reason revoked.
  • Up to a day after a subscription key's grace period ends or a trial key expires, KeyGrant also revokes it and sends a license.revoked webhook.
  • A perpetual key issued with an expiry is refused once it expires, but is not revoked when it does, and nor is a timed key, so that buying it again can extend it (see Timed models).

The device clock

The SDK never checks a lease against a time earlier than the latest time it has saved, so setting the clock back does not revive a lease the SDK has seen expire. If the clock jumped ahead and was then corrected, the app is licensed again once KeyGrant signs its next lease.

One call at a time

Calls on one License run one at a time, so when several parts of your app call status() at once, the SDK contacts KeyGrant only once. The calls that change nothing stored run beside any call: has(), require(), upgradeUrl() and both pickUp() calls (each SDK names its own).

Each call waits for the one before it, but no longer than the request timeout plus 15 seconds (30 seconds by default). If an earlier call is still stuck on storage after that, then until it finishes or the app restarts:

  • status() returns what is stored with stalled: true.
  • activate() returns network.
  • deactivate() rejects.

If another program keeps the license file locked, status() returns unknown and writes nothing, while activate() and deactivate() reject so your app can tell the customer.