Skip to main content

Sync Engine

Resibibo supports opt-in E2E encrypted sync between the mobile app and the Django server.

Overview

Sync is entirely optional. When enabled:

  1. A keypair is generated on the device
  2. Financial data is encrypted before leaving the device
  3. The server stores only encrypted blobs
  4. Data is decrypted only on the device

Encryption

Key Management

  • Algorithm: X25519 (key exchange) + XSalsa20-Poly1305 (symmetric encryption)
  • Library (mobile): tweetnacl
  • Library (server): PyNaCl (libsodium bindings)
  • Key storage: Private key in expo-secure-store (Keychain on iOS, Keystore on Android)
  • Public key: Sent to server and stored on User model

Encryption Flow

Sync Flow

Push (Local -> Server)

  1. Query all records where syncStatus = 'pending'
  2. Encrypt each record's data with the user's keypair
  3. Batch POST to /api/v1/sync/push/ with encrypted payloads
  4. On success, update syncStatus = 'synced'

Pull (Server -> Local)

  1. GET /api/v1/sync/pull/?since={last_sync_timestamp}
  2. Decrypt received records with private key
  3. For each record:
    • If local copy doesn't exist: insert
    • If local updated_at < server updated_at: update local
    • If local updated_at > server updated_at: mark as conflict
  4. Conflicts are presented to the user for manual resolution

Conflict Resolution

Conflicts use a last-write-wins strategy with manual override:

  • The conflicts.tsx screen shows conflicting records
  • Users can choose to keep the local version, server version, or merge manually
  • Resolution calls POST /api/v1/sync/resolve/

Auto-Sync Triggers

  • App foreground: Syncs when app comes to foreground (if > 5 minutes since last sync)
  • Manual: Sync button in Settings
  • Sync state is managed with Zustand (lib/sync/sync-store.ts)

Server Endpoints

MethodEndpointDescription
POST/api/v1/sync/push/Push encrypted records
GET/api/v1/sync/pull/Pull records since timestamp
GET/api/v1/sync/status/Get sync status
POST/api/v1/sync/resolve/Resolve conflicts

SyncStatus Column

All syncable tables have a syncStatus column:

ValueMeaning
pendingRecord has local changes not yet synced
syncingCurrently being synced (transient state)
syncedSuccessfully synced with server
conflictServer and local versions differ