Files
takana/scripts/consenso.sh
T
Sergio 24bcf1783c takana etapa 4: los 10 crates de librería y el CLI pasan a takana-*
hammer-{core,build,bootstrap,overlay,journal,mirror,upgrade,agent,recover,cli}
→ takana-*, con sus deps de workspace, sus identificadores en el fuente y las
referencias -p de los scripts.

VERIFICADO que no mueve nada del corpus: `takana hash recipes/zlib.toml`
devuelve b3:dc363f26… , idéntico a antes del renombre. Los nombres de crate no
entran en hash_inputs, pero eso se comprueba, no se supone. 600 tests en verde.

DOS BINARIOS SE CONGELAN, y no por prolijidad:

- `hammerd` — paquete Y binario. Es componente de Stage 1 de la distro (musl,
  busybox, hammerd, arje-zero), lo supervisa arje-zero en el sistema arrancado,
  `PRESEED=hammerd` lo nombra en selfhost-verify y sus bytes anclan el baseline
  of_tree. El nombre del crate va en los símbolos ⇒ renombrarlo mueve los bytes.

- `hammer-recover` — el PAQUETE se renombra a takana-recover, el BINARIO no.
  hammer-live-install.sh lo copia a /usr/sbin/hammer-recover en sistemas ya
  instalados y hornea un hook de arranque que lo invoca por ese nombre:
  renombrarlo rompe máquinas instaladas, no el repo.

Consecuencia que hay que anotar igual: al renombrar hammer-core, los bytes de
hammerd cambian de todos modos porque linkea contra un crate con otro nombre.
El baseline of_tree del selfhost hay que rehacerlo — es efecto de la etapa 4,
no de un cambio de hammerd.

Las referencias en comentarios de recetas y docs (rutas hammer-core/src/…)
quedan para la etapa 5: son texto, no mueven hash.
2026-09-09 18:46:41 +00:00

86 lines
4.2 KiB
Bash
Executable File

#!/bin/sh
# consenso.sh <receta> [N] — CONSENSO DE RECONSTRUCCIÓN (SDD 17 §1).
#
# N builders INDEPENDIENTES reproducen el mismo hash ⇒ el binario es confiable **sin que nadie
# firme nada**. Convierte la reproducibilidad de QA en primitiva de distribución: cualquiera
# puede ser mirror, nadie puede envenenar el repo.
#
# scripts/consenso.sh recipes/bzip2.toml 2
#
# Builders: este hub (laptop) + N-1 workers efímeros de la granja, cada uno una máquina física
# distinta con su propio OS. Si coinciden ⇒ CONSENSO. Si divergen ⇒ candidato a `why-differs`
# (SDD 17 §2) — y eso también es información: una divergencia es un canal impuro encontrado.
#
# Por qué esto no lo puede ofrecer nix/debian: su repro es incompleta. hammer ya paga el
# invariante (bit-repro verificada: selfhost, kernel), así que esto cae casi solo del invariante
# — que es la tesis del SDD 17: buscar "qué cae del invariante", no "qué frontera agregar".
#
# NO firma ni publica: sólo produce el veredicto de consenso. El log de transparencia
# (`fork-proof`, hash-encadenado + detección de equivocation) es el paso siguiente y YA está
# escrito en tawasuyu — no hay que construir un rekor.
set -eu
RECETA="${1:?uso: consenso.sh <receta.toml> [N-builders]}"
N="${2:-2}"
HUB="$(cd "$(dirname "$0")/.." && pwd)"
cd "$HUB"
IMAGE="${IMAGE:-408909310}"
TYPE="${TYPE:-ccx23}"
LOCATION="${LOCATION:-hel1}"
SSHKEY="${SSHKEY:-desarrollo@jlsoltech.com}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
SSH="ssh -i $SSH_KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes"
n=$(basename "$RECETA" .toml)
OUT="work/consenso"; mkdir -p "$OUT"
echo "══ CONSENSO DE RECONSTRUCCIÓN — $n, $N builders independientes"
# Builder 1: el hub. Es un builder legítimo (otra máquina, otro OS que los workers).
echo "── builder 1: hub local ($(uname -s), $(grep -m1 '^NAME=' /etc/os-release 2>/dev/null | cut -d'"' -f2 || echo '?'))"
h1=$(timeout "${POR:-900}" ./target/debug/hammer build "$RECETA" --store "$PWD/store" 2>/dev/null \
| grep -oE '^b3:[a-f0-9]{64}' | head -1)
echo " → ${h1:-FALLÓ}"
[ -z "$h1" ] && { echo " el hub no puede construir $n ⇒ sin consenso posible desde acá"; exit 3; }
echo "$h1 hub" > "$OUT/$n.hashes"
# Builders 2..N: workers efímeros, cada uno una máquina física distinta.
i=2
while [ "$i" -le "$N" ]; do
name="hkc-$i"
echo "── builder $i: worker efímero $name"
hcloud server create --name "$name" --image "$IMAGE" --type "$TYPE" --location "$LOCATION" \
--ssh-key "$SSHKEY" >/dev/null 2>&1
ip=$(hcloud server ip "$name")
for _ in $(seq 1 30); do $SSH "root@$ip" true 2>/dev/null && break; sleep 6; done
rsync -az -e "$SSH" --exclude /work --exclude /store --exclude '/store-*' --exclude /target \
--exclude /dist --exclude /tandas --exclude /.dev-fs --exclude /.git --exclude /.scratch \
--exclude '*.png' --exclude '/content*' ./ "root@$ip:/opt/hammer/" >/dev/null 2>&1
hN=$($SSH "root@$ip" "export PATH=\$HOME/.cargo/bin:\$PATH; cd /opt/hammer && \
cargo build --release -q -p takana-cli 2>/dev/null; \
timeout ${POR:-900} ./target/release/takana build $RECETA --store \$PWD/store 2>/dev/null \
| grep -oE '^b3:[a-f0-9]{64}' | head -1" 2>/dev/null | tr -d '\r')
echo " → ${hN:-FALLÓ}"
[ -n "$hN" ] && echo "$hN $name" >> "$OUT/$n.hashes"
hcloud server delete "$name" >/dev/null 2>&1
i=$((i + 1))
done
echo
echo "══ VEREDICTO"
cat "$OUT/$n.hashes" | sed 's/^/ /'
distintos=$(awk '{print $1}' "$OUT/$n.hashes" | sort -u | wc -l)
cuantos=$(wc -l < "$OUT/$n.hashes")
if [ "$cuantos" -lt 2 ]; then
echo " SIN CONSENSO: sólo $cuantos builder(s) produjeron hash. No se afirma nada."
exit 3
elif [ "$distintos" = 1 ]; then
echo " ✅ CONSENSO ($cuantos/$cuantos builders, mismo hash bit a bit)"
echo " ⇒ el artefacto es confiable SIN FIRMA: N máquinas independientes lo reprodujeron."
exit 0
else
echo " ⚠️ DIVERGENCIA: $distintos hashes distintos entre $cuantos builders."
echo " ⇒ hay un canal impuro. Es trabajo para \`why-differs\` (SDD 17 §2) — y la"
echo " divergencia YA es información: el invariante bit-repro se rompió acá."
exit 1
fi