Cas d'usage
Vendre des mises à niveau et contrôler les versions
Décidez quelles versions de votre produit chaque licence couvre — mises à jour à vie, période de support, une version majeure, protection de mise à niveau, abonnements avec ou sans repli perpétuel — et appliquez-le dans votre application ou sur votre propre page de téléchargement, sans réactivation, sans nouvelle saisie de clé et avec une prise en charge complète du mode hors ligne.
Objectif
Vendez les mises à jour comme votre activité fonctionne réellement — « toutes les versions 2.x », « toute version publiée dans l’année qui suit l’achat », « tout ce qui sort tant que l’abonnement est actif » — et laissez votre application (et votre propre page de téléchargement) obtenir une réponse oui/non pour n’importe quelle version. C’est la couverture des versions : Keyright enregistre ce que chaque licence couvre, le signe, le livre sur la machine du client avec la lease, et décide de l’éligibilité, sans jamais demander à un client de ressaisir une clé ni de réactiver, et sans interrompre une installation en cours d’exécution.
Keyright est un oracle de couverture : il décide si une licence couvre une version. Il n’héberge jamais de téléchargements, de flux de mise à jour ni de listes de versions — vous continuez d’exploiter votre propre système de mise à jour et votre propre page de téléchargement, et vous demandez à Keyright si un client donné peut disposer d’une version donnée.
Quand l’utiliser
- Utilisez ceci si vous facturez les mises à niveau, proposez une fenêtre de maintenance, vendez par version majeure, ou exploitez un abonnement pour application de bureau — tout cas où « posséder le logiciel » et « avoir droit à ce build » sont deux questions différentes.
- Utilisez ceci si vous voulez contrôler l’accès à votre propre page de téléchargement ou à votre système de mise à jour client par client, en ligne ou isolé (air-gapped), depuis une source unique de vérité signée.
- N’utilisez pas ceci simplement pour empêcher une licence expirée de s’exécuter — c’est le rôle de l’expiration propre à la licence (voir Activer, désactiver et déplacer des sièges). La couverture ajoute la dimension date de publication / version par-dessus.
La couverture des versions fait partie du plan supérieur du service hébergé (Scale et Enterprise) et des licences auto-hébergées qui incluent la fonctionnalité de couverture. Les plans inférieurs voient la fonctionnalité verrouillée dans le tableau de bord avec une invitation à monter de plan. Le SDK est identique quel que soit le plan : une licence sans couverture se lit simplement comme non configurée et exécute toutes les versions.
Fonctionnement
Chaque licence porte un ensemble de conditions de couverture. Une condition est une plage de versions (2.0.0 ≤ v < 3.0.0), une fenêtre de dates (tout ce qui est publié entre deux dates calendaires), ou les deux — éventuellement limitée à un canal de publication (stable par défaut ; les bêtas exigent une condition beta explicite). Une version est couverte lorsqu’au moins une condition correspond à son numéro de version et à sa date d’éligibilité (normalement la date à laquelle elle a été publiée).
Trois propriétés rendent cette application des règles sûre :
- Un instantané, pas une valeur vive. Lorsque vous émettez une licence, la politique de couverture de l’édition se développe en conditions stockées sur cette licence. Modifier la politique par la suite ne change jamais ce que les clients existants ont acheté ; vous appliquez de nouvelles conditions aux licences existantes par une action explicite et prévisualisée (voir Appliquer une politique aux clients existants).
- Les conditions ne font que croître. Les conditions forment un registre en ajout seul. Les renouvellements et les mises à niveau ajoutent des conditions ; le support peut retirer une condition, mais rien de ce qu’un client peut exécuter aujourd’hui ne lui est jamais retiré silencieusement par une modification de politique.
- Signé et hors ligne. La couverture voyage sous la forme d’un bloc de couverture signé séparément, lié à la licence et au produit. L’application le vérifie avec la clé publique qu’elle embarque déjà, le met en cache et décide de la couverture localement — en ligne, hors ligne ou isolée (air-gapped). Un numéro de séquence monotone empêche de rejouer un bloc plus ancien pour restaurer une couverture échue, et un bloc manquant ou altéré n’est jamais considéré comme « couvert ».
Deux autres notions que vous rencontrerez tout au long de cette page :
- Date d’éligibilité et date de publication. La couverture est jugée d’après la date d’éligibilité d’une version, qui vaut par défaut sa date de publication mais peut être définie séparément. C’est ainsi qu’un correctif de sécurité publié après la fin de la fenêtre de support d’un client peut tout de même être couvert pour lui : datez le correctif comme sa version de base.
- Le registre des versions est facultatif. L’application des règles fonctionne entièrement à partir de la version et de la date d’éligibilité embarquées dans le build lui-même. Si vous enregistrez aussi les versions (depuis la CI ou le tableau de bord), Keyright peut indiquer aux clients la version la plus récente qu’ils peuvent utiliser, prévisualiser pour combien de clients une version est couverte, et dater les correctifs pour vous.
Quel modèle de couverture choisir
Six préréglages en langage clair couvrent les modèles d’affaires courants. Choisissez-en un par édition ; il se développe en conditions sur chaque licence que vous émettez à partir de ce moment.
| Préréglage | Ce que le client obtient | Usage typique |
|---|---|---|
| Lifetime updates (mises à jour à vie) | Toutes les versions, pour toujours. | Un achat unique qui promet des mises à niveau gratuites. |
| Updates for a period (mises à jour pendant une période) | Toute version publiée dans les N mois suivant l’achat ; la copie continue de fonctionner pour toujours. | La licence perpétuelle classique avec « un an de mises à jour ». |
| One major version (une version majeure) | Toutes les publications d’une ligne majeure (p. ex. toutes les 2.x), pour toujours. | Tarification de mise à niveau par version majeure. |
| One major + upgrade protection (une majeure + protection de mise à niveau) | Une majeure pour toujours, plus tout ce qui est publié dans les N mois — y compris la majeure suivante si elle sort à temps. | Tarification par majeure avec plan de maintenance. |
| Subscription (abonnement) | Tout ce qui est publié tant que l’abonnement est actif ; rien après son échéance. | Licences de bureau à la manière du SaaS. |
| Subscription with fallback (abonnement avec repli) | Comme Subscription, mais après l’échéance le client conserve les versions publiées pendant qu’il payait. | Des abonnements qui ne doivent jamais laisser un client sur le carreau. |
Tout le reste — une seule version figée, une ligne mineure, une fenêtre sur mesure — est une condition personnalisée que vous ajoutez à une licence depuis la section Advanced (Avancé) de son onglet Coverage (ou via l’API grants ci-dessous).
Mises à jour à vie (Lifetime updates)
Scénario. Vous vendez une clé à l’achat unique et promettez des mises à niveau gratuites à vie (ou vous ne contrôlez tout simplement pas les mises à jour et voulez que le portail le dise). Rien n’expire jamais.
Conditions. Une condition ouverte : toutes les versions, toutes les dates. Ce que le client lit (portail, SDK, API) : « Votre licence couvre toutes les versions. » — un état non couvert ne peut jamais survenir.
Configuration. Politique de l’édition → Lifetime updates (aucune question), ou sur une licence Change coverage… → Lifetime updates. API : {"preset":"lifetime"}.
Mises à jour pendant une période (Updates for a period)
Scénario. Une licence perpétuelle avec douze mois de mises à jour. Le client conserve sa copie pour toujours et peut installer tout ce qui est publié dans la fenêtre — y compris une nouvelle majeure si elle sort à temps. Une version publiée après la fenêtre est « non couverte — fenêtre échue », l’application propose donc Renew (renouveler).
Conditions. Une fenêtre de dates allant de la date d’achat à l’achat + N mois, toute version. Le renouvellement ajoute une autre fenêtre (contiguë, ou avec un intervalle que vous pouvez choisir de laisser non couvert). Le client lit : « Votre licence couvre toute version publiée jusqu’au 9 oct. 2027. »
Configuration. Politique de l’édition → Updates for a period → Updates for (months after purchase) (mises à jour pendant N mois après l’achat). API : {"preset":"updatesForPeriod","periodMonths":12}.
Une version majeure (One major version)
Scénario. Tarification par majeure : une licence 2.x exécute toutes les versions 2.x jamais publiées, y compris celles sorties des années plus tard, mais pas la 3.0. Un build 3.0 est « non couvert — hors de la plage de versions », l’application propose donc Upgrade (mettre à niveau). Les lignes plus anciennes (1.x) sont toujours autorisées — le droit de rétrogradation est intégré.
Conditions. Une plage de versions [major.0.0, major+1.0.0), toute date. Le client lit : « Votre licence couvre toutes les versions d’Acme Studio 2. »
Configuration. Politique de l’édition → One major version → Which major version? (quelle version majeure ?). API : {"preset":"oneMajor","major":2}. Lorsque votre édition vend « la majeure courante », incrémentez la majeure de la politique quand vous publiez la suivante — les clients existants conservent la ligne qu’ils ont achetée.
Une majeure + protection de mise à niveau (One major + upgrade protection)
Scénario. Tarification par majeure plus plan de maintenance : le client possède la 2.x pour toujours et obtient tout ce qui est publié dans les N mois suivants — ainsi, si la 3.0 sort dans la fenêtre, il l’obtient, de même que tout correctif 3.x publié avant la fermeture de la fenêtre. Une version 3.x datée après la fenêtre est hors des deux conditions (→ Renew, puisque c’est la fenêtre qui a expiré). C’est le modèle « mises à niveau payantes avec filet de sécurité » le plus courant.
Conditions. Deux conditions — la ligne majeure, et une fenêtre de dates allant de l’achat à l’achat + N mois. Le client lit : « Votre licence couvre toutes les versions d’Acme Studio 2, plus toute version publiée jusqu’au 9 oct. 2027. »
Configuration. Politique de l’édition → One major + upgrade protection → majeure + mois. API : {"preset":"oneMajorPlusUpgradeProtection","major":2,"periodMonths":12}.
Abonnement (Subscription)
Scénario. Le client peut utiliser toute version publiée tant qu’il paie. La licence elle-même porte normalement une expiration alignée sur la période de facturation (l’application s’arrête donc à la fin de l’abonnement) ; la couverture ajoute la dimension date de publication pour que, par exemple, un client qui rétrograde vers un plan moins cher ou dont la durée est prolongée sans nouvelle clé obtienne toujours exactement les versions auxquelles il a droit.
Conditions. Une fenêtre de dates allant de l’émission à l’émission + N mois. Chaque renouvellement ajoute la fenêtre suivante. Après une échéance, les versions datées après la fenêtre sont « fenêtre échue » → Renew. Le client lit : « Votre licence couvre toute version publiée jusqu’au 9 oct. 2027. »
Configuration. Politique de l’édition → Subscription → Term (months) (durée en mois). API : {"preset":"subscription","periodMonths":12}.
Abonnement avec repli (Subscription with fallback)
Scénario. Un abonnement qui ne laisse jamais un client sur le carreau : lorsqu’il cesse de payer, il conserve les versions publiées pendant qu’il payait (un « repli perpétuel »), et seules les versions postérieures à l’échéance nécessitent un renouvellement.
Conditions. À l’émission, ce préréglage se développe exactement comme Subscription — une fenêtre pour la durée payée. Le repli consiste simplement à ne pas révoquer cette fenêtre à l’échéance : comme les conditions ne font que croître, un client dont l’abonnement prend fin détient toujours une condition couvrant tout ce qui a été publié pendant celui-ci. Lorsque vous traitez une échéance dans votre flux de facturation, ne révoquez pas la condition de couverture ; si vous laissez aussi la licence elle-même expirer, l’application suit votre comportement d’expiration habituel. Pour consigner l’échéance explicitement, ajoutez une condition personnalisée avec la raison Fallback (repli) se terminant à la date d’échéance (voir conditions personnalisées).
Configuration. Politique de l’édition → Subscription with fallback → Term (months). API : {"preset":"subscriptionWithFallback","periodMonths":12}.
Bêtas et accès anticipé
Une condition sans canal ne couvre que les versions stables. Pour permettre à un client d’exécuter des builds bêta, donnez-lui une condition sur le canal beta (activez Include beta builds (inclure les builds bêta) sur la politique, ou définissez "channel":"beta" sur une condition personnalisée) et compilez vos préversions avec KeyrightCoverageChannel=beta. Keyright couvre les bêtas ; il ne fait jamais expirer les builds — si vous voulez qu’une préversion s’auto-expire, intégrez-y KeyrightCoverageBuildExpiry et vérifiez CoverageBuildInfo.Current.BuildExpiryUtc dans votre propre code de démarrage.
Configuration dans le tableau de bord
Tout ce qui suit se trouve sous Releases & coverage (Versions et couverture) — dans les vues Intermediate (Intermédiaire) et Advanced (Avancé) du tableau de bord ; la section apparaît aussi dans la vue Basic (Basique) dès que vous enregistrez une version ou sauvegardez une politique — voir Tableau de bord d’administration → Niveau d’expérience.
1. Enregistrer les versions (facultatif, recommandé)
Enregistrer une version consigne son numéro de version, sa date de publication, une date d’éligibilité distincte facultative et son canal. Rien d’autre — ni fichiers ni URL. Avec un registre, les clients voient la version la plus récente qu’ils peuvent utiliser, le tableau de bord prévisualise pour qui chaque version est couverte, et le vérificateur de couverture peut dater une version pour vous.
Depuis la CI, enregistrez chaque version au moment de sa publication :
# KEYRIGHT_URL et KEYRIGHT_ADMIN_TOKEN peuvent aussi être passés via --url / --admin-token
keyright release --register --product acme-studio --version 2.4.0 --release-date 2026-10-01
# un correctif qui doit rester couvert pour tous ceux qui avaient la 2.3.0 : datez-le comme la version de base
keyright release --register --product acme-studio --version 2.3.1 --release-date 2026-09-30 --entitlement-date 2026-07-02
# un build de préversion sur le canal bêta
keyright release --register --product acme-studio --version 3.0.0 --release-date 2026-10-08 --channel beta
Ou via l’API (idempotente par produit + version) :
curl -X POST $BASE/admin/products/acme-studio/releases -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"version":"2.3.1","releaseDate":"2026-09-30","entitlementDate":"2026-07-02","channel":"stable"}'
# -> { "product":"acme-studio", "version":"2.3.1", "releaseDate":"2026-09-30", "entitlementDate":"2026-07-02", "channel":"stable" }
# combien de licences couvriraient une version, avant de la publier
curl "$BASE/admin/products/acme-studio/releases/preview?version=2.5.0&on=2026-10-09" -H "X-Admin-Token: $TOKEN"
# -> { "product":"acme-studio", "version":"2.5.0", "covered":4, "notCovered":1, "notConfigured":0, "total":5 }
La boîte de dialogue Register release (enregistrer une version) affiche la même prévisualisation en direct pendant que vous saisissez.
2. Donner à chaque édition une politique de couverture
Ouvrez Releases & coverage → Coverage policy (politique de couverture), choisissez l’édition (tier), sélectionnez un préréglage, répondez à sa question ou à ses deux questions, puis cliquez sur Save policy (enregistrer la politique). Le panneau latéral prévisualise, en langage clair, ce qu’un acheteur obtient, la phrase qu’il lira, et le message qu’il verrait s’il ouvrait une version hors de ses conditions.
Signification de chaque contrôle :
| Contrôle | Signification |
|---|---|
| Edition (édition) | Les politiques sont définies par édition. Les nouvelles licences de cette édition reçoivent ces conditions à l’émission ; les autres éditions ne sont pas touchées. |
| Preset (préréglage) | L’un des six modèles ci-dessus. |
| Which major version? (quelle version majeure ?) | Pour les deux préréglages « une majeure » : la ligne couverte par la licence (2 → toutes les 2.x). |
| Updates for / Term (months) (mises à jour pendant / durée en mois) | Pour les préréglages fondés sur les dates : la longueur de la fenêtre, comptée à partir du jour d’émission de chaque licence. |
| Downgrade rights (droit de rétrogradation) | Documente que les versions plus anciennes sont toujours autorisées — une ligne de versions inclut déjà tout ce qui se trouve en dessous, et une fenêtre inclut tout ce qui a été publié avant elle. |
| Hotfixes keep their base release’s date (les correctifs conservent la date de leur version de base) | Documente la règle de la date d’éligibilité : datez un correctif comme sa version de base lorsque vous l’enregistrez et il reste couvert. |
| Late renewals cover the gap (les renouvellements tardifs couvrent l’intervalle) | Documente votre façon de renouveler : une condition de renouvellement qui commence à l’ancienne date de fin couvre l’intervalle ; une qui commence aujourd’hui le laisse non couvert. |
| Include beta builds (inclure les builds bêta) | Place les conditions de la politique sur le canal beta au lieu du canal stable uniquement. |
| Advanced: what happens when a customer runs a version they don’t have (Avancé : que se passe-t-il quand un client exécute une version qu’il ne possède pas) | Consigne le comportement que vous avez choisi pour votre application — informer, exécuter en lecture seule, ou bloquer. Le serveur renvoie la décision ; votre application met en œuvre le comportement (voir Dans votre application). |
| Advanced: apply this policy to existing customers (Avancé : appliquer cette politique aux clients existants) | Une migration prévisualisée pour les licences émises avant cette politique — voir ci-dessous. |
Enregistrer une politique n’affecte que les licences émises à partir de maintenant. La même chose via l’API — notez qu’un upsert d’édition remplace l’édition entière, envoyez donc aussi ses sièges et ses fonctionnalités :
curl -X POST $BASE/admin/products/acme-studio/tiers -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"name":"pro","seats":5,"entitlements":{"export":"true"},
"coveragePolicy":{"preset":"oneMajorPlusUpgradeProtection","major":2,"periodMonths":12}}'
# effacer une politique : envoyez un préréglage vide (omettre coveragePolicy laisse la politique actuelle inchangée)
curl -X POST $BASE/admin/products/acme-studio/tiers -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"name":"pro","seats":5,"entitlements":{"export":"true"},"coveragePolicy":{"preset":""}}'
preset est l’un de lifetime, updatesForPeriod, oneMajor, oneMajorPlusUpgradeProtection, subscription, subscriptionWithFallback ; major est requis par les deux préréglages « une majeure » et periodMonths par les quatre préréglages fondés sur les dates ; channel est facultatif (beta).
3. Émettre les licences comme d’habitude
Rien ne change dans votre façon d’émettre : Licenses → Issue license (Licences → Émettre une licence) (ou POST /admin/licenses, ou le webhook de traitement des commandes de votre boutique). À l’émission, la politique de l’édition se développe en conditions sur la nouvelle licence. Le volet affiche les fonctionnalités de l’édition ; la couverture que porte la licence est visible ensuite sur sa page.
4. Voir et modifier la couverture d’une licence
La page d’une licence gagne un onglet Coverage (Couverture) (il apparaît automatiquement dès que la licence a une couverture, quelle que soit votre vue du tableau de bord). Il affiche la phrase destinée au client, la politique en vigueur, la version enregistrée la plus récente qu’il peut utiliser, si l’application des règles est active, un vérificateur de couverture par licence, et un historique en langage clair de chaque condition que la licence a jamais eue.
Change coverage… (modifier la couverture) applique un préréglage à cette licence — c’est ainsi que vous traitez un renouvellement (« Updates for a period » à nouveau), une mise à niveau (« One major version » 3) ou une conversion en abonnement. Ici, la couverture ne peut que croître : les nouvelles conditions s’ajoutent à côté des conditions existantes.
# appliquer un préréglage à une licence (renouvellement / mise à niveau / conversion)
curl -X POST $BASE/admin/licenses/LIC-…/coverage/preset -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"preset":"updatesForPeriod","periodMonths":12}'
# lire la couverture d'une licence : ce qui est en vigueur, le numéro de séquence et le registre complet
curl $BASE/admin/licenses/LIC-…/coverage -H "X-Admin-Token: $TOKEN"
# -> { "configured":true, "mode":"enforced", "seq":3, "supportUntilUtc":"2027-10-09T00:00:00.0000000Z",
# "activeGrants":[ { "id":"…", "min":"2.0.0", "maxExcl":"3.0.0", "dates":[], "channel":null,
# "reason":"purchase", "sourceRef":null, "createdUtc":"…", "active":true }, … ],
# "ledger":[ … chaque condition jamais ajoutée, avec "active":false pour celles qui ont été retirées … ] }
Conditions personnalisées et registre technique
La section repliée Advanced: coverage history (technical) (Avancé : historique de couverture (technique)) est destinée au personnel de support. Chaque ligne est une condition avec sa plage de versions, ses dates, son canal, sa raison et son état ; depuis cette section vous pouvez ajouter une condition personnalisée (une version figée, une ligne mineure, une fenêtre avec des dates explicites, un canal beta), retirer une condition active, suspendre / reprendre l’application des règles pour cette licence, et télécharger le fichier de couverture signé.
# ajouter une condition personnalisée : tout ce qui est publié dans une fenêtre, sur le canal bêta, consigné comme promotion
curl -X POST $BASE/admin/licenses/LIC-…/coverage/grants -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"minVersion":null,"maxExclVersion":null,"dates":[{"from":"2026-10-01","to":"2026-12-31"}],
"channel":"beta","reason":"promo","sourceRef":"launch-week"}'
# une version figée : {"minVersion":"2.4.1","maxExclVersion":"2.4.2"} — une ligne mineure : {"minVersion":"2.4.0","maxExclVersion":"2.5.0"}
# retirer une condition active (consigné, jamais supprimé)
curl -X POST $BASE/admin/licenses/LIC-…/coverage/revoke -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"grantId":"<id from the ledger>","sourceRef":"refund #4821"}'
# suspendre l'application des règles pour une licence (le bloc suivant est marqué unenforced → toutes les versions s'exécutent) ; "enforced" la reprend
curl -X POST $BASE/admin/licenses/LIC-…/coverage/mode -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"mode":"unenforced"}'
reason est l’un de purchase, renewal, upgrade, promo, fallback, migration, legacy ; sourceRef est un texte libre pour votre piste d’audit (un identifiant de commande, un ticket). Les plages de versions ne comparent que major.minor.patch — les suffixes de préversion sont ignorés, donc 2.5.0-beta est dans 2.x et 3.0.0-rc1 n’y est pas. Les dates sont des dates calendaires comparées de façon inclusive, sans conversion de fuseau horaire.
5. Vérifier n’importe quelle version pour n’importe quelle licence
Le vérificateur de couverture — dans l’onglet Coverage checker du hub et dans l’onglet Coverage de chaque licence — répond à « cette licence peut-elle utiliser la version X ? » exactement comme le feraient l’application et votre page de téléchargement, avec une raison en une ligne et, lorsque le registre est renseigné, la version la plus récente qu’elle peut utiliser.
curl "$BASE/admin/licenses/LIC-…/coverage/check?version=3.0.0" -H "X-Admin-Token: $TOKEN"
# -> { "status":"NotCovered", "reason":"OutsideVersionRange", "newestCovered":"2.4.0" }
# ajoutez &on=2026-11-01 pour évaluer à une date d'éligibilité explicite (par défaut : la date enregistrée de la version, sinon aujourd'hui)
# ajoutez &channel=beta pour vérifier un build de préversion
Appliqué, non appliqué et « non configuré »
Chaque bloc de couverture porte un mode :
- Appliqué (enforced) — l’état normal d’une licence qui a des conditions de couverture : l’application compare le build en cours d’exécution aux conditions.
- Non appliqué (unenforced) — le bloc dit « traitez toutes les versions comme couvertes ». Keyright émet des blocs non appliqués lorsque vous suspendez l’application des règles sur une licence, et — automatiquement, après une période de grâce — lorsque le plan d’un espace de travail ou une licence auto-hébergée n’inclut plus la couverture. Vos clients ne sont jamais verrouillés à cause de votre état de facturation ; lorsque le droit revient, les blocs appliqués reprennent avec les conditions intactes.
- Non configuré — une licence qui n’a jamais eu de conditions de couverture (une licence plus ancienne, ou un espace de travail qui n’utilise pas la couverture) reçoit un bloc signé « non configuré ». Le SDK le signale comme
NotConfiguredet traite la licence comme sans restriction. Cet état ne provient jamais que d’un bloc signé : un bloc manquant n’est jamais lu comme « sans restriction », si bien que retirer le bloc d’une machine n’est pas un moyen de contourner l’application des règles.
L’application des règles dans votre application est une décision prise à la compilation (KeyrightRequireCoverage, ci-dessous). Les builds sans cette propriété ignorent entièrement la couverture ; les builds qui la portent interrogent Keyright, rafraîchissent une fois avant de refuser, et échouent en mode fermé vers un état « importez un fichier de licence à jour » lorsqu’ils sont hors ligne sans rien en cache.
Appliquer une politique aux clients existants
Enregistrer une politique ne touche jamais les licences déjà émises. Lorsque vous voulez que les clients existants gagnent quelque chose — vous avez introduit la protection de mise à niveau et souhaitez l’étendre à tous les clients en 2.x, par exemple — utilisez Advanced: apply this policy to existing customers (Avancé : appliquer cette politique aux clients existants) dans l’onglet de la politique. Saisissez une version de référence, cliquez sur Preview who is affected… (prévisualiser qui est concerné) pour voir combien de licences gagneraient la couverture pour cette version (et combien l’ont déjà), puis Apply (appliquer). La couverture ne peut que croître : personne ne perd une version qu’il peut utiliser aujourd’hui.
# prévisualisation : combien de licences acme-studio couvriraient nouvellement la 3.0.0 si tout le monde recevait une condition 2.x + fenêtre
curl -X POST "$BASE/admin/products/acme-studio/coverage/migrate?dryRun=true&version=3.0.0" \
-H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"minVersion":null,"maxExclVersion":null,"dates":[{"from":"2026-10-09","to":"2027-10-09"}],"reason":"migration","sourceRef":"upgrade-protection-rollout"}'
# -> { "total":5, "coveredBefore":1, "coveredAfter":5, "gain":4, "applied":false }
# appliquer (dryRun=false) : la même condition est ajoutée à chaque licence non révoquée du produit
Licences héritées : rattrapage (backfill) avant d’appliquer les règles
Une licence émise avant que vous n’adoptiez la couverture n’a aucune condition. Si vous livriez un build RequireCoverage à un tel client sans rien faire, il serait invité à importer un fichier de licence. L’onglet Enforcement readiness (préparation à l’application des règles) existe pour que cela n’arrive jamais : il compte les licences qui n’ont toujours pas de conditions et bloque la liste de contrôle tant qu’elles n’ont pas été rattrapées.
Choisissez l’un des trois rattrapages — chacun donne à la licence une condition marquée existing customer (client existant) (reason: legacy) :
| Rattrapage | Résultat | Quand |
|---|---|---|
| Treat them as lifetime (les traiter comme à vie) | Chaque licence héritée couvre toutes les versions, pour toujours. | Vous n’avez jamais facturé les mises à jour auparavant. |
| Use each license’s expiry date (utiliser la date d’expiration de chaque licence) | Une fenêtre se terminant à la date d’expiration propre à la licence (les licences perpétuelles reçoivent la couverture à vie). | Des licences à durée déterminée dont le support prenait fin avec la licence. |
| Import coverage dates (CSV) (importer les dates de couverture) | Des dates de fin de support par licence depuis un fichier licenseId,supportUntil ; une date vide signifie à vie. | Vous suiviez les fenêtres de support ailleurs. |
curl $BASE/admin/products/acme-studio/coverage/readiness -H "X-Admin-Token: $TOKEN"
# -> { "total":5, "configured":4, "missing":1 }
# rattraper chaque licence qui n'a toujours pas de conditions (defaultRule : "lifetime" ou "supportEnd")
curl -X POST $BASE/admin/products/acme-studio/coverage/backfill -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"defaultRule":"lifetime"}'
# -> { "backfilled":1 }
# ou par licence (l'import CSV envoie la même structure)
curl -X POST $BASE/admin/products/acme-studio/coverage/backfill -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
-d '{"rows":[{"licenseId":"LIC-AAAA","supportUntil":"2027-03-12"},{"licenseId":"LIC-BBBB","supportUntil":null}]}'
Fixez les bonnes dates avant de livrer un build qui applique les règles : une fois le bloc signé d’un client livré, il peut être étendu mais pas véritablement repris. Les clients en ligne récupèrent leurs conditions rattrapées au prochain rafraîchissement ; les clients isolés (air-gapped) ont besoin de l’export ci-dessous.
Clients isolés (air-gapped)
Une machine qui n’atteint jamais votre service ne peut pas recevoir le bloc à l’activation. Livrez-le sous forme de fichier à la place :
- Une licence : le bouton Download signed coverage file (télécharger le fichier de couverture signé) dans son onglet Coverage (ou
GET /admin/licenses/{id}/coverage/block→{ "block": "…" }). - Toutes les licences d’un produit : Export offline license files (exporter les fichiers de licence hors ligne) dans l’onglet Enforcement readiness (ou
GET /admin/products/{slug}/coverage/export→{ "product", "count", "items": [ { "licenseId", "coverageBlock" } ] }).
Le fichier est l’enveloppe du bloc signé — du JSON avec un payload et une sig. Remettez-le au client avec son fichier de licence hors ligne ou sa lease ; son application l’importe avec ImportCoverageBlock (ci-dessous). Un renouvellement pour un client isolé (air-gapped) est simplement un fichier plus récent : il porte un numéro de séquence plus élevé et remplace donc celui en cache, tandis qu’un fichier plus ancien est refusé. L’export respecte votre droit exactement comme le fait l’activation — des blocs non appliqués si l’espace de travail n’inclut plus la couverture — il ne peut donc jamais verrouiller qui que ce soit.
Ce que voit le client
Dans le portail client
La page de licence du portail affiche la phrase de couverture — formulée en termes de possession, sans jargon interne — juste sous le résumé de la licence, et la liste Devices (appareils) en dessous. Les clients savent ce qu’ils possèdent sans ouvrir de ticket de support.
Si vous exploitez votre propre portail client, l’API du portail (en session authentifiée) renvoie la même phrase et la date de fin de support dérivée : GET /portal/license/{id}/coverage → { "status": "Your license covers …", "supportUntilUtc": "2027-10-09T00:00:00.0000000Z" } (null lorsque la couverture n’est pas bornée par une date).
Les phrases et messages par défaut
Voici les chaînes que produisent le SDK et l’API (le tableau de bord les prévisualise avec le même sens). Toutes peuvent être remplacées état par état — voir messages ci-dessous.
| État | Texte par défaut |
|---|---|
| À vie | Votre licence couvre toutes les versions. (Your license covers every version.) |
Une majeure ({line} = « Acme Studio 2 ») | Votre licence couvre toutes les versions d’Acme Studio 2. (Your license covers all Acme Studio 2 versions.) |
| Fenêtre (période / abonnement) | Votre licence couvre toute version publiée jusqu’au 9 oct. 2027. (Your license covers any version released through 9 Oct 2027.) |
| Majeure + protection de mise à niveau | Votre licence couvre toutes les versions d’Acme Studio 2, plus toute version publiée jusqu’au 9 oct. 2027. (Your license covers all Acme Studio 2 versions, plus any version released through 9 Oct 2027.) |
| Fenêtre ouverte | Votre licence couvre toutes les versions publiées à ce jour. (Your license covers every version released so far.) |
| Non couverte — fenêtre échue | Cette version (2.5.0) a été publiée après la fin de vos mises à jour le 9 oct. 2027. Vous pouvez continuer à utiliser la 2.4.0. Renouvelez pour utiliser la 2.5.0. (This version (2.5.0) was released after your updates ended on 9 Oct 2027. You can keep using 2.4.0. Renew to use 2.5.0.) (sans registre, la phrase « continuer à utiliser » est omise) → action Renew |
| Non couverte — hors de la plage de versions | Cette version (3.0.0) n’est pas incluse dans votre licence Acme Studio 2. Vous pouvez continuer à utiliser la 2.4.0. Mettez à niveau pour utiliser la 3.0.0. (This version (3.0.0) isn’t included in your Acme Studio 2 license. You can keep using 2.4.0. Upgrade to use 3.0.0.) → action Upgrade |
| Indisponible hors ligne | Acme Studio n’a pas pu confirmer hors ligne que votre licence inclut cette version. Connectez-vous à Internet, ou importez un fichier de licence à jour depuis Acme Software. (Acme Studio couldn’t confirm that your license includes this version while offline. Connect to the internet, or import an updated license file from Acme Software.) → action Import license file |
Dans votre application (SDK .NET)
La couverture a besoin de deux choses de votre build : de quelle version il s’agit (pour pouvoir la comparer aux conditions) et s’il doit appliquer les règles. Les deux sont des propriétés de compilation, de sorte qu’un binaire livré ne peut pas en être détourné à l’exécution.
1. Embarquer l’identité de version du build (MSBuild)
Le paquet Keyright.NET fournit des cibles MSBuild qui lisent ces propriétés et les compilent dans l’assembly sous forme de métadonnées que le SDK relit au démarrage :
<!-- dans le .csproj de l'application, ou Directory.Build.props — normalement définis depuis votre pipeline de publication -->
<PropertyGroup>
<KeyrightCoverageVersion>2.4.0</KeyrightCoverageVersion> <!-- la version produit de ce build (major.minor.patch) -->
<KeyrightCoverageEntitlementDate>2026-10-01</KeyrightCoverageEntitlementDate> <!-- la date à laquelle la couverture est jugée (yyyy-MM-dd) -->
<KeyrightCoverageChannel>stable</KeyrightCoverageChannel> <!-- stable (par défaut) ou beta -->
<KeyrightRequireCoverage>true</KeyrightRequireCoverage> <!-- intégrer l'application des règles dans ce build -->
<!-- facultatif : une auto-expiration stricte pour les builds de préversion, relue comme CoverageBuildInfo.BuildExpiryUtc -->
<!-- <KeyrightCoverageBuildExpiry>2027-01-01T00:00:00Z</KeyrightCoverageBuildExpiry> -->
</PropertyGroup>
Depuis la CI, passez-les en ligne de commande pour que le pipeline soit la source unique de vérité :
dotnet publish -c Release \
-p:KeyrightCoverageVersion=2.4.0 \
-p:KeyrightCoverageEntitlementDate=2026-10-01 \
-p:KeyrightRequireCoverage=true
Règles que les cibles font respecter et sur lesquelles le SDK s’appuie :
- Un build
KeyrightRequireCoverage=truedoit définirKeyrightCoverageEntitlementDate; sinon la compilation échoue avec l’erreur KR0001 (il n’y aurait rien à évaluer). Les propriétés non définies n’émettent aucune métadonnée. - Un correctif hérite de la date d’éligibilité de sa version de base. Compilez la
2.3.1avecKeyrightCoverageEntitlementDate=2026-07-02(la date de la 2.3.0) et elle reste couverte pour tous ceux qui avaient la 2.3.0 — et enregistrez-la avec le même--entitlement-datepour que le vérificateur et le registre soient d’accord. - Les suffixes de préversion (
2.5.0-beta.1) sont ignorés pour la correspondance des plages ; utilisez le canal pour les bêtas.
À l’exécution, les valeurs embarquées sont disponibles via CoverageBuildInfo.Current — Version, EntitlementDate, Channel, BuildExpiryUtc, RequireCoverage et HasMetadata.
2. Vérifier la couverture au démarrage
Le SDK fait déjà la moitié du travail : chaque ActivateAsync (et chaque rafraîchissement de lease) envoie la version et la date d’éligibilité du build, reçoit le bloc de couverture signé avec la lease, le vérifie avec votre clé publique embarquée et le met en cache à côté de la licence. Vous ajoutez un seul appel :
using Keyright.Client;
using Keyright.Coverage;
static readonly KeyrightClient License = KeyrightClient.Initialize(new KeyrightOptions
{
Product = "acme-studio",
PublicKeyBase64 = "MIIBIjANBgkq...", // la même clé vérifie les licences, les leases ET les blocs de couverture
ServiceUrl = "https://licensing.acme.example",
});
static async Task<bool> GateOnCoverageAsync(CancellationToken ct)
{
var build = CoverageBuildInfo.Current;
if (!build.RequireCoverage) return true; // un build qui n'applique pas les règles ignore entièrement la couverture
// Décide à partir du bloc en cache ; si non couvert (ou rien en cache), rafraîchit silencieusement la licence une fois
// (délai court, hors ligne accepté) et décide à nouveau. Ne lève jamais d'exception pour un problème réseau.
CoverageCheckResult check = await License.CheckCoverageAsync(ct: ct);
if (check.IsCovered) // Covered, ou NotConfigured (la couverture n'impose aucune restriction)
return true;
// Non couvert, ou indisponible hors ligne : construire le message + l'action unique et laisser l'application décider.
var message = BuildMessage(check);
switch (check.Status)
{
case CoverageCheckStatus.UnavailableOffline: // aucun bloc utilisable et le rafraîchissement a échoué (air-gapped)
ShowImportLicenseFile(message); // "…import an updated license file from Acme Software."
return false;
case CoverageCheckStatus.NotCovered:
return ShowNotCovered(message); // bloquer, exécuter en lecture seule, ou simplement informer — à vous de choisir
default:
return true;
}
}
CheckCoverageAsync(version?, entitlementDate?) donne par défaut à ses deux arguments les métadonnées embarquées du build, de sorte que l’appel sans paramètre répond à « ce build est-il couvert ? ». Il ne lève une exception que s’il n’y a aucune version du tout (ni embarquée ni passée en paramètre). Le résultat :
| Membre | Signification |
|---|---|
Status | Covered, NotCovered, NotConfigured ou UnavailableOffline. |
IsCovered | true pour Covered et NotConfigured — l’application peut s’exécuter. |
Reason | Pour NotCovered : WindowLapsed (ses mises à jour ont pris fin avant cette version → renouveler) ou OutsideVersionRange (cette ligne de versions n’est pas la sienne → mettre à niveau). |
NewestCovered | La version couverte la plus récente lorsque le bloc ou le registre la fait connaître (SemVer?), pour « vous pouvez continuer à utiliser la 2.4.0 ». |
Refreshed | Indique si un rafraîchissement silencieux a été tenté pendant cette vérification. |
UnavailableOffline est délibérément distinct de NotCovered : il signifie rien n’est en cache et le service n’a pas pu être joint, ce qui, sur un build appliquant les règles, est une situation « veuillez importer le fichier que nous vous avons envoyé » — jamais « votre licence est invalide », et jamais une invite à saisir la clé.
3. Décider localement, à tout moment
Pour vos propres messages dans l’application ou votre système de mise à jour — « dois-je afficher la bannière 3.0 ? », « quelle est la version la plus récente que ce client peut installer ? » — lisez le bloc en cache sans aucun accès réseau :
CoverageInfo cov = License.GetCoverage(); // depuis le bloc vérifié en cache ; ne contacte jamais un endpoint de distribution
if (cov.Available)
{
bool notConfigured = cov.IsNotConfigured; // bloc signé « aucune restriction »
CoverageMode mode = cov.Mode; // Enforced / Unenforced
DateTime? supportTo = cov.SupportUntilUtc; // fin de la dernière fenêtre, ou null si non borné par une date
long seq = cov.Seq; // le numéro de séquence du bloc (anti-rollback)
foreach (Grant g in cov.Grants) // les conditions : plage de versions, intervalles de dates, canal, raison
Console.WriteLine($"{g.Versions?.MinInclusive}..{g.Versions?.MaxExclusive} {g.Reason}");
CoverageDecision d = cov.Decide(SemVer.Parse("3.0.0"), CalendarDate.Parse("2026-11-01"), channel: null);
}
// En une ligne : cette version est-elle couverte pour cette licence ? La date d'éligibilité et le canal prennent par défaut les métadonnées du build.
bool can30 = License.IsCovered("3.0.0");
bool canOn = License.IsCovered("3.0.0", entitlementDate: "2026-11-01", channel: "beta");
IsCovered renvoie true pour une licence non configurée et false lorsqu’aucun bloc valide n’est disponible — un bloc manquant n’est jamais « couvert ». GetCoverage() renvoie CoverageInfo.Unavailable (Available == false) lorsque la licence elle-même ne se valide pas ou que rien n’est en cache.
4. Les messages et l’action unique
CoverageMessaging transforme un résultat en texte destiné au client et en exactement un appel à l’action, avec vos URL et votre formulation :
static readonly CoverageMessageOptions Wording = new()
{
Product = "Acme Studio",
Vendor = "Acme Software",
UpdatesTerm = "maintenance", // « updates » par défaut ; ou « support »
LineTemplate = "{product} {major}", // comment se lit une ligne de versions, p. ex. « Acme Studio 2 » ; ou "{major}.x"
RenewUrl = "https://acme.example/renew?key={key}",
UpgradeUrl = "https://acme.example/upgrade",
ImportLicenseFileUrl = "https://acme.example/support/offline-license",
RenewLabel = "Renew maintenance",
FormatDate = d => d.ToString("d MMMM yyyy", new CultureInfo("en-GB")), // {date} doit se lire sans ambiguïté
};
// Localisez ou réécrivez n'importe quel état : les clés sont les constantes CoverageMessaging.Key*, les modèles peuvent utiliser
// {product} {version} {newestCovered} {line} {date} {vendor} {updatesTerm}
// Wording.TemplateOverrides[CoverageMessaging.KeyWindowLapsed] = "…";
static CoverageMessage BuildMessage(CoverageCheckResult check)
{
var cov = License.GetCoverage();
int? lineMajor = cov.Grants.Where(g => g.Versions?.MinInclusive is not null)
.Select(g => (int?)g.Versions!.MinInclusive!.Value.Major).Max();
var ctx = new CoverageMessaging.Context
{
Version = CoverageBuildInfo.Current.Version ?? "",
LineMajor = lineMajor, // pour {line}
BoundaryDate = cov.SupportUntilUtc, // pour {date} dans « fenêtre échue »
NewestCovered = check.NewestCovered?.ToString(),
};
return CoverageMessaging.Build(check.Status, check.Reason, ctx, Wording);
}
// message.Text -> "This version (3.0.0) isn't included in your Acme Studio 2 license. You can keep using 2.4.0. Upgrade to use 3.0.0."
// message.Action -> CoverageAction.Upgrade (Renew | Upgrade | ImportLicenseFile | None)
// message.ActionUrl -> "https://acme.example/upgrade"
// message.ActionLabel -> "Upgrade"
La correspondance est fixe, de sorte que l’interface affiche toujours le bon bouton : fenêtre échue → Renew, hors de la plage de versions → Upgrade, indisponible hors ligne → Import license file ; Covered et NotConfigured produisent un message vide avec CoverageAction.None. Pour une ligne d’état dans l’application (« qu’est-ce que je possède ? »), utilisez CoverageMessaging.StatusSentence(cov.Grants, cov.IsNotConfigured, Wording) — la même phrase que celle affichée par le portail.
5. Importer un fichier de couverture (isolé, air-gapped)
string envelopeJson = File.ReadAllText("northwind.coverage.json"); // depuis votre export ou le bouton de téléchargement de la licence
CoverageVerifyResult r = License.ImportCoverageBlock(envelopeJson); // vérifie, lie à la licence, applique l'anti-rollback ; aucun accès réseau
if (!r.Ok)
{
// r.Status: Malformed | UnknownKey | BadSignature | RolledBack | WrongBinding | UnsupportedVersion
ShowImportError(r.Status);
}
Un résultat RolledBack signifie que le fichier est plus ancien que ce que la machine possède déjà (numéro de séquence inférieur) et a été ignoré ; WrongBinding signifie qu’il a été émis pour une autre licence ou un autre produit ; UnknownKey signifie qu’il a été signé par une clé que votre build n’embarque pas — pendant une rotation de clés, conservez l’ancienne clé publique dans AdditionalPublicKeysBase64 exactement comme vous le faites pour les licences et les leases.
6. Contrôler vos propres téléchargements : l’API d’éligibilité
Votre page de téléchargement ou votre système de mise à jour pose la même question à Keyright de serveur à serveur, authentifiée par la clé du client. Keyright répond ; c’est vous qui décidez de servir ou non le fichier.
curl -X POST $BASE/v1/coverage -H "content-type: application/json" \
-d '{"key":"LIC-…","product":"acme-studio","version":"2.5.0"}'
# -> { "eligible":true, "licenseValid":true, "status":"Covered", "reason":"None", "newestCovered":"2.4.0" }
# facultatif : "on":"2026-11-01" (date d'éligibilité ; par défaut = la date enregistrée, sinon aujourd'hui), "channel":"beta"
eligible vaut licenseValid && (status is Covered or NotConfigured) ; status/reason sont la décision brute, pour que vous puissiez formuler la page vous-même. Depuis un backend .NET :
using var http = new HttpClient { BaseAddress = new Uri("https://licensing.acme.example/") };
var resp = await http.PostAsJsonAsync("v1/coverage", new { key, product = "acme-studio", version = "2.5.0" });
var e = await resp.Content.ReadFromJsonAsync<Eligibility>();
if (e is { Eligible: true }) ServeDownload("2.5.0"); else ShowWhy(e!.Status, e.Reason, e.NewestCovered);
record Eligibility(bool Eligible, bool LicenseValid, string Status, string Reason, string? NewestCovered);
Référence des comportements
| Situation | Ce qui se passe |
|---|---|
| Le client renouvelle / met à niveau | Vous ajoutez des conditions (tableau de bord ou API). L’application les récupère à son prochain lancement ou rafraîchissement — sans nouvelle saisie de clé, sans réactivation. |
| Le client revient à un build plus ancien | Toujours couvert : une version plus ancienne est dans chaque ligne de versions et avant chaque fenêtre. |
| Deux versions côte à côte, ou des versions différentes par machine | Chaque build s’évalue lui-même par rapport à la même licence ; les sièges restent par machine, comme auparavant. |
| Horloge reculée | La couverture compare la date d’éligibilité signée du build aux conditions, jamais l’horloge locale ; la protection existante contre la manipulation de l’horloge s’applique toujours aux essais et aux licences à durée déterminée. |
| Bloc retiré ou modifié | La signature ou la liaison échoue → le SDK rafraîchit ; hors ligne sans rien de valide → UnavailableOffline. Jamais « couvert ». |
| Bloc plus ancien rejoué | Numéro de séquence inférieur → refusé ; la couverture reste au bloc le plus récent accepté. |
| Remboursement / révocation / transfert | Inchangé : révoquez la licence (l’application retombe en Free au rafraîchissement) ; les transferts conservent les conditions. |
| Votre plan n’inclut plus la couverture | Après une période de grâce, le service émet des blocs non appliqués ; les builds appliquant les règles continuent de fonctionner ; les conditions sont préservées et les blocs appliqués reprennent dès la remontée de plan. |
Cloud ou auto-hébergé
La couverture se comporte de façon identique dans les deux modes — les préréglages de politique, le bloc signé, les props MSBuild et les appels du SDK sont les mêmes. Ce qui diffère :
- Droit d’accès. Sur Keyright Cloud, la couverture fait partie du plan supérieur (Scale et Enterprise) ; les plans inférieurs la voient verrouillée avec une invitation à monter de plan. En auto-hébergé, elle est incluse dans les licences qui portent la fonctionnalité de couverture. Dans les deux cas, un espace de travail qui perd ce droit reçoit des blocs non appliqués après une période de grâce plutôt que de verrouiller ses clients, et les blocs appliqués reprennent lorsque le droit revient.
- URL de base / jeton.
$BASEesthttps://keyright.delta1labs.comsur Cloud, ou l’URL de votre propre service d’émission en auto-hébergé ;$TOKENest votre jeton de session du tableau de bord (Cloud) ou le jeton d’administration de tenant/d’environnement (auto-hébergé). LesServiceUrletPublicKeyBase64du SDK pointent vers l’instance correspondante et sa clé de signature. - Tout le reste — l’API d’éligibilité (
/v1/coverage), l’export/import hors ligne, le registre — est la même surface dans les deux modes.
Vérifier
- Donnez une politique à une édition (tableau de bord ou
coveragePolicysur l’upsert du tier), émettez une licence et ouvrez son onglet Coverage — confirmez que la phrase en langage clair et les conditions correspondent au préréglage. - Lancez le vérificateur de couverture (ou
GET /admin/licenses/{id}/coverage/check?version=…) pour une version que vous attendez couverte et une autre que vous attendez non couverte — confirmez le statut et la raison (WindowLapsedouOutsideVersionRange). - Compilez votre application avec
KeyrightCoverageVersion+KeyrightCoverageEntitlementDate+KeyrightRequireCoverage=true, activez avec une licence couverte et confirmez queCheckCoverageAsyncrenvoieCovered; passez la version du build au-delà des conditions et confirmez qu’il renvoieNotCoveredavec la bonne action unique. - Pour votre page de téléchargement, faites un
POST /v1/coverageavec une clé client + une version et confirmez queeligiblecorrespond à ce que voit l’application. - Isolé (air-gapped) : téléchargez le bloc signé, importez-le avec
ImportCoverageBlocksur une machine déconnectée et confirmez la même décision sans aucun accès réseau.
Voir aussi
- Intégration du SDK .NET — le guide client complet sur lequel s’appuie cette page.
- Tableau de bord d’administration — chaque écran, et les vues Basique / Intermédiaire / Avancé.
- Activer, désactiver et déplacer des sièges — leases, fichiers hors ligne et activation isolée (air-gapped).
- Mesurer l’usage et vendre des crédits — vendre de la consommation par-dessus une licence.
- Référence de l’API HTTP — la carte des endpoints.