Aller au contenu
Bondry

Programme de publicateurs

Enregistrez votre module, publiez une version, récupérez le paquet signé et recevez les événements dont votre serveur a besoin.

Nous n'hébergeons pas votre module. Vous vendez et distribuez où vous voulez : votre site, votre boutique, une place de marché, un revendeur. Ce qui reste ici, c'est la vérité sur ce fichier, et c'est cette vérité que l'installateur de l'acheteur vérifie avant d'extraire le moindre octet.

Ce que vous faites, une fois#

  1. Enregistrez l'artefact dans votre compte, section Publication. Le slug est réservé ici, et non à votre première version : c'est le nom que votre module emmène dans chaque installation, il va dans votre module.json et il ne change jamais. Les noms de notre espace (bondry-, core, admin, designer, members, lms) sont refusés.
  2. Générez le jeton de publication. C'est un id bkp_ plus un secret montré une seule fois. Un jeton par artefact, révocable sur-le-champ. Révoquer ne retire pas ce qui est déjà publié : cela empêche la prochaine annonce.
  3. Indiquez votre URL de webhook si vous voulez que votre serveur ait de nos nouvelles. Https uniquement, port 443, et le nom doit résoudre vers des adresses publiques.

Ce que fait votre serveur, à chaque version#

Tout est signé avec la même enveloppe que vous connaissez déjà de l'API de licence, il n'y a donc rien de nouveau à apprendre :

Code
X-Bondry-Key:        bkp_3f7a91c25e08
X-Bondry-Timestamp:  1789459200
X-Bondry-Nonce:      3f7a91c25e0844b1
X-Bondry-Signature:  hex(hmac_sha256(secret, METHOD\nPATH\nTIMESTAMP\nNONCE\nsha256(corps)))

La fenêtre du timestamp est de 300 secondes, le nonce est à usage unique et le corps entre dans la signature par son sha256 : pas un octet ne change en route.

Appel Ce qu'il fait
POST /v1/publisher/artifacts/{slug} Met à jour le nom, le résumé, la description par langue, le site et le canal de support. Idempotent : un champ que vous n'envoyez pas est un champ que nous ne touchons pas. L'anglais est obligatoire dans tout texte par langue
POST /v1/publisher/artifacts/{slug}/releases Annonce une version : version, min_core, max_core, php, notes par langue, size, sha256
PUT /v1/publisher/releases/{version}/package Envoie le zip, brut dans le corps, jusqu'à 40 Mo. Le sha256 doit correspondre à l'annonce
GET /v1/publisher/artifacts/{slug} Tout ce dont votre pipeline a besoin pour décider s'il continue : état de la revue, version publiée, empreinte de validation, dernière annonce
GET /v1/publisher/releases/{version}/package Après approbation, une URL signée et courte pour récupérer le zip signé
GET /v1/publisher/releases/{version}/hash L'empreinte de validation de cette version

Annoncer coûte peu et se répète ; envoyer 40 Mo, non, et c'est pourquoi ce sont deux appels. Une annonce sans paquet expire d'elle-même au bout de sept jours, et ce numéro de version redevient libre.

Chaque refus indique le champ, ce qui est arrivé et ce qui était attendu. « Invalid payload » n'est pas un message.

La revue#

La moitié automatique tourne dès que votre paquet arrive : aucun chemin hors de l'archive, aucun lien symbolique, aucune bombe de décompression, taille sous le plafond, manifeste valide avec le slug enregistré et la version annoncée, version qui avance, min_core qui existe, traduction anglaise présente, et l'analyse statique qui lève un signalement sur eval, un grand littéral dans base64_decode, shell_exec, une URL avec une IP fixe et du code obfusqué.

Un signalement ne refuse rien à lui seul : du code légitime les utilise tous de temps en temps. Il part chez un relecteur humain, avec le fichier et la ligne exacte.

La moitié humaine regarde votre manifeste, les permissions et les hooks que vous déclarez, les migrations et les fichiers de routes du paquet, les signalements et la différence avec votre version précédente. La décision est approuver, refuser, demander des changements ou suspendre, et toutes sauf l'approbation portent un motif écrit qui vous parvient mot pour mot.

« Demander des changements » remet la version en brouillon, le seul état où votre serveur peut renvoyer le paquet de la même version.

Le zip signé est à vous de distribuer#

Approuvée, nous signons votre zip avec la clé de paquet Bondry et enregistrons le sha256 du fichier signé. Cette empreinte est l'identité de cette version pour toujours.

Le zip signé, c'est votre zip plus deux entrées à la racine :

Code
bondry-artifact.json     {"slug":…,"version":…,"publisher":…,"files":{"<chemin>":"<sha256>"}}
bondry-artifact.sig      base64 de RSA-SHA256 sur le manifeste canonique

La liste de fichiers couvre chaque entrée de votre zip d'origine, et l'installateur vérifie dans les deux sens : rien du zip absent de la liste, rien de la liste absent du zip. Sans cela, il suffirait d'ajouter un .php au paquet après signature et la signature tiendrait encore.

Vous récupérez le fichier signé depuis votre compte ou par l'API, et nous supprimons notre copie dès que vous le téléchargez, et dans tous les cas sept jours après l'approbation, avec un e-mail le troisième jour si vous ne l'avez pas récupéré. Ce qui reste ici, c'est le manifeste, la signature, l'empreinte et les notes. Le fichier est à vous, sa conservation aussi.

Ce que fait l'installateur de l'acheteur#

Il calcule le sha256 du fichier qu'il a en main, vérifie la signature intégrée avec la clé publique qu'il porte déjà pour les mises à jour du core, et interroge le registre :

Code
GET /v1/artifacts/{slug}/verify?version=1.4.2&sha256=<64 hex>

Sans identifiant, une réponse parmi trois, toujours en HTTP 200 :

Réponse Ce que voit l'acheteur
verified « Fichier vérifié auprès du registre Bondry, version 1.4.2 du publicateur », et l'installation continue
altered Le mur rouge : ce fichier n'est pas celui que l'auteur a publié. L'installation refuse par défaut
unknown Ne figure pas au registre : traité comme n'importe quel zip trouvé sur internet

Une version suspendue répond unknown. Sans réseau, la signature intégrée vaut toujours et l'installateur dit qu'il n'a pas pu confirmer : une absence de réseau n'est jamais une accusation, et jamais une approbation silencieuse.

Les événements que vous recevez#

Vous enregistrez une URL https par artefact et nous l'appelons :

Événement Quand
artifact.approved / artifact.rejected la décision sur votre enregistrement
release.approved la version a passé la revue et est publiée
release.rejected refusée, ou renvoyée pour changements, avec le motif
release.suspended retirée après publication, avec le motif
package.altered une installation sous licence a reçu un fichier qui ne correspond pas à votre empreinte approuvée

Le corps :

JSON
{
  "id": "01J8ZC5E7Q2R8VQ1F0M4V8N0PA",
  "type": "release.approved",
  "created_at": "2026-09-17T11:31:55+00:00",
  "data": { "slug": "directory", "version": "1.1.0", "validation_hash": "…" }
}

L'id est stable : le renvoi porte le même id, traitez-le donc comme votre clé d'idempotence. Quand un type couvre plusieurs décisions, data.decision porte le mot exact et data.reason, le motif écrit.

Vérifier la signature#

Nous signons l'appel avec le secret de webhook de cet artefact, dans la même enveloppe que ci-dessus, avec une différence : X-Bondry-Key porte whk_<12 hex>, l'id public du secret. C'est ainsi que votre côté sait quel secret utiliser après une rotation, et c'est pourquoi il vaut mieux indexer vos secrets par cet id.

PHP
$canonical = implode("\n", [
    'POST',
    '/votre/chemin/de/webhook',
    $request->header('X-Bondry-Timestamp'),
    $request->header('X-Bondry-Nonce'),
    hash('sha256', $request->getContent()),
]);

$attendue = hash_hmac('sha256', $canonical, $votreSecretDe($request->header('X-Bondry-Key')));

if (! hash_equals($attendue, (string) $request->header('X-Bondry-Signature'))) {
    abort(401);
}

Faire tourner le secret depuis votre compte garde le précédent valide 24 heures, pour changer votre configuration sans perdre d'événement.

Répondez 2xx en moins de 10 secondes. Sinon, nous réessayons après 1 min, 5 min, 30 min, 2 h, 12 h et 24 h, puis le point de terminaison passe en quarantaine et vous recevez un e-mail. Chaque livraison et chaque tentative restent visibles dans votre compte, avec la réponse de votre serveur et un bouton pour renvoyer.

Quand quelqu'un diffuse une copie altérée#

L'appel de verdict est public, et c'est pourquoi il ne déclenche jamais d'alerte : n'importe qui pourrait l'appeler mille fois avec une empreinte inventée, et le registre deviendrait un amplificateur de spam braqué sur vous.

Ce qui déclenche l'alerte, c'est l'événement : une installation sous licence active signalant que le fichier reçu n'est pas celui que nous avons signé. Cela devient le webhook package.altered, un e-mail pour vous avec l'empreinte reçue et celle que nous avons approuvée, et une ligne dans notre file, groupée par artefact et par empreinte. La même empreinte altérée apparaissant sur beaucoup de licences, c'est du piratage à grande échelle, et c'est exactement ce que le groupement sert à montrer.

Rien de tout cela ne révoque quoi que ce soit tout seul. Suspendre, c'est une personne, avec un motif écrit.

Confidentialité

Ce site utilise uniquement des cookies nécessaires : session, langue, thème et la vérification antispam hCaptcha dans les formulaires. Il n'y a ni suivi ni publicité. Politique de confidentialité