License file verification (Ed25519, offline) #24

Closed
opened 2026-07-25 16:26:05 +00:00 by Cordy · 1 comment
Owner

ARCHITECTURE §8.1. A license is a signed file: JSON claims (customer, tier, max users, expiry) + Ed25519 signature, verified locally against a public key embedded in the binary. Fully offline-capable.

  • Client behavior: check ≤ once daily, cache last good result, on failure warn for 30 days before limiting only administrative actions — never lock data access.
  • Fair-code enforcement is legal/social, not a hard technical lock.

Licensing plumbing (kept in v0.4 with the product; public launch materials moved to v0.5).

ARCHITECTURE §8.1. A license is a signed file: JSON claims (customer, tier, max users, expiry) + Ed25519 signature, verified locally against a public key embedded in the binary. **Fully offline-capable.** - Client behavior: check ≤ once daily, cache last good result, on failure warn for 30 days before limiting only *administrative* actions — never lock data access. - Fair-code enforcement is legal/social, not a hard technical lock. Licensing plumbing (kept in v0.4 with the product; public launch materials moved to v0.5).
Author
Owner

Scope expanded (Nikola, 2026-07-26) — full enforcement model:

Free tier: ≤50 users, no license, no phone-home ever. The 50-user boundary is the only trigger.

Seat counting: distinct authenticated usernames. Local mode: config users count. OIDC/LDAP: distinct usernames ever seen, persisted in a small JSON on /data (same pattern as app-passwords) — no DB.

Over 50 without a valid license:

  • The 51st+ new user cannot sign in (401/403 with a clear "seat limit" message). Existing ≤50 users are never locked out.
  • Admin UI shows a persistent warning + buy prompt (link to the site).

With a license (Ed25519-signed license file, verified offline — this issue's core):

  • License carries: org, seat count, expiry, key ID, signature. Public key embedded in the cairnd binary; verification needs no network.
  • Check-in: once per month against the license server, only when >50 users (≤50 never phones home even with a key pasted).
  • Check-in failure (connectivity): retry once per day for 7 days. During the grace window the admin sees a warning banner. After 7 failed days → revert to unlicensed behavior (block sign-ins above 50 + admin warning). Never lock existing users out, never block reads — enforcement only ever gates new sign-ins above the free tier.
  • Check-in rejected by the server (revoked/expired) → same unlicensed behavior immediately, distinct admin message.

Admin UI (Settings → License, admin-gated): paste the license key, show status (licensed org, seats used/max, expiry, last successful check-in, grace-window countdown if failing).

Related: server side in cairn-license-server (repo created, #25), purchase/account flow on the landing page (cairn-site repo, #26). Still blocked on the signing keypair (M1).

**Scope expanded (Nikola, 2026-07-26) — full enforcement model:** **Free tier:** ≤50 users, no license, no phone-home ever. The 50-user boundary is the only trigger. **Seat counting:** distinct authenticated usernames. Local mode: config `users` count. OIDC/LDAP: distinct usernames ever seen, persisted in a small JSON on `/data` (same pattern as app-passwords) — no DB. **Over 50 without a valid license:** - The 51st+ *new* user cannot sign in (401/403 with a clear "seat limit" message). Existing ≤50 users are never locked out. - Admin UI shows a persistent warning + buy prompt (link to the site). **With a license (Ed25519-signed license file, verified offline — this issue's core):** - License carries: org, seat count, expiry, key ID, signature. Public key embedded in the cairnd binary; verification needs no network. - **Check-in: once per month** against the license server, only when >50 users (≤50 never phones home even with a key pasted). - **Check-in failure (connectivity): retry once per day for 7 days.** During the grace window the admin sees a warning banner. After 7 failed days → revert to unlicensed behavior (block sign-ins above 50 + admin warning). **Never lock existing users out, never block reads** — enforcement only ever gates *new* sign-ins above the free tier. - Check-in rejected by the server (revoked/expired) → same unlicensed behavior immediately, distinct admin message. **Admin UI (Settings → License, admin-gated):** paste the license key, show status (licensed org, seats used/max, expiry, last successful check-in, grace-window countdown if failing). Related: server side in cairn-license-server (repo created, #25), purchase/account flow on the landing page (cairn-site repo, #26). Still blocked on the signing keypair (M1).
Cordy closed this issue 2026-07-26 17:11:27 +00:00
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#24
No description provided.