Skip to content
← All posts
· Delta1 Labs LicensingSecurityGuide

Licensing for Air-Gapped and Offline Environments

How air-gapped license activation works: signed offline license files, manual activation flows, offline expiry and grace, and revocation with no network.

The most nerve-wracking licensing question I’ve ever been asked came from a defence contractor: “Our production network has no internet, ever, and it never will. Can your licensing work in there?” It’s a fair question that trips up a surprising number of licensing products, because so many of them quietly assume a machine can phone home. In a truly air-gapped environment — a classified network, an isolated industrial control system, a secure facility — there is no phone home. There is a USB stick and a security officer who inspects what’s on it.

The reassuring answer is that offline licensing isn’t a degraded mode; done right, it’s a fully legitimate one, built on the same cryptography as everything else. This post explains how air-gapped and offline activation actually works: signed license blobs, the manual activation dance, expiry and grace without a clock server, and revocation with no network.

Key takeaway: Air-gapped licensing works because trust comes from cryptography, not connectivity. A signed license blob verifies locally against an embedded public key with zero network calls, so a fully disconnected machine can prove a license is authentic, unaltered, bound to it, and unexpired — and reject a revoked one via a signed list you ship out of band.

Why offline validation is possible at all

If you’ve read offline license validation explained, you know the core asymmetry, so I’ll keep this brief and build on it toward the air-gapped case. Your server holds a private key that can create signatures; the client embeds only the matching public key, which can verify a signature but never forge one. Verifying a signature is pure math on data the app already holds, so the client can confirm a license was signed by you — and hasn’t been altered by a single byte — with no live server. That one property is what makes air-gapped licensing real rather than a compromise.

Everything in this post is an application of that idea to the hardest deployment: a machine that will never see the network, not even once.

The signed license blob

The unit of an offline license is a signed blob: a small payload of facts with a signature over it. Conceptually:

{
  "licensee": "Acme Defense Systems",
  "product": "acme-analyzer",
  "tier": "enterprise",
  "seats": 5,
  "expiryUtc": "2027-09-29T00:00:00Z",
  "machineFingerprint": "b3f1…c9a2",
  "entitlements": { "export": "true", "max-projects": "50" },
  "signature": "MEUCIQ…"   // over every field above, made with the vendor's private key
}

The client verifies the signature against its embedded public key, and only then reads the now-trusted fields. Flip tier from enterprise to anything else, or edit expiryUtc, and the signature no longer matches — validation fails. The machineFingerprint field is what node-locks the blob to one machine so the same file can’t be copied across an air-gapped fleet (the fingerprinting and swap-tolerance details are in node-locking and seat management).

The manual activation flow

Here’s the part unique to air-gapped operation: how does a machine that can’t make a request get a signed blob bound to its fingerprint? Through a file exchange — the offline activation dance:

  1. Generate a request on the offline machine. The app produces a small request file containing the machine’s fingerprint (and the license key or order reference). No network involved; it just writes a file.
  2. Carry it out. A USB stick, an approved file-transfer channel, whatever the facility permits. The request goes to a connected machine.
  3. Submit it to the issuing service. From the connected machine — a browser hitting the vendor’s portal, or an admin console — the request is submitted. The service validates it, consumes a seat if appropriate, and returns a signed activation file bound to that exact fingerprint.
  4. Carry it back and import. The signed file returns to the offline machine on the same channel and is imported. The app verifies the signature locally and activates.

From then on the machine validates entirely offline, forever if need be. The connected step happened once, on a different machine, and moved only signed files — no live connection from the secure host ever existed. That’s what satisfies a security officer: the air-gapped machine’s network posture never changed.

For deployments where even that round trip is impractical, the simpler variant is to skip the request/response and issue a pre-bound file — the vendor issues a signed blob for a known fingerprint (or an unbound, non-node-locked file for a fixed number of installs) and ships it with the software. Less precise, but sometimes it’s the only workable flow.

Expiry and the clock-tampering problem

A subscription or trial carries an expiryUtc, and offline validation just compares it to the current time — easy. The obvious attack is just as easy: roll the system clock backward and a trial never ends, or an expired license springs back to life. An air-gapped machine has no time server to appeal to, so the defence has to be local.

The standard approach is monotonic time tracking: the app remembers the latest time it has legitimately seen, and treats a clock that jumps meaningfully backward as tampering, refusing to validate until the time is corrected. A small tolerance window (a day or so) absorbs honest clock adjustments and time-zone quirks without flagging them. Perpetual licenses, having no expiry, aren’t subject to the check at all. It’s not bulletproof — nothing client-side is — but it turns “just change the date” from a trivial bypass into something that visibly breaks the license.

Grace windows in disconnected setups

Grace is usually discussed for intermittently connected clients — cache a lease, survive a brief outage — and I covered that pattern in the offline-validation post. In a fully air-gapped deployment the concept shifts: there’s no lease to refresh, so “grace” becomes about how you handle the transition around expiry.

Two humane practices matter here. First, warn early: because there’s no server to email a renewal reminder, the client itself should surface “your license expires in 14 days” well ahead, since re-issuing an air-gapped license is a multi-day physical process, not a one-click renewal. Second, consider a short read-only or reduced-function grace period after expiry rather than an instant hard stop, so a lapsed license in a secure facility doesn’t halt critical work while the paperwork for a new signed file works its way through. Both are about respecting that, in these environments, renewal has real latency.

Revocation without a network

The one thing offline genuinely can’t do instantly is revoke. A blob signed last year knows nothing about a refund or leak last week; its payload is fixed at issue time. The offline answer is a signed revocation list: a list of revoked license ids, itself signed by your private key so the client trusts it, distributed with a software update or as an out-of-band file the facility imports. The client checks incoming licenses against the list and drops revoked ones.

It moves at the speed of your distribution — days or weeks, not seconds — but it means an air-gapped install isn’t defenceless against a known-bad key. For most air-gapped customers, whose update cadence is deliberate anyway, that’s an acceptable trade.

Being honest about the limits

Offline validation proves authenticity, integrity, binding and expiry beautifully, and it’s the right tool for air-gapped and enterprise deployments — but it’s not magic. The check runs on the customer’s machine, so a determined attacker with a decompiler can patch it out, and offline revocation is never instant. That’s not a reason to avoid offline licensing; it’s a reason to be precise about its job and to pair the client check with obfuscation so patching costs real effort (see protecting your licensing checks). And remember the distinction from self-hosted vs cloud licensing: serving air-gapped customers only needs signed offline files from your issuing service — you self-host the service only when the issuing service itself must run disconnected.

Doing this with Keyright

Keyright is offline-first by design, so air-gapped deployments are a supported path, not an afterthought. It issues signed offline license files that verify locally against a public key you embed at build time — no network call, so they work on fully disconnected machines — node-locks them with tolerance, defends against clock tampering, and ships signed revocation lists you distribute out of band. Every tenant gets its own isolated signing key whose private half never leaves the server, and the SDKs for .NET, Node, Python and Java fail closed throughout. When the issuing service itself must live inside an isolated network, Keyright self-hosts — the identical build, fully air-gapped, on your own database.

Start on the free managed plan to wire up offline validation before you pay a cent — see the plans — or, if your deployment is fully air-gapped end to end, read how self-hosting works and talk to us about an on-prem instance.

Try Nebula.NET

Harden your .NET code in minutes — start with the free edition.