Integrity & signing
Two independent checks guard every pull: the payload must hash to its digest, and the manifest must carry a signature from a key you trust. They fail with different exit codes on purpose - a corrupted download and an untrusted publisher are not the same problem.
The digest check
pull streams the payload and hashes it as it goes. At the end it compares the SHA-256 against the sha256:… in the manifest. If they differ, the partial file is discarded and the command exits 3. This catches truncated transfers and tampered stores without any signing set up at all.
The signature check
The manifest is signed with an Ed25519 key; the signature is detached in manifest.sig and covers the canonical bytes of manifest.json - sorted keys, LF line endings, no trailing whitespace. verify recomputes that canonical form before checking the signature, so re-pretty-printing a manifest does not invalidate it, but changing any value does.
Trust
Trust is a flat list of key ids in your user config. A key the store used but you have not listed yields exit 4 - the bytes are fine, you just have not said you trust who signed them. A key that is listed but malformed or revoked yields exit 5.
anvil verify io.byteanvil.gateway@2.4.1 # digest ok · signature ok · key 7c1d9a trusted → 0 anvil show io.byteanvil.gateway@2.4.1 --json | grep key # "key":"7c1d9a" trusted since 2026-02-11
Key 7c1d9a has signed the demo store since the 2.0.0 cutover; you can see it on every artifact in the browser.