Aller au contenu
DockBoard
Parcourir la documentation
API

API de licence

Activez une licence, envoyez un heartbeat, et récupérez les clés publiques vérifiant un jeton de licence.

Voici l’API de ce site — celle qui émet et vérifie les licences. Elle est distincte de l’API de votre propre instance, et vous l’appelez rarement à la main : DockBoard s’active et se renouvelle tout seul.

Elle compte dans un cas : vous vendez ou provisionnez DockBoard pour d’autres, et vous voulez qu’une machine démarre déjà licenciée, sans intervention humaine.

Les surfaces

PréfixeQui l’appelleAuthentification
/api/v1/license/*Les instances DockBoard, de machine à machineLa clé de licence elle-même, sur une requête signée
/api/admin/*Le back-office, et les intégrations de provisionnementUne session staff — ou un jeton machine là où un handler a été ouvert
/api/shop/*Les clients, dans leur espaceUne session client

Cette page traite de la deuxième.

Jetons machine

Un jeton machine est un secret porteur sans session, sans second facteur, sans humain à l’autre bout. Il se place dans une variable de CI, un panneau d’hébergeur, un script cloud-init. Frappez-le dans Admin → Jetons, ou via l’API avec une session staff :

BASH
curl -X POST https://licenses.example.com/api/admin/machine-tokens \
  -H "Authorization: Bearer $STAFF_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "OVH provisioning",
    "permissions": [
      "customers:view", "customers:manage",
      "subscriptions:view", "subscriptions:manage",
      "licenses:view", "licenses:manage",
      "plans:view", "instances:view", "instances:manage"
    ]
  }'
Le secret apparaît dans cette réponse et nulle part ailleurs. Seul sha256(secret) est stocké ; aucun endpoint ne le ressert et aucun support ne peut le récupérer.

Les permissions inconnues ou interdites sont écartées à la frappe ; si rien de valide ne subsiste, la frappe est refusée plutôt que de produire un jeton plus faible que demandé. Les routes de jetons sont elles-mêmes réservées aux sessions, pour la même raison que la gestion des clés : un jeton qui frappe des jetons survit à sa propre révocation.

Ce qu’un jeton machine ne peut jamais détenir

RefuséParce que
staff:view staff:manageLire ou modifier le personnel, c’est ainsi qu’un jeton fuité se frappe un SUPERADMIN humain et cesse d’être un simple problème de jeton.
settings:view settings:manageLa rotation des clés de signature s’y trouve — faites-la tourner et toutes les licences en circulation sont resignées par la clé d’un autre. La frappe de jetons aussi.
payments:manageRemboursements et avoirs déplacent de l’argent réel. payments:view reste octroyable — lire un historique de paiement relève du provisionnement ordinaire ; déplacer de l’argent est un acte humain.

La liste d’interdiction est appliquée à trois endroits, pas un : à la frappe, à la résolution, et à la porte de chaque requête. Un jeton antérieur à une modification de la liste n’en tire aucun bénéfice.

Ce qui borne un jeton

Un jeton machine n’a pas de session qui expire, ni d’humain pour remarquer qu’il traîne encore. Trois limites tiennent lieu de l’attention qu’il ne recevra jamais.

Une liste d’adresses sources
allowedIps — une IP ou une plage CIDR par entrée, les deux familles. Présenté depuis ailleurs, il est refusé. La correspondance est fermée par défaut : un appel dont l’adresse source est indéterminable est refusé aussi, car une implémentation qui les laisserait passer désactiverait le contrôle partout d’un coup. Une règle malformée est un 400 à la frappe plutôt qu’un blocage à 3 h du matin, et 0.0.0.0/0 est refusé net — cela se lit comme une restriction et n’en est pas une. Laissez le champ vide et le jeton est joignable de partout, ce qui est la valeur par défaut.
Une expiration qui veut dire quelque chose
Facultative en général — la clé de provisionnement d’un hébergeur est faite pour survivre à une fenêtre de rotation — mais obligatoire, et plafonnée à 90 jours, pour `versions:manage` : ce jeton adoube la version vers laquelle toutes les installations se mettent à jour, et l’avoir immortel dans un coffre de CI en fait la cible la plus précieuse de la plateforme. Préférez GitHub OIDC, qui ne stocke aucun secret. Toute expiration que vous fixez doit être future et à moins de 365 jours : une date passée frappe une clé morte-née, et 2099-01-01 est un « jamais » déguisé en politique de rotation.
Un plafond sur le nombre
50 jetons vivants par installation. Les lignes révoquées ou expirées ne comptent pas — c’est de l’historique d’audit, et les compter bloquerait une installation qui fait tourner ses clés, précisément le comportement que la règle des 90 jours ci-dessus cherche à imposer.
JSON
{
  "name": "OVH provisioning",
  "permissions": ["licenses:manage", "customers:manage"],
  "allowedIps": ["203.0.113.0/24", "2001:db8::/32"],
  "expiresAt": "2027-02-17T00:00:00.000Z"
}

La liste dans Admin → Jetons affiche l’allowlist de chaque jeton, et lastUsedIp à côté de lastUsedAt — « d’où s’en sert-on ? » est la question qu’on se pose devant un jeton que personne ne reconnaît, et c’était celle à laquelle la liste ne savait pas répondre.

Provisionnement : un appel, une machine licenciée

BASH
curl -X POST https://licenses.example.com/api/admin/provisioning \
  -H "Authorization: Bearer $DOCKBOARD_MACHINE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "customer@example.com",
    "planSlug": "pro",
    "seats": 1,
    "reference": "order-12345"
  }'
JSON
{
  "created": true,
  "customer":     { "id": "…", "email": "customer@example.com", "created": true },
  "subscription": { "id": "…", "planSlug": "pro", "seats": 1, "status": "ACTIVE" },
  "license":      { "id": "…", "keyId": "lic_…", "expiresAt": "2027-08-17T…" },
  "licenseKey":   "eyJ…",
  "installCommand": "curl -fsSL https://get.dockboard.io/install.sh | sudo DOCKBOARD_LICENSE_KEY='eyJ…' sh"
}

Passez installCommand à cloud-init et la machine du client démarre déjà licenciée — il ne voit jamais Admin → Licence. Voir installer DockBoard pour ce qui se passe à l’arrivée.

ChampRequisNotes
emailouiLe client final. Un compte existant est réutilisé, jamais modifié — ni renommé, ni doté d’un nouveau mot de passe, ni re-rôlé.
planSlugouiPar slug (pro), pas par identifiant — un slug survit à une restauration de base, pas un identifiant. Une formule inactive est refusée.
seatsnonPar défaut 1.
billingCyclenonMONTHLY ou YEARLY.
statusnonPar défaut `ACTIVE`, pas TRIALING — un appel de provisionnement a lieu parce que le client a déjà payé l’hébergeur.
currentPeriodEndnonISO-8601. La licence expire avec lui ; absent, la valeur par défaut d’un an s’applique.
maxActivationsnonVaut le plafond de la formule, relevé au plafond précédent de l’abonnement s’il est supérieur, jamais abaissé.
referencenonClé d’idempotence. Voir ci-dessous.

`reference` fait tout le dessein

Les trois étapes que compose cet appel — compte, abonnement, licence — existent chacune séparément. Elles sont fusionnées ici parce que l’intérêt n’est aucune des trois : c’est que la séquence doit pouvoir être rejouée sans danger.

Un webhook qui redélivre. Une étape de pipeline rejouée. Un panneau où quelqu’un clique deux fois. Sans clé pour reconnaître la répétition, chacun de ces cas facture un second abonnement au client et frappe une seconde licence — et comme émettre révoque la précédente, l’installation en service du client tourne alors silencieusement sur une clé révoquée.

  • Le contrôle de rejeu s’exécute en premier, avant toute création : une nouvelle tentative est une simple lecture.
  • Il renvoie la licence active actuelle de l’abonnement, pas celle que l’appel avait frappée à l’origine — un changement de formule ou un renouvellement remplace l’ancienne clé, et un hébergeur relisant sa référence doit obtenir celle qui fonctionne aujourd’hui.
  • created: false dans la réponse signale un rejeu, et le journal d’audit l’enregistre comme tel plutôt que comme un provisionnement.
Un cas de rejeu est volontairement une erreur : une référence dont l’abonnement n’a aucune licence active — toutes révoquées, abonnement annulé — répond 400. Le reprovisionner ressusciterait un accès délibérément retiré.

Ce qu’un jeton peut atteindre

DomaineChemin de basePermissions
ProvisionnementPOST /api/admin/provisioninglicenses:manage
Clients/api/admin/customers — lister, lire, créer, changer le statut, réinitialiser le mot de passecustomers:view / customers:manage
Abonnements/api/admin/subscriptions — lister, lire, créer, modifier, aperçu de changement, annuler, historique de paiementsubscriptions:*, payments:view
Licences/api/admin/licenses — lister, lire, lire la clé, émettre, révoquer, suspendre, reprendre, prolonger, réémettrelicenses:view / licenses:manage
Instances/api/admin/instances — lister, lire, désactiver, réactiverinstances:view / instances:manage
Formules et fonctionnalités/api/admin/plans, /api/admin/featuresplans:*, features:*

Deux entrées méritent une note. GET /api/admin/licenses/:id/key renvoie le jeton de licence signé lui-même, et est ouvert aux jetons machine à dessein — un appel de provisionnement incapable de relire la clé qu’il vient d’émettre ne peut rien installer. Chaque lecture est auditée, pour une session comme pour un jeton : c’est ce contrôle qui la rend sûre. Et POST /api/admin/instances/:id/deactivate libère le créneau d’activation qu’une VM reconstruite occupe encore : sans lui, un hébergeur qui recycle ses machines a des clients dont la prochaine installation est refusée pour un serveur qui n’existe plus.

Tout ce qui est hors de ce tableau — personnel, réglages, clés de signature, remboursements — répond à un jeton :

JSON
{
  "statusCode": 403,
  "message": "This endpoint is not available to machine tokens. Use a staff session."
}

Erreurs

StatutSignification
401 « Invalid or expired machine token »Inconnu, révoqué ou expiré. Un seul message pour les trois — ces différences sont précisément ce qu’un sondeur utiliserait pour cartographier l’espace des jetons.
403 « not available to machine tokens »Le handler est réservé aux sessions.
403 « may never hold X »La permission du handler est sur la liste d’interdiction.
400 « Plan … is not active »Vendre une formule retirée via une intégration d’hébergeur, c’est ainsi qu’un tarif abandonné survit des années.
400 « Reference … has no active license »La cible du rejeu a été annulée. Réactivez l’abonnement, ou provisionnez avec une nouvelle référence.

Exploiter un jeton machine suit les mêmes règles que l’exploitation d’une clé API : rotation par recouvrement, lastUsedAt comme signal de vie et non comme journal d’accès, et en cas de fuite révoquer d’abord, lire le journal d’audit ensuite.

API de licence — DockBoard