Offline & air-gapped activation¶
GRCFlow never contacts a licence server. A licence key is an Ed25519-signed claim that the backend verifies locally against a public key shipped with the product, so an install with no route to the internet activates exactly like one that is online.
There is no activation call, no phone-home, no periodic re-check, and nothing that expires because a network was unreachable.
What that means in practice¶
| Network needed to activate | No |
| Network needed to keep running | No |
| Bound to a machine / MAC / CPU | No — move it between hosts freely |
| Where the key is stored | /etc/sovereign-grc/license.key |
| Survives restart | Yes — read from disk at startup |
Because keys are not hardware-bound, you can buy on a laptop, receive the key by email, and carry it to the air-gapped server on whatever media your policy allows.
Activating with no internet¶
- Buy from any internet-connected device. Checkout emails the signed key to the address on the order.
- Move the key to the air-gapped host. It is a single line of text — copy it via the same channel you use for any other config material.
- Apply it, either way:
Through the UI: Setup → License → paste the key.
Or with curl, from the host itself:
curl -X POST http://localhost:8000/api/v1/setup/license \
-H "Content-Type: application/json" \
-d '{"license_key":"<paste the key here>"}'
- Confirm it took. The response reports the tier, seat count and expiry. It is also
written to
/etc/sovereign-grc/license.keyand reloaded on every restart.
If the key is rejected, the error distinguishes the cases: Invalid signature means the
key was altered or is not ours; License expired means the term has ended;
System clock appears to have been tampered with is covered below.
Why it works without a network¶
The backend holds only the public half of the signing key:
LICENSE_PUBLIC_KEY_HEX=302a300506032b6570032100bd52efaf3c8a0c8940a09d0dc5de5d7cd43802ec4a0d12b65a67b01cce9a27fe
That value ships in deploy/public/.env.example and as the default in
deploy/public/docker-compose.yml, so a stock install already has it. Publishing it is
safe and deliberate — a public key can verify a signature but cannot create one. Only
the vendor's private half, held in the mint service, can sign a licence.
Do not blank this value
If LICENSE_PUBLIC_KEY_HEX is unset, the backend cannot verify signatures and
rejects every licence with "License validation unavailable — public key not
configured" — including keys you have paid for. A legitimate key then fails
indistinguishably from a forged one.
Clock integrity on air-gapped hosts¶
An offline host has no NTP, so its clock can drift or be set backwards — accidentally or
to extend an expired term. GRCFlow keeps a high-water mark at
/etc/sovereign-grc/hwm.dat recording the furthest point in time it has seen. If the
system clock later reads meaningfully earlier than that mark, validation returns
CLOCK_TAMPER rather than silently accepting the licence.
If you hit this after a legitimate clock correction — a CMOS battery replacement, or a
VM restored from an old snapshot — set the clock correctly and remove hwm.dat; it is
rebuilt on the next validation.
Community edition¶
The Community edition needs no key at all. It self-activates when the backend runs with
AUTH_MODE=local, and its licence rolls forward automatically while the backend runs,
so an offline install never hits an expiry cliff. See Pricing.