# MotorSignal Cloud Architecture **Status:** Planning baseline **Date:** 2026-09-01 **Backend direction:** Firebase Authentication + Firebase cloud services, integrated with the existing local-first Flutter application ## Purpose Define the cloud architecture required for MotorSignal accounts, ride backup, synchronization, profiles, friends, planned rides, trips, and group rides. This document intentionally favors an **offline-first** architecture. Local data remains the immediate source for recording and ride operation; cloud services provide identity, backup, synchronization, collaboration, and cross-device continuity. --- # Architectural Goals 1. Recording must work without internet access. 2. Cloud synchronization must be resilient to intermittent connectivity. 3. A user's data must be isolated from other users by security rules. 4. Synchronization must be idempotent and safe to retry. 5. Reinstalling the app must not require recreating a user's ride history. 6. Cloud functionality must support both Free and Pro entitlements. 7. The data model must support future social/group-riding features without a major rewrite. 8. Sensitive location information must receive stronger privacy treatment than ordinary profile information. 9. Account deletion must have a defined, testable data-deletion path. 10. Raw high-volume telemetry should not automatically be treated like ordinary profile documents. --- # High-Level Architecture ```text MotorSignal Flutter App │ ┌────────────────────┴────────────────────┐ │ │ Local Database Cloud Services / Local Files / Firebase │ │ │ ┌──────────┼──────────┐ │ │ │ │ │ Auth Firestore Storage │ │ │ │ └──────────────┬───────────────┴──────────┴──────────┘ │ Sync Engine │ Entitlement │ Pro / Free ``` --- # Identity Firebase Authentication provides the account identity. The internal account identifier should remain stable even if a user changes: - Display name. - Username. - Profile photo. - Optional public location. Google sign-in should remain compatible with the existing application authentication flow. The Firebase user ID should be treated as an internal identifier, not a public username. --- # Proposed Data Model The exact collection structure can be refined during implementation, but the logical model should include: ```text users/{userId} users/{userId}/motorcycles/{motorcycleId} users/{userId}/rides/{rideId} users/{userId}/trips/{tripId} users/{userId}/events/{eventId} users/{userId}/friends/{friendId} ``` Potential future top-level collaborative resources: ```text groupRides/{groupRideId} publicRoutes/{routeId} ``` These should only be introduced when query patterns and security requirements justify them. --- # User Document Conceptual fields: ```text id username displayName profilePhotoUrl bio location createdAt updatedAt privacySettings preferences ``` Do not store sensitive secrets in the user document. Avoid storing a complete ride history or large telemetry payload directly inside the user document. --- # Motorcycle Document Conceptual fields: ```text id ownerId year make model nickname photoUrl photoSource photoAttribution createdAt updatedAt privacy ``` The motorcycle should have a stable ID independent of its displayed name. --- # Ride Document Conceptual metadata: ```text id ownerId motorcycleId tripId? groupRideId? startedAt endedAt duration distance summaryStatistics routeReference? telemetryReference? createdAt updatedAt syncState visibility ``` High-volume telemetry should not be duplicated unnecessarily across multiple Firestore documents. Where appropriate, use compressed/structured files in Firebase Storage and keep searchable summary metadata in Firestore. --- # Trip Document Conceptual fields: ```text id ownerId name startDate endDate description routeReferences waypoints participants createdAt updatedAt visibility ``` Trips may contain multiple daily routes and completed rides. --- # Event Document For meetups and planned events: ```text id ownerId name description startAt location attendees linkedGroupRideId? createdAt updatedAt visibility ``` An event does not automatically imply live location sharing. --- # Friend Relationship Friend relationships should have explicit state. ```text PENDING ACCEPTED DECLINED BLOCKED ``` Friendship should not automatically grant access to private ride data. Visibility remains controlled by the owner's privacy settings. --- # Group Ride A group ride should be a shared activity context while each rider retains ownership of their own ride record. Conceptually: ```text Group Ride ├── Organizer ├── Participants ├── Route ├── Meeting Point ├── Schedule ├── Live Ride State └── Completed Ride References ``` A rider's individual telemetry remains associated with that rider and motorcycle. --- # Local-First Sync The app should never require a successful cloud write before considering a ride locally saved. ### Write flow ```text User records ride ↓ Save locally ↓ Mark PENDING_UPLOAD ↓ Continue normal app operation ↓ Connectivity available ↓ Sync engine uploads ↓ Server acknowledgement ↓ Mark SYNCED ``` ### Offline flow ```text No internet ↓ Record normally ↓ Queue cloud operation ↓ Internet returns ↓ Retry automatically ``` --- # Sync States Recommended common state model: ```text LOCAL_ONLY PENDING_UPLOAD SYNCED PENDING_DELETE CONFLICT SYNC_FAILED ``` The implementation may combine or rename states, but synchronization must be observable and debuggable. --- # Idempotency Cloud writes must be safe to retry. Do not create a new ride document every time an upload request is retried. Use stable application-generated IDs so: ```text same ride ID + repeated upload = same logical ride ``` not: ```text same ride + repeated upload = duplicate rides ``` --- # Conflict Handling Conflicts should be rare but must be explicitly designed. Potential strategy: - Server metadata determines whether an object changed. - Immutable ride telemetry should generally not be merged field-by-field. - User-editable metadata can use last-write-wins or explicit conflict resolution where appropriate. - Never silently overwrite a user's ride data without a defined policy. For example, if two devices edit a motorcycle nickname, last-write-wins may be acceptable. If two devices produce different telemetry for the same ride ID, preserve enough information to diagnose the conflict rather than silently destroying one copy. --- # Cloud Storage Use Firebase Storage for large binary objects where appropriate: - Ride telemetry files. - Exported reports if cloud retention is required. - Profile photos. - Motorcycle photos where user-uploaded. - Future ride media. Keep Firestore documents focused on metadata, indexes, relationships, and summaries. --- # Security Model Every user-owned resource must enforce ownership server-side. Conceptually: ```text request.auth.uid == resource.ownerId ``` or an equivalent rule based on the final data model. Never rely solely on Flutter UI restrictions to protect data. A hidden button is not a security boundary. --- # Social Security Friendship and group participation must not automatically permit access to all account data. Examples: - Being a friend does not expose private ride history. - Joining a group ride does not expose a user's historical rides. - Live location is only available during explicitly authorized group activity. - Public profile data is distinct from private account data. --- # Location Privacy Ride telemetry and GPS tracks can reveal a user's home, workplace, and routine. The platform should support: - Private rides. - Friends-only rides. - Public rides. - Optional location sharing. - Home/start/end location obfuscation for shared rides. - No continuous background social tracking merely because a user has an account. Live group location must require explicit participation/consent and should automatically stop at the end of the group ride. --- # Cloud Entitlements Cloud usage must be controlled through the centralized entitlement system. Example capabilities: ```text canCloudBackup canUnlimitedCloudHistory canMultiDeviceRestore ``` The client may use these capabilities for UX, but backend rules must also enforce limits and ownership where practical. Do not trust a client-provided `isPro=true` field as proof of payment. --- # Subscription / Account Separation A user account should continue to exist even if a Pro subscription expires. Conceptually: ```text Firebase Account │ ├── Profile ├── Garage ├── Rides └── Entitlement │ ┌──────┴──────┐ │ │ Free Pro Monthly/Lifetime ``` Billing provider state determines the paid entitlement. The account owns the data. --- # Expired Pro Data Policy The final product policy must be defined before launch. Recommended direction: - Do not silently delete historical rides when Pro expires. - Preserve account and existing data. - Restrict creation/synchronization of additional premium-only data if the user exceeds Free limits. - Allow upgrade to restore full Pro functionality. The exact storage allowance and retention policy must be finalized after Firebase cost modeling and legal review. --- # Backup / Restore Backup should be automatic where the user's entitlement allows it. Restore should be explicit and understandable. New device flow: ```text Install MotorSignal ↓ Sign in ↓ Authenticate ↓ Discover cloud account ↓ Download motorcycle/profile metadata ↓ Download ride metadata ↓ Download required ride data ↓ Rebuild local database ↓ Ready ``` The restore process should be resumable if interrupted. --- # Account Deletion Account deletion must be a real backend workflow. It should address: - Firebase Authentication identity. - User profile. - Motorcycles. - Rides. - Telemetry files. - Profile photos. - Social relationships. - Trips/events owned by the user. - Group ride participation data where legally/technically appropriate. Do not simply delete the authentication record while leaving user data orphaned indefinitely. --- # Data Export Provide an account-level export capability where required/appropriate. Potential export contents: - Profile information. - Motorcycle data. - Ride metadata. - Ride telemetry. - Routes. - Trip data. The existing CSV/PDF ride exports are not necessarily sufficient to satisfy an account-level data export requirement. --- # Observability Cloud synchronization should provide enough logging to diagnose: - Authentication failures. - Upload failures. - Download failures. - Permission-denied errors. - Duplicate prevention failures. - Conflict events. - Storage failures. - Billing/entitlement synchronization problems. Do not log raw sensitive telemetry or credentials unnecessarily. --- # Testing Matrix ### Authentication - [ ] Fresh account. - [ ] Existing account. - [ ] Sign out. - [ ] Sign in again. - [ ] Multiple devices. - [ ] Account deletion. ### Offline - [ ] Record without internet. - [ ] Save ride locally. - [ ] Restore connectivity. - [ ] Automatic upload. - [ ] Retry after failure. ### Sync - [ ] New motorcycle sync. - [ ] Ride sync. - [ ] Ride update. - [ ] Ride deletion. - [ ] Duplicate upload prevention. - [ ] Conflict behavior. ### Restore - [ ] Reinstall. - [ ] New device. - [ ] Large ride history. - [ ] Interrupted restore. - [ ] Partial network failure. ### Security - [ ] User A cannot read User B's private rides. - [ ] User A cannot write User B's data. - [ ] Friend access respects privacy. - [ ] Group membership does not grant historical access. - [ ] Client cannot self-grant Pro entitlement. --- # Implementation Order 1. Audit existing Firebase/auth implementation. 2. Document current local Room/database models. 3. Define cloud DTOs and stable IDs. 4. Define Firestore/Storage collections. 5. Write security rules before exposing new data. 6. Implement profile document. 7. Implement motorcycle synchronization. 8. Implement ride metadata synchronization. 9. Implement telemetry/blob synchronization. 10. Implement retry queue. 11. Implement conflict/error handling. 12. Implement backup status UI. 13. Implement restore. 14. Add account deletion/data export. 15. Connect cloud capability checks to entitlement manager. 16. Test Free/Pro storage behavior. 17. Add friends/social resources. 18. Add planned rides/trips. 19. Add group rides and explicit live-location sessions. --- # Design Rule The cloud system must make MotorSignal more reliable, not more fragile. The critical riding path should remain: ```text Sensors → Local Recording → Local Database → Ride Processing ``` Cloud synchronization should be downstream: ```text Local Ride → Sync Queue → Cloud Backup ``` A cloud outage must not prevent a rider from recording a ride. --- # Server-Verified Google Play Entitlements (Stage 4B-3) ## Verification Pipeline ```text Google Play Billing Client ↓ MotorSignal BillingService (PlayBillingService) ↓ Unverified StorePurchase (productId, purchaseToken) ↓ DurableVerificationQueue (FlutterSecureStorage) ↓ PurchaseVerificationService ↓ Cloudflare Worker (POST /billing/verify-play-purchase) ↓ (Authenticated via Service Account JWT assertion) Google Play Developer API v3 (Android Publisher) - Subscriptions: purchases.subscriptionsv2.get - Lifetime: purchases.products.get ↓ Verified Entitlement Payload (Plan, Status, ExpiresAt, TokenHash) ↓ EntitlementCoordinator ↓ Secure Local Entitlement Cache (FlutterSecureStorage) ↓ PlayBillingService.completePurchase() (Google Play Acknowledgement) ``` ## Security & Architecture Contracts 1. **Server Authority**: The mobile client never self-asserts its entitlement tier. Entitlement is determined authoritatively by Google Play Developer API responses. 2. **Account Independence**: MotorSignal login is strictly optional. Riders without a MotorSignal account can purchase, verify, restore, and own Pro entitlements. Optional Firebase ID tokens sent in verification requests associate convenience metadata without overriding Google Play ownership. 3. **Secret Isolation**: Google Service Account credentials (`GOOGLE_SERVICE_ACCOUNT_EMAIL`, `GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY`) reside exclusively as Cloudflare Worker secrets. They are never compiled into the APK, never committed, and never exposed in logs. 4. **Token Handling**: Raw Google Play purchase tokens are never logged or stored in general application databases (Drift, SharedPreferences). Bounded retry queuing utilizes encrypted `FlutterSecureStorage` and purges tokens upon terminal verification. 5. **Rider Safety Invariant**: Active ride recording and telemetry persistence are strictly isolated from billing verification operations and network outages.