Guías · Parte 7 de 8
Ancle sus versiones desde CI
Cada versión que publica es un conjunto de bytes: tarballs, wheels, imágenes de contenedor, un rango de commits. Anclar una Prueba de Existencia (PoE) de Label 309 para esos bytes convierte «confíe en nuestra palabra de que esta compilación es lo que publicamos» en «aquí tiene una transacción de Cardano que lo atestigua». Calcula el hash de cada artefacto para obtener una hoja, pliega las hojas en una única raíz de Merkle y publica esa raíz en la cadena bajo la etiqueta de metadatos 309. A partir de ahí, cualquiera que tenga la referencia de la transacción puede demostrar que un artefacto existía en la hora de su bloque o antes, solo a partir de la cadena pública, sin cuenta y sin confiar en su canalización ni en su proveedor.
Los bytes del artefacto nunca salen del runner. La CLI calcula el hash localmente y publica solo digests, así que esto es seguro para repositorios privados y compilaciones de código cerrado: lo que llega a la cadena es un hash de longitud fija, nunca su código.
La herramienta central: cardanowall attest
Todo en esta página se ejecuta a través de un único comando de la CLI
cardanowall, independiente del gateway (crate cardanowall-cli en crates.io,
con binarios precompilados en las
versiones de label-309-cli).
attest es el punto de entrada pensado para CI: calcula el hash de sus entradas,
cotiza y publica un registro a través de un gateway y espera al estado del ciclo
de vida que usted indique.
Apúntela a cualquier gateway de Label 309 con una URL base y una clave de API con alcance de publicación, y luego dele algo cuyo hash calcular:
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 y --api-key también se leen de CARDANOWALL_BASE_URL y
CARDANOWALL_API_KEY, así que ambos desaparecen del comando en CI, donde se
definen desde su almacén de secretos.
Tres formas de elegir qué anclar
Defina exactamente una entrada; el modo se deduce de ella.
Archivos. --paths acepta una ruta literal o un patrón glob, repetible. Cada
hoja es el SHA-256 de los bytes de un archivo. La selección se deduplica y se
ordena byte a byte por ruta relativa normalizada, de modo que el mismo árbol de
trabajo siempre produce la misma raíz, con independencia del orden en que la shell
expandiría un glob. Entrecomille el glob para que su shell no lo expanda antes:
cardanowall attest --paths 'dist/**/*.tar.gz' --paths 'dist/**/*.whl'Commits. --commits acepta un rango de git rev-list; cada hoja es el SHA-256
de un objeto de commit en bruto, del más antiguo al más reciente. Esto ancla la
procedencia del historial en sí. Necesita el historial de git completo en el
runner, ya que un clon superficial no puede resolver el rango:
cardanowall attest --commits v1.0.0..v1.1.0Digests precalculados. --leaf acepta un digest de 64 hex que usted calculó
en otro lugar, repetible y conservado en el orden de los argumentos. Úselo para
anclar algo que la CLI nunca ve como archivo, como el digest de una imagen OCI:
cardanowall attest --leaf 9f86d0818840…0a08 # a 64-hex digest, e.g. an image digestUna sola hoja publica un registro de un único elemento; varias hojas publican un registro de Merkle cuya raíz se ancla en la cadena, con la lista de hojas subida para que cada elemento pueda obtener después un certificado de inclusión.
El manifiesto y el recibo
En modo de archivos, attest escribe un poe-manifest.json determinista junto a
su salida (renómbrelo con --manifest-out). El manifiesto registra la
correspondencia de nombre a hash de cada archivo que ancló, y las mismas entradas
siempre producen bytes de manifiesto idénticos. Añada --anchor-manifest para
plegar el SHA-256 del manifiesto como hoja final, de modo que el vínculo entre
nombres de archivo y hashes forme parte a su vez de aquello a lo que la raíz se
compromete.
--receipt-out escribe un recibo JSON versionado que lleva el registro, la
cotización, la transacción y la instantánea de la espera. Consérvelo como la
prueba de su compilación: es todo lo que un verify posterior necesita para
encontrar y comprobar el anclaje. Guárdelo como artefacto del flujo de trabajo,
adjúntelo a la versión o inclúyalo en un commit junto al changelog.
Espera, estado pendiente y reejecuciones
De forma predeterminada, attest espera a que la transacción cruce el umbral de
confirmación (--wait confirmed); --wait submitted regresa en cuanto llega a la
red. La espera tiene un plazo (--timeout, 600 segundos por defecto). Si el plazo
vence, la salida y el recibo se escriben de todos modos y el proceso termina con 3
(pendiente): la publicación no se pierde, continúa en el gateway y usted puede
volver a comprobarla más tarde con el recibo. Un tope --max-usd rechaza la
publicación (código de salida 1, antes de cualquier subida) cuando la cotización
supera su límite, de modo que un pico de precio nunca puede facturar por sorpresa
a una canalización.
Las reejecuciones son seguras por construcción. attest no envía ninguna cabecera
de idempotencia de forma predeterminada; en su lugar, el gateway deduplica los
registros idénticos byte a byte, así que reejecutar la misma compilación nunca
ancla una segunda vez ni factura dos veces. Ancle el mismo dist/ de nuevo y la
segunda ejecución reproduce el primer registro sin coste.
GitHub Actions
La acción cardanowall/poe-attest envuelve la misma CLI para los flujos de
trabajo de GitHub. Es de código abierto y está fijada en la cadena de suministro:
incrusta los digests SHA-256 de la versión de la CLI que ejecuta y verifica tanto
el archivo descargado como el binario extraído antes de cada ejecución, de modo
que un asset de versión intercambiado no pueda colarse.
Guarde dos secretos en el repositorio, GATEWAY_URL (la URL base del plano de
datos de su gateway, que termina en /api/v1) y GATEWAY_API_KEY (una clave con
alcance de publicación), y luego ancle sus assets de versión al publicarla:
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/**/*.whlEl paso escribe el recibo, imprime un resumen con la transacción y un enlace de
verificación, y expone salidas (tx, record-id, verify-url y más) para pasos
posteriores.
Firmar el flujo con una identidad de CI dedicada
Para que los anclajes de una canalización sean atribuibles y localizables, firme
cada registro con una semilla de identidad. El gateway indexa entonces el registro
por la clave pública de quien firma, de modo que cualquiera puede listar todo el
historial de una canalización desde el feed de registros del gateway con
?signer=<public-key>. La firma sigue siendo opcional, y los verificadores nunca
la exigen.
Use una identidad dedicada y desechable por canalización, nunca una semilla personal. Guárdela como un secreto protegido por entorno para que solo las ejecuciones de ese entorno puedan leerla:
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 semilla se enmascara en los registros y se pasa a la CLI solo por stdin, nunca en la línea de comandos, y ningún secreto llega jamás al gateway: el cálculo del hash y la firma ocurren localmente, y solo se publican el registro y los datos públicos.
Nunca ancle desde pull_request_target
Ese disparador expone los secretos de su repositorio a código de pull requests bifurcados. Ancle
solo desde eventos que usted controle, como release, push o workflow_dispatch. El job mínimo
de arriba necesita solo contents: read; añada contents: write únicamente si además adjunta el
recibo a la versión.
La acción admite más entradas de las mostradas aquí, entre ellas un certificado de inclusión por hoja, la adjunción de assets a la versión, un tope de precio y la política de tiempo de espera. Consulte el README de la acción para ver el conjunto completo.
GitLab CI/CD
En GitLab, el mismo wrapper se distribuye como componente de CI/CD. El job se ejecuta dentro de la propia imagen de contenedor de la CLI, fijada por versión y digest, de modo que no se instala nada en tiempo de ejecución — y, como en toda esta página, funciona cualquier gateway Label 309, alojado o autogestionado:
include:
- component: gitlab.com/cardanowall/poe-attest/attest@1
inputs:
gateway-url: https://your-gateway.example/api/v1
paths: |
dist/**/*.tar.gz
dist/**/*.whlAñada los secretos como variables de CI/CD en Settings → CI/CD → Variables,
enmascaradas y protegidas: CARDANOWALL_API_KEY (la clave con ámbito de
publicación) y, solo si firma, CARDANOWALL_SEED — defínala en el propio
proyecto, nunca en un grupo: la herencia firmaría en silencio los anclajes de
cada proyecto hijo con una sola identidad. Por defecto, el job se ejecuta solo
en pipelines de etiquetas protegidas, de modo que una referencia sin proteger
nunca puede gastar su saldo; sobrescriba la entrada rules para anclar en
otros eventos.
Los resultados vuelven como un informe dotenv: un job posterior que referencie
el job de attest con needs: lee directamente la transacción, el enlace de
verificación y el resto de las dieciocho variables POE_*:
announce:
needs: [poe-attest]
script:
- echo "anchored in $POE_TX"
- echo "verify at $POE_VERIFY_URL"El componente admite más entradas de las mostradas aquí, entre ellas certificados de inclusión, un tope de precio, etiquetas de runner y la política de tiempo de espera. Consulte el README del componente para ver el conjunto completo.
Otros sistemas de CI
La misma CLI se ejecuta en cualquier lugar. Use la imagen de contenedor
ghcr.io/cardanowall/label-309-cli (cuyo entrypoint es cardanowall) o un
binario precompilado de la página de versiones.
Cualquier otro runner, con el binario en 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.Qué necesita de un gateway
Publicar pone una transacción en Cardano, y eso cuesta una comisión, así que
attest necesita un gateway a través del cual enviarla. Sirve cualquier gateway
de Label 309: un operador alojado, o su propio
gateway autoalojado (el de código abierto
label-309-gateway, un
binario de Rust más Postgres). Desde CI solo necesita dos cosas de él: una URL
base del plano de datos y una clave de API con alcance de publicación
(poe:create), respaldada por saldo prepago.
El gateway posee la billetera de Cardano financiada y paga la comisión desde su propio modelo de saldo. Su CI no guarda ninguna clave de billetera ni fondos en la cadena. Lo peor que puede hacer una clave de API filtrada es gastar el saldo prepago de esa cuenta en más anclajes; no puede mover fondos, leer su contenido ni firmar en su nombre. Rótela o revóquela en cualquier momento.
Verificar el anclaje
Ese anclaje vale algo precisamente porque cualquiera puede comprobarlo sin usted. Dada la referencia de la transacción del recibo, la verificación se ejecuta de forma autónoma contra la cadena pública y un explorador de su elección, sin cuenta y sin gateway:
cardanowall verify <tx-hash>Resuelve la transacción, valida estructuralmente el registro, comprueba cualquier
firma, confirma que el registro está asentado y devuelve un veredicto como su
código de salida, de modo que encaja en una comprobación posterior con la misma
limpieza con que attest encaja en el lado de la publicación. Para confirmar un
artefacto contra su anclaje, calcule el hash del archivo y compárelo, o para un
registro de Merkle construya un
certificado de inclusión que fije un artefacto a
la raíz publicada. El modelo completo del verificador está en
Verificación.
La prueba sobrevive a la canalización
Un anclaje de Label 309 son simples metadatos bajo la etiqueta 309, no un recibo de proveedor. Mucho después de que el runner haya desaparecido, el registro se haya rotado y el sistema de CI sea un recuerdo, la transacción sigue atestiguando que sus artefactos existían en su hora de bloque. Cualquiera puede verificarla desde la cadena pública, sin cuenta y sin confiar en quien la publicó.