keygrant library
KeyGrant licensing for Flutter apps: offline-verified leases, trials, licence files, metered uses and key pickup, on iOS, Android, Windows, macOS and Linux.
Classes
- Activated
- The key is activated on this device.
- ActivateFailed
- The key was not activated, and why.
- ActivateResult
-
What
activate()did. - BaseFigure
-
A meter's figures as a lease signs them (
met) or an answer gives them, without the time of the answer: what was left, the uses it holds, the offline allowance each device may spend, and whether it comes back each billing period. - DeactivateResult
-
What
deactivate()did about the seat. The local licence is dropped either way; released says whether the device's seat is free: true when KeyGrant confirmed the release (a 200), or when this device held no seat to release; false when it could not be asked, or answered anything else. - DesktopFingerprinter
-
The desktop default: the machine id (
readMachineId) hashed, the host name's when the machine has no id to read; the host name's always offered as an alternate. get waits as long as an activation does and throws when the id still could not be read. - DeviceIdentity
- What a fingerprinter says of this device: its fingerprint (null when it cannot be told this time), what it hashes (kind, null when unknown), and the other fingerprints this device answers to.
- Entitlement
-
One entitlement's value: a flag, a number (a double, as JSON reads one) or
a text. Compared by content:
NumberEntitlement(5)equalsNumberEntitlement(5.0). - FileStorage
-
JSON file storage. Read: only a MISSING file is no state (its newest whole
temp file read first); any other failure is tried again for about a second
and then throws; a file that does not parse is set aside as
<file>.<ms>.corrupt(the newest three kept) and an empty state written in its place. Written atomically: a temp file beside it, flushed, renamed over it (tried again when busy), the folder flushed after on POSIX. Each save stampsrevone higher and checks just before the rename that the file'srevis the one it read, merging again onto a newer state up to five times. - Fingerprinter
- A fingerprinter: what identifies this device. get alone says nothing of what the fingerprint hashes, so its seats are never held to their device; implement identify and report DeviceKind.machine for a stable machine id that should be.
- FlagEntitlement
- A flag: granted when true.
- HttpAdapter
-
POST JSON. Answers every status (a 4xx and a 5xx included) rather than
throwing; throws only when there was no answer (no network, a timeout).
timeoutis how long the call may take: an adapter that ignores it is still held to it by the SDK. - HttpResponse
- What a POST came back with: its HTTP status, and its body as JSON (null when it had none, or none that parses).
- ImportLicenseFileResult
-
What
importLicenseFile()did. - KeychainTrialStash
-
A trial's start kept in the iOS Keychain (service
dev.keygrant.trial, account the product slug), which outlives the app, through the plugin. - KeyGrantLicense
- Client-side licensing for an app. Verifies signed leases OFFLINE and only contacts KeyGrant to activate, to renew a lease nearing its end or past it, to tell it of a newer major, to spend a metered use or report the uses this device spent with no answer, or (at most once a day, in the background) to report a running trial's launch: so an outage, no network, a 5xx, a 429, an ambiguous 4xx or an unusable answer never locks out a paying customer while their lease is valid. Only an explicit verdict from the server, or the lease's own end, does.
- KeyGrantPlugin
-
Registers the plugin's Dart side: Flutter calls registerWith for every
platform when the app starts (the plugin's
dartPluginClass). An isolate the app starts itself callsDartPluginRegistrant.ensureInitialized()first. - LicenseAdapters
- Adapter overrides; any left null uses the default.
- LicenseClock
- The device clock, in milliseconds since the epoch.
- LicenseConfig
- How a licence is set up.
- LicensedTo
- Who a licence file is licensed to: a name, an email address, or both.
- LicenseFileActivationFailed
-
A key file whose key's activation failed, as
activate()answers it. - LicenseFileImported
- An offline file is stored and licenses the app from now on; a key file's key is activated. licensedTo is who the file names.
- LicenseFileRefused
- The file was not taken, and why; nothing was stored.
- LicenseStatus
- The licence's status. Compared by content: an app comparing two statuses sees no change from a lease that grants nothing.
- MeterFigure
-
A meter's figures as the newest
useanswer gave them; at is the server's time (ms). - MeterStatus
-
One meter of a key as
status().metersshows it: what is left net of this device's unsynced spends (never below 0), the uses it holds, its offline allowance, the uses this device spent that KeyGrant has not heard of, and whether it comes back each billing period. - MobileFingerprinter
-
The phone and tablet default (4.1): iOS's Keychain install id (raw
machine-id|ios:<id>) and Android'sANDROID_ID(rawmachine-id|android:<id>, lower case), kindmachine, no alternate. An Android device with no usableANDROID_ID(none, or the one old builds share) gets an install id of the SDK's own in the app's private storage, rawinstall-id|android:<uuid>, of no kind, so its seats are never held to the device. Anything that fails is no fingerprint this time. - NoRegistryStash
- The stash that keeps nothing: the default where no platform stash applies.
- NumberEntitlement
- A number: granted when not 0 (a negative counts: -1 for unlimited).
- PackageHttp
-
HTTP over
package:http, every call bounded: abandoned (the request aborted) and thrown oncetimeouthas passed, which the SDK treats as no network. Without a bound a network that accepts a connection and never answers (a captive portal, a dead proxy) would hold a check-in for minutes. - PickedKey
-
One key a pickup handed back: enough for the app to let the customer pick
one (its model, what it grants, its versions, its seats), never shown as a
key. Activate it with
activate(picked.key, email: ..., code: picked.activationCode), or letrestoreByEmailchoose and activate one. - PickedSale
- The sale of several keys a picked key is one of.
- PickedVersions
- The majors a picked key covers; maxMajor null for no top.
- PickUpCodeSent
- The code is on its way when the address holds a key of the product; the answer is the same when it does not.
- PickUpKeysFailed
- The confirm was refused; retryAfter (seconds) when the server says.
- PickUpKeysFound
- The address's live keys of the product, for the customer to pick one by its model (never shown as a key).
- PickUpKeysResult
-
What
confirmPickUp(email, code)did. - PickUpSendFailed
- The ask was refused; retryAfter (seconds) when the server says how long to wait.
- PickUpSendResult
-
What
pickUp(email)did. - RegistryStash
- A trial's start kept where it outlives the app (the Windows registry, the iOS Keychain), so a reinstall does not restart a trial. Either call may throw; neither ever costs the customer anything.
- Restored
- A key of the address is activated on this device.
- RestoreFailed
- Nothing was activated, and why; retryAfter (seconds) when a pickup refusal says.
- RestoreResult
-
What
restoreByEmail(email, code)did. - SpendEntry
- A spend this device made that KeyGrant has not confirmed (the journal).
- StorageAdapter
- Where the stored state lives.
- StoredState
- The licence state a device keeps between runs, one per product. Read-only: the SDK saves a new one. A field this SDK does not know (a newer SDK's) is kept as it was and written back.
- SystemClock
- The system clock.
- TextEntitlement
- A text: granted when not empty.
- UseRefused
-
Not spent, and why; left for
used-upandoffline-limit. - UseResult
-
What
use(meter, n)did. - UseSpent
- Spent. left is what is left of the meter now, net of this device's unsynced spends; offline is true when KeyGrant could not be asked and the spend came from the offline allowance.
Enums
- DeviceKind
- What a fingerprint hashes: the operating system's machine id, or the host name.
- LicenseError
-
Why
activate()did not activate. - LicenseFileError
-
Why a licence file was not taken (
importLicenseFile). - LicenseFileKind
- The kinds of licence file: one that carries a key to activate, and one that licenses offline for good.
- LicenseReason
-
Why a licence does not license this build.
upgrade: the build is NEWER than the key covers.outdated: OLDER than its lowest version.device: activated on another device.tampered: the lease is not this licence's at all.unknown-key: signed by a key this build does not carry.storage: the state could not be read just now.revoked-file: the stored offline licence file is one this build lists as revoked. - LicenseSource
- Where a status comes from when it is not a key's lease or a trial.
- LicenseState
-
What
status()says of the licence.unknown(reasonstorage): the state could not be read just now: keep running, and ask again shortly. - PickupError
- Why a pickup call (section 13) did not do what it asked.
- Refusal
- A definitive refusal from the server, as the stored state keeps it.
- RestoreError
-
Why
restoreByEmail()did not activate a key: a pickup's error, 13.4'sno-key(upgradeandoutdatedwhen keys were found but none covers this build), or an activation's. - SpendKind
- What kind of entry the journal holds: spends made with no request sent (one entry for each meter, growing), one spend whose own request got no answer, or one such spend refused after (reported so the server takes it back).
- UseError
-
Why
use()did not spend (12.9).
Constants
- defaultHttpTimeout → const Duration
- How long a request may take before it is abandoned, unless the call or the configuration says otherwise.
- licenseFileExtension → const String
- The file's extension, for an app's open dialog and drop filter.
- licenseFileMediaType → const String
- The media type of a licence file attached to an email.
- maxLicenseFileBytes → const int
- The most a licence file may be, in bytes.
Functions
-
grants(
Map< String, Entitlement> ? entitlements, String code) → bool -
Whether entitlements grant
code: a flag that is on, a number that is not zero, or a text that is not empty. Absent, off, zero or empty is not granted. The code is matched exactly, case included. -
parseJwkSet(
String json) → List< Map< String, Object?> > -
The keys of a key set given as JSON text, as
/pubkeyanswers it ({"keys":[...]}, or that whole answer), a list of JWKs, or one JWK, for LicenseConfig.publicJwks. An object whosekeysmember is not a list is refused, never read as one JWK. Throws a LicenseConfigError for text that is none of these.
Typedefs
-
StatePatch
= Map<
String, Object?> - A change to the stored state: each member a field's new value, null to clear it; a field the patch does not name is left as it is.
Exceptions / Errors
- LicenseCallUnfinished
-
What
deactivate(),importLicenseFile()andremoveLicenseFile()throw behind a call on this licence that has not finished: the customer's own action, which the app asks them to try again. - LicenseConfigError
- A configuration the SDK cannot run with (a public key that is not one, both key members given, a denylist that is not a list of texts): thrown when the licence is made, so it is found the first time the app runs.
- MissingEntitlement
-
What
KeyGrantLicense.requirethrows when the licence does not grant an entitlement: it names it as entitlement.