Decide: is air-gapped licensing a shipped design feature? If yes, document the manual key lifecycle #91

Closed
opened 2026-07-31 09:57:30 +00:00 by Cordy · 3 comments
Owner

Deferred product decision from Nikola (2026-07-31), parked for later — do not implement anything yet.

Question: offline/air-gapped operation is currently a first-class property of the license mechanism (Ed25519 verified offline against the embedded public key; license.checkinUrl unset ⇒ no check-in, test-pinned as of #89). It's load-bearing in the ARCHITECTURE.md §8.1 rationale ("fully offline-capable — non-negotiable for the air-gapped and public-sector deployments the sovereignty positioning attracts") and in the Enterprise/Public-sector tier ("offline licensing"). But is it something real customers will actually use, and that we want to commit to shipping and supporting?

If yes (kept as a design feature), the docs owe customers the full manual lifecycle, which is currently written down nowhere:

  • An air-gapped instance can never receive renewals, seat changes, or revocations — the key string is a sealed snapshot, and without check-in nothing ever replaces it.
  • Therefore the operator must manually re-ingest a fresh key every time the license expires or changes (renewal → new expiry → new string; seat change → new string). Yearly billing is strongly preferable to monthly for these customers — twelve hand-carried strings a year is not a workable product experience.
  • Revocation is unenforceable offline (known and accepted per §8's honest-constraint paragraph, but it should be stated in the air-gapped context too).
  • Where to document: site docs licensing section + ARCHITECTURE.md §8.1 + probably a short operator note in the license panel copy.

If no (dropped): simplify the posture — e.g. require a check-in URL for licensed operation above the free tier — and adjust §8.1, the tier table ("offline licensing" line), and the sales pitch accordingly. Note this would weaken the sovereignty story that motivated offline verification in the first place, so the decision isn't purely technical.

Current mechanics for reference (all shipped): free tier ≤50 users needs no key and never phones home; licensed + check-in URL auto-maintains itself monthly (#89) with on-demand refresh (#86); licensed + no URL = air-gapped mode, works but frozen at the installed snapshot.

Deferred product decision from Nikola (2026-07-31), parked for later — do not implement anything yet. **Question:** offline/air-gapped operation is currently a first-class property of the license mechanism (Ed25519 verified offline against the embedded public key; `license.checkinUrl` unset ⇒ no check-in, test-pinned as of #89). It's load-bearing in the ARCHITECTURE.md §8.1 rationale ("fully offline-capable — non-negotiable for the air-gapped and public-sector deployments the sovereignty positioning attracts") and in the Enterprise/Public-sector tier ("offline licensing"). But is it something real customers will actually use, and that we want to commit to shipping and supporting? **If yes (kept as a design feature), the docs owe customers the full manual lifecycle, which is currently written down nowhere:** - An air-gapped instance can never receive renewals, seat changes, or revocations — the key string is a sealed snapshot, and without check-in nothing ever replaces it. - Therefore the operator must manually re-ingest a fresh key **every time the license expires or changes** (renewal → new expiry → new string; seat change → new string). Yearly billing is strongly preferable to monthly for these customers — twelve hand-carried strings a year is not a workable product experience. - Revocation is unenforceable offline (known and accepted per §8's honest-constraint paragraph, but it should be stated in the air-gapped context too). - Where to document: site docs licensing section + ARCHITECTURE.md §8.1 + probably a short operator note in the license panel copy. **If no (dropped):** simplify the posture — e.g. require a check-in URL for licensed operation above the free tier — and adjust §8.1, the tier table ("offline licensing" line), and the sales pitch accordingly. Note this would weaken the sovereignty story that motivated offline verification in the first place, so the decision isn't purely technical. **Current mechanics for reference** (all shipped): free tier ≤50 users needs no key and never phones home; licensed + check-in URL auto-maintains itself monthly (#89) with on-demand refresh (#86); licensed + no URL = air-gapped mode, works but frozen at the installed snapshot.
Author
Owner

it is a shipped design feature. yes, interesting for any structures that require air-gapped instances

it is a shipped design feature. yes, interesting for any structures that require air-gapped instances
Author
Owner

Triage: tandem (decision → then I document). Decide whether air-gapped licensing (config/env key, no check-in) is a supported, shipped feature. If yes, I document the manual key lifecycle (issue/renew/seat-change without phone-home) in the handbook (#109). No code needed — the offline path already exists; this is a product/positioning call.

**Triage: tandem (decision → then I document).** Decide whether air-gapped licensing (config/env key, no check-in) is a supported, shipped feature. If yes, I document the manual key lifecycle (issue/renew/seat-change without phone-home) in the handbook (#109). No code needed — the offline path already exists; this is a product/positioning call.
Cordy closed this issue 2026-08-16 12:33:21 +00:00
Author
Owner

Decision: yes, air-gapped licensing is a shipped design feature — documented. docs/handbook/licensing-faq.md's air-gapped answer is expanded into the full manual key lifecycle: run with license.checkinUrl unset; every renewal and seat change re-signs the same license id into a new string that must be hand-re-pasted before expiry; revocation is unenforceable offline (accepted, documented limit); annual billing is strongly preferred (twelve hand-carried strings a year isn't workable); and where to get the re-signed key. Closing.

Decision: **yes, air-gapped licensing is a shipped design feature** — documented. `docs/handbook/licensing-faq.md`'s air-gapped answer is expanded into the full manual key lifecycle: run with `license.checkinUrl` unset; every **renewal** and **seat change** re-signs the same license id into a new string that must be hand-re-pasted before expiry; **revocation is unenforceable offline** (accepted, documented limit); **annual billing is strongly preferred** (twelve hand-carried strings a year isn't workable); and where to get the re-signed key. Closing.
Sign in to join this conversation.
No labels
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/Cairn#91
No description provided.