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:

ClaimTypBedeutung
planstringPlanname (z. B. free, pro, enterprise)
statusstringactive, suspended, revoked, …
expiresstring (ISO-8601, …Z)Ablauf; immer gesetzt, gekappt auf min(Lizenzablauf, jetzt+ENTITLEMENT_TTL_DAYS)
limitsobject<string, number>z. B. {servers, players, apiRph}; -1 = unbegrenzt
featuresstring[]Feature-Flags des Plans
modulesstring[]freigeschaltete module_ids, oder ["*"] für alle
premium_modulesstring[]lizenzpflichtige module_ids (im JuByteStore-Admin markiert) — der Core sperrt jedes davon, das nicht in modules steht
grace_hoursnumberKulanzzeit 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 Manifest premium=true deklariert.
  • Ist ein lizenzpflichtiges Modul nicht in modules freigeschaltet, blockt ein Interceptor jeden Aufruf /api/v1/modules/{id}/… mit 403 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).

Planlimits.serverslimits.playersPreis
Free1150 €
Pro50150020 € einmalig
Enterpriseunbegrenzt (-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

MethodePfadAuthBeschreibung
POST/api/v1/license/issue– (Rate-Limit 30/min/IP){key, serverId} → signiertes JWS. Erzwingt Server-Bindung + maxServers wie /verify.
POST/api/v1/license/verifyKlassische Plugin-Verifikation (unverändert; kein JWS)
GET/PUT/api/admin/licenses/{id}/modulesadmin.licenses.{view|manage}Modul-Grants einer Lizenz lesen/setzen ({moduleIds:[…]})
GET/api/admin/licenses/{id}/entitlementadmin.licenses.viewSigniertes JWS einer Lizenz zum Kopieren
GET/POST/api/admin/modulesadmin.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)

MethodePfadAuthBeschreibung
GET/api/v1/licenseZusammengeführte Entitlements über alle Lizenzen (plan, status, expiresAt, limits, features, modules)
GET/api/v1/license/settingscore.license.manage+ maskierter Core-Schlüssel, Bereitschaftsflags und licenses[] — alle aktiven Lizenzen mit kind: CORE|MODULE (Token-Übersicht)
POST/api/v1/license/activate-keycore.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/activatecore.license.manage{key:"<JWS>"} → JWS direkt einspielen (manuelles Einfügen)
POST/api/v1/license/refreshcore.license.manageAlle 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.manageEine einzelne Lizenz (Core oder Modul) entfernen
DELETE/api/v1/license/keycore.license.manageAlle 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 / EnvDefaultZweck
public-key / NEXUS_LICENSE_PUBLIC_KEYzentral eingebackenX.509-SPKI (DER, base64) zum Verifizieren
server-url / NEXUS_LICENSE_SERVER_URLhttps://test.jubyte.comBasis-URL von JuByteWeb
key / NEXUS_LICENSE_KEYleerLizenzschlüssel → Auto-Aktivierung beim Start
server-id / NEXUS_LICENSE_SERVER_IDautogeneriert (<data-dir>/instance.id)Server-Bindung für maxServers
refresh-minutes / NEXUS_LICENSE_REFRESH_MINUTES360Refresh-Intervall
free-servers / NEXUS_LICENSE_FREE_SERVERS1Server-Limit ohne Lizenz (Free-Plan); -1 = unbegrenzt
free-players / NEXUS_LICENSE_FREE_PLAYERS15Spieler-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

EnvZweck
LICENSE_SIGNING_PRIVATE_KEYRSA-Privatschlüssel (base64 PKCS#8) zum Signieren. Leer → /issue = 503. Per npm run license:keygen.
ENTITLEMENT_TTL_DAYSMax. 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), 0600
  • license.key – gemerkter Lizenzschlüssel für Refresh, 0600
  • instance.id – stabile Server-Kennung für die Bindung

Datenmodell (JuByteWeb)

  • nexus_modules (module_id unique, name, kind CORE/MODULE, premium, active, sort_order)
  • license_module_grants (license_id × nexus_module_id, unique)