MotorSignal-App/docs/architecture/cloud-architecture.md

16 KiB

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

                       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:

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:

groupRides/{groupRideId}
publicRoutes/{routeId}

These should only be introduced when query patterns and security requirements justify them.


User Document

Conceptual fields:

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:

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:

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:

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:

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.

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:

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

User records ride
      ↓
Save locally
      ↓
Mark PENDING_UPLOAD
      ↓
Continue normal app operation
      ↓
Connectivity available
      ↓
Sync engine uploads
      ↓
Server acknowledgement
      ↓
Mark SYNCED

Offline flow

No internet
   ↓
Record normally
   ↓
Queue cloud operation
   ↓
Internet returns
   ↓
Retry automatically

Sync States

Recommended common state model:

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:

same ride ID + repeated upload = same logical ride

not:

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:

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:

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:

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:

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:

Sensors → Local Recording → Local Database → Ride Processing

Cloud synchronization should be downstream:

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

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.