Revocation without a network call at verification time
The certificate ecosystem spent two decades learning that asking a server whether something is revoked, at the moment you need to know, is a bad design. That lesson transfers directly.
Why does online revocation checking fail open?
Because an unreachable responder cannot be allowed to block all verification. The certificate ecosystem learned this over two decades: a check that is skipped when the responder is unavailable is a check an attacker can disable by making the responder unavailable.
- Online status checks leak the subject being verified to the responder and add latency to every verification.
- Because an unreachable responder cannot be allowed to block everything, implementations fail open — which makes the check optional in practice.
- A signed list distributed ahead of time is checkable offline, leaks nothing, and fails closed on the entries it covers.
Part of Receipt cryptography and standards
The three problems with asking
| Problem | Detail |
|---|---|
| Privacy | The responder learns which subject you are verifying, when, and from where |
| Latency | A network round trip on the verification path, at the worst possible moment |
| Availability | An unreachable responder blocks verification, so implementations fail open |
The third is the one that matters most. A check that is skipped when the responder is unavailable is a check an attacker can disable by making the responder unavailable.
What a signed list does instead
Publish the revoked set, signed, on a schedule. Verifiers fetch it periodically and check locally.
{
"issued": "2026-03-09T12:00:00Z",
"next_update": "2026-03-09T12:05:00Z",
"revoked_keys": [
{ "kid": "2025-11-c", "effective": "2026-02-14T08:22:00Z",
"reason": "key_compromise" }
],
"revoked_nonces": [
"n_88213aa1", "n_4471bf02", "n_9c1f8a3e"
],
"signature": "..."
}
Two categories, and both matter. Revoking a key invalidates a class of signatures from a point in time. Revoking a nonce invalidates one specific authorisation — which is what you need when an individual approval was obtained fraudulently.
The staleness objection, addressed
A list is as fresh as its publication interval. The usual complaint is that this is worse than asking in real time.
In practice online responses are also cached, often for hours, so the freshness advantage is smaller than it appears. And the interval is a choice: a list published every minute is a minute stale, which for revocation of a specific authorisation is entirely adequate.
| Interval | Suitable for |
|---|---|
| Every few seconds | Halting an in-flight automated process |
| Every minute | Revoking a specific authorisation |
| Every hour | Key revocation, which is rare |
| Daily | Not adequate for anything that matters |
Keeping the list small
The obvious concern with a full list is that it grows without bound. Three techniques keep it bounded.
- Expire entries. A nonce for a statement that has expired can leave the list — the statement is unusable anyway.
- Use effective times for keys. One entry with a timestamp covers every signature from that key after that moment, rather than listing signatures.
- Partition by scope. A verifier for one service does not need revocations for another, so publish per-scope lists.
With those, the steady-state list for a typical deployment is small enough to fetch frequently without concern.
The stapling middle ground
A third option worth knowing: the party presenting the receipt includes a recent signed status assertion alongside it.
This removes the verifier's network call and the privacy leak, and shifts the freshness burden to the presenter. It is a good fit where receipts are presented rather than looked up, and it adds a moving part.
Comparing honestly
| Property | Online status check | Signed list | Stapled assertion |
|---|---|---|---|
| Verifier network call | Yes | Only to refresh | No |
| Privacy leak to responder | Yes | No | No |
| Freshness | Real time, if uncached | Publication interval | As stapled |
| Behaviour when unreachable | Fails open in practice | Cached list still works | Presenter cannot staple |
| Implementation complexity | Low | Low | Moderate |
The signed list wins on the properties that determine whether the control actually operates, and gives up real-time freshness that online checks mostly do not deliver either.
Where to check
At the point of effect, not at a gateway. A revocation checked at an API gateway does not stop work already past the gateway: queued jobs, retries, in-flight requests.
And fail closed on unknown. If a receipt references a key id that is not in your cached key set, that is not an unknown-but-probably-fine situation; it is a refusal.
Two kinds of revocation, both needed
| Entry | Scope |
|---|---|
| Revoked key, with an effective time | Every signature from that key after that moment |
| Revoked nonce | One specific authorisation |
The second is what you need when an individual approval was obtained fraudulently and the credential itself is fine. Key-level revocation is too blunt for that case and is what most systems offer.
Objections and honest limits
“A list is staler than a real-time check.” Online responses are typically cached for hours, so the freshness advantage is smaller than it appears. A list published every minute is frequently fresher than what the callback actually delivers.
“The list will grow without bound.” Expire entries whose statements have expired, use effective times for keys rather than listing signatures, and partition by scope. Steady-state size is small.
Implementing revocation well
- Publish a signed, cacheable list. Per scope, on a short interval.
- Support both key and nonce revocation. Blunt and precise.
- Check at the point of effect. A gateway check misses queued work and retries.
- Fail closed on unknown key ids. An unrecognised key is a refusal, not a maybe.
Terms used here
- Soft-fail
- Proceeding when a revocation check cannot be completed — the near-universal behaviour, and the reason the check is optional in practice.
- Effective time
- The moment from which a key revocation applies, so earlier signatures remain valid.
- Stapling
- The presenter attaching a recent signed status assertion, removing the verifier's network call.
Frequently asked questions
Why do online status checks fail open? Because an unreachable responder would otherwise block all verification. That makes the check optional whenever the responder can be made unavailable.
Isn't a list staler than a real-time check? Online responses are typically cached for hours anyway. A list published every minute is adequate for revoking a specific authorisation.
How do you keep the list small? Expire entries for statements that have expired, use effective timestamps for keys rather than listing signatures, and partition by scope.
Where should the check happen? At the point of effect. A gateway check misses queued work, retries and in-flight requests already past it.
Why do online checks fail open? Because blocking all verification when a responder is down is unacceptable, which makes the check optional whenever the responder can be made unavailable.
Is a published list too stale? Online responses are cached for hours anyway. A list published every minute is adequate for revoking a specific authorisation.
Why revoke a nonce rather than a key? Because when one approval was obtained fraudulently the credential is fine. Key revocation is too blunt for that case.
Where this fits in Manav
Manav's receipt is deliberately small: RFC 8785 canonicalisation, one signature algorithm, keys published at a well-known URL, and a verifier short enough to audit or reimplement.