Guide · Parte 7 di 8
Ancorare le release dalla CI
Ogni release che distribuisci è un insieme di byte: tarball, wheel, immagini di container, un intervallo di commit. Ancorare una Proof of Existence (prova di esistenza, PoE) Label 309 per quei byte trasforma «fidati della nostra parola: la build è ciò che abbiamo pubblicato» in «ecco una transazione Cardano che lo attesta». Calcoli l’hash di ogni artefatto per ottenere una foglia, pieghi le foglie in un’unica radice di Merkle e pubblichi quell’unica radice sulla catena sotto l’etichetta di metadati 309. Da quel momento chiunque abbia il riferimento della transazione può dimostrare che un artefatto esisteva all’orario del suo blocco o prima, dalla sola catena pubblica, senza account e senza fidarsi della tua pipeline o del tuo fornitore.
I byte dell’artefatto non lasciano mai il runner. La CLI calcola gli hash localmente e pubblica solo i digest, quindi l’operazione è sicura per repository privati e build a codice chiuso: ciò che finisce sulla catena è un hash di lunghezza fissa, mai il tuo codice.
Lo strumento centrale: cardanowall attest
Tutto in questa pagina passa da un unico comando della CLI cardanowall,
indipendente dal gateway (crate cardanowall-cli su crates.io, con binari
precompilati nelle
release di label-309-cli).
attest è il punto di ingresso pensato per la CI: calcola l’hash dei tuoi input,
richiede un preventivo e pubblica un record attraverso un gateway, e attende lo
stato del ciclo di vita che indichi.
Puntalo a un qualsiasi gateway Label 309 con un URL di base e una chiave API con ambito di pubblicazione, poi dagli qualcosa di cui calcolare l’hash:
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 e --api-key si leggono anche da CARDANOWALL_BASE_URL e
CARDANOWALL_API_KEY, quindi entrambi spariscono dal comando in CI, dove vengono
impostati dal tuo archivio dei segreti.
Tre modi per scegliere cosa ancorare
Imposta esattamente un input; la modalità ne consegue.
File. --paths accetta un percorso letterale o un pattern glob, ripetibile.
Ogni foglia è lo SHA-256 dei byte di un file. La selezione viene deduplicata e
ordinata byte per byte per percorso relativo normalizzato, così lo stesso albero di
lavoro produce sempre la stessa radice, indipendentemente dall’ordine in cui la
shell espanderebbe un glob. Metti il glob tra apici affinché la tua shell non lo
espanda prima:
cardanowall attest --paths 'dist/**/*.tar.gz' --paths 'dist/**/*.whl'Commit. --commits accetta un intervallo git rev-list; ogni foglia è lo
SHA-256 di un oggetto commit grezzo, dal più vecchio al più recente. Questo ancora
la provenienza della storia stessa. Richiede la storia git completa sul runner,
poiché un clone superficiale non può risolvere l’intervallo:
cardanowall attest --commits v1.0.0..v1.1.0Digest precalcolati. --leaf accetta un digest di 64 cifre esadecimali che hai
calcolato altrove, ripetibile e mantenuto nell’ordine degli argomenti. Usalo per
ancorare qualcosa che la CLI non vede mai come file, come il digest di
un’immagine OCI:
cardanowall attest --leaf 9f86d0818840…0a08 # a 64-hex digest, e.g. an image digestUna singola foglia pubblica un record a un solo elemento; più foglie pubblicano un record di Merkle la cui radice è ancorata sulla catena, con l’elenco delle foglie caricato affinché ogni elemento possa in seguito ottenere un certificato di inclusione.
Il manifest e la ricevuta
In modalità file, attest scrive un poe-manifest.json deterministico accanto al
proprio output (rinominalo con --manifest-out). Il manifest registra la
corrispondenza nome-hash di ogni file ancorato, e gli stessi input producono
sempre byte di manifest identici. Aggiungi --anchor-manifest per ripiegare lo
SHA-256 del manifest come foglia finale, così che il legame tra nomi di file e hash
faccia a sua volta parte di ciò a cui la radice si impegna.
--receipt-out scrive una ricevuta JSON versionata che riporta il record, il
preventivo, la transazione e l’istantanea dell’attesa. Conservala come prova della
tua build: è tutto ciò di cui un verify successivo ha bisogno per trovare e
controllare l’ancoraggio. Salvala come artefatto del workflow, allegala alla
release oppure inseriscila in un commit accanto al changelog.
Attesa, stato in sospeso e riesecuzioni
Per impostazione predefinita attest attende che la transazione superi la soglia
di conferma (--wait confirmed); --wait submitted ritorna non appena raggiunge
la rete. L’attesa ha una scadenza (--timeout, 600 secondi per impostazione
predefinita). Se la scadenza passa, gli output e la ricevuta vengono comunque
scritti e il processo termina con 3 (in sospeso): la pubblicazione non va persa,
prosegue sul gateway, e potrai ricontrollarla più tardi con la ricevuta. Un tetto
--max-usd rifiuta la pubblicazione (codice di uscita 1, prima di qualsiasi
caricamento) quando il preventivo supera il tuo limite, così un picco di prezzo non
può mai addebitare a sorpresa una pipeline.
Le riesecuzioni sono sicure per costruzione. attest non invia alcun header di
idempotenza per impostazione predefinita; il gateway deduplica invece i record
identici byte per byte, così rieseguire la stessa build non ancora mai una seconda
volta e non addebita mai due volte. Ancora di nuovo lo stesso dist/ e la seconda
esecuzione riproduce il primo record senza costi.
GitHub Actions
L’azione cardanowall/poe-attest avvolge la stessa CLI per i workflow di GitHub.
È open source e vincolata sulla catena di fornitura: incorpora i digest SHA-256
della release della CLI che esegue e verifica sia l’archivio scaricato sia il
binario estratto prima di ogni esecuzione, così un asset di release sostituito non
può passare.
Salva due segreti nel repository, GATEWAY_URL (l’URL di base del piano dati del
tuo gateway, che termina con /api/v1) e GATEWAY_API_KEY (una chiave con ambito
di pubblicazione), poi ancora i tuoi asset di release alla pubblicazione:
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/**/*.whlIl passo scrive la ricevuta, stampa un riepilogo con la transazione e un link di
verifica, ed espone output (tx, record-id, verify-url e altri) per i passi
successivi.
Firmare il flusso con un’identità di CI dedicata
Per rendere gli ancoraggi di una pipeline attribuibili e reperibili, firma ogni
record con un seed di identità. Il gateway indicizza allora il record per la chiave
pubblica del firmatario, così chiunque può elencare l’intera storia di una pipeline
dal feed dei record del gateway con ?signer=<public-key>. La firma resta
facoltativa, e i verificatori non la richiedono mai.
Usa un’identità dedicata e usa-e-getta per ogni pipeline, mai un seed personale. Salvala come segreto protetto dall’ambiente, così che solo le esecuzioni di quell’ambiente possano leggerla:
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.gzIl seed viene mascherato nei log e passato alla CLI solo tramite stdin, mai sulla riga di comando, e nessun segreto raggiunge mai il gateway: il calcolo dell’hash e la firma avvengono localmente, e vengono pubblicati solo il record e dati pubblici.
Non ancorare mai da pull_request_target
Quel trigger espone i segreti del tuo repository a codice proveniente da pull request forkate.
Ancora solo da eventi che controlli, come release, push o workflow_dispatch. Il job minimo
qui sopra necessita solo di contents: read; aggiungi contents: write solo se alleghi anche la
ricevuta alla release.
L’azione offre più input di quelli mostrati qui, tra cui un certificato di inclusione per foglia, l’allegato degli asset alla release, un tetto di prezzo e la politica di timeout. Consulta il README dell’azione per l’insieme completo.
GitLab CI/CD
Su GitLab lo stesso wrapper arriva come componente CI/CD. Il job gira dentro l’immagine di container della CLI stessa, bloccata per versione e digest, quindi a runtime non si installa nulla — e, come ovunque in questa pagina, funziona qualsiasi gateway Label 309, gestito da un operatore o self-hosted:
include:
- component: gitlab.com/cardanowall/poe-attest/attest@1
inputs:
gateway-url: https://your-gateway.example/api/v1
paths: |
dist/**/*.tar.gz
dist/**/*.whlAggiungi i segreti come variabili CI/CD in Settings → CI/CD → Variables,
mascherate e protette: CARDANOWALL_API_KEY (la chiave con scope di
pubblicazione) e, solo se firmi, CARDANOWALL_SEED — impostala sul progetto
stesso, mai su un gruppo: l’ereditarietà firmerebbe in silenzio gli ancoraggi
di ogni progetto figlio con una sola identità. Per impostazione predefinita il
job gira solo nelle pipeline dei tag protetti, così un ref non protetto non può
mai spendere il tuo saldo; sovrascrivi l’input rules per ancorare su altri
eventi.
I risultati tornano come report dotenv: un job a valle che referenzia il job di
attestazione con needs: legge direttamente la transazione, il link di
verifica e le restanti delle diciotto variabili POE_*:
announce:
needs: [poe-attest]
script:
- echo "anchored in $POE_TX"
- echo "verify at $POE_VERIFY_URL"Il componente offre più input di quelli mostrati qui, tra cui i certificati di inclusione, un tetto di prezzo, i tag dei runner e la politica di timeout. Consulta il README del componente per l’insieme completo.
Altri sistemi di CI
La stessa CLI gira ovunque. Usa l’immagine di container
ghcr.io/cardanowall/label-309-cli (il cui entrypoint è cardanowall) o un
binario precompilato dalla pagina delle release.
Qualsiasi altro runner, con il binario nel 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.Cosa ti serve da un gateway
Pubblicare mette una transazione su Cardano, e questo costa una commissione, quindi
attest ha bisogno di un gateway attraverso cui inviarla. Va bene qualsiasi
gateway Label 309: un operatore ospitato, oppure il tuo
gateway self-hosted (il
label-309-gateway open
source, un binario Rust più Postgres). Dalla CI te ne servono solo due cose: un URL
di base del piano dati e una chiave API con ambito di pubblicazione (poe:create),
sostenuta da un saldo prepagato.
Il gateway possiede il wallet Cardano finanziato e paga la commissione dal proprio modello di saldo. La tua CI non detiene alcuna chiave del wallet né fondi sulla catena. Il peggio che una chiave API trapelata possa fare è spendere il saldo prepagato di quell’account in altri ancoraggi; non può spostare fondi, leggere i tuoi contenuti o firmare al posto tuo. Ruotala o revocala in qualsiasi momento.
Verificare l’ancoraggio
Quell’ancoraggio vale qualcosa proprio perché chiunque può controllarlo senza di te. Dato il riferimento della transazione preso dalla ricevuta, la verifica gira in modo autonomo contro la catena pubblica e un explorer a tua scelta, senza account e senza gateway:
cardanowall verify <tx-hash>Risolve la transazione, valida strutturalmente il record, controlla un’eventuale
firma, conferma che il record è definitivo e restituisce un verdetto come codice di
uscita, così si inserisce in un controllo a valle con la stessa pulizia con cui
attest si inserisce sul lato pubblicazione. Per confermare un artefatto rispetto
al suo ancoraggio, calcola l’hash del file e confronta, oppure per un record di
Merkle costruisci un
certificato di inclusione che lega un artefatto
alla radice pubblicata. Il modello completo del verificatore è in
Verifica.
La prova sopravvive alla pipeline
Un ancoraggio Label 309 è semplice metadato sotto l’etichetta 309, non una ricevuta del fornitore. Molto dopo che il runner è sparito, il registro è stato ruotato e il sistema di CI è solo un ricordo, la transazione attesta ancora che i tuoi artefatti esistevano all’orario del loro blocco. Chiunque può verificarla dalla catena pubblica, senza account e senza fidarsi di chi l’ha pubblicata.