SDK-Integration
Node.js-SDK-Integration
Keyright-Lizenzierung zu einer Node.js-App hinzufügen: das keyright-Paket installieren, Lizenzen offline prüfen, online aktivieren, Funktionen über Entitlements schützen und Testversionen sowie netzwerkisolierte (air-gapped) Maschinen handhaben — konstruktionsbedingt fail-closed.
Dies ist das Node.js-Gegenstück zum .NET-SDK-Rundgang — dieselben Lizenz- und Lease-Formate, derselbe öffentliche Schlüssel, dieselben fail-closed-Garantien, aber für eine Node-Laufzeit. Das Paket keyright prüft Lizenzen offline gegen Ihren eingebetteten öffentlichen Schlüssel und aktiviert online, wenn Sie Platzdurchsetzung und Widerruf möchten, um einen kurzlebigen signierten Lease zwischenzuspeichern. Jeder Codeblock unten verwendet die echte SDK-Oberfläche; kopieren Sie ihn unverändert.
Da alle Keyright-SDKs dieselben Drahtformate prüfen, kann eine mehrsprachige Produktlinie einen Keyright-Mandanten und einen öffentlichen Schlüssel teilen. Wenn Ihr Produkt außerdem eine .NET-, Python- oder Java-Komponente ausliefert, können alle exakt dieselben Schlüssel und Leases validieren.
Das Modell in einem Absatz
Sie stellen Lizenzschlüssel serverseitig in Keyright aus — jeder Mandant signiert sie mit einem privaten RSA-Schlüssel, der den Server nie verlässt. Ihre Node-App bettet nur den passenden öffentlichen Schlüssel ein und prüft Lizenzen offline, sodass eine Lizenzprüfung kein Netzwerk braucht. Für Platzdurchsetzung und Widerruf aktiviert die App zusätzlich online: Sie tauscht einen Lizenzschlüssel gegen einen kurzlebigen signierten Lease, der an die Maschine gebunden ist, und arbeitet dann offline weiter, bis dieser Lease abläuft. Alles, was nicht stimmt — ungültige Signatur, falsches Produkt, Ablauf, Widerruf, eine zurückgestellte Uhr — löst zur Edition Free auf, statt eine Ausnahme zu werfen. Es ist fail-closed.
Anbietereinrichtung (einmalig)
Die Dashboard-Schritte sind sprachunabhängig und identisch mit dem .NET-Leitfaden, daher hält diese Seite sie kurz:
- Legen Sie Ihr Produkt an und notieren Sie seinen Slug (z. B.
acme-app) — dieser String ist das, was Ihr Client alsproductsendet. - Kopieren Sie Ihren öffentlichen Schlüssel — den base64-kodierten
SubjectPublicKeyInfovom Tab Integration des Dashboards. Er wird in Ihrer App ausgeliefert und ist kein Geheimnis; er kann Signaturen nur prüfen, nie erzeugen. - Definieren Sie Ihre Tiers & Entitlements — jedes Tier trägt eine Platzanzahl und die benannten Flags/Limits, die in jede Lizenz dieses Tiers eingebacken werden.
- Stellen Sie Schlüssel aus — über das Dashboard, Ihren Abrechnungs-Webhook oder die Admin-API.
Die vollständige Version dieser Schritte (Dashboard-Screenshots, curl-Aufrufe, das Tier-/Entitlement-Modell und wie Schlüssel zu Kunden gelangen) finden Sie unter Erste Schritte sowie in den Schritten 1–4 und 8 des .NET-SDK-Rundgangs. Der Rest dieser Seite ist ausschließlich Node-Client-Code.
SDK installieren
keyright ist auf npm verfügbar (>= 1.1.4). Es ist ein CommonJS-Paket mit null Abhängigkeiten — die Signaturprüfung läuft über Nodes eingebautes crypto.
npm install keyright
const { KeyrightClient } = require('keyright');
Client initialisieren
Erzeugen Sie beim Start einen Client mit KeyrightClient.initialize. Die Optionen sind ein einfaches Objekt mit camelCase-Schlüsseln — es gibt keine Optionsklasse, die Sie mit new instanziieren müssten:
const { KeyrightClient } = require('keyright');
const license = KeyrightClient.initialize({
product: 'acme-app', // muss mit dem Produkt-Slug übereinstimmen, für den Sie Schlüssel ausstellen
publicKeyBase64: 'MIIBIjANBgkq...', // der base64-kodierte öffentliche Schlüssel aus der Anbietereinrichtung
serviceUrl: 'https://keyright.delta1labs.com', // weglassen, wenn Sie nur Offline-Lizenzdateien ausliefern
// Optional: einen alten Schlüssel während eines Rotationsfensters gültig halten
// additionalPublicKeysBase64: ['<previous public key>'],
});
Nur product und publicKeyBase64 sind erforderlich; initialize wirft sofort eine Ausnahme, wenn publicKeyBase64 fehlt, und parst den bzw. die Schlüssel vorab. serviceUrl wird nur für das Online-activate/deactivate benötigt.
Darüber hinaus ermittelt der Client eine Lizenz aus mehreren Quellen in Rangfolge (höchste zuerst):
| Option | Was sie ist |
|---|---|
licenseString | Ein Lizenz-JSON-String, den Sie direkt übergeben. |
licenseFilePath | Ein Pfad zu einer Lizenzdatei. |
envVarName | Name einer Umgebungsvariablen, die einen Lizenzdateipfad enthält. Setzen Sie ihn auf 'KEYRIGHT_LICENSE', um dieser Konvention zu folgen — sie wird nicht gelesen, solange Sie sie nicht benennen. |
configLicensePath | Ein aus der Konfiguration stammender Lizenzdateipfad. |
| (Standard) | Der App-Daten-Pfad des Betriebssystems …/Keyright/<product>/license.json. |
| (Lease) | Der zwischengespeicherte Aktivierungs-Lease — von activate für Sie geschrieben. |
Normalerweise setzen Sie keine davon — die Aktivierung schreibt den Lease-Cache automatisch. Weitere optionale Stellschrauben: revocationListJson / revocationListPath (eine signierte Widerrufsliste für Offline-Builds ausliefern), nodeLockTolerance (Standard 1), clockTamperToleranceHours (Standard 24), additionalPublicKeysBase64 und machineComponents (anpassen, was eine Maschine identifiziert).
Funktionen offline schützen
client.validate() ist synchron. Es ermittelt die beste Lizenz aus den obigen Quellen und prüft RSA-Signatur, Produktübereinstimmung, Node-Lock, Ablauf, die optionale mitgelieferte Widerrufsliste sowie den Testversions-/Uhrmanipulationsstatus — alles offline — und es wirft nie eine Ausnahme. Es liefert { info, source }: Destrukturieren Sie es. Bei jedem Fehler ist info eine LicenseInfo in der Edition free, die den Grund enthält, und source sagt Ihnen, welche Quelle gewonnen hat (z. B. 'lease', 'default', 'none').
const { info, source } = license.validate();
if (info.isPaid) { // true für jede Edition oberhalb von Free
// bezahlte Funktionen freischalten
}
if (info.edition === 'enterprise') {
// Enterprise-exklusive Funktionen freischalten
}
LicenseInfo stellt camelCase-Felder und Getter bereit: status, isValid, isPaid, isTrial, edition, licensee, message, expiryUtc (ein Date oder null), daysRemaining (ein Getter, null, wenn es keinen Ablauf gibt), statusBadge (z. B. "Enterprise Trial") und entitlements. status ist einer der kleingeschriebenen LicenseStatus-Strings: 'valid', 'no_license', 'malformed', 'signature_invalid', 'expired', 'machine_mismatch', 'revoked', 'clock_tampered'. edition ist 'free', 'licensed' oder 'enterprise'.
Schützen Sie einzelne Funktionen über Entitlements statt über die Edition, damit eine Änderung der Tier-Vorlage nicht bedeutet, neuen Code auszuliefern. info.entitlements ist ein EntitlementSet mit isEnabled(name) und getLimit(name, fallback), und beide sind fail-closed:
const { info } = license.validate();
// Boolesches Flag — fehlend/nicht parsbar gilt als deaktiviert
if (info.entitlements.isEnabled('export')) {
showExportCommand();
}
// Numerisches Limit — den fail-closed-Fallback geben Sie selbst an; 'unlimited' gilt als sehr große Zahl
const maxProjects = info.entitlements.getLimit('max-projects', 1);
if (currentProjectCount >= maxProjects) {
promptToUpgrade();
}
isEnabled wertet 'true', '1', 'yes', 'enabled' (ohne Beachtung der Groß-/Kleinschreibung) als eingeschaltet; alles andere oder ein fehlendes Flag ist ausgeschaltet. getLimit liefert Ihren fallback für einen fehlenden oder nicht ganzzahligen Wert und die größte sichere Ganzzahl für das Literal 'unlimited'. Entitlement-Namen unterscheiden nicht zwischen Groß- und Kleinschreibung.
Der Client bietet außerdem Einmal-Komfortwrapper, die an Ort und Stelle validieren — license.isEnabled('export'), license.getLimit('max-projects', 1), license.entitlements() und license.edition() —, aber wenn Sie mehrere Entitlements auf einmal prüfen, rufen Sie validate() einmal auf und verwenden Sie info.entitlements wieder, um wiederholte Prüfarbeit zu vermeiden.
Um herauszufinden, warum eine Prüfung fehlgeschlagen ist (für einen „Registrieren“-Dialog oder Diagnosen), lesen Sie info.status und die für Menschen gedachte info.message.
Online mit einem Lizenzschlüssel aktivieren
Wenn ein Kunde einen Schlüssel eingibt, rufen Sie await client.activate(key) auf. Es sendet den Schlüssel plus eine stabile Maschinen-ID per POST an <serviceUrl>/v1/activate; der Service belegt einen Platz und gibt einen kurzlebigen, an diese Maschine gebundenen signierten Lease zurück. Das SDK prüft den Lease gegen Ihren eingebetteten öffentlichen Schlüssel und speichert ihn lokal zwischen, sodass jedes spätere validate() ohne Netzwerk erfolgreich ist, bis die Kulanzfrist des Lease abläuft.
activate liefert eine LicenseInfo und wirft für die gewöhnlichen Fehlerpfade (ungültiger Schlüssel, Platzlimit, offline, widerrufen) keine Ausnahme — es liefert wie validate() eine fail-closed LicenseInfo. Prüfen Sie das Ergebnis:
const info = await license.activate(customerEnteredKey);
if (info.isValid && info.isPaid) {
// Aktiviert. Der Lease ist zwischengespeichert; die App arbeitet jetzt offline, bis er abläuft.
showLicensedUi(info.statusBadge); // z. B. "Enterprise" oder "Enterprise Trial"
} else {
// info.message anzeigen; die App bleibt im Modus Free.
showActivationError(info.message); // "All seats for this license are in use." usw.
}
- Plätze werden serverseitig durchgesetzt. Das Aktivieren von mehr Maschinen, als die Lizenz erlaubt, liefert ein Platzlimit-Ergebnis (
message: "All seats for this license are in use.") und keinen Lease. Das erneute Aktivieren einer bereits gebundenen Maschine ist idempotent — es wird kein zusätzlicher Platz belegt. - Offline-Kulanz. Ist der Service nicht erreichbar, greift
activateauf einen noch gültigen zwischengespeicherten Lease zurück und liefert ihn, sodass ein kurzer Ausfall den Benutzer nicht aussperrt. Nur wenn kein gültiger zwischengespeicherter Lease vorhanden ist, liefert es eine Free-Info mit dem Verbindungsfehler inmessage. - Widerruf wird bei der nächsten Aktualisierung wirksam.
activate wirft nur bei Programmierfehlern eine Ausnahme: eine fehlende serviceUrl oder ein leerer Schlüssel.
Die gesendete Maschinen-ID ist MachineFingerprint.current().toBoundString() — ein stabiler Fingerabdruck pro Gerät. nodeLockTolerance (Standard 1) lässt eine getauschte Netzwerkkarte oder Festplatte durchgehen, ohne den Benutzer auszusperren.
Deaktivieren — einen Platz freigeben
Um den Platz dieser Maschine freizugeben (bevor Sie einen Rechner außer Betrieb nehmen oder damit der Kunde die Lizenz umziehen kann), rufen Sie await client.deactivate(key) auf. Es sendet { key, machineId, product } per POST an <serviceUrl>/v1/free-seat und liefert den Status-String — 'ok' oder 'not_found'. Bei 'ok' löscht es außerdem den lokal zwischengespeicherten Lease, sodass das nächste validate() auf Free zurückfällt.
Anders als activate hat deactivate keinen Offline-Fallback: Schlägt der HTTP-Aufruf fehl, wirft es eine Ausnahme. Kapseln Sie es:
try {
const status = await license.deactivate(customerEnteredKey);
if (status === 'ok') {
// Platz freigegeben und lokaler Lease gelöscht.
} else {
// 'not_found' — für diesen Schlüssel/diese Maschine war nichts gebunden.
}
} catch (err) {
// Issuing-Service nicht erreichbar — erneut versuchen, sobald wieder online.
}
Offline- & netzwerkisolierte (air-gapped) Maschinen
Für eine Maschine, die den Service nie erreichen kann, signiert ein Operator einen Offline-Lease für deren Maschinen-ID (Dashboard Licenses → offline lease oder POST /admin/licenses/{id}/offline-lease) und liefert das JSON per Datei aus. Die Maschinen-ID, die Sie dem Operator geben, ist exakt das, was das SDK ableitet:
const { MachineFingerprint } = require('keyright');
console.log(MachineFingerprint.current().toBoundString());
Importieren Sie den signierten Lease mit client.importOfflineLease(json). Es prüft den Lease über denselben Codepfad, den ein zwischengespeicherter Lease verwendet (Signatur, Produkt, Maschine, Ablauf), speichert ihn zwischen, damit spätere Offline-validate()-Aufrufe erfolgreich sind, und liefert eine LicenseInfo. Anders als activate wirft es eine Ausnahme, wenn der Lease ungültig ist — behandeln Sie sie:
const fs = require('fs');
try {
const info = license.importOfflineLease(fs.readFileSync('acme.lease.json', 'utf8'));
// info.isValid === true; der Lease ist jetzt auf dieser Maschine zwischengespeichert.
} catch (err) {
// falsche Signatur / falsches Produkt / falsche Maschine oder abgelaufen
showActivationError('This offline lease could not be verified: ' + err.message);
}
Es akzeptiert entweder einen JSON-String oder ein bereits geparstes Objekt.
Testversionen
Ein Testschlüssel wird über exakt denselben activate-Pfad aktiviert wie ein bezahlter und wird nahtlos abgelöst, wenn der Kunde später einen bezahlten Schlüssel aktiviert. Lesen Sie den Testversionsstatus direkt aus LicenseInfo:
const { info } = license.validate();
if (info.isTrial) {
const left = info.daysRemaining; // z. B. 27 oder null, wenn kein Ablauf
showTrialBadge(info.statusBadge, left); // statusBadge -> "Enterprise Trial"
// info.expiryUtc ist ein Date, das Sie für eine Zeile „Testversion endet am …“ formatieren können
}
Die Dauer einer Testversion zählt ab der ersten Aktivierung auf der Maschine: Das SDK persistiert eine kleine lokale Zustandsdatei, und sobald das Testfenster verstrichen ist, liefert validate() eine Free-Info mit status: 'expired'. Wird die Systemuhr bei einer Testversion oder einem Abonnement um mehr als clockTamperToleranceHours (Standard 24 h) zurückgestellt, liefert validate() status: 'clock_tampered'; es erholt sich beim nächsten Aufruf, sobald die korrekte Zeit eingestellt ist. Unbefristete Lizenzen unterliegen keiner der beiden Prüfungen. Vollständiger Ablauf: Kostenlose Self-Service-Testversionen.
Ein minimales Ende-zu-Ende-Beispiel
const { KeyrightClient } = require('keyright');
const license = KeyrightClient.initialize({
product: 'acme-app',
publicKeyBase64: 'MIIBIjANBgkq...',
serviceUrl: 'https://keyright.delta1labs.com',
});
async function main() {
// 1. Offline-Prüfung beim Start — kein Netzwerk nötig.
let { info } = license.validate();
// 2. Wenn unlizenziert und der Benutzer einen Schlüssel eingefügt hat, online aktivieren.
if (!info.isPaid && process.env.LICENSE_KEY) {
info = await license.activate(process.env.LICENSE_KEY);
}
// 3. Funktionen über Entitlements schützen, fail-closed.
if (info.isPaid && info.entitlements.isEnabled('export')) {
console.log('Export enabled for', info.licensee, '(' + info.statusBadge + ')');
} else {
console.log('Running in Free mode:', info.message || 'no paid license');
}
}
main();
Fehlerbehebung
- Die Aktivierung meldet, der Schlüssel sei nicht erkannt worden. Die häufigste Ursache ist eine Produktabweichung: Der Schlüssel wurde für ein anderes Produkt (oder einen anderen Mandanten) ausgestellt als der
product-Slug, mit dem Ihr Client konfiguriert ist. Der Server begrenzt die Aktivierung auf das Produkt, daher aktiviert ein gültiger Schlüssel füracme-appkeinen Client, der mitproduct: 'other-app'initialisiert wurde. Stellen Sie sicher, dass der Slug mit dem Produkt übereinstimmt, unter dem Sie den Schlüssel ausgestellt haben. - Alles wird als Free erkannt. Das ist Absicht — das SDK ist bei jedem Prüfproblem fail-closed, statt eine Ausnahme zu werfen. Lesen Sie
info.status('no_license','signature_invalid','expired','machine_mismatch','revoked','clock_tampered') undinfo.message, um zu sehen, welches Problem vorliegt. Ein'signature_invalid'bedeutet fast immer, dass der eingebettetepublicKeyBase64nicht zu dem Mandanten passt, der den Schlüssel signiert hat. - Eine zeitlich begrenzte Lizenz lässt sich plötzlich nicht mehr validieren. Wurde die Systemuhr bei einer Testversion oder einem Abonnement um mehr als
clockTamperToleranceHours(Standard 24 h) zurückgestellt, lautet der Status'clock_tampered'. Das bleibt nicht haften — stellen Sie die korrekte Zeit ein, und die Validierung erholt sich beim nächsten Aufruf. Unbefristete Lizenzen unterliegen dieser Prüfung nie. deactivatewirft eine Ausnahme. Es hat konstruktionsbedingt keinen Offline-Fallback — ein fehlgeschlagener HTTP-Aufruf wirft eine Ausnahme, statt stillschweigend erfolgreich zu sein. Nur wenn es'ok'liefert, ist der Platz freigegeben und der lokale Lease gelöscht; versuchen Sie es erneut, sobald die Verbindung wieder besteht.importOfflineLeasewirft eine Ausnahme. Der Lease hat die Prüfung nicht bestanden (falsche Signatur/falsches Produkt/falsche Maschine oder abgelaufen) — stellen Sie sicher, dass der Operator ihn für die Maschinen-ID ausMachineFingerprint.current().toBoundString()auf diesem Rechner signiert hat.
Siehe auch
- Erste Schritte — der komprimierte Anbieterpfad und die Go-live-Checkliste.
- Aktivierungs-Lebenszyklus — wie Schlüssel, Leases, Plätze und Aktualisierungen zusammenspielen.
- Sicherheit & Leases — das Signaturmodell, das Lease-Format und das Design der Offline-Kulanz.
- HTTP-API-Referenz — die vollständige Endpunktübersicht hinter
activateunddeactivate. - Keyright-Beispiele auf GitHub — lauffähige Client-Beispiele.