diff --git a/infra/devnet/restart-seed.sh b/infra/devnet/restart-seed.sh index 8fc76da5..b95bcb01 100755 --- a/infra/devnet/restart-seed.sh +++ b/infra/devnet/restart-seed.sh @@ -13,6 +13,15 @@ SRC="${IGNEUMD_LINUX:-/Users/joshm/Projects/igneum/infra/cross/out-039/igneumd}" WANT="${IGNEUMD_LINUX_SHA256:-7e26e374586ad8ba539e371c6cbbd74f0f77aee1e2e97cd25f0182fcbda458a5}" echo "$OVJSON" | python3 -c 'import json,sys; o=json.load(sys.stdin); assert isinstance(o, dict) and "fees_v1_activation_daa" in o; print("override:", json.dumps(o, sort_keys=True))' test "$(shasum -a 256 "$SRC" | cut -c1-64)" = "$WANT" || { echo "$SRC is not the 2b6d23ef Linux node ($WANT)"; exit 1; } +# the seed is Debian 12 (glibc 2.36): a binary built on igneum-build-1 (Ubuntu 24.04, glibc 2.39) does not start there +# ("version GLIBC_2.38 not found", a restart loop; 6 October 2026, 21:43Z, three minutes down). The zigbuild from +# infra/cross/build-linux.sh (glibc 2.36 target) is the seed's binary. Refused here before anything is copied. +NEED="$(strings "$SRC" | grep -oE 'GLIBC_2\.[0-9]+' | sort -t. -k2 -n | tail -1 | sed 's/GLIBC_//')" +HOST_GLIBC="$(ssh -i "$HOME/.ssh/igneum_ed25519" -o ConnectTimeout=8 root@188.245.5.161 'ldd --version 2>/dev/null | head -1 | grep -oE "[0-9]+\.[0-9]+$"' 2>/dev/null || echo 2.36)" +if [ -n "$NEED" ] && [ "$(printf '%s\n%s\n' "$NEED" "$HOST_GLIBC" | sort -t. -k1,1n -k2,2n | tail -1)" != "$HOST_GLIBC" ]; then + echo "$SRC needs GLIBC $NEED but the seed has $HOST_GLIBC: build it with infra/cross/build-linux.sh (zigbuild, glibc 2.36) and pass that one"; exit 1 +fi +echo "binary needs GLIBC $NEED, the seed has $HOST_GLIBC" SSH="ssh -i $HOME/.ssh/igneum_ed25519 -o ConnectTimeout=20 root@188.245.5.161" # 6 October 2026 (the exec recovery after the 15:44Z reorg): IGNEUMD_EXEC_SNAPSHOT_FILE= copies that exec snapshot to the # seed (/root/v4/exec-snapshot.bin) and adds --igneum-exec-snapshot=,0x to EXTRA_ARGS in seed-v4.env (replacing an diff --git a/packaging/ota/publish-jobs.sh b/packaging/ota/publish-jobs.sh index 38d2fdc2..fb5248f1 100755 --- a/packaging/ota/publish-jobs.sh +++ b/packaging/ota/publish-jobs.sh @@ -404,6 +404,33 @@ if [ -n "$NEXT_FOLDER" ]; then cp "$JOBS" "$JOBS.sig" "$SIGNED" "$NEXT_FOLDER/" && echo "jobs file, signature and envelope mirrored to the next folder" fi +# --deploy ships the JOBS FILE ONLY (6 October 2026, 22:0x UK rule): the downloads folder is shared disk state and a deploy +# ships all of it, so a manifest or a package staged there by another lane went live with the next job publish (the Mac and +# PC 1 took earlier 0.3.15 builds that way). Before deploying, every file in the live token folder and dl/public/ apart from +# the jobs file and its signature must equal what is live (size and sha256 against the served copy); anything else differing +# refuses the deploy and names the files. The release's own deploy goes through packaging/ota/publish-manifest.sh --deploy or +# tools/ship-app.mjs, never through here. IGNEUM_JOBS_DEPLOY_ALL=1 overrides, knowingly, for a full-folder deploy. +jobs_only_check() { + [ "${IGNEUM_JOBS_DEPLOY_ALL:-0}" = 1 ] && { echo "IGNEUM_JOBS_DEPLOY_ALL=1: deploying the whole folder" >&2; return 0; } + local base="https://dl.igneum.network" bad=0 f n live_size local_size local_sha live_sha + for f in "$DEST"/* "$DLSITE"/dl/public/*; do + [ -f "$f" ] || continue + n="$(basename "$f")" + case "$n" in igneum-jobs.json|igneum-jobs.signed.json|igneum-jobs.json.sig) continue ;; esac + local rel; rel="${f#"$DLSITE"}" + local_size="$(stat -f %z "$f" 2>/dev/null || stat -c %s "$f")" + live_size="$(curl -sI -m 10 -H 'Cache-Control: no-cache' "$base$rel?x=$RANDOM" | awk 'tolower($1)=="content-length:"{print $2}' | tr -d '\r' | tail -1)" + if [ -z "$live_size" ]; then echo " not live yet: $rel (a new file; the release step deploys it)" >&2; bad=1; continue; fi + if [ "$live_size" != "$local_size" ]; then echo " differs from the live copy: $rel (local $local_size B, live $live_size B)" >&2; bad=1; continue; fi + case "$n" in *.json|*.sig|*.sha256) # small: compare the bytes + local_sha="$(shasum -a 256 "$f" | cut -c1-64)"; live_sha="$(curl -s -m 15 -H 'Cache-Control: no-cache' "$base$rel?x=$RANDOM" | shasum -a 256 | cut -c1-64)" + [ "$local_sha" = "$live_sha" ] || { echo " differs from the live copy: $rel (content)" >&2; bad=1; } ;; + esac + done + if [ "$bad" = 1 ]; then echo "publish-jobs: the folder holds changes besides the jobs file (above); a job publish never deploys them. Deploy the release through publish-manifest.sh --deploy or ship-app.mjs, or stage elsewhere. Not deployed." >&2; return 1; fi + return 0 +} +if [ "$DEPLOY" = 1 ] && ! jobs_only_check; then DEPLOY=0; echo "the jobs file is written and signed locally; the deploy was refused" >&2; exit 1; fi if [ "$DEPLOY" = 1 ]; then [ -n "$DLSITE" ] || { echo "--deploy needs the real downloads folder (no --dest)" >&2; exit 1; } echo "deploying $DLSITE"