Anleitungen

Anleitungen · Teil 7 von 8

Releases aus der CI verankern

Jedes Release, das Sie ausliefern, ist eine Menge von Bytes: Tarballs, Wheels, Container-Images, ein Bereich von Commits. Einen Label-309-Existenznachweis (engl. Proof of Existence, PoE) für diese Bytes zu verankern, verwandelt „Vertrauen Sie unserem Wort, dass dieser Build das ist, was wir veröffentlicht haben" in „Hier ist eine Cardano-Transaktion, die es bezeugt." Sie hashen jedes Artefakt zu einem Blatt, falten die Blätter zu einer einzigen Merkle-Wurzel zusammen und veröffentlichen diese eine Wurzel auf der Blockchain unter dem Metadaten-Label 309. Von da an kann jede und jeder mit der Transaktionsreferenz beweisen, dass ein Artefakt zum Zeitpunkt seiner Blockzeit oder davor existierte, allein anhand der öffentlichen Blockchain, ohne Konto und ohne Vertrauen in Ihre Pipeline oder Ihren Anbieter.

Die Artefakt-Bytes verlassen den Runner niemals. Das CLI hasht lokal und veröffentlicht nur Digests, daher ist dies auch für private Repositorys und Closed-Source-Builds sicher: Was auf die Blockchain gelangt, ist ein Hash fester Länge, niemals Ihr Code.

Das zentrale Werkzeug: cardanowall attest

Alles auf dieser Seite läuft über einen einzigen Befehl des Gateway-unabhängigen cardanowall-CLI (Crate cardanowall-cli auf crates.io, mit vorgefertigten Binärdateien auf den label-309-cli-Releases). attest ist der auf CI zugeschnittene Einstiegspunkt: Es hasht Ihre Eingaben, holt ein Preisangebot ein, veröffentlicht einen Datensatz über ein Gateway und wartet auf den Lebenszyklus-Zustand, den Sie angeben.

Richten Sie es mit einer Basis-URL und einem auf Veröffentlichung beschränkten API-Schlüssel auf ein beliebiges Label-309-Gateway und geben Sie ihm dann etwas zu hashen:

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 und --api-key lesen auch aus CARDANOWALL_BASE_URL und CARDANOWALL_API_KEY, sodass beide in der CI aus dem Befehl herausfallen, wo diese aus Ihrem Secret-Store gesetzt werden.

Drei Wege, um zu wählen, was Sie verankern

Setzen Sie genau eine Eingabe; der Modus ergibt sich daraus.

Dateien. --paths nimmt einen konkreten Pfad oder ein Glob-Muster entgegen, wiederholbar. Jedes Blatt ist der SHA-256 der Bytes einer Datei. Die Auswahl wird dedupliziert und byteweise nach normalisiertem relativem Pfad sortiert, sodass derselbe Arbeitsbaum immer dieselbe Wurzel erzeugt, unabhängig von der Reihenfolge, in der die Shell ein Glob auflösen würde. Setzen Sie das Glob in Anführungszeichen, damit Ihre Shell es nicht vorab auflöst:

cardanowall attest --paths 'dist/**/*.tar.gz' --paths 'dist/**/*.whl'

Commits. --commits nimmt einen git-rev-list-Bereich entgegen; jedes Blatt ist der SHA-256 eines rohen Commit-Objekts, das älteste zuerst. Dies verankert die Provenienz der Historie selbst. Es benötigt die vollständige git-Historie auf dem Runner, da ein flacher Klon den Bereich nicht auflösen kann:

cardanowall attest --commits v1.0.0..v1.1.0

Vorgehashte Digests. --leaf nimmt einen 64-stelligen Hex-Digest entgegen, den Sie anderswo berechnet haben, wiederholbar und in der Argumentreihenfolge beibehalten. Verwenden Sie es, um etwas zu verankern, das das CLI nie als Datei sieht, etwa einen OCI-Image-Digest:

cardanowall attest --leaf 9f86d0818840…0a08   # a 64-hex digest, e.g. an image digest

Ein einzelnes Blatt veröffentlicht einen Datensatz mit einem einzigen Element; mehrere Blätter veröffentlichen einen Merkle-Datensatz, dessen Wurzel auf der Blockchain verankert wird, wobei die Blattliste hochgeladen wird, sodass jedes Element später ein Inklusionszertifikat erhalten kann.

Das Manifest und die Quittung

Im Dateimodus schreibt attest eine deterministische poe-manifest.json neben seine Ausgabe (mit --manifest-out umbenennen). Das Manifest hält die Zuordnung von Name zu Hash für jede verankerte Datei fest, und dieselben Eingaben erzeugen immer byteidentische Manifest-Bytes. Fügen Sie --anchor-manifest hinzu, um den SHA-256 des Manifests als letztes Blatt einzufalten, sodass die Bindung zwischen Dateinamen und Hashes selbst Teil dessen wird, worauf sich die Wurzel festlegt.

--receipt-out schreibt eine versionierte JSON-Quittung, die den Datensatz, das Preisangebot, die Transaktion und den Warte-Schnappschuss trägt. Bewahren Sie sie als Nachweis Ihres Builds auf: Sie ist alles, was ein späteres verify braucht, um den Anker zu finden und zu prüfen. Speichern Sie sie als Workflow-Artefakt, hängen Sie sie an das Release an oder committen Sie sie neben dem Changelog.

Warten, Ausstehen und erneute Läufe

Standardmäßig wartet attest, bis die Transaktion die Bestätigungsschwelle überschreitet (--wait confirmed); --wait submitted kehrt zurück, sobald sie das Netzwerk erreicht. Das Warten hat eine Frist (--timeout, standardmäßig 600 Sekunden). Läuft die Frist ab, werden die Ausgaben und die Quittung dennoch geschrieben und der Prozess beendet sich mit 3 (ausstehend): Die Veröffentlichung geht nicht verloren, sie läuft auf dem Gateway weiter, und Sie können später mit der Quittung erneut nachsehen. Eine --max-usd-Obergrenze verweigert die Veröffentlichung (Exit-Code 1, vor jedem Upload), wenn das Angebot über Ihrem Limit liegt, sodass eine Preisspitze eine Pipeline niemals überraschend belasten kann.

Erneute Läufe sind von Grund auf sicher. attest sendet standardmäßig keinen Idempotenz-Header; stattdessen dedupliziert das Gateway byteidentische Datensätze, sodass ein erneuter Lauf desselben Builds nie ein zweites Mal verankert und nie doppelt abgerechnet wird. Verankern Sie dasselbe dist/ erneut, und der zweite Lauf wiederholt den ersten Datensatz kostenlos.

GitHub Actions

Die Action cardanowall/poe-attest umschließt dasselbe CLI für GitHub-Workflows. Sie ist quelloffen und über die Lieferkette gepinnt: Sie bettet die SHA-256-Digests des von ihr ausgeführten CLI-Releases ein und verifiziert vor jedem Lauf sowohl das heruntergeladene Archiv als auch die extrahierte Binärdatei, sodass ein ausgetauschtes Release-Asset nicht durchrutschen kann.

Hinterlegen Sie zwei Secrets im Repository, GATEWAY_URL (die Basis-URL der Datenebene Ihres Gateways, die auf /api/v1 endet) und GATEWAY_API_KEY (einen auf Veröffentlichung beschränkten Schlüssel), und verankern Sie dann Ihre Release-Assets beim Veröffentlichen:

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/**/*.whl

Der Schritt schreibt die Quittung, gibt eine Zusammenfassung mit der Transaktion und einem Verifizierungslink aus und stellt Ausgaben (tx, record-id, verify-url und weitere) für spätere Schritte bereit.

Den Datenstrom mit einer dedizierten CI-Identität signieren

Um die Anker einer Pipeline zurechenbar und auffindbar zu machen, signieren Sie jeden Datensatz mit einem Identitäts-Seed. Das Gateway indiziert den Datensatz dann nach seinem Signierer-Public-Key, sodass jede und jeder die gesamte Historie einer Pipeline über den Datensatz-Feed des Gateways mit ?signer=<public-key> auflisten kann. Das Signieren bleibt optional, und Verifizierer verlangen es nie.

Verwenden Sie eine dedizierte, wegwerfbare Identität pro Pipeline, niemals einen persönlichen Seed. Hinterlegen Sie ihn als umgebungsgeschütztes Secret, sodass nur Läufe aus dieser Umgebung ihn lesen können:

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.gz

Der Seed wird in Logs maskiert und nur über stdin an das CLI übergeben, niemals auf der Befehlszeile, und kein Secret erreicht jemals das Gateway: Hashen und Signieren geschehen lokal, und nur der Datensatz und öffentliche Daten werden veröffentlicht.

Niemals aus pull_request_target verankern

Dieser Trigger legt Ihre Repository-Secrets gegenüber Code aus geforkten Pull Requests offen. Verankern Sie nur aus Ereignissen, die Sie kontrollieren, etwa release, push oder workflow_dispatch. Der minimale Job oben benötigt nur contents: read; fügen Sie contents: write nur hinzu, wenn Sie die Quittung zusätzlich an das Release anhängen.

Die Action trägt mehr Eingaben, als hier gezeigt werden, darunter ein Inklusionszertifikat pro Blatt, das Anhängen von Release-Assets, eine Preisobergrenze und die Timeout-Richtlinie. Den vollständigen Satz finden Sie im Action-README.

GitLab CI/CD

Auf GitLab kommt derselbe Wrapper als CI/CD-Komponente. Der Job läuft im eigenen Container-Image des CLI, festgepinnt auf Version und Digest, sodass zur Laufzeit nichts installiert wird — und wie überall auf dieser Seite funktioniert jedes Label-309-Gateway, gehostet oder selbst betrieben:

include:
  - component: gitlab.com/cardanowall/poe-attest/attest@1
    inputs:
      gateway-url: https://your-gateway.example/api/v1
      paths: |
        dist/**/*.tar.gz
        dist/**/*.whl

Hinterlegen Sie die Secrets als CI/CD-Variablen unter Settings → CI/CD → Variables, maskiert und geschützt: CARDANOWALL_API_KEY (der Schlüssel mit Publish-Scope) und, nur wenn Sie signieren, CARDANOWALL_SEED — gesetzt am Projekt selbst, niemals an einer Gruppe: Vererbung würde die Anker jedes Unterprojekts stillschweigend mit einer einzigen Identität signieren. Standardmäßig läuft der Job nur in Pipelines geschützter Tags, sodass ein ungeschützter Ref niemals Ihr Guthaben ausgeben kann; überschreiben Sie die Eingabe rules, um bei anderen Ereignissen zu verankern.

Die Ergebnisse kommen als dotenv-Report zurück: Ein nachgelagerter Job, der den Attest-Job per needs: referenziert, liest die Transaktion, den Verify-Link und die übrigen der achtzehn POE_*-Variablen direkt:

announce:
  needs: [poe-attest]
  script:
    - echo "anchored in $POE_TX"
    - echo "verify at $POE_VERIFY_URL"

Die Komponente trägt mehr Eingaben, als hier gezeigt werden, darunter Inklusionszertifikate, eine Preisobergrenze, Runner-Tags und die Timeout-Richtlinie. Den vollständigen Satz finden Sie im Komponenten-README.

Andere CI-Systeme

Dasselbe CLI läuft überall. Verwenden Sie das Container-Image ghcr.io/cardanowall/label-309-cli (dessen Entrypoint cardanowall ist) oder eine vorgefertigte Binärdatei von der Releases-Seite.

Jeder andere Runner, mit der Binärdatei auf 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.

Was Sie von einem Gateway brauchen

Das Veröffentlichen bringt eine Transaktion auf Cardano, und das kostet eine Gebühr, daher braucht attest ein Gateway, über das es übermittelt. Jedes Label-309-Gateway funktioniert: ein gehosteter Betreiber oder Ihr eigenes selbst gehostetes Gateway (das quelloffene label-309-gateway, eine Rust-Binärdatei plus Postgres). Aus der CI brauchen Sie nur zwei Dinge davon: eine Basis-URL der Datenebene und einen auf Veröffentlichung beschränkten API-Schlüssel (poe:create), gedeckt durch ein vorausbezahltes Guthaben.

Das Gateway besitzt die finanzierte Cardano-Wallet und zahlt die Gebühr aus seinem eigenen Guthabenmodell. Ihre CI hält keinen Wallet-Schlüssel und keine Blockchain-Mittel. Das Schlimmste, was ein durchgesickerter API-Schlüssel anrichten kann, ist, das vorausbezahlte Guthaben dieses Kontos für weitere Anker auszugeben; er kann keine Mittel bewegen, Ihre Inhalte lesen oder in Ihrem Namen signieren. Rotieren oder widerrufen Sie ihn jederzeit.

Den Anker verifizieren

Diese Verankerung ist nur deshalb etwas wert, weil jede und jeder sie ohne Sie prüfen kann. Mit der Transaktionsreferenz aus der Quittung läuft die Verifizierung eigenständig gegen die öffentliche Blockchain und einen Explorer Ihrer Wahl, ohne Konto und ohne Gateway:

cardanowall verify <tx-hash>

Es löst die Transaktion auf, validiert den Datensatz strukturell, prüft eine etwaige Signatur, bestätigt, dass der Datensatz abgeschlossen ist, und gibt ein Ergebnis als Exit-Code zurück, sodass es sich ebenso sauber in eine nachgelagerte Prüfung einfügt, wie attest auf die Veröffentlichungsseite passt. Um ein einzelnes Artefakt gegen seinen Anker zu bestätigen, hashen Sie die Datei und vergleichen Sie, oder erstellen Sie für einen Merkle-Datensatz ein Inklusionszertifikat, das ein Artefakt an die veröffentlichte Wurzel bindet. Das vollständige Verifizierer-Modell finden Sie unter Verifizierung.

Der Nachweis überdauert die Pipeline

Ein Label-309-Anker ist schlichte Metadaten unter Label 309, keine Anbieter-Quittung. Lange nachdem der Runner verschwunden ist, die Registry rotiert wurde und das CI-System nur noch Erinnerung ist, bezeugt die Transaktion weiterhin, dass Ihre Artefakte zu ihrer Blockzeit existierten. Jede und jeder kann sie verifizieren über die öffentliche Blockchain, ohne Konto und ohne Vertrauen in denjenigen, der sie veröffentlicht hat.