mirror of
https://github.com/block/buzz.git
synced 2026-11-02 07:22:55 +00:00
Implements the B2/B4 client-side lifecycle rules from the iOS cold review as a pure decision machine plus a Keychain persistence seam, all in BuzzPushKit and fully unit-tested without App Attest/network: - BuzzPushInstallationState: durable installation record with a two-phase pendingRotation intent and local APNs-token fingerprinting (SHA-256 of the lowercase-hex token), so change detection never retains the token. - BuzzPushLifecycle.onDeviceToken: rotate ONLY on a real token fingerprint change (iOS re-delivers the token every launch; unconditional rotate bumps the server epoch and kills every outstanding relay delegation); proactive re-enroll inside a 7-day renewal window before expires_at; surviving intents resume instead of desyncing. - BuzzPushLifecycle.applyRotate: persist-then-confirm epoch commit; transient retains the intent; invalid_attestation (zombie key after backup-restore) clears and re-enrolls; ambiguous not_authorized after a crash window adopts the pending epoch and escalates once before giving up — deterministic reconvergence without reading server state. - KeychainPushStateStore: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly (NSE must read while locked; ThisDeviceOnly excludes the record from backup-restore, killing the zombie-installation class at the storage layer) with optional access group for the extension; versioned JSON envelope that fails closed to 'corrupt' instead of crashing. swift test: 32/32 green (10 transcript vectors + 22 lifecycle/store). Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz>