From the TRUST-01 adversarial review (PR #132): when kr.Rotate fails during devices revoke/lost, the old epoch stays active, so events pushed before a later successful rotation — including the device.revoked event itself — remain decryptable by the revoked device (it still holds the old WCK).
The trust flip is deliberately kept on rotation failure (refusing the revoke would keep a compromised device APPROVED, strictly worse), and the CLI now warns loudly with the keys rotate remedy (PR #132). Candidate hardening, in order of preference:
- Auto-retry:
sync detects a revoked device still holding the current epoch (rotation pending) and re-attempts Rotate before pushing — turning the manual remedy into a self-healing path (mirrors the age-triggered auto-rotation precedent, keys.rotate_max_age).
- Push gate: refuse to push NEW namespace events (trust event exempt) while a post-revoke rotation is pending, bounding the exposure window at the cost of availability.
- Preflight recipient wrap-checks before the trust write so the common failure class (malformed recipient) is caught before any state changes.
spec/15 documents the residual as of PR #132.
From the TRUST-01 adversarial review (PR #132): when
kr.Rotatefails duringdevices revoke/lost, the old epoch stays active, so events pushed before a later successful rotation — including thedevice.revokedevent itself — remain decryptable by the revoked device (it still holds the old WCK).The trust flip is deliberately kept on rotation failure (refusing the revoke would keep a compromised device APPROVED, strictly worse), and the CLI now warns loudly with the
keys rotateremedy (PR #132). Candidate hardening, in order of preference:syncdetects a revoked device still holding the current epoch (rotation pending) and re-attemptsRotatebefore pushing — turning the manual remedy into a self-healing path (mirrors the age-triggered auto-rotation precedent,keys.rotate_max_age).spec/15 documents the residual as of PR #132.