fix(release): reproducible packages, a deb that carries its copyright, and an apt source that resolves - #9
Merged
Merged
Conversation
Two defects found while verifying v0.5.0. The deb and rpm were not reproducible. nfpm stamps the package and its files with the clock unless told otherwise, and mod_timestamp only covers the Go binary, so the two goreleaser runs inside a single release -- the check build and the publishing build -- produced different package bytes while the three archives came out identical: x86_64.rpm 093793c9... -> c157240a... aarch64.rpm 331baca6... -> eef48bea... amd64.deb 633db06a... -> 3a275f3d... arm64.deb 1af43504... -> 0ceb7292... Nothing was published inconsistently, because the publishing run rebuilds dist from scratch and checksums what it uploads, but the recovery procedure rebuilds a tagged commit and expects the same artifacts, and docs/release.md claimed that reproducibility for every artifact. nfpms.mtime fixes the package, and a file_info.mtime per declared content fixes the files inside it: a declared entry otherwise keeps the mtime it had on the runner, which is the checkout time. The deb also shipped without its copyright file. `type: license` and `type: readme` are rpm markers, %license and %doc, and nfpm drops an entry carrying one from the deb without reporting it, so monmux_0.5.0_amd64.deb contains /usr/bin/monmux and nothing else -- no /usr/share/doc/monmux/copyright, which Debian policy requires. Each file is now declared twice, scoped with `packager:`, so the rpm keeps its markers and the deb gets plain files. Verified locally with the pinned goreleaser v2.18.1: building the same commit twice now yields identical SHA-256 for all four packages, the deb lists copyright and README.md with the commit's timestamp, and the rpm still carries both.
The documented apt line named `deb/any-distro any-version`, which is where the release workflow pushes but not where anything is served from. Every path under it answers 404, so `sudo apt update` failed for anyone who followed the README: deb/any-distro/dists/any-version/InRelease 404 deb/any-distro/dists/any-version/main/binary-amd64/Packages 404 Cloudsmith serves a package uploaded to any-distro/any-version through each real distribution index instead, so the working form names one: deb/ubuntu/dists/resolute/InRelease 200 deb/ubuntu/dists/noble/InRelease 200 deb/debian/dists/bookworm/InRelease 200 The source is now written in deb822 to a `.sources` file, which is what a current apt prefers and what a modern system's /etc/apt/sources.list.d already looks like, with $ID and $VERSION_CODENAME read from /etc/os-release and an Architectures line naming the only two the repository holds. A note says the suite does not affect which bytes arrive -- the packages are built per architecture and carry nothing distribution-specific -- so a derivative whose $ID Cloudsmith does not know can name its upstream, and that the one-line .list form still works. The rpm section was already correct: rpm/any-distro/any-version/$basearch serves repodata/repomd.xml. Verified against the live repository, in a throwaway APT root so that no system configuration was touched: apt parses the file, resolves it to deb/ubuntu/dists/resolute, fetches InRelease and both Packages indexes with no signature warning -- it is signed by 7F28A3C96319158609805131D5A5560F8D6A68B5, the fingerprint the README documents -- and reports monmux 0.5.0 as the candidate, whose SHA256 3a275f3d... is the published amd64 deb.
A reader deciding whether to let this write to their monitor should know how it was written. The section also points at where the review actually bites: the catalog evidence rule, and the rule that no agent writes to a monitor at any point, including during review.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three defects found by verifying the v0.5.0 release rather than reading the green ticks, plus one disclosure section.
1. The deb and rpm were not reproducible
nfpm stamps the package and its files with the clock unless told otherwise, and
mod_timestampcovers only the Go binary. So the two goreleaser runs inside a single release — the check build and the publishing build — produced different package bytes, while the three archives came out identical:Nothing was published inconsistently: the publishing run rebuilds
distfrom scratch and checksums what it uploads, and I verified the publishedchecksums.txtagainst the published archive. But the recovery procedure rebuilds a tagged commit and expects the same artifacts, anddocs/release.mdclaimed that reproducibility for every artifact.Fixed with
nfpms.mtime, plus afile_info.mtimeper declared content — a declared entry otherwise keeps the mtime it had on the runner, which is the checkout time.docs/release.mdnow says which mechanism covers which artifact, and records that v0.5.0 did not have it.2. The deb shipped without its copyright file
type: licenseandtype: readmeare rpm markers (%license,%doc), and nfpm drops an entry carrying one from the deb without reporting it. Somonmux_0.5.0_amd64.debcontains/usr/bin/monmuxand nothing else — no/usr/share/doc/monmux/copyright, which Debian policy requires:Each file is now declared twice, scoped with
packager:, so the rpm keeps its markers and the deb gets plain files.3. The documented apt source answered 404
The README named
deb/any-distro any-version, which is where the release workflow pushes but not where anything is served from:Cloudsmith serves a package uploaded to
any-distro/any-versionthrough each real distribution index. The source is now deb822 in a.sourcesfile, with$IDand$VERSION_CODENAMEfrom/etc/os-release, anArchitecturesline naming the only two the repository holds, and an armored.asckey that apt reads directly — nogpg --dearmorstep. A note explains that the suite does not affect which bytes arrive, so a derivative whose$IDCloudsmith does not know can name its upstream. The rpm section was already correct.4. How this was built
A short README section saying monmux is written largely with agentic AI coding tools under human review, pointing at where that review bites: the catalog evidence rule, and the rule that no agent writes to a monitor at any point.
Monitor writes
None. Nothing in this PR runs
monmux, and no step of either workflow touches a monitor. The--unsafe-modelsurface is unchanged, and no catalog value moved —internal/catalog/models.yamlis not in the diff, so the catalog release-note section of the next release will be empty and the version rule will not raise anything.Testing
Every claim above was checked, not inferred.
dpkg-deb -clistscopyrightandREADME.mdstamped2026-09-10 19:39, the commit's own time, not the build clock. The rpm still carries both files.type:and rebuilding, which made both files appear — that is what identified the field as the culprit rather than a guess at it.Dir=, its own lists, cache and dpkg status), so no system configuration was touched: apt parses the deb822 file, resolves it todeb/ubuntu/dists/resolute, fetchesInReleaseand bothPackagesindexes with no signature warning — the index is signed by7F28A3C96319158609805131D5A5560F8D6A68B5, the fingerprint the README documents — and reportsmonmux 0.5.0as the candidate, whoseSHA256 3a275f3d…is the published amd64 deb.goreleaser checkpasses;make check-stageclean.Note on versioning
All three commits are
fix/docs, and nothing here changes a byte monmux can send, so the next release is a patch as far as the catalog rule is concerned.