Cas d'usage
Activer, désactiver et déplacer des sièges
Liez une clé à une machine, libérez ce siège pour que le client puisse passer sur une nouvelle machine, et faites-le pour les licences d'essai, annuelles et perpétuelles — en ligne, hors ligne/isolé (air-gapped) ou côté éditeur quand une machine a disparu — avec exactement ce que voit votre application dans chaque cas.
Objectif
Transformer une clé de licence en une installation sous licence, en cours d’exécution sur une machine précise — et permettre au client (ou à vous) de libérer ce siège plus tard afin qu’il puisse passer sur une autre machine sans acheter un siège supplémentaire. C’est le cycle de vie d’activation du quotidien : activer lie une clé à une machine et consomme un siège ; désactiver le libère ; la même clé fonctionne pour les licences d’essai, annuelles (à durée déterminée) et perpétuelles.
Le code ci-dessous est en C# (Keyright.NET), mais la forme du SDK est identique dans les paquets Node, Python et Java — voir le dépôt d’exemples exécutables.
Quand l’utiliser
- Utilisez ceci pour le flux normal : un client saisit sa clé et votre application l’active ; plus tard, il met un portable au rebut et veut récupérer ce siège.
- Utilisez le chemin côté éditeur quand une machine est morte, réinstallée ou injoignable et que le client ne peut pas libérer le siège lui-même.
- Utilisez le chemin hors ligne pour une machine qui ne peut pas du tout joindre le service — vous générez une lease signée liée à cette seule machine.
- N’utilisez pas ceci pour changer ce qu’accorde une licence — l’expiration, les sièges et les renouvellements sont traités sous renouvellement/expiration, et les versions du produit qu’elle couvre relèvent de Vendre des mises à niveau et contrôler les versions.
Les trois formes de licence
Une licence porte un niveau, un nombre de sièges, une expiration facultative et un indicateur essai facultatif. Ils se combinent en trois formes que voient vos clients :
| Forme | ExpiryUtc | IsTrial | Ce qu’affiche votre application |
|---|---|---|---|
| Perpétuelle | null | false | « Licensed — perpetual » |
| Annuelle / à durée déterminée | une date | false | « Licensed — expires 31 Dec 2027 » |
| Essai / évaluation | une date | true | « Evaluation — expires 31 Dec 2027 (N days left) » |
Dans votre code
Votre application lit tout cela dans la LicenseInfo renvoyée par Activate/Validate — vous n’analysez jamais vous-même une chaîne de clé :
var info = await client.ActivateAsync(licenseKey);
if (info.Status != LicenseStatus.Valid) // échec en mode fermé
return ShowUnlicensed(info.Message);
string banner = info.IsTrial
? $"Evaluation — expires {info.ExpiryUtc:d} ({info.DaysRemaining} days left)"
: info.ExpiryUtc is null
? "Licensed — perpetual"
: $"Licensed — expires {info.ExpiryUtc:d}";
Activer (en ligne)
ActivateAsync(key) envoie l’empreinte de la machine et la clé au service d’émission. Le service vérifie le nombre de sièges, lie la licence à cette machine et renvoie une lease signée que le SDK met en cache localement. À partir de là, Validate() fonctionne hors ligne avec cette lease en cache (voir Sécurité et modèle de lease).
Réactiver la même machine est idempotent — cela rafraîchit la lease et ne consomme jamais un second siège. Appeler ActivateAsync à chaque démarrage (quand la machine est en ligne) est le modèle recommandé : il garde silencieusement la lease à jour.
L’appel a la même forme dans chaque SDK, et en HTTP brut c’est un simple POST sur /v1/activate qui renvoie la lease signée à mettre en cache :
.NET
LicenseInfo info = await License.ActivateAsync(customerEnteredKey, ct);
if (info.IsValid && info.IsPaid) ShowLicensedUi(info.StatusBadge); // lease mise en cache ; fonctionne hors ligne jusqu'à son expiration
else ShowActivationError(info.Message); // reste en Free, p. ex. "All seats … are in use."
Node.js
const info = await license.activate(customerEnteredKey);
if (info.isValid && info.isPaid) showLicensedUi(info.statusBadge);
else showActivationError(info.message);
Python
info = license.activate(customer_entered_key)
if info.is_valid and info.is_paid:
show_licensed_ui(info.status_badge)
else:
show_activation_error(info.message)
Java
LicenseInfo info = client.activate(customerEnteredKey);
if (info.isValid() && info.isPaid()) showLicensedUi(info.statusBadge());
else showActivationError(info.message());
Tout langage (HTTP brut)
curl -s -X POST "$BASE/v1/activate" -H "content-type: application/json" \
-d '{"key":"LIC-…","machineId":"<stable-machine-id>","product":"acme-app"}'
# status:"ok" renvoie une chaîne "lease" signée — stockez-la telle quelle et vérifiez-la vous-même hors ligne.
# Échouez en mode fermé sur tout autre statut, un code non-2xx, ou une lease dont la vérification échoue.
Activation d’un essai. Une clé d’essai s’active exactement comme une clé payante — même appel, même modèle de sièges — elle revient simplement avec IsTrial == true et une expiration :
var info = await client.ActivateAsync(trialKey);
// info.IsTrial == true, info.ExpiryUtc == la fin de l'essai, info.DaysRemaining décompte
Affichez la bannière d’évaluation et conditionnez ce que vous voulez à info.IsTrial. Une fois la date de l’essai passée, l’activation/validation renvoie LicenseStatus.Expired et vous retombez dans votre état sans licence. Les essais tiennent compte de la durée depuis la première activation et de la manipulation de l’horloge — un client ne peut pas regagner du temps en reculant l’horloge (voir la page sécurité). Les essais sont généralement obtenus en libre-service depuis votre propre site ; voir Proposer un essai gratuit.
Activation perpétuelle. Une licence perpétuelle n’a pas d’expiration. Activez une fois ; ExpiryUtc vaut null, donc votre branche « perpétuelle » s’exécute. La lease conserve néanmoins un TTL court et est rafraîchie à la prochaine activation en ligne — la licence n’expire jamais, mais la lease hors ligne reste un jeton de grâce de courte durée (c’est ce qui empêche une licence perpétuelle d’être copiée sur un nombre illimité de machines hors ligne).
Activation annuelle / à durée déterminée. Appel identique ; ExpiryUtc est la date jusqu’à laquelle la licence est payée. Utilisez info.IsExpiringSoon() / info.DaysRemaining pour inciter les clients à renouveler avant l’échéance. Après cette date, l’activation renvoie Expired. Le renouvellement est une modification de la licence côté éditeur (POST /admin/licenses/{id}/renew) — le client conserve la même clé et se contente de réactiver pour récupérer la nouvelle expiration.
Quand il ne reste plus de sièges. Si une nouvelle machine tente d’activer une licence dont tous les sièges sont utilisés, le service renvoie LicenseStatus.SeatLimit — affichez un message clair du type « tous les sièges sont utilisés — libérez-en un d’abord ». (Là encore, la même machine qui se réactive ne rencontre jamais ce cas.)
Déplacer une machine (désactivation à l’initiative du client)
La désactivation libère le siège de cette machine et efface la lease locale, de sorte que le siège est immédiatement disponible ailleurs. C’est ainsi qu’un client change de machine : il désactive sur l’ancienne machine, puis active sur la nouvelle, qui prend le siège libéré. Chaque SDK expose le même appel, et en HTTP brut c’est POST /v1/free-seat :
.NET
await License.DeactivateAsync(key); // libère le siège de cette machine et efface la lease en cache
Node.js
const status = await license.deactivate(key); // 'ok' | 'not_found' ; lève une exception si le service est injoignable
Python
status = license.deactivate(key) # "ok" | "not_found" ; lève une exception si le service est injoignable
Java
String status = client.deactivate(key); // "ok" | "not_found" ; lève IllegalStateException si injoignable
Tout langage (HTTP brut)
curl -s -X POST "$BASE/v1/free-seat" -H "content-type: application/json" \
-d '{"key":"LIC-…","machineId":"<stable-machine-id>","product":"acme-app"}'
# -> {"status":"ok"} — supprimez votre lease en cache au même moment
La désactivation n’a aucun repli hors ligne dans aucun SDK — libérer un siège est une modification côté serveur, donc l’appel échoue (lève une exception) si le service ne peut pas être joint. Cela fonctionne de la même façon pour les licences perpétuelles, annuelles et d’essai. Après la désactivation, Validate() renvoie NoLicense sur cette machine.
Ce que vous faites (côté éditeur)
Libérer un siège pour le client (désactivation forcée)
Quand une machine tombe en panne, est réinstallée, ou qu’un client n’y a plus accès, vous libérez le siège depuis votre backend — aucun client requis.
Tableau de bord : ouvrez la licence dans la vue Licenses, consultez ses machines actives et libérez celle qui a disparu.
API :
curl -X POST "https://keyright.delta1labs.com/admin/licenses/<KEY>/free-seat" \
-H "X-Admin-Token: $KEYRIGHT_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"machineId":"KRM1:..."}'
POST /admin/licenses/{id}/free-seat est accessible à partir du rôle Support+ ; POST /admin/licenses/{id}/deactivate (Admin+) fait la même chose. Le prochain Validate() en ligne de la machine constate que le siège a disparu. Vous pouvez lister les machines d’une licence avec GET /admin/licenses/{id}/activations.
Activation hors ligne / isolée (air-gapped)
Pour une machine qui ne peut pas du tout joindre le service, l’éditeur émet une lease signée de longue durée liée à cette seule machine. Cela fonctionne à l’identique pour les licences d’essai et payantes.
-
Sur la machine hors ligne, lisez son identifiant :
var machineId = MachineFingerprint.Current().ToBoundString(); // KRM1:...(ou exécutez
keyright machine-id). Envoyez cette chaîne à l’éditeur. -
L’éditeur (avec une connexion + un jeton admin) génère une lease pour cette machine — TTL par défaut de 365 jours :
curl -X POST "https://keyright.delta1labs.com/admin/licenses/<KEY>/offline-lease" \ -H "X-Admin-Token: $KEYRIGHT_ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"machineId":"KRM1:...","days":365}' > offline-lease.jsonCela confirme un siège pour cette machine (les activations hors ligne comptent donc aussi dans la limite de sièges — vous ne pouvez pas générer un nombre illimité de leases hors ligne au-delà du nombre de sièges).
-
De retour sur la machine hors ligne, importez-la — sans aucun réseau :
client.ImportOfflineLease(File.ReadAllText("offline-lease.json")); var info = client.Validate(); // Valid, entièrement hors ligne
Comme la lease est liée à l’empreinte de cette machine, le fichier est inutilisable sur toute autre machine (il renvoie MachineMismatch). Voir la page sécurité pour comprendre pourquoi cela tient même hors ligne, et Fonctionner hors ligne / isolé (air-gapped) pour l’histoire hors ligne complète.
Entièrement hors ligne, sans aucun service
Si vous ne voulez jamais que la machine (ni l’éditeur) touche un réseau, signez un fichier de licence hors ligne autonome avec la CLI et livrez-le avec le produit :
keyright license --sign --licensee "Acme Corp" --product yourapp --tier pro \
--private-key yourapp.private.json --expiry 2027-12-31 --out license.json
Le client le valide de façon purement hors ligne via Validate() (il est découvert par chemin, par la variable d’environnement KEYRIGHT_LICENSE, ou à l’emplacement par défaut des données d’application). Il n’y a pas de suivi des sièges dans ce mode — c’est le bon choix pour les déploiements véritablement isolés (air-gapped) ou embarqués.
Une note sur la désactivation hors ligne
Il n’existe pas aujourd’hui de « preuve de désactivation » générée par le client, de sorte qu’une machine isolée (air-gapped) ne peut pas restituer son siège par elle-même. Libérez-le côté éditeur (désactivation forcée, ci-dessus) une fois que votre backend dispose d’une connexion, ou émettez des leases hors ligne avec un TTL assez court pour que le siège d’une machine mise hors service revienne de lui-même à l’échéance de la lease. Voir le dépôt d’exemples.
Cloud ou auto-hébergé
Le flux d’activation est identique dans les deux modes. Seuls les endpoints changent :
- Les appels à l’exécution (
/v1/activate,/v1/validate,/v1/free-seat) pointent vers votreServiceUrl—https://keyright.delta1labs.comsur Keyright Cloud, l’URL de votre propre service d’émission en auto-hébergé. - Les appels côté éditeur (
/admin/licenses/...) utilisent le même$BASEet un jeton admin. Sur Keyright Cloud, le jeton provient de votre connexion au tableau de bord ; en auto-hébergé, c’est le jeton admin de votre tenant/environnement. - La liaison des sièges, les leases et les chemins hors ligne se comportent de la même façon dans les deux cas.
Vérifier
- Activez une clé sur la machine A et confirmez
LicenseInfo.Status == Validet la bonne bannière (essai / durée déterminée / perpétuelle). La liste Licenses → activations du tableau de bord affiche la machine A. - Activez à nouveau la même clé sur la machine A — confirmez qu’aucun second siège n’est consommé (nombre de sièges inchangé).
- Depuis une nouvelle machine B alors que les sièges sont pleins, confirmez que vous obtenez
LicenseStatus.SeatLimit. DeactivateAsyncsur A (ou libération du siège côté éditeur), puis activez sur B — B prend désormais le siège libéré et le tableau de bord reflète le déplacement.
Voir aussi
- Proposer un essai gratuit — comment les clés d’essai sont générées et présentées.
- Fonctionner hors ligne / isolé (air-gapped) — leases hors ligne signées et fichiers de licence en profondeur.
- Intégration du SDK .NET — le parcours client complet.
- Référence de l’API HTTP — la carte des endpoints.