forked from unom/punktfunk
A release page offered a DMG, an MSIX, a setup.exe, an APK and a decky zip with nothing to check them against — the download either matched what we built or it didn't, and there was no way for anyone to tell which. upsert_asset now attaches `<asset>.sha256` next to each asset, so verifying is `sha256sum -c punktfunk-1.2.3.dmg.sha256` in the download directory. Doing it in the helper rather than in the callers means all eight packaging workflows inherit it at once, and a future one can't forget. Sidecars rather than one shared SHA256SUMS: those workflows attach to the SAME release object concurrently, so a single manifest would be a read-modify-write race that silently drops whichever leg lost. One file per asset has no shared mutable state. The digest is over the file, but the name written into the sidecar is the ASSET name — callers rename on upload (Punktfunk-$VERSION.dmg), and `sha256sum -c` looks up the name it reads. The PowerShell twin writes the line byte-exactly (LF, no BOM): GNU sha256sum folds a trailing CR into the filename, so PowerShell's default CRLF would have failed every check on the box doing the verifying. Verified on both sides — bash and POSIX sh locally (`shasum -a 256 -c` passes), pwsh on the windows-amd64 runner (91 bytes, last byte 0x0A, no CR, no BOM, same digest as the bash path). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>