#!/usr/bin/env bash # Move one devnet hand from this Mac to igneum-build-1, one at a time so one hand always serves (the project lead, 6 October 2026: the Mac # runs nothing the network depends on). Plan and order: docs/plans/hands-on-build-1.md. Dry run by default; --go executes. # # infra/build-server/hands/move-hand.sh binary --node [--override-json ''] # build 0.3.15's Linux igneumd on the box, install it to # /srv/hands/bin, write the override (the shipper's exact # object, else the Mac's file) and the snapshot (step 1) # infra/build-server/hands/move-hand.sh observer-node [--go] hot rsync, stop the Mac's observer node (launchd), final rsync, # start igneum-observer-node, print its first executing line (step 2) # infra/build-server/hands/move-hand.sh observer [--go] copy ~/.config/igneum/env to /srv/observer/env (600), stop the # Mac's run.sh + autosync + observer.mjs, start igneum-observer (step 3) # infra/build-server/hands/move-hand.sh node1 [--go] the same as observer-node for node 1 (step 4) # infra/build-server/hands/move-hand.sh unload [--go] bootout the Mac's two launchd agents for good (step 5, last) # infra/build-server/hands/move-hand.sh status both sides: units, pids, tips, peers # # Needs ~/.config/igneum/build-server (build@) and the ops key; root ssh to the box for systemctl, scp of the env file and chown. # Nothing here prints a secret: the env file travels by scp and is only ever stat'ed. set -euo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO="$(cd "$HERE/../../.." && pwd)" # shellcheck disable=SC2034 BS_TOOL=move-hand # shellcheck source=../lib.sh . "$HERE/../lib.sh" bs_host IP="${BS_HOST#*@}" ROOT_SSH=(ssh -i "$BS_KEY" -o BatchMode=yes -o StrictHostKeyChecking=accept-new "root@$IP") MAC_OV=/tmp/igneum-devnet/override-v3.json MAC_SNAP=/tmp/igneum-devnet/node1-copy-snapshot.bin MAC_SNAP_SHA=ac101f13576179fd7d7f5e8ee902c9a7b6cc47730e3a3c069f389f0ca46d9221 # the launchd agents' value (6 Oct 2026); recomputed below H=/srv/hands MODE="${1:-}"; shift || true GO=0; NODE_WT=""; OV_JSON="" while [ $# -gt 0 ]; do case "$1" in --go) GO=1; shift ;; --node) NODE_WT="$2"; shift 2 ;; --override-json) OV_JSON="$2"; shift 2 ;; *) bs_die "unknown argument $1" ;; esac; done say() { bs_log "$*"; } run() { if [ "$GO" = 1 ]; then "$@"; else say "DRY RUN: $*"; fi; } rssh() { "${ROOT_SSH[@]}" "$@"; } first_exec_line() { # : wait up to 180 s for the node's exec line, print it (the proof main wants) and the chain tip local u="$1" line="" for _ in $(seq 1 36); do line=$(rssh "journalctl -u $u --no-pager -o cat --since '5 min ago' | grep -m1 -E 'igneum-exec\] exec (sync: resumed|state loaded)'" 2>/dev/null || true) [ -n "$line" ] && break; sleep 5 done if [ -n "$line" ]; then say "$u first executing line: ${line:0:220}"; else say "$u: no exec line in 180 s; last lines:"; rssh "journalctl -u $u --no-pager -o cat -n 5" | sed 's/^/ /'; fi rssh "journalctl -u $u --no-pager -o cat --since '5 min ago' | grep -E 'Accepted [0-9]+ blocks|PoW accepted|IBD|Consensus params digest' | tail -3" | cut -c1-200 | sed 's/^/ /' || true } sync_dir() { # : rsync a data dir (hot or final), keeping RocksDB files whole bs_rsync -a --delete --exclude '*.out' "$1/" "$BS_HOST:$2/" } mac_stop_agent() { #