igneum/infra/cloud-devnet/provision.sh
igneum-labs d8dc8c6ba3 infra: cloud devnet (20 nodes), rented GPU bench and seed node scripts; plans for both
infra/cloud-devnet: hcloud (doctl variant) create, builder-VM provision from a git-archive source tarball,
systemd units for igneumd --devnet-suffix with a sparse --addpeer mesh and a CPU trickle miner per node,
stdlib wRPC client, experiments (latency, partition, hop, collect, observer hookup), README with the command
sequence and the Hetzner API prices of 3 Oct 2026.
infra/gpu-bench: RunPod image recipes (CUDA 12.8, ROCm), bundle, run.sh (vectors gate, 10-min raw, sweep,
inline shortcut ratio, nvcc/NVRTC/OpenCL recompile timings, results row, intake upload), bench-log template.
infra/seed-nodes: create-seed (persistent IPv4, firewall), provision on the VM, health check, addPeer from the
Mac over grpcurl, seeds.txt; igneum-seed-1 created at 188.245.5.161 (Hetzner cx23, fsn1).
docs/plans/cloud-devnet.md and docs/plans/seed-nodes.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 22:01:50 +00:00

78 lines
4.2 KiB
Bash
Executable file

#!/usr/bin/env bash
# Build the node once and install it on every node.
#
# ./provision.sh source tarball -> builder VM -> build/bin/{igneumd,igneum-miner} -> every node (systemd units)
# ./provision.sh build only the build (creates the builder VM if build/builder.ip is missing, deletes it afterwards)
# ./provision.sh install only the install on the nodes (needs build/bin/igneumd)
#
# Why a builder VM and not a cross-compile on the Mac: rustup here has only aarch64-apple-darwin and
# x86_64-pc-windows-gnu installed and there is no x86_64 Linux linker (no musl-cross, zig, cross or docker; checked
# 3 Oct 2026). A Linux cross-compile would need `rustup target add x86_64-unknown-linux-gnu` plus a linker
# (`brew install filosottile/musl-cross/musl-cross` and a musl target), and the rocksdb bindgen trick of the Windows
# cross-build again. The builder costs about EUR 0.05 for the hour (cpx41, approximate) and the Mac stays free.
# The binaries are cached in build/bin; REBUILD=1 forces a new build.
. "$(dirname "$0")/lib/common.sh"
what="${1:-all}"
mkdir -p "$BUILD_DIR" "$BIN_DIR"
# ---- build ---------------------------------------------------------------------------------------------------------
do_build() {
if [ -x "$BIN_DIR/igneumd" ] && [ "${REBUILD:-0}" != 1 ]; then
log "build/bin/igneumd exists ($(cat "$BIN_DIR/version.txt" 2>/dev/null | tr '\n' ' ')); REBUILD=1 to rebuild"
return
fi
"$HERE/make-source.sh"
if [ ! -s "$BUILD_DIR/builder.ip" ]; then
YES="${YES:-0}" "$HERE/create.sh" builder
fi
bip=$(cat "$BUILD_DIR/builder.ip")
log "builder at $bip; waiting for ssh"
for try in $(seq 1 12); do nssh "$bip" true >/dev/null 2>&1 && break; sleep 10; done
nssh "$bip" true || die "builder $bip not reachable over ssh"
log "uploading source ($(du -h "$BUILD_DIR/src.tar.gz" | cut -f1)) and the build script"
nscp "$BUILD_DIR/src.tar.gz" "$HERE/builder/build-on-builder.sh" "$SSH_USER@$bip:/root/"
nssh "$bip" "bash /root/build-on-builder.sh" | tee "$BUILD_DIR/build.log"
nscp "$SSH_USER@$bip:/root/out/igneumd" "$SSH_USER@$bip:/root/out/igneum-miner" "$SSH_USER@$bip:/root/out/version.txt" "$BIN_DIR/"
chmod +x "$BIN_DIR/igneumd" "$BIN_DIR/igneum-miner"
cp "$BUILD_DIR/src.stamp" "$BIN_DIR/src.stamp"
log "binaries in $BIN_DIR: $(cat "$BIN_DIR/version.txt" | tr '\n' ' ')"
if [ "${KEEP_BUILDER:-0}" = 1 ]; then
log "KEEP_BUILDER=1: builder $bip stays up (it bills by the hour)"
else
log "deleting the builder VM"
if [ "$PROVIDER" = digitalocean ]; then doctl compute droplet delete -f "$PREFIX-builder"; else hcloud server delete "$PREFIX-builder" >/dev/null; fi
rm -f "$BUILD_DIR/builder.ip"
fi
}
# ---- install ------------------------------------------------------------------------------------------------------
do_install() {
require_nodes
[ -x "$BIN_DIR/igneumd" ] || die "no $BIN_DIR/igneumd: run ./provision.sh build"
local n; n=$(node_count); local bits; bits=$(genesis_bits_decimal)
log "installing on $n nodes (devnet suffix $DEVNET_SUFFIX, genesis bits $GENESIS_BITS = $bits, $MESH_OUT outbound peers each, $MINER_THREADS miner thread)"
while IFS=$'\t' read -r name idx reg ip; do
peers=$(peers_of "$idx")
(
for try in 1 2 3; do nssh "$ip" "mkdir -p /opt/igneum/bin /etc/igneum" && break; sleep 10; done
nscp "$BIN_DIR/igneumd" "$BIN_DIR/igneum-miner" "$HERE/node/wrpc.py" "$HERE/node/run-igneumd.sh" "$HERE/node/blocklog.sh" "$SSH_USER@$ip:/opt/igneum/bin/"
nscp "$HERE/node/install-node.sh" "$HERE/node/igneumd.service" "$HERE/node/igneum-miner.service" "$HERE/node/igneum-blocklog.service" "$SSH_USER@$ip:/root/"
nssh "$ip" "bash /root/install-node.sh '$name' '$idx' '$DEVNET_SUFFIX' '$bits' '$peers' '$ip' '$MINER_THREADS' '$MINER_STATUS_SECS'" 2>&1 | sed "s/^/[$name] /"
) &
# at most 10 installs at once
while [ "$(jobs -r | wc -l)" -ge 10 ]; do sleep 1; done
done < "$NODES_FILE"
wait
log "installed. Peer map:"
while IFS=$'\t' read -r name idx reg ip; do printf ' %s (%s, %s) -> %s\n' "$name" "$reg" "$ip" "$(peers_of "$idx")"; done < "$NODES_FILE"
log "next: ./start.sh"
}
case "$what" in
all) do_build; do_install ;;
build) do_build ;;
install) do_install ;;
*) die "usage: provision.sh [all|build|install]" ;;
esac