Verify a download
Every release is signed with a key that is never on this server. Here is how to check a file you downloaded — and, just as importantly, what each check does and does not prove.
What this actually proves
- The checksum proves the file is intact. If
sha256summatches the digest we publish, your download is byte-for-byte what this registry meant to serve. It catches a truncated transfer, a dropped connection, and disk corruption. - The signature proves the digests were attested offline — but only if you got the public key from somewhere that is not this website. The signing key lives on a machine that never touches the web host, so a signature that verifies means the release was assembled by whoever holds that key, and the digests bind the bytes.
- Copying the key from this page does not protect you against this site being compromised. If it is, then the key, the digests, the bytes and these instructions are all controlled by the same person. A key served from the same origin as the file it vouches for adds convenience, not trust. To get the real guarantee, obtain the key — or at least its fingerprint — through a channel we do not control, and compare.
Step 1 — checksum
Each download on the registry pages shows its SHA-256. Recompute it and compare:
sha256sum devgrail-server-v12-amd64.tar.gz
# macOS:
shasum -a 256 devgrail-server-v12-amd64.tar.gzThe file is gzip even though it contains a tar archive — that is why it is named .tar.gz. Do not decompress it before hashing; the digest is over the compressed bytes. docker load reads it as-is.
Step 2 — the release signature
The signature is not over the manifest — the manifest contains download URLs that differ per host, so nothing derived from it is stable enough to sign offline. What is signed is a payload naming every artifact's version and digest, which is what actually has to be trusted.
curl -fsSL https://web-dev.trixibot.com/api/registry/releases/v0.4.19 -o release.json
# -j, not -r: -r appends a newline the signer never saw, and the check fails.
jq -j '.signature.payload' release.json > payload
jq -r '.signature.signature' release.json | base64 -d > payload.sigopenssl dgst -sha256 -verify devgrail-release.pem -signature payload.sig payload
# -> Verified OKhead -2 payload
# devgrail-release-signature-v1
# release v0.4.19
# -qxF: whole line, literal. A substring match would accept the wrong digest.
grep -qxF 'image devgrail-server 12 amd64 <sha256>' payload && echo coveredThe version in that line is the registry build number, not the release tag — every download on the image pages shows it next to the digest for exactly this reason.
The public key
Algorithm: ecdsa-p256-sha256 — ECDSA P-256 with SHA-256, chosen because it verifies with the OpenSSL already on any VPS, with nothing to install.
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEK1NbYrxBM8Ia1iM4BcR0m/8DelN5
ZDvOwKmEOJA24dci3ftqHWwsxzbw8bACRps0rIOTqa45wblm6260fQVdIw==
-----END PUBLIC KEY-----Confirm a key you obtained anywhere is this key by recomputing its id — the same derivation our release tooling uses:
openssl pkey -pubin -in devgrail-release.pem -pubout -outform DER | sha256sum | cut -c1-16
# f1f1eaf642454d65The same key is embedded in install.sh. That copy is also served from this origin, so it is a convenience, not an independent second source — compare against the DevGrail source repository if you want one.
Or let the installer do it
Everything on this page is what the one-command installer does automatically on every run and every upgrade: it checks each digest against the manifest, verifies the signature against the key embedded in the script, and refuses any artifact whose digest is not covered. Set DEVGRAIL_REQUIRE_SIGNATURE=1 to make a failed or missing signature fatal rather than a warning.