Skip to content

Re-attest on any backward wall-clock step (RD-4) - #50

Merged
dbccpage merged 1 commit into
mainfrom
claude/rd4-monotonic-attestation
Nov 11, 2026
Merged

dbccpage merged 1 commit into
mainfrom
claude/rd4-monotonic-attestation

Conversation

@dbccpage

Copy link
Copy Markdown
Owner

Requested by Jeremy · project thread

What

  • SignedMirror._subjects reused a cached role attestation while now - cached_ms < attest_ttl_ms // 3. A backward wall-clock step makes that negative, so the old result was reused indefinitely and its attested_ms sat in the new clock's future. Readers on that clock then honored a revoked subject for attest_ttl_ms plus the step.
  • The cache is now valid only while young on both clocks: 0 <= wall_delta < refresh and 0 <= monotonic_delta < refresh (time.monotonic_ns, injectable for tests).
  • BoundedReader.read refuses a head whose attested_ms > issued_ms or > now as attestation_clock_regressed. (attest_expires_ms already bounds now - attested_ms < attest_ttl_ms.)

Evidence

New tests in tests/test_clock_boundaries.py:

  • test_backward_wall_step_forces_reattestation: shared host clock steps back 20 s after a REVOKE. On main the cached attestation is reused (calls == [1]) and the revoked subject still hits; here it re-attests and misses with authorization_not_attested.
  • test_monotonic_age_forces_reattestation_when_the_wall_clock_stalls
  • test_reader_refuses_an_attestation_newer_than_its_head

black, mypy clean; heads + clock-boundary tests: 54 passed.

Note: the new reader check is skew-sensitive the same way the existing observed_ms check is (a reader host behind the signer by more than issued_ms - attested_ms misses).


Generated by Claude Code

SignedMirror reused a cached role attestation while now - cached_ms was
below the refresh interval, which a backward wall step makes negative:
the old result was then reused indefinitely, and its attested_ms lay in
the new clock's future, so readers on that clock honored a revoked
subject for attest_ttl_ms plus the step. The cache is now valid only
while young on both the wall clock (0 <= delta < refresh) and the
monotonic clock. BoundedReader also refuses a head whose attestation
postdates the head or the reader's clock (attestation_clock_regressed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0194kAmY4QqQGabdoB3xGFsr
@dbccpage dbccpage self-assigned this Nov 11, 2026
@dbccpage
dbccpage marked this pull request as ready for review November 11, 2026 10:07
@dbccpage
dbccpage merged commit dad3a68 into main Nov 11, 2026
5 checks passed
@dbccpage
dbccpage deleted the claude/rd4-monotonic-attestation branch November 11, 2026 10:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants