Lizenz-API & Entitlement-Vertrag
Verbindliche Schnittstelle zwischen dem JuByte-Lizenzsystem (JuByteWeb) und dem Nexus Core. Siehe auch den allgemeinen API-Vertrag.
Das Entitlement-Dokument (JWS, RS256)
JuByteWeb stellt ein kompaktes JWS (RS256) aus, das der Core mit seinem
Public-Key offline verifiziert (io.jsonwebtoken). Header: {"alg":"RS256","typ":"JWT"}.
Claims:
| Claim | Typ | Bedeutung |
|---|---|---|
plan | string | Planname (z. B. free, pro, enterprise) |
status | string | active, suspended, revoked, … |
expires | string (ISO-8601, …Z) | Ablauf; immer gesetzt, gekappt auf min(Lizenzablauf, jetzt+ENTITLEMENT_TTL_DAYS) |
limits | object<string, number> | z. B. {servers, players, apiRph}; -1 = unbegrenzt |
features | string[] | Feature-Flags des Plans |
modules | string[] | freigeschaltete module_ids, oder ["*"] für alle |
premium_modules | string[] | lizenzpflichtige module_ids (im JuByteStore-Admin markiert) — der Core sperrt jedes davon, das nicht in modules steht |
grace_hours | number | Kulanzzeit nach expires (Default 72) |
Modul-Lizenzierung (welche Module eine Lizenz brauchen)
Ob ein Modul eine Lizenz benötigt, wird im JuByteStore-Admin pro Modul gesetzt
(nexus_modules.premium) — nicht im ACP (Kundenoberfläche). Diese Menge wandert
als premium_modules signiert ins Entitlement, ist also für Kunden nicht
manipulierbar. Der Core setzt sie zentral durch:
- Ein Modul gilt als lizenzpflichtig, wenn es im
premium_modules-Claim steht oder sein Manifestpremium=truedeklariert. - Ist ein lizenzpflichtiges Modul nicht in
modulesfreigeschaltet, blockt ein Interceptor jeden Aufruf/api/v1/modules/{id}/…mit403 NX-2001— unabhängig davon, ob das Modul selbst prüft (nicht umgehbar). Aktivieren ist ebenfalls gesperrt. - Ein Produkt kann mehrere Module direkt verkaufen (Produkt↔Module-Zuordnung, kein Bundle nötig): Der Kauf schaltet alle verknüpften Module frei (
modules).
Damit die Sperre auch ohne aktive Lizenz (Free-Tier) greift, holt der Core die
lizenzpflichtige Menge zusätzlich als signierten Modul-Katalog:
GET /api/v1/modules/catalog (öffentlich, signiertes JWS mit premium_modules).
Der Core ruft ihn beim Start und im Refresh-Intervall ab, verifiziert ihn mit dem
Public-Key und wendet ihn an — so ist ein im Store als lizenzpflichtig markiertes
Modul sofort überall gesperrt, ohne dass ein Kunde es lokal umgehen kann.
Regeln (sonst verwirft der Verifier still → Free-Tier):
limits-Werte müssen Zahlen sein; expires ein ISO-Instant; kein
Standard-exp-Claim (würde JJWT erzwingen und Neustarts brechen).
Lizenzpläne
limits.servers und limits.players ergeben sich aus dem Plan (-1 = unbegrenzt).
Der Core setzt sie durch: ein neuer Server — sowohl beim Provisionieren
(POST /api/v1/servers/provision) als auch beim Bridge-Handshake (NX-3010
bei Überschreitung) — bzw. ein neuer Spieler beim Join (über dem Limit → der
Spieler wird nicht registriert, kein harter Kick, Hinweis im ACP via
license.limit.exceeded).
| Plan | limits.servers | limits.players | Preis |
|---|---|---|---|
| Free | 1 | 15 | 0 € |
| Pro | 50 | 1500 | 20 € einmalig |
| Enterprise | unbegrenzt (-1) | unbegrenzt (-1) | 100 € / Monat |
Die Preise sind als ProductPriceTier-Einträge des Produkts nexus-core
hinterlegt (Tier-slug = Planname free/pro/enterprise). duration_days
steuert die Laufzeit: leer = einmalig (Pro), 30 = monatlich (Enterprise) — beim
Kauf-Capture wird daraus License.expiresAt abgeleitet.
JuByteWeb – öffentliche & Admin-Endpoints
| Methode | Pfad | Auth | Beschreibung |
|---|---|---|---|
| POST | /api/v1/license/issue | – (Rate-Limit 30/min/IP) | {key, serverId} → signiertes JWS. Erzwingt Server-Bindung + maxServers wie /verify. |
| POST | /api/v1/license/verify | – | Klassische Plugin-Verifikation (unverändert; kein JWS) |
| GET/PUT | /api/admin/licenses/{id}/modules | admin.licenses.{view|manage} | Modul-Grants einer Lizenz lesen/setzen ({moduleIds:[…]}) |
| GET | /api/admin/licenses/{id}/entitlement | admin.licenses.view | Signiertes JWS einer Lizenz zum Kopieren |
| GET/POST | /api/admin/modules | admin.licenses.{view|manage} | Registry lesen / neues Modul registrieren |
/issue-Antwort: { jws, plan, status, modules, features, expiresAt, graceHours }.
Fehler: 404 Lizenz unbekannt · 403 PLAN_LIMIT_REACHED/Lizenz inaktiv ·
400 ungültiges Key-Format · 503 ENTITLEMENT_SIGNING_DISABLED
(kein LICENSE_SIGNING_PRIVATE_KEY).
Nexus Core – Lizenz-Endpoints (/api/v1/license)
| Methode | Pfad | Auth | Beschreibung |
|---|---|---|---|
| GET | /api/v1/license | – | Zusammengeführte Entitlements über alle Lizenzen (plan, status, expiresAt, limits, features, modules) |
| GET | /api/v1/license/settings | core.license.manage | + maskierter Core-Schlüssel, Bereitschaftsflags und licenses[] — alle aktiven Lizenzen mit kind: CORE|MODULE (Token-Übersicht) |
| POST | /api/v1/license/activate-key | core.license.manage | {key} → holt JWS bei JuByteWeb, erkennt automatisch Core- vs. Modul-Lizenz (kind in der Antwort) und fügt sie zu den aktiven Lizenzen hinzu; der Key wird gemerkt |
| POST | /api/v1/license/activate | core.license.manage | {key:"<JWS>"} → JWS direkt einspielen (manuelles Einfügen) |
| POST | /api/v1/license/refresh | core.license.manage | Alle gemerkten Keys neu abholen; eine auf dem Server gelöschte (404) oder widerrufene/gesperrte/abgelaufene (403) Lizenz wird dabei einzeln entfernt — alle anderen bleiben aktiv |
| DELETE | /api/v1/license/keys/{id} | core.license.manage | Eine einzelne Lizenz (Core oder Modul) entfernen |
| DELETE | /api/v1/license/key | core.license.manage | Alle Lizenzen entfernen → zurück auf Free-Tier |
Mehrere Lizenzen & automatische Erkennung: Es können beliebig viele Lizenzen
gleichzeitig aktiv sein. Jede wird beim Aktivieren automatisch klassifiziert:
Eine Core-Lizenz (ihr Entitlement enthält das Modul acp bzw. * bei der
Dev-Lizenz) bestimmt Plan, Status, Limits und Laufzeit der Token-Übersicht.
Eine Modul-Lizenz schaltet nur ihre Module frei — sie ändert weder Plan noch
Limits. Ohne Core-Lizenz gilt der Free-Plan, freigeschaltete Module aus
Modul-Lizenzen bleiben trotzdem nutzbar. Eine abgelaufene Lizenz (nach Ablauf
der Grace-Zeit) wird einzeln entfernt und lässt alle anderen unberührt.
Fehler werden als NX-1042 (Validation) mit Klartextgrund zurückgegeben, z. B.
„Lizenzserver-Fehler (HTTP 500)", „Lizenzschlüssel unbekannt", „Public-Key passt
nicht …".
Core-Konfiguration (nexus.license.*)
| Property / Env | Default | Zweck |
|---|---|---|
public-key / NEXUS_LICENSE_PUBLIC_KEY | zentral eingebacken | X.509-SPKI (DER, base64) zum Verifizieren |
server-url / NEXUS_LICENSE_SERVER_URL | https://test.jubyte.com | Basis-URL von JuByteWeb |
key / NEXUS_LICENSE_KEY | leer | Lizenzschlüssel → Auto-Aktivierung beim Start |
server-id / NEXUS_LICENSE_SERVER_ID | autogeneriert (<data-dir>/instance.id) | Server-Bindung für maxServers |
refresh-minutes / NEXUS_LICENSE_REFRESH_MINUTES | 360 | Refresh-Intervall |
free-servers / NEXUS_LICENSE_FREE_SERVERS | 1 | Server-Limit ohne Lizenz (Free-Plan); -1 = unbegrenzt |
free-players / NEXUS_LICENSE_FREE_PLAYERS | 15 | Spieler-Limit ohne Lizenz (Free-Plan); -1 = unbegrenzt |
Ohne aktive Lizenz läuft der Core im Free-Plan (1 Server / 15 registrierte Spieler) — genau das zeigt die Token-Übersicht im ACP. Eine aktivierte Pro-/ Enterprise-Lizenz überschreibt die Limits über ihr signiertes Entitlement.
JuByteWeb-Konfiguration
| Env | Zweck |
|---|---|
LICENSE_SIGNING_PRIVATE_KEY | RSA-Privatschlüssel (base64 PKCS#8) zum Signieren. Leer → /issue = 503. Per npm run license:keygen. |
ENTITLEMENT_TTL_DAYS | Max. Laufzeit des ausgestellten Dokuments (Default 14). Kürzer = Widerruf greift schneller offline. |
Persistierte Dateien (Core, unter <data-dir>)
license.jws– zuletzt aktiviertes Dokument (Wiederherstellung beim Start),0600license.key– gemerkter Lizenzschlüssel für Refresh,0600instance.id– stabile Server-Kennung für die Bindung
Datenmodell (JuByteWeb)
nexus_modules(module_idunique,name,kindCORE/MODULE,premium,active,sort_order)license_module_grants(license_id×nexus_module_id, unique)