Nobody wrote a bug. Her access was removed. The document changed a moment later. The check that guards it was answered by a replica that had seen neither event yet. Google's Zanzibar paper names this the new enemy problem.
Every write hands one back. A check that carries it says: don't answer me with data older than that write. Without it, the check takes whatever the replica has.
The replica gave the right answer for the data it had. Nothing is corrupted and no code is faulty.
The problem is not correctness. It is freshness — which is why it reproduces under load and disappears when you go looking for it.
Every authorization system caches, because walking the graph on every request is expensive. A cache is just another replica that can be behind.
So the question is not whether a system caches. It is what happens to a cached answer the moment a revocation lands.
Someone is offboarded and the document is edited minutes later. A contractor's access ends mid-project. A support case is closed while the agent still has the record open.
Each is ordinary. Each produces a read that should not have happened, and none of them leaves a trace.
A ZedToken marks the exact point at which your change landed. Keep it alongside whatever you just changed — in the demo above, it is what step one produced.
Per request, not per deployment. Ask for minimize_latency where speed matters, or
at_least_as_fresh with a token where the answer has to reflect a specific write.
A menu or a search result can take the fast path. The check on a document that was just re-shared takes the pinned one. You choose per call, not once for the whole system.
This scenario is deliberately small so the mechanism is visible. To try it against your own objects, the SpiceDB Playground runs in the browser with nothing to install — play.authzed.com. SpiceDB is open source, so none of this has to be taken on trust.