Guides · Partie 7 sur 8
Ancrer vos versions depuis la CI
Chaque version que vous livrez est un ensemble d’octets : archives tar, wheels, images de conteneur, une plage de commits. Ancrer une preuve d’existence (Proof of Existence, PoE) Label 309 pour ces octets transforme un « croyez-nous sur parole, ce build est bien ce que nous avons publié » en un « voici une transaction Cardano qui en témoigne ». Vous calculez l’empreinte de chaque artefact pour en faire une feuille, vous pliez les feuilles en une seule racine de Merkle, et vous publiez cette unique racine sur la chaîne sous le label de métadonnées 309. Dès lors, quiconque détient la référence de la transaction peut prouver qu’un artefact existait à l’heure de son bloc ou avant, à partir de la seule chaîne publique, sans compte et sans faire confiance à votre pipeline ni à votre fournisseur.
Les octets de l’artefact ne quittent jamais le runner. La CLI calcule les empreintes localement et ne publie que des digests, ce qui rend l’opération sûre pour les dépôts privés et les builds à source fermée : ce qui arrive sur la chaîne est une empreinte de longueur fixe, jamais votre code.
L’outil central : cardanowall attest
Tout sur cette page passe par une seule commande de la CLI cardanowall,
indépendante du gateway (crate cardanowall-cli sur crates.io, avec des binaires
précompilés dans les
versions de label-309-cli).
attest est le point d’entrée pensé pour la CI : il calcule l’empreinte de vos
entrées, obtient un devis et publie un enregistrement via un gateway, puis attend
l’état de cycle de vie que vous demandez.
Pointez-la vers n’importe quel gateway Label 309 avec une URL de base et une clé d’API à portée de publication, puis donnez-lui quelque chose dont calculer l’empreinte :
export CARDANOWALL_API_KEY="…" # a publish-scoped key from your gateway
cardanowall attest \
--paths 'dist/*' \
--base-url https://your-gateway.example/api/v1 \
--wait confirmed \
--receipt-out poe-receipt.json--base-url et --api-key se lisent aussi depuis CARDANOWALL_BASE_URL et
CARDANOWALL_API_KEY, si bien que les deux disparaissent de la commande en CI, où
elles sont définies depuis votre coffre à secrets.
Trois façons de choisir ce que vous ancrez
Définissez exactement une entrée ; le mode en découle.
Fichiers. --paths accepte un chemin littéral ou un motif glob, répétable.
Chaque feuille est le SHA-256 des octets d’un fichier. La sélection est
dédupliquée et triée octet par octet selon le chemin relatif normalisé, de sorte
que le même arbre de travail produit toujours la même racine, quel que soit
l’ordre dans lequel le shell développerait un glob. Mettez le glob entre guillemets
pour que votre shell ne le développe pas d’abord :
cardanowall attest --paths 'dist/**/*.tar.gz' --paths 'dist/**/*.whl'Commits. --commits accepte une plage git rev-list ; chaque feuille est le
SHA-256 d’un objet commit brut, du plus ancien au plus récent. Cela ancre la
provenance de l’historique lui-même. Il faut l’historique git complet sur le
runner, car un clone superficiel ne peut pas résoudre la plage :
cardanowall attest --commits v1.0.0..v1.1.0Digests précalculés. --leaf accepte un digest de 64 caractères hexadécimaux
que vous avez calculé ailleurs, répétable et conservé dans l’ordre des arguments.
Utilisez-le pour ancrer quelque chose que la CLI ne voit jamais comme un fichier,
tel que le digest d’une image OCI :
cardanowall attest --leaf 9f86d0818840…0a08 # a 64-hex digest, e.g. an image digestUne seule feuille publie un enregistrement à un seul élément ; plusieurs feuilles publient un enregistrement de Merkle dont la racine est ancrée sur la chaîne, la liste des feuilles étant téléversée pour que chaque élément puisse obtenir plus tard un certificat d’inclusion.
Le manifeste et le reçu
En mode fichiers, attest écrit un poe-manifest.json déterministe à côté de sa
sortie (renommez-le avec --manifest-out). Le manifeste consigne la
correspondance nom-empreinte de chaque fichier ancré, et les mêmes entrées
produisent toujours des octets de manifeste identiques. Ajoutez --anchor-manifest
pour replier le SHA-256 du manifeste comme feuille finale, de sorte que le lien
entre noms de fichiers et empreintes fasse lui-même partie de ce à quoi la racine
s’engage.
--receipt-out écrit un reçu JSON versionné portant l’enregistrement, le devis,
la transaction et l’instantané de l’attente. Conservez-le comme preuve de votre
build : c’est tout ce dont un verify ultérieur a besoin pour retrouver et
vérifier l’ancrage. Stockez-le comme artefact de workflow, joignez-le à la version
ou committez-le à côté du changelog.
Attente, état en attente et réexécutions
Par défaut, attest attend que la transaction franchisse le seuil de confirmation
(--wait confirmed) ; --wait submitted revient dès qu’elle atteint le réseau.
L’attente a une échéance (--timeout, 600 secondes par défaut). Si l’échéance
expire, les sorties et le reçu sont tout de même écrits et le processus se termine
avec le code 3 (en attente) : la publication n’est pas perdue, elle se poursuit
sur le gateway, et vous pourrez la revérifier plus tard avec le reçu. Un plafond
--max-usd refuse la publication (code de sortie 1, avant tout téléversement)
lorsque le devis dépasse votre limite, de sorte qu’un pic de prix ne peut jamais
facturer un pipeline par surprise.
Les réexécutions sont sûres par construction. attest n’envoie aucun en-tête
d’idempotence par défaut ; à la place, le gateway déduplique les enregistrements
identiques octet pour octet, de sorte que réexécuter le même build n’ancre jamais
une seconde fois et ne facture jamais deux fois. Ancrez le même dist/ à nouveau
et la seconde exécution rejoue le premier enregistrement gratuitement.
GitHub Actions
L’action cardanowall/poe-attest enveloppe la même CLI pour les workflows GitHub.
Elle est open source et épinglée côté chaîne d’approvisionnement : elle intègre les
digests SHA-256 de la version de la CLI qu’elle exécute et vérifie à la fois
l’archive téléchargée et le binaire extrait avant chaque exécution, si bien qu’un
asset de version substitué ne peut pas passer.
Enregistrez deux secrets sur le dépôt, GATEWAY_URL (l’URL de base du plan de
données de votre gateway, se terminant par /api/v1) et GATEWAY_API_KEY (une clé
à portée de publication), puis ancrez vos assets de version à la publication :
name: anchor-release
on:
release:
types: [published]
permissions:
contents: read
jobs:
attest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: cardanowall/poe-attest@v1
with:
gateway-url: ${{ secrets.GATEWAY_URL }}
api-key: ${{ secrets.GATEWAY_API_KEY }}
paths: |
dist/**/*.tar.gz
dist/**/*.whlL’étape écrit le reçu, affiche un récapitulatif avec la transaction et un lien de
vérification, et expose des sorties (tx, record-id, verify-url, et d’autres)
pour les étapes suivantes.
Signer le flux avec une identité de CI dédiée
Pour rendre les ancrages d’un pipeline attribuables et repérables, signez chaque
enregistrement avec une graine d’identité. Le gateway indexe alors l’enregistrement
par la clé publique du signataire, de sorte que quiconque peut lister tout
l’historique d’un pipeline depuis le flux d’enregistrements du gateway avec
?signer=<public-key>. La signature reste facultative, et les vérificateurs ne
l’exigent jamais.
Utilisez une identité dédiée et jetable par pipeline, jamais une graine personnelle. Enregistrez-la comme un secret protégé par environnement pour que seules les exécutions de cet environnement puissent la lire :
jobs:
attest:
runs-on: ubuntu-latest
environment: release-signing
steps:
- uses: actions/checkout@v7
- uses: cardanowall/poe-attest@v1
with:
gateway-url: ${{ secrets.GATEWAY_URL }}
api-key: ${{ secrets.GATEWAY_API_KEY }}
seed: ${{ secrets.CI_SIGNING_SEED }}
paths: dist/**/*.tar.gzLa graine est masquée dans les journaux et transmise à la CLI uniquement par stdin, jamais sur la ligne de commande, et aucun secret n’atteint jamais le gateway : le calcul de l’empreinte et la signature se font localement, et seuls l’enregistrement et des données publiques sont publiés.
N’ancrez jamais depuis pull_request_target
Ce déclencheur expose les secrets de votre dépôt à du code issu de pull requests forkées. N’ancrez
que depuis des événements que vous contrôlez, comme release, push ou workflow_dispatch. Le
job minimal ci-dessus n’a besoin que de contents: read ; n’ajoutez contents: write que si vous
joignez aussi le reçu à la version.
L’action comporte plus d’entrées que celles montrées ici, dont un certificat d’inclusion par feuille, la jonction d’assets à la version, un plafond de prix et la politique de délai d’attente. Consultez le README de l’action pour l’ensemble complet.
GitLab CI/CD
Sur GitLab, le même wrapper est distribué comme composant CI/CD. Le job s’exécute dans l’image de conteneur de la CLI elle-même, épinglée par version et par digest, donc rien n’est installé à l’exécution — et, comme partout sur cette page, n’importe quel gateway Label 309 convient, hébergé ou auto-hébergé :
include:
- component: gitlab.com/cardanowall/poe-attest/attest@1
inputs:
gateway-url: https://your-gateway.example/api/v1
paths: |
dist/**/*.tar.gz
dist/**/*.whlAjoutez les secrets comme variables CI/CD dans Settings → CI/CD →
Variables, masquées et protégées : CARDANOWALL_API_KEY (la clé à portée de
publication) et, seulement si vous signez, CARDANOWALL_SEED — définie sur le
projet lui-même, jamais sur un groupe : l’héritage signerait en silence les
ancrages de chaque projet enfant avec une seule identité. Par défaut, le job ne
s’exécute que dans les pipelines de tags protégés, si bien qu’une référence non
protégée ne peut jamais dépenser votre solde ; redéfinissez l’entrée rules
pour ancrer sur d’autres événements.
Les résultats reviennent sous forme de rapport dotenv : un job en aval qui
référence le job d’attestation via needs: lit directement la transaction, le
lien de vérification et le reste des dix-huit variables POE_* :
announce:
needs: [poe-attest]
script:
- echo "anchored in $POE_TX"
- echo "verify at $POE_VERIFY_URL"Le composant comporte plus d’entrées que celles montrées ici, dont les certificats d’inclusion, un plafond de prix, les tags de runner et la politique de délai d’attente. Consultez le README du composant pour l’ensemble complet.
Autres systèmes de CI
La même CLI s’exécute partout. Utilisez l’image de conteneur
ghcr.io/cardanowall/label-309-cli (dont l’entrypoint est cardanowall) ou un
binaire précompilé depuis la page des versions.
N’importe quel autre runner, avec le binaire dans le PATH :
export CARDANOWALL_BASE_URL="https://your-gateway.example/api/v1"
export CARDANOWALL_API_KEY="$YOUR_CI_SECRET"
cardanowall attest \
--paths 'dist/*' \
--wait confirmed \
--receipt-out poe-receipt.json
# exit 0 = reached the wait target; 3 = pending (publish continues on the gateway);
# 1 = refused (for example over --max-usd) or failed.Ce qu’il vous faut d’un gateway
Publier place une transaction sur Cardano, et cela coûte des frais, si bien
qu’attest a besoin d’un gateway par lequel soumettre. N’importe quel gateway
Label 309 convient : un opérateur hébergé, ou votre propre
gateway auto-hébergé (le
label-309-gateway open
source, un binaire Rust plus Postgres). Depuis la CI, il ne vous en faut que deux
choses : une URL de base du plan de données, et une clé d’API à portée de
publication (poe:create) adossée à un solde prépayé.
Le gateway détient le portefeuille Cardano approvisionné et paie les frais depuis son propre modèle de solde. Votre CI ne détient aucune clé de portefeuille ni aucun fonds sur la chaîne. Le pire qu’une clé d’API divulguée puisse faire, c’est dépenser le solde prépayé de ce compte en ancrages supplémentaires ; elle ne peut ni déplacer des fonds, ni lire votre contenu, ni signer en votre nom. Renouvelez-la ou révoquez-la à tout moment.
Vérifier l’ancrage
Cet ancrage n’a de valeur que parce que quiconque peut le vérifier sans vous. Munie de la référence de transaction issue du reçu, la vérification s’exécute de façon autonome contre la chaîne publique et un explorateur de votre choix, sans compte et sans gateway :
cardanowall verify <tx-hash>Elle résout la transaction, valide structurellement l’enregistrement, vérifie une
éventuelle signature, confirme que l’enregistrement est établi, et renvoie un
verdict comme code de sortie, si bien qu’elle s’insère dans une vérification en
aval aussi proprement qu’attest s’insère du côté publication. Pour confirmer un
artefact face à son ancrage, calculez l’empreinte du fichier et comparez, ou pour
un enregistrement de Merkle, construisez un
certificat d’inclusion qui rattache un artefact à
la racine publiée. Le modèle complet du vérificateur figure dans
Vérification.
La preuve survit au pipeline
Un ancrage Label 309, ce sont de simples métadonnées sous le label 309, pas un reçu de fournisseur. Longtemps après que le runner a disparu, que le registre a été renouvelé et que le système de CI n’est plus qu’un souvenir, la transaction témoigne toujours que vos artefacts existaient à l’heure de leur bloc. Quiconque peut la vérifier depuis la chaîne publique, sans compte et sans faire confiance à celui qui l’a publiée.