Offline (air-gapped) activation
A machine with intermittent connectivity doesn’t need file-based activation — the .NET SDK caches the license on disk and validates it offline for as long as the offline-grace policy allows. This page is for machines that never connect: activation and lease renewal travel as files.
Everything rests on one trust anchor: license artifacts are signed with the
provider’s key, and the device verifies them with the public key you shipped —
no network required. The public key is safe to ship: it can verify, not mint, so
carrying it on the device grants an attacker nothing. What is worth protecting
is the cached license itself once it lands on disk — see
at-rest encryption,
which the offline flow gets automatically since it uses the same LicenseFileStore.
How it works
Section titled “How it works”- On the device — the SDK writes an activation request file (activation code, fingerprint, application info).
- Carry it out — USB stick, one-way transfer, whatever your site allows.
- In the portal — an operator uploads the request under Offline activation; the server runs the normal activation pipeline and returns a signed response file.
- Carry it back — return the response file to the device.
- On the device — the SDK imports the response, verifies the signature and installs the license.
Add-on package: Revenusion.MonetizeIt.Client.OfflineStore (alongside
Client.LicenseConsumer).
Build the request on the device
Section titled “Build the request on the device”using Revenusion.MonetizeIt.Client.LicenseConsumer.Features.OfflineActivation;
var request = new OfflineActivationRequestBuilder() .WithActivationCode("ABC123-DEF456-GHI789") .WithFingerprint(nodeIdProvider) .WithApplicationInfo(applicationInfoProvider) .Build();
await OfflineActivationRequestWriter.WriteToFileAsync(request, @"D:\transfer\activation-request.json");The fingerprint provider is the same one the online flow uses — including per-component hashes, which are included in the request.
Issue the response
Section titled “Issue the response”In the portal, open Offline activation, upload the request file, and download
the signed response. From your own backend, the same operation is
POST /api/v1/provision/operational/activations/offline/issue.
Submitting a byte-identical request file twice within a short server-side window returns the same signed bytes; a re-export from the device produces a fresh signature. A stronger guarantee holds at the session level: the same device (fingerprint) re-activating reuses its existing session, so a re-issued response file — however it was produced — can never consume a second seat.
Import on the device
Section titled “Import on the device”using Revenusion.MonetizeIt.Client.LicenseConsumer.Features.OfflineActivation;
var importer = new OfflineActivationResponseImporterBuilder() .WithFingerprint(nodeIdProvider) // required: refuses a license issued for another device .WithLicenseStore(licenseStore) .WithPublicKeyProvider(publicKeyProvider) .WithLicenseService(licenseService) // optional: refreshes the in-memory cache too .Build();
var result = await importer.ImportAsync(@"D:\transfer\activation-response.json");if (!result.Success) throw new InvalidOperationException(result.Message);The importer verifies the envelope signature against the pinned public key before touching the license store. From here the application runs as if it had activated online.
The usage and lease loop
Section titled “The usage and lease loop”Air-gapped machines accumulate usage locally (FileTransactionStore).
Periodically, swap files again to upload usage and renew the lease:
-
Export on the device:
using Revenusion.MonetizeIt.Client.LicenseConsumer.Services.Store;var exporter = new LicenseZipFileExporter(@"D:\transfer\usage-export.zip", licenseStore, transactionStore);await exporter.ExportAsync(); -
Upload in the portal — Offline activation → Ingest transactions — and download the signed receipt. (API:
POST /api/v1/provision/operational/usage/offline/ingest.) The server records the usage, extends the lease, and wraps a refreshed license in the receipt. -
Import the receipt on the device:
using Revenusion.MonetizeIt.Client.LicenseConsumer.Features.OfflineActivation;var importer = new OfflineTransactionReceiptImporterBuilder().WithFingerprint(nodeIdProvider) // required: refuses a license issued for another device.WithTransactionStore(transactionStore).WithPublicKeyProvider(publicKeyProvider).WithLicenseService(licenseService).Build();var result = await importer.ImportAsync(@"D:\transfer\usage-receipt.json");On success the refreshed license is installed first, then the uploaded transactions are purged from the local log — if anything fails before the purge, the log stays intact for retry.
Repeat the swap on whatever cadence the entitlement’s lease window requires, and an air-gapped fleet stays licensed and metered indefinitely.
Operational notes
Section titled “Operational notes”- The whole loop is operator-driven through the portal in this release — there is no end-user self-service for offline activation.
- The request file is not secret (it contains hashes, not raw device traits), but treat response and receipt files as license material: they’re single-purpose, device-bound, and verified by signature on import.