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:
- 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.
- 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.
- The lease no longer licenses the app: it has run out, does not verify, or does not cover this build or this device.
- KeyGrant refused the key earlier, and the refusal still applies to this build. Checking in again lets a renewed or reinstated key work again.
- A device trial's lease no longer verifies while the trial still has time left.
- A device trial is running: the SDK reports the launch to KeyGrant at most once a day, in the background (see Device trials).
- 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
| Case | Waits |
|---|---|
| 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 request | At most 5 seconds |
| A launch report | status() 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()anddeactivate()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.
| Status | Means | Tell 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
upgradeoroutdatedrefusal 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
invalidwith reasonrevoked. -
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.revokedwebhook. - 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 withstalled: true.activate()returnsnetwork.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.