Roles change. People move between locations. Records are reassigned. That means a log entry from March cannot be judged against today's permission table — the honest question is whether the access was permitted at the time.
Two replay modes
- As of then — re-derive the access envelope exactly as it stood when the query ran. This catches tampering, enforcement bugs, and forged log rows.
- As of now — re-derive it under today's rules. This catches authorization drift: access that was legitimate then and would not be permitted today.
What a replay returns
A verdict, not a reassurance. The result is one of: verified, envelope mismatch, unauthorized retrieval, no longer authorized, or chain broken. Each is a specific finding you can act on, and each replay run is itself recorded.
An audit trail that only records successes is not an audit trail. Refused reveals and failed verifications are the entries that matter most in a review.
Why there is no stream of access-denied events
Record access resolves as a filter. A user never requests a record they have no role on and gets turned away, because the list, search, or panel they were looking at was built from their entitled set and never offered it. There is no rejection to log because there was no attempt to make.
That is deliberately stronger than refusing. An error response confirms a record exists and usually reveals something about it. Filtering at the source makes an unentitled record and a non-existent record indistinguishable from outside. Refusal is reserved for the operations that genuinely warrant it — revealing an encrypted field, exporting, and privileged actions — where the request is explicit and the refusal is meaningful.