LexActivator
LexActivator is the Cryptlex client library you embed in your app to implement node-locked licenses, trials, hosted floating licenses, and named user licenses. This page shows you how to add the library to your app, activate and verify a license, and handle the hosted floating, named user, and offline activation flows, with code samples for every supported language.
What LexActivator does for you
Under the hood, LexActivator is a libcurl based HTTP client that invokes the Cryptlex Web API endpoints from your application. You can invoke these endpoints with any HTTP library, but LexActivator does the hard parts for you:
- Abstracts away the HTTPS layer, so you call functions like
ActivateLicense()andActivateTrial()instead of sending HTTPS requests. - Verifies the HTTPS response signature using an RSA public key.
- Stores the HTTPS response on disk in encrypted form using AES encryption, in a per-user or system-wide location depending on the permission flag you pass to
SetProductId(). Where disk writes are restricted, it can instead keep the license data in memory, which requires activating the license again on each app restart and is typical for hosted floating licenses with a per-instance leasing strategy. See activation data storage for the exact locations on each OS and how to change them. - Generates multiple fingerprints of the machine using an advanced device fingerprinting algorithm, which allows for different fingerprint matching strategies.
- Detects virtual machines, so you can prevent users from activating your licenses in them (virtual machines can be cloned, which may result in the same fingerprint on different machines).
- Detects system clock tampering, so users cannot extend a time-limited license or trial by turning back the machine's clock.
- Periodically invokes the Cryptlex Web API endpoints in a separate thread to sync the server license data with the client.
- Handles offline activations.
Minimum requirements
For the supported platforms, versions, and architectures, see minimum requirements.
Adding licensing to your app
Prerequisites
Before you integrate LexActivator, create a product for your app in the admin portal and create a license (node-locked or hosted floating) for it. Then, from the product's page, get the two things you will use in your code:
- The Product ID identifies your product in your code.
- The Product.dat file contains product data used by LexActivator. Download or copy it from the actions menu on the
Productspage.
For a complete, working integration in your language, refer to the example projects on GitHub.
Adding the library to your app
Setting the product data and product ID
Embed the product data (the contents of the Product.dat file) in your app using the SetProductData() function. Then set your product ID using the SetProductId() function.
Call both functions once during your app's initialization, before any activation or verification.
The permission flag you pass to SetProductId() determines where the activation data is stored:
LA_USER(the default) stores the activation under the OS user who activated the license, so it is visible only to that user, and each OS user who runs the app activates separately. They all share the same seat. Enable OS user-locked to have each OS user consume a separate seat.LA_ALL_USERSstores the activation system-wide and shares it across all OS users on the machine. It is supported on Windows and macOS; on Linux, achieve the same result by pointing LexActivator at a system-wide directory withSetDataDirectory()and passing theLA_ALL_USERSflag.LA_IN_MEMORYkeeps all data in memory instead of on disk. Use it when your app has no write access to the disk, and for hosted floating licenses with a per-instance leasing strategy. Note that the license must be activated again every time the app restarts.
For the exact storage locations on each OS and how to handle environments with restricted storage, see activation data storage.
License activation
Set the license key with SetLicenseKey(), then call ActivateLicense(). This assumes you have already set the product data and ID, as shown above. Call it when the user activates the license, for example on a button click. Use SetLicenseKey() and ActivateLicense() only in your activation flow, not during normal startup.
ActivateLicense() invokes the /v3/activations Web API endpoint and verifies the encrypted, digitally signed response to validate the license.
Verifying license activation
Each time your app starts, verify whether the license is already activated with IsLicenseGenuine(). This assumes the product data and ID are already set during initialization.
The check runs locally by verifying the cryptographic signature of the stored activation, so it works offline. LexActivator also syncs with the Cryptlex servers periodically in the background to pick up server-side changes such as renewals, suspension, or revocation.
Hosted floating licenses
LexActivator implements hosted floating licenses the same way as node-locked licenses; the only difference is how you activate and release seats. A floating seat is held while your app runs and must be released on exit, otherwise it becomes a zombie that keeps consuming a seat.
Validate the license first, activate based on the validation status, and release the seat on exit only for hosted floating licenses:
// On startup: check the license once, then act on the result
int status = IsLicenseGenuine();
if (status == LA_FAIL) {
// not activated yet
char licenseKey[256];
if (GetLicenseKey(licenseKey, sizeof(licenseKey)) == LA_OK) {
// a key is already stored
ActivateLicense();
} else {
// no key stored, prompt the user for one
SetLicenseKey(userKey);
ActivateLicense();
}
} else if (status == LA_OK) {
// license is valid (node-locked or floating), continue launching the app
}
// On exit: release the seat only for hosted floating licenses
char licenseType[256];
if (GetLicenseType(licenseType, sizeof(licenseType)) == LA_OK && strcmp(licenseType, "hosted-floating") == 0) {
DeactivateLicense();
}For when to choose hosted floating and how to configure the license template, see hosted floating licenses.
Named user licenses
Named user licenses (also known as identity-based or user-locked licensing) can be implemented with LexActivator: the user logs in with their own credentials and their linked license is activated, instead of entering a license key. See named user licenses for the full activation flow and portal setup.
Offline activation
LexActivator can activate licenses on machines with no internet access, using an offline activation request and response file pair. For the full walkthrough, see offline activations.
Server sync
Changes you make to a license on the server, such as extending, suspending, or revoking it, or updating its metadata or feature entitlements, do not reach an activated machine instantly. LexActivator verifies the license locally and picks up server-side changes through a periodic background sync. This section explains when a change reaches the client and how to react to it in your app.
How server sync works
Local verification and server sync are two separate mechanisms:
- Local verification is synchronous and offline.
IsLicenseGenuine()verifies the cryptographic signature of the locally stored activation data and returns immediately, without contacting the Cryptlex servers. - Server sync is asynchronous and periodic. The first time
IsLicenseGenuine()is called after your app starts, LexActivator triggers a server sync in a separate background thread, about 3 seconds later. Further syncs repeat at the server sync interval configured in the license template, for as long as the app runs.
A successful sync updates the local activation data with the current server-side state of the license. The result of every sync, whether the state changed or the sync failed, is delivered to the callback you register with SetLicenseCallback().
When a change reaches the client
Every server-side change follows the same rule: it takes effect on the server immediately and reaches the activated machine on its next sync, or right away when your app calls SyncLicenseActivation(). This applies to extending, suspending, resuming, or revoking the license, updating its metadata or feature entitlements, and deleting the activation in the admin portal. Until that sync happens, the machine keeps running on its last synced state. The one exception is DeactivateLicense() called on the machine itself, which removes the local activation data on the spot.
Registering the license callback
The callback registered with SetLicenseCallback() is tied to the license key, so the key must be set before the callback can be registered:
- In the first-run activation flow, call
SetLicenseCallback()afterSetLicenseKey()and beforeActivateLicense(). - On later runs, when the license is already activated, the key is already stored, so call
SetLicenseCallback()beforeIsLicenseGenuine(). - If the callback is registered more than once, the last registration before a sync wins.
The callback is invoked from the background sync thread, not your main thread. Do not touch UI elements from it directly; marshal back to the main thread using your framework's mechanism.
How revocation appears in your app
When a license is revoked server-side, the sync deletes the activation data on the machine. From then on, IsLicenseGenuine() returns LA_FAIL, meaning no activation, rather than LA_E_REVOKED. To tell a revoked license apart from a machine that was never activated, call GetLastActivationError(), which returns the error code that caused the activation data to be cleared, LA_E_REVOKED in this case. The license callback also receives LA_E_REVOKED during the session in which the revocation arrives, and ActivateLicense() returns it if the user tries to activate a revoked key again.
Suspension behaves differently: the activation data stays on the machine, IsLicenseGenuine() returns LA_SUSPENDED, and access is restored automatically on the sync after you resume the license.
The in-memory cache
LexActivator serves reads from an in-memory cache that is populated when the library is first used in a process. If a different process activates the license, for example a service or installer activating on behalf of your app, a process that has already called IsLicenseGenuine() does not see the new activation until it restarts.
Sync failure and the grace period
A sync that fails due to a network error does not invalidate the license. The server sync grace period in the license template defines how long sync failures are tolerated; once exceeded, verification returns LA_GRACE_PERIOD_OVER until a sync succeeds. Set the grace period to -1 for machines that are rarely or never online, or to a finite value to force the machine to reconnect periodically.
Activation data storage
After a successful activation, LexActivator persists the activation data on the machine in encrypted form using AES encryption, and verifies it locally on every later run. Where it is stored depends on the OS and on the permission flag you pass to SetProductId():
LA_USERstores the data in the current user's profile: the registry on Windows, and a per-user application data directory on macOS and Linux.LA_ALL_USERSstores the data in a system-wide location shared by every user of the machine. On Linux, there is no default system-wide location, so set one withSetDataDirectory().
Call GetDataStoreInfo() to get the exact location LexActivator is using on the current machine, which is useful when debugging a license that appears to be lost.
With the LA_IN_MEMORY flag, nothing is written to disk at all; the activation data lives only in memory and the license must be activated again on every app restart.
Changing the storage location
Call SetDataDirectory() to store the activation data in a directory of your choice instead of the default location:
SetDataDirectory("/path/to/writable/directory");Three rules apply:
- Call it at every start of your app, before
SetProductData()andSetProductId(). - The directory must already exist, and your app must have read and write permissions in it.
- It works on the file-based platforms: macOS, Linux, and Android. On Windows,
LA_USERactivation data always lives in the registry, so the function does not apply there.
Restricted environments
If LexActivator cannot write to the default location, SetProductData() or SetProductId() fails with LA_E_SYSTEM_PERMISSION ("Insufficient system permissions"), or the activation succeeds but is not found on the next run, so the app asks for the license key again. The fix in every case is to point SetDataDirectory() at a location your app is allowed to write to:
- macOS App Sandbox: apps using Apple's App Sandbox, including everything distributed through the Mac App Store and plugins whose host app uses it, cannot write to
~/Library/Application Supportdirectly. Add thecom.apple.security.network.cliententitlement so LexActivator can reach the Cryptlex servers, and callSetDataDirectory()with a path inside your app's container. - Android: Android apps, including Unity and other engine-based builds, must store the data in the app's own storage area. In Unity, pass
Application.persistentDataPathtoSetDataDirectory(). The library also requires theandroid.permission.INTERNETandcom.google.android.providers.gsf.permission.READ_GSERVICESpermissions in the manifest. - Linux services and non-login users: a process running as a user without a home directory, such as a daemon under a service account or a binary invoked from a web server, has no writable default location. Either create a home directory for that user or call
SetDataDirectory()with a directory the service account can write to. - No writable storage at all: pass the
LA_IN_MEMORYpermission flag toSetProductId()instead. The license must then be activated again on every app restart, which is the standard setup for hosted floating licenses with a per-instance leasing strategy.
Cleaning up on uninstall
To remove the activation data when your app is uninstalled, call the Reset() function from your uninstaller. It clears the stored activation data for your product only.
Next steps
- Review the best practices for permission flags and license verification.
- Use feature entitlements to control feature access per license, and retrieve them at runtime as described in Accessing Feature Entitlements.
- Support air-gapped customers with offline activations.
- Implement hosted floating licenses or timed trials.
- Deliver updates to licensed users with auto-updates.
- Handle restrictive customer networks with proxies and firewall whitelisting.
Need more help?
If you need more help adding LexActivator to your app, we'll be glad to help with the integration. Contact us through the contact page.