mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02: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>
Buzz Mobile
Flutter mobile client for Buzz.
Setup
cd mobile
flutter pub get
Run
# From repo root (recommended — starts Docker, relay, and simulator):
just mobile-dev
# Direct (requires services and relay already running):
cd mobile && flutter run
Checks
dart format --output=none --set-exit-if-changed .
flutter analyze
flutter test
Or from the repo root: just mobile-check and just mobile-test.
Android release signing
Android release builds fail unless all upload-key inputs are supplied through the environment:
BUZZ_ANDROID_UPLOAD_KEYSTORE_PATH: path to a CI-vended keystore fileBUZZ_ANDROID_UPLOAD_KEYSTORE_PASSWORDBUZZ_ANDROID_UPLOAD_KEY_ALIASBUZZ_ANDROID_UPLOAD_KEY_PASSWORD
The keystore path must be absolute, and the keystore must remain outside the repository. Development and debug builds do not require these variables.
Architecture
lib/
├── main.dart # Entry point, Riverpod bootstrap
├── app.dart # MaterialApp with theme
├── shared/
│ └── theme/ # Catppuccin light/dark, spacing tokens, extensions
└── features/
└── home/ # Placeholder home surface
- State management: Riverpod + Hooks (
HookConsumerWidget) - Theme: Catppuccin Latte (light) / Macchiato (dark) — matches desktop
- Spacing:
Gridtokens for consistent spacing - Linting:
flutter_lints+riverpod_lintviacustom_lint - Feature isolation: No cross-feature imports except
shared/