Une licence, quatre runtimes : vérifier la même clé Keyright depuis .NET, Node, Python et Java
Une gamme de produits est rarement un seul langage — une application de bureau .NET, une CLI Node, un outil de données Python, un service JVM, le tout vendu comme un seul produit. La réponse paresseuse donne à chacun son propre système de licence et laisse le client jongler avec quatre clés. La bonne : un seul tenant signe une licence avec une clé privée RSA, chaque composant intègre la même clé publique, et quatre SDK vérifient hors ligne le format identique de licence et de bail signés. Ce billet montre le même flux initialiser-valider-restreindre en C#, JavaScript, Python et Java côte à côte, explique pourquoi l'empreinte machine est compatible octet pour octet entre tous pour qu'un siège couvre chaque composant sur une machine, et couvre les deux choses qui sont véritablement par composant — le cache local du bail et le chemin de résolution de la licence — ainsi que la façon dont une rotation de clé traverse toute la gamme d'un coup.
La plupart des produits que les gens appellent « une appli » sont en réalité plusieurs programmes en trench-coat. Ce qu”un client achète comme Acme Suite peut être une appli de bureau WPF en C#, un outil en ligne de commande dotnet-et-npm en Node, une bibliothèque de calcul de données en Python, et un service de longue durée sur la JVM — quatre runtimes, un achat. La question qui décide si licencier ce produit est agréable ou misérable est simple : le client obtient-il une licence que chaque composant honore, ou quatre activations séparées qui se désynchronisent dès que quelqu”un touche la page des tarifs ?
La réponse de Keyright est que les quatre SDK — .NET, Node.js, Python et Java — vérifient exactement le même format signé de licence et de bail contre la même clé publique. Un tenant signe ; quatre runtimes vérifient ; le client voit une clé. Ce billet montre ce flux dans les quatre langages d”un coup, puis consacre son temps aux deux choses véritablement partagées (le modèle de signature et l”empreinte machine) et aux deux véritablement par composant (le cache du bail et le chemin de résolution), car c”est là qu”un déploiement polyglotte réussit ou trébuche.
Le modèle partagé, une fois
Le modèle est identique quel que soit le runtime qui demande. Vous émettez une clé de licence côté serveur ; votre tenant la signe avec une clé privée RSA qui ne quitte jamais le serveur. Chaque composant de votre produit n”intègre que la clé publique correspondante — une chaîne base64 SubjectPublicKeyInfo qui n”est pas un secret et ne peut que vérifier, jamais frapper. Une vérification de routine vérifie la clé hors ligne contre cette clé publique : signature, slug de produit, verrouillage de nœud, expiration, révocation, état de falsification de l”horloge. Tout ce qui ne colle pas se résout en édition Free au lieu de lever une exception. Cela échoue fermé, partout, de la même façon.
Le même flux en quatre langages
Voici toute l”histoire du client — initialiser une fois, valider hors ligne, restreindre une fonctionnalité selon un droit — dans chaque SDK. Notez que rien sauf le langage ne change : le même slug de produit, la même clé publique, le même droit export.
.NET — validate() retourne un LicenseInfo ; l”activation est asynchrone :
using Keyright.Client;
static readonly KeyrightClient License = KeyrightClient.Initialize(new KeyrightOptions
{
Product = "acme-suite", // one slug for the whole product line
PublicKeyBase64 = "MIIBIjANBgkq...", // the same public key in every component
ServiceUrl = "https://licensing.acme.example", // your issuing service
});
LicenseInfo info = License.Validate(); // offline, never throws, fails closed to Free
if (info.IsPaid && License.IsEnabled("export"))
EnableExport();
Node.js — validate() retourne { info, source } ; activate est asynchrone :
const { KeyrightClient } = require('keyright');
const license = KeyrightClient.initialize({
product: 'acme-suite',
publicKeyBase64: 'MIIBIjANBgkq...',
serviceUrl: 'https://licensing.acme.example',
});
const { info } = license.validate(); // synchronous, fails closed to free
if (info.isPaid && info.entitlements.isEnabled('export'))
enableExport();
Python — validate() retourne un tuple (info, source), avec validate_info() pour seulement l”info :
from keyright import KeyrightClient, KeyrightOptions
license = KeyrightClient.initialize(KeyrightOptions(
product="acme-suite",
public_key_base64="MIIBIjANBgkq...",
service_url="https://licensing.acme.example",
))
info = license.validate_info() # fails closed to Free
if info.is_paid and info.entitlements.is_enabled("export"):
enable_export()
Java — validate() retourne un Result avec des champs publics info/source ; l”activation est synchrone :
import com.delta1labs.keyright.*;
KeyrightClient client = KeyrightClient.initialize(
new KeyrightOptions()
.product("acme-suite")
.publicKeyBase64("MIIBIjANBgkq...")
.serviceUrl("https://licensing.acme.example"));
LicenseInfo info = client.validateInfo(); // fails closed to Free
if (info.isPaid() && info.entitlements.isEnabled("export"))
enableExport();
Quatre runtimes, un comportement. Les seules différences sont idiomatiques — PascalCase contre camelCase contre snake_case, un tuple contre un objet contre un paramètre out pour le source de résolution, async en C# et Node contre des appels synchrones en Python et Java. La vérification RSA, la garantie d”échouer fermé vers Free et la sémantique des droits sont octet pour octet identiques.
Le gain : un siège couvre toute la machine
La fonctionnalité qui fait de ceci plus qu”une commodité est le verrouillage de nœud. Chaque SDK dérive une empreinte machine — ancrée sur le GUID machine de l”OS plus des signaux souples comme le nom d”hôte et l”OS — et cette empreinte est compatible octet pour octet entre les quatre SDK. La documentation du SDK Java le dit clairement : son empreinte est « compatible octet pour octet avec les autres SDK ». La conséquence pratique est grande.
Quand n”importe quel composant s”active, il envoie machineId = MachineFingerprint.current().toBoundString() (la même chaîne dans chaque langage) et le serveur lie un siège à (key, machineId). Comme l”id de machine est identique entre l”appli .NET, la CLI Node, l”outil Python et le service JVM sur une machine, et comme réactiver une machine déjà liée est idempotent, les quatre composants sur cette machine se résolvent au même siège. Une licence de trois sièges, ce sont trois machines, pas douze activations de composant. Votre client installe la suite sur trois portables et est pleinement sous licence — exactement le modèle mental qu”il a déjà, et exactement ce qu”un compteur par composant aurait mal fait.
Pour obtenir l”id d”une machine pour un appel au support ou une activation air-gapped, chaque SDK offre la même chose dans son propre idiome — MachineFingerprint.current().toBoundString() en .NET et Java, .to_bound_string() en Python, MachineFingerprint.current().toBoundString() en Node — et une forme d”affichage courte KR-XXXX-XXXX pour les humains.
Ce qui est véritablement par composant
Deux choses ne traversent pas les frontières de composant automatiquement, et un déploiement propre les prévoit toutes deux.
Le cache du bail est local à chaque SDK. Quand un composant s”active, il met en cache le bail signé dans son propre chemin app-data de l”OS (.../Keyright/<product>/lease.json et un fichier de bail frère). Le cache de l”appli .NET n”est pas le cache de la CLI Node. Alors dans la configuration par défaut chaque composant s”active une fois — le premier fait un vrai aller-retour et consomme le siège ; chaque composant ultérieur sur la même machine réactive, correspond au siège existant gratuitement, et met en cache son propre bail. C”est inoffensif mais ce sont N activations. Si vous préférez activer une fois pour toute la suite, vous avez deux options propres :
- Pointez chaque composant vers un unique fichier de licence via la variable d”environnement partagée. Les quatre SDK honorent
KEYRIGHT_LICENSEcomme source de résolution (Node la lit quand vous fixezenvVarName: 'KEYRIGHT_LICENSE'; les autres l”utilisent par défaut), alors un installeur qui dépose un fichier de licence activé et exporteKEYRIGHT_LICENSElicencie tout le paquet sans aller-retour par composant. - Ou laissez un composant posséder l”activation et écrire son bail vers un chemin que les autres lisent via
configLicensePath/LicenseFilePath.
L”ordre de résolution est propre à chaque SDK. Chaque SDK cherche une licence dans la même précédence — une chaîne de licence explicite, puis un chemin de fichier explicite, puis la variable d”environnement KEYRIGHT_LICENSE, puis un chemin de config, puis l”app-data par défaut de l”OS, puis le bail en cache — mais il parcourt cette liste dans son processus, contre ses chemins configurés. C”est une fonctionnalité : un service JVM hébergé en conteneur peut recevoir sa licence par KEYRIGHT_LICENSE depuis son coffre de secrets tandis que l”appli de bureau C# sur le portable du même client utilise une activation interactive et son bail en cache, et les deux vérifient le document signé identique.
Faire tourner la clé sur toute la gamme
Comme il y a une seule paire de clés derrière tout le produit, une rotation de clé de signature est une seule version coordonnée plutôt que quatre pagailles indépendantes. Chaque SDK expose la même issue de secours : à côté de la nouvelle clé publique vous listez la sortante, et les licences et baux signés par l”une ou l”autre continuent de valider pendant la transition.
// .NET
AdditionalPublicKeysBase64 = { "<previous public key>" },
// Node
additionalPublicKeysBase64: ['<previous public key>'],
# Python
additional_public_keys_base64=["<previous public key>"],
// Java (public field, no fluent setter)
opts.additionalPublicKeysBase64.add("<previous public key>");
Livrez chaque composant avec la nouvelle clé en primaire et l”ancienne dans la liste additionnelle, laissez le champ se vider à mesure que les anciens baux expirent, puis retirez l”ancienne clé dans une version ultérieure. Le billet faire tourner vos clés de signature couvre le calendrier ; la seule ride polyglotte est que « livrer une version » signifie désormais quatre versions portant les deux mêmes clés.
À retenir
Une gamme de produits en quatre langages n”a pas besoin de quatre systèmes de licence — elle a besoin d”un format signé et de quatre vérificateurs de celui-ci, ce qui est exactement ce que sont les SDK Keyright. Le modèle de signature est partagé (une clé privée signe, une clé publique vérifie, partout), l”empreinte machine est partagée (compatible octet pour octet, alors un siège couvre chaque composant sur une machine), et la sémantique des droits est partagée (les mêmes drapeaux et limites, lus fermés par défaut depuis le même claim). Ce qui est local — le cache du bail et le chemin de résolution — est local par conception, et la variable d”environnement KEYRIGHT_LICENSE l”effondre en une seule activation quand vous le voulez. Le client achète une licence ; C#, JavaScript, Python et la JVM l”honorent tous ; et vous maintenez un tenant au lieu de quatre.
Voir les guides par langage pour la surface complète du client — .NET, Node.js, Python et Java — et licencier des services en conteneur pour le composant de la suite qui passe à l”échelle horizontalement.
Essayez Nebula.NET
Renforcez votre code .NET en quelques minutes — commencez avec l'édition gratuite.