virp-verify 0.1.2 — the binary and the tag name the same commit
A provenance fix. This release's binary was built at this release's tag, and
that is now a rule with no exceptions.
A reviewer caught what 0.1.1 got wrong: its envelope — tag, notes, trusted
comment — named commit 236e683, while the binary inside reported 0.1.0 (commit 01ce2e2, clean, release), because 0.1.1 deliberately shipped the 0.1.0
binary unchanged. Every statement in that release was true and the notes said
which tag reproduced the hash, but a reader checking the first line of a report
against the release it came from found two commits and no way to tell, from the
release alone, which was the mistake. A provenance chain that needs a paragraph
to survive its own first look is not doing its job. The rule is written down in
docs/VERIFIER-RELEASE.md.
Requirements: Linux on x86_64 — native, WSL2, or an x86_64 container
(docker run --rm -it debian:stable-slim) — with curl, jq, minisign,
tar and sha256sum installed. The binary below is x86_64 Linux only; on
Apple Silicon or arm Linux it will not run, and Build it yourself is your
path.
Everything, in one block
keys=https://raw.githubusercontent.com/nhowardtli/virp-verify/411d008a04eb9a52b3ade9a4afe58ae4b358099b/keys
mkdir -p virp-verify-0.1.2 && cd virp-verify-0.1.2
# Every release asset except the keys — those come from the commit above, which
# is dated and cannot be replaced, unlike a file attached to a release page.
curl -fsSL https://api.github.com/repos/nhowardtli/virp-verify/releases/tags/virp-verify-v0.1.2 \
| jq -r '.assets[].browser_download_url' | grep -vE '\.(hex|keys\.json)$' \
| xargs -n1 curl -fsSLO
for k in $(grep -oE '[^ ]+\.(hex|keys\.json)$' SHA256SUMS) seal-virp-ad48b20f-2026-09-05.pub; do
curl -fsSL "$keys/$k" -o "$k"
done
sha256sum -c SHA256SUMS
minisign -Vm SHA256SUMS -p seal-virp-ad48b20f-2026-09-05.pub
tar -xzf axis-20260904-v6.tar.gz
chmod +x virp-verify
show='^virp-verify |^ (witness {16}|referenced_artifact_binding )[A-Z]|^OVERALL VERDICT'
./virp-verify axis-20260904-v6 | grep -E "$show"
./virp-verify \
--pin chain-313-c1104805-2026-08-28.hex \
--producer-key producer-axis-m3085v-fae0d249-2026-09-03.hex \
--witness-key witness-virp-systems-2a771e12-2026-09-03.hex \
axis-20260904-v6 | grep -E "$show"sha256sum -c prints OK seven times, minisign prints Signature and comment signature verified, and the two runs print four lines each. The first of those
lines reads virp-verify 0.1.2 (commit 1519d57, clean, release) — the same
commit this release is tagged at, which is the point of the release.
What you just saw
OVERALL VERDICT: CRYPTOGRAPHICALLY-CONSISTENT (signer trust not established)
OVERALL VERDICT: CRYPTOGRAPHICALLY-VERIFIED
The same bundle, twice. Nothing about the evidence changed between those two
lines; every hash, link and signature verified in both runs. What changed is
where the verifying key came from — inside the bundle in the first run,
from a commit you chose in the second. A bundle that vouches for itself proves
internal consistency and nothing about who produced it.
The witness line flips the same way: UNVERIFIABLE ("no --witness-key was
supplied") to VERIFIED (leaf 476 of tree 571). UNVERIFIABLE is not a
failure and not a pass. referenced_artifact_binding does not flip, because
it needs no key: 51 cited artifacts recomputed against the fields that cite
them.
Everything above ran offline. After the downloads, neither run touched the
network: the witness result comes from an inclusion proof and a signed tree
head carried inside the bundle and recomputed here, and --witness-url — which
re-checks the carried tree against the log serving it now — is the only flag
that would go out and ask anyone anything.
CRYPTOGRAPHICALLY-VERIFIED does not mean the entries are true, that the
video shows what anyone says it shows, or that any particular physical device
produced it — source_device_established grades NO in this very bundle.
README.md is the full version of all of this, and it is one of the assets.
Build it yourself
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
git clone https://github.com/nhowardtli/virp-verify && cd virp-verify
git checkout virp-verify-v0.1.2
rustup target add x86_64-unknown-linux-musl # or aarch64-unknown-linux-musl
tools/release/build-verifier.shBuilt from two separate clean clones at two unrelated paths, checked out at tag
virp-verify-v0.1.2 (commit 1519d57), byte-identical both times:
| target | sha256 |
|---|---|
x86_64-unknown-linux-musl |
bf5188681d107ecff09ac91c68449aa74f85b2e978f3766c93043d8508b26985 |
aarch64-unknown-linux-musl |
a45d67847b9344017339c42e06dd72c93a1f266ad7603017288b6285e598bfee |
The x86_64 line is the binary attached here and the virp-verify line of the
signed SHA256SUMS. Check yours against the manifest you downloaded, not
against this page: this page is controlled by whoever produced the file it
describes, and the manifest is signed.
Check out the tag, not main. The commit is compiled into the binary, so a
build from any other commit is a different binary with a different hash by
design — the binary cannot misreport where it came from.
The arm build, and why its hash is not the x86_64 hash
No aarch64 binary is attached here. It is a build path for people on arm,
and the hash above is published so an arm builder has something to compare
notes about, not something to check a download against. It is different machine
code, and it is also built by different machinery because it has to be — GNU
strip does not recognise the format and the host cc cannot link for another
architecture — so it links with the rust-lld that ships with the pinned
toolchain and has rustc strip at link time. Two targets never produce one
hash. It has been built but not executed: this release was prepared on x86_64
with no arm hardware and no emulation available.
Supersedes 0.1.1 and 0.1.0
Both are left in place for anyone holding a copy of their bytes, and both are
still exactly what their notes say they are. Use this one: it is the first
release whose binary and whose envelope name the same commit.
The seal key
SHA256SUMS.minisig is signed with AD48B20F5D11CED6, generated
2026-09-05:
minisign -Vm SHA256SUMS -P RWTWzhFdD7JIrUh5gRyPshZDiGwwwHhL8QUaiNX/RLjcaNxa0JCoR611
The previous seal key, 4F0B72D4DA341448 (2026-08-23), is retained, not
revoked: it still verifies what it signed, notably seal-2026-08.json, and
it verifies nothing in this release. Trying the old key against this signature
fails with a key-id mismatch, which is the correct outcome.
Both public halves are committed under
keys/ in commit
411d008a04eb9a52b3ade9a4afe58ae4b358099b, whose message says which key is
which, when each was generated and which host holds each private half, and
which carries an OpenTimestamps proof of its own date. A release asset can be
replaced silently and a dated commit cannot, which is why the block above
fetches every key from that commit rather than from this page. The four bundle
keys are attached here too, under the bare filenames SHA256SUMS names, so
downloading every asset into one directory and running the check passes with
nothing to arrange; the manifest covers them, so writing the commit's copies
over the downloaded ones and re-running sha256sum -c is what proves the two
are byte-identical. The commit is the canonical source; the assets are a
convenience the hashes keep honest.