Skip to content
LogoLogo

Release verification

Before a Cosmovisor upgrade, curators recurate the published terpd ELF from the git tag, not from a developer laptop that already has module caches and gitignored crates. Operators can treat a passing recurate as: this S3 object is the same bytes we get by compiling that tag in a throwaway GOPATH.

This page is the record of that workflow. Scripts live in terp-core scripts/release/fresh-vm/.

What a pass actually proves

A pass means:

  1. git clone of tag vX.Y.Z (annotated tag commit, not feat/…-dev HEAD).
  2. Git submodules crates/zk-wasmd, crates/zk-wasmvm, and crates/cosmwasm at the gitlinks on that tag.
  3. Checksummed fetch of ibc-hooks-v11.tar.gz (gitignored; not copied from a laptop).
  4. Rebuild the alpine muslc builder image from crates/zk-wasmvm/builders/Dockerfile.alpine, then cargo libwasmvm_muslc.{aarch64,x86_64}.a. Rebuilt .a must match the published muslc pin unless FRESH_VM_ALLOW_MUSLC_DRIFT=1.
  5. GOPATH / GOMODCACHE / GOCACHE are a throwaway directory, not $HOME/go.
  6. Linux ELFs: make build-reproducible-amd64 / arm64 with Docker Buildx --no-cache (rebuilds the terpd image, then extracts the binary).
  7. SHA-256 of those ELFs equals https://s3.terp.network/releases/terp-core/<tag>/sha256sum.txt.
  8. If two guests both rebuilt the same ELF name, those hashes also equal each other.

That is from-source muslc + terpd on a fresh guest, plus GOPROXY module zips. WASMVM_MUSLC_FETCH=1 skips the Rust rebuild and only links the published .a (weaker).

What a pass does not prove

InputWhat happensWhy it matters
Go modules (SDK, IAVL, IBC, …)go build downloads module zips via GOPROXY into the empty cacheFresh bytes for go.mod / go.sum. Not a rebuild of each upstream git tree.
Docker Hub rust: / golang: parents--no-cache --pull on our Dockerfiles. Parent tags still come from the registry.Independent of the curator’s terp layer cache.
Hasher / BLAKE3 vendorcrates/cosmos/{iavl,store-v2} must not appear on a fresh clone. Published v6.1.0 / v6.2.0 do not replace store/v2.Recurate of those tags proves the published ELF (live CMS SHA-256). It does not compile the gitignored hasher patch.
Upgrade behaviorNot run.Cosmovisor halt, TSH, ict-rs, and gov info JSON are separate gates. This workflow only checks binary identity.

make recurate-upgrade-binaries is a different tool: it rebuilds in a git worktree and compares ARTIFACT_LOCK. Fresh-VM compares S3. Do not mix them.

Two guests, one script

Firecracker and Wasmer are backends for the same guest.sh. They are not two compilers.

Loading diagram...
BackendWhat you setWhat must be true of the machine
firecrackerFIRECRACKER_SSH=user@microvmLinux with git, docker, curl. Empty GOPATH is created in /tmp. The script does not boot Firecracker itself.
wasmerWASMER_SSH=user@linuxSame payload as Firecracker, different host. That is the redundant confirmation.
wasmer CLIWASMER_PACKAGE=…WASIX. Public packages do not include Docker. Cannot run linux muslc recurate unless the package actually has docker/git/make.
local(default if no SSH)Throwaway clone on the curator laptop. Weaker: Docker Desktop may be the same daemon used for daily work. --no-cache still applies.

Two SSH hosts matching S3 and each other is the high-certainty path: two kernels, two Docker daemons, two empty GOPATHs, one checksum file.

wasmer run on a WASIX package is not that path.

How curators run it

From a terp-core checkout (or any machine that can SSH the guest). The guest fetches ibc-hooks itself; it does not need our laptop’s crates/ tree:

# one Linux guest
FIRECRACKER_SSH=root@fc TAG=v6.2.0 \
  PLATFORMS=linux/amd64,linux/arm64 \
  ./scripts/release/fresh-vm/run.sh
 
# two independent Linux guests
FIRECRACKER_SSH=root@fc WASMER_SSH=root@wasmer-linux \
  GUESTS=firecracker,wasmer TAG=v6.2.0 \
  PLATFORMS=linux/amd64,linux/arm64 \
  ./scripts/release/fresh-vm/run.sh

Or TAG=v6.2.0 make verify-fresh-vm with those env vars set.

Per-tag extras are historical files: scripts/release/fresh-vm/releases/v6.1.0.sh, v6.2.0.sh, …. Copy forward for a new tag; do not edit a published tag’s file. Do not put guest logic in networks/upgrades/ or recurate_upgrade_binaries.sh.

Hashes from each guest: /tmp/terp-fresh-toolkit-<tag>/out/<guest>.sha256sum.txt.

How operators should read a recurate during an upgrade

  1. Confirm the Cosmovisor plan name (v6.1, v6.2, …) and the binary tag (v6.1.0, …) in the proposal / upgrade guide.
  2. Download sha256sum.txt and the tarball from https://s3.terp.network/releases/terp-core/<tag>/.
  3. sha256sum -c sha256sum.txt --ignore-missing.
  4. Treat a published curator recurate as extra evidence that those checksums are the tag, not a laptop cache. If you want to repeat it, you need a Linux Docker host and the extras for that tag — not only git clone && make build on a dirty GOPATH.

Darwin/arm64 installer ELFs are a separate Darwin-host rebuild. Firecracker and Wasmer Linux guests skip Mach-O.