Withdrawal¶
Publishing an artefact-version is permanent. The bytes never change, the signature stays valid, and the manifest keeps describing exactly what it always described.
That is the point — it is what stops a replayed manifest answering for a version it does not belong to. It also means that on its own, nothing can ever be un-approved. An artefact later found to be malicious would keep resolving forever, verifying perfectly, because the signature was and remains genuine.
The index is the mechanism that says stop using this.
Standing is not advice¶
Two different things get called "status", and conflating them causes real mistakes.
Standing — approved or withdrawn — is whether an artefact may be used at
all. It lives in the signed index, and the resolver enforces it.
Advisory — supported, legacy, untested — is a recommendation to a
person choosing a version. It lives in the channel's inventory, appears on the
site, and nothing resolves against it. A legacy version resolves exactly as
readily as a supported one; the label is for the human deciding what to pin.
Treating advice as standing would make an untested version unresolvable, which
is not what the label means. Treating standing as advice would make a withdrawal
a suggestion.
It reaches a warm cache¶
A withdrawn version is refused even for a client that already has the artefact on disk. That is the case worth designing for: the population holding an artefact is precisely the population a withdrawal is aimed at, and a design where a cache hit short-circuits the check would reach everyone except them.
It is why the resolver consults the index before the cache, and why a cache hit is verified against a freshly fetched manifest rather than trusted for being present.
Withdrawal is terminal¶
approved → withdrawn, never back. A correction ships as a new version.
An entry that could return would make "withdrawn" a state to wait out rather than a decision — and a consumer would have no way to distinguish "withdrawn, will return" from "withdrawn, stop". So the vocabulary has one direction.
Withdrawn artefacts stay published. Deleting the bytes would turn a security signal into a 404, which is indistinguishable from an outage — and a consumer must be able to tell "this was withdrawn" from "I could not reach the channel", because those warrant opposite responses. Keeping them also lets an incident be reconstructed afterwards.
The latency is bounded, not zero¶
A withdrawal reaches a consumer within the index lifetime plus propagation — 24 hours, as published.
A client holding a still-valid index is entitled to keep using it, and that is what makes the index usable at all: without an expiry a client would have to fetch it on every resolution and treat any failure as fatal, which it already does, but with no bound on how stale an accepted answer may be.
Shortening the window means shortening the expiry and republishing more often. That dial trades availability margin against withdrawal latency, and it is the whole design in one number. For an urgent withdrawal the operational answer is to shorten the window for that period, not to pretend the mechanism is instant.
What it does not do¶
It is not a transparency log. There are no third-party witnesses and no append-only proof, so this does not make a targeted attack provably detectable by strangers.
It does not make a compromised publisher safe. Anyone able to sign a manifest can sign an index, and the same key does both. Withdrawal defends against an artefact that should not have been approved, not against a publisher that should not be trusted.
It does not survive a frozen index indefinitely. An attacker who can serve a stale-but-unexpired index delays a withdrawal by up to the index lifetime. The expiry bounds that; a generation high-water mark stops it going further backwards once a client has seen something newer.