igneum/packaging/prover/push-server.sh

63 lines
4.9 KiB
Bash
Executable file

#!/usr/bin/env bash
# Publishes the project's GPU prover server (prover floor, 6 October 2026): the binary CI built
# (.github/workflows/prover-server.yml, artifact `sp1-gpu-server-floor`) or a given build folder, its signed manifest
# prover-server.json (format app/igneum-app/src/proverserver.rs: the SP1 version and commit it patches, the patch's
# sha256, the CUDA targets, the binary's sha256 and size, the build) and prover-server.json.sig, the detached Ed25519
# signature made on this Mac with the OTA key the apps already trust, into the downloads folder (dl/<token>/), and
# deploys it. The Windows build (.github/workflows/windows.yml) and packaging/prover/fetch-server.sh fetch the three
# files from there and verify them before the payload carries them (wsl2\bin\sp1-gpu-server, wsl2\prover-server.json
# and .sig); the app verifies them again before use.
#
# packaging/prover/push-server.sh --run <ci run id> the CI artifact (gh run download; gh must be logged in as igneum-labs)
# packaging/prover/push-server.sh --dir <folder> a folder with sp1-gpu-server, sp1-gpu-server.sha256 and build.json
# [--no-deploy]
#
# Same secrets layout as push-inputs.sh: ~/.config/igneum/{dl-token, dlsite-dir, vercel, ota-signing-key(.pub)}.
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"; ROOT="$(cd "$HERE/../.." && pwd)"
RUN=""; DIR=""; DEPLOY=1
while [ $# -gt 0 ]; do case "$1" in --run) RUN="$2"; shift 2 ;; --dir) DIR="$2"; shift 2 ;; --no-deploy) DEPLOY=0; shift ;; *) echo "unknown argument $1" >&2; exit 2 ;; esac; done
[ -n "$RUN" ] || [ -n "$DIR" ] || { echo "usage: push-server.sh --run <ci run id> | --dir <folder> [--no-deploy]" >&2; exit 2; }
TOKEN="$(tr -d '[:space:]' < "$HOME/.config/igneum/dl-token")"
DLSITE="${IGNEUM_DLSITE:-}"; [ -n "$DLSITE" ] || DLSITE="$(tr -d '[:space:]' < "$HOME/.config/igneum/dlsite-dir")"
[ -d "$DLSITE/dl/$TOKEN" ] || { echo "no downloads folder at $DLSITE/dl/<token>" >&2; exit 1; }
KEY="$HOME/.config/igneum/ota-signing-key"; PUB="$HOME/.config/igneum/ota-signing-key.pub"
SIGNER="$ROOT/app/igneum-app/target/release/igneum-ota-sign"
if [ ! -x "$SIGNER" ]; then echo "building igneum-ota-sign"; (cd "$ROOT/app/igneum-app" && nice -n 19 cargo build --release -j 4 --bin igneum-ota-sign --quiet); fi
if [ -n "$RUN" ]; then
DIR="$(mktemp -d)/server"; mkdir -p "$DIR"
gh auth status 2>/dev/null | grep -q "igneum-labs" || echo "warning: gh's active account is not igneum-labs (gh auth switch --user igneum-labs)"
gh run download "$RUN" --repo igneum-network/igneum --name sp1-gpu-server-floor --dir "$DIR"
BUILT_BY="ci:$RUN"
else
BUILT_BY="dir:$(hostname -s)"
fi
BIN="$DIR/sp1-gpu-server"; [ -f "$BIN" ] || { echo "no sp1-gpu-server in $DIR" >&2; exit 1; }
[ -f "$DIR/build.json" ] || { echo "no build.json in $DIR (build-server.sh writes it)" >&2; exit 1; }
# the hash the builder recorded must be the file's hash now (a corrupt download is refused here)
SHA="$(shasum -a 256 "$BIN" | cut -c1-64)"; BYTES="$(stat -f %z "$BIN")"
REC="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["sha256"])' "$DIR/build.json")"
[ "$SHA" = "$REC" ] || { echo "the binary's sha256 $SHA is not build.json's $REC" >&2; exit 1; }
[ -f "$DIR/sp1-gpu-server.sha256" ] && { grep -q "^$SHA " "$DIR/sp1-gpu-server.sha256" || { echo "sp1-gpu-server.sha256 disagrees" >&2; exit 1; }; }
DEST="$DLSITE/dl/$TOKEN"
python3 - "$DIR/build.json" "$DEST/prover-server.json" "$BUILT_BY" "$SHA" "$BYTES" <<'PY'
import json, sys, datetime
b = json.load(open(sys.argv[1]))
m = {"format": "igneum-prover-server/1", "sp1_version": b["sp1_version"], "sp1_commit": b["sp1_commit"], "patch_sha256": b["patch_sha256"],
"cuda_archs": b.get("elf_targets") or b["cuda_archs"], "built_at": b["built_at"], "built_by": sys.argv[3],
"published_at": datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
"files": {"sp1-gpu-server": {"sha256": sys.argv[4], "bytes": int(sys.argv[5])}}}
open(sys.argv[2], "w").write(json.dumps(m, sort_keys=True, separators=(",", ":")) + "\n")
PY
"$SIGNER" sign-server "$KEY" "$DEST/prover-server.json" > "$DEST/prover-server.json.sig"
cp "$BIN" "$DEST/sp1-gpu-server"
"$SIGNER" verify-server "$PUB" "$DEST/prover-server.json" "$DEST/prover-server.json.sig" --binary "$DEST/sp1-gpu-server"
"$SIGNER" verify-server embedded "$DEST/prover-server.json" "$DEST/prover-server.json.sig" >/dev/null || { echo "the embedded key does not verify this signature: the OTA key on this Mac is not the one the apps carry" >&2; exit 1; }
cat "$DEST/prover-server.json"; echo "signature: $(cut -c1-16 "$DEST/prover-server.json.sig")..."
if [ "$DEPLOY" = 1 ]; then
echo "deploying $DLSITE"
(cd "$DLSITE" && npx vercel@latest --global-config "$HOME/.config/igneum/vercel" deploy --prod --yes 2>&1 | grep -v "$TOKEN" || true)
echo "published: https://dl.igneum.network/dl/<token>/{sp1-gpu-server,prover-server.json,prover-server.json.sig}"
else
echo "not deployed (--no-deploy); run the Vercel deploy from $DLSITE when ready"
fi