TL;DR: We pointed an audit at our own published surfaces. A crate we yanked in March was still advertising that our binary format was IANA-registered. In September! Three more findings followed, and none of them were bugs in our code — they were things about registries, tarballs and version ranges that we only learned by getting them wrong.
In Plain English. Publishing something is not like editing a web page. Once it's out, parts of it are set in stone — and the tools that look like an undo button mostly aren't. Here is what that actually costs, measured on our own work.
1. A yank does not edit anything
We published a small crate in March under the name fafb. Its description
said the binary format was IANA-registered. It isn't, and it doesn't need to be: .fafb is the compiled form of .faf, and .faf is
the registered one — application/vnd.faf+yaml. The sentence was simply
pointing at the wrong half.
We yanked it. That felt like the fix. It wasn't. A yank stops new installs and changes nothing else — not the crate page, not the search result, not the description. The sentence sat there for six months, in the first place anyone searching that name would look.
The only remedy is to publish again. So we did: a deliberately empty crate whose whole job is to carry a correct sentence and point at the real implementation.
| Action | What it changed |
|---|---|
| Yank the version | Installs only. Description untouched. |
| Publish a corrected version | Description, page, search result |
2. Every tracked file ships in your tarball
A Rust crate with no include or exclude in its manifest packages every file git tracks in that directory. We knew that. We had not thought about
what it meant for a design-notes file sitting quietly next to the code.
That file carried the same wrong registration claim, plus a "canonical spec" link pointing at a repository that no longer exists. Both went out inside the published tarball, where anyone vendoring the crate or auditing the supply chain would find them.
A README you can at least see on the crate page. A markdown file three directories down is invisible until someone unpacks it — and it is just as permanent.
3. A caret range can move the bytes your identity is built on
Our binary format has a content identity: a hash over the compiled chunks, so two files
can be compared without reading them. That hash is computed over the exact bytes our compiler emits — and those bytes come out of a YAML serializer we depended on as "0.10".
A caret range is the normal, polite thing to write. It also means a patch release of that
serializer — one that re-quotes a single scalar, say yes or 007 —
would change our payload bytes, and therefore every content ID, with no change to our
specification at all.
It is now pinned exactly. And because a pin is only as good as the test behind it, we added a second golden file carrying the quoting cases a serializer upgrade actually moves: bool-like strings, number-like strings, block scalars, embedded colons, tabs, unicode, a line long enough to tempt wrapping. Flip one byte and the suite fails.
4. Your public download can be a generation behind your source
Our compiler moved to a new wire format on a Sunday. The source said so, the tests said so, the spec said so. But the only binary a stranger could actually download still wrote the old format — because the release assets had been cut a week earlier, and nothing connects those two facts automatically.
A public repository's CI even pinned that build. Nothing failed, which is the uncomfortable part: that job never compiled anything, so the version gap was invisible. Drift between what you build and what people download does not announce itself.
Fixed by cutting the release from current source and verifying it the only way that counts — a cold install into an empty home directory, then comparing the file it produced against the reference byte for byte. Identical.
What it cost, and what it bought
Four publishes, four repositories corrected, and a page we did not have before. The findings were not exotic. Every one of them is a property of tooling that thousands of projects use daily, and every one of them was invisible until something made us look.
If you take one thing: the undo buttons are narrower than they look. A yank is not an edit. A tarball is not a branch. A version range is not a promise about bytes. Assume each of those is permanent until you have checked, because the cheapest moment to find out is before someone else does.
The brick itself, and why it is shaped the way it is, is on its own page now: Anatomy of a Brick.
