Technical Documentation

Architecture, Tech Stack & RevenueCat Implementation

Tech Stack

re:mind is built on a modern, production-grade stack chosen for cross-platform reach, offline resilience, real-time sync, and developer velocity. Every layer serves a specific purpose in delivering a responsive, cross-platform experience.

Layer Technology Purpose
Frontend Framework React Native 0.81 + Expo SDK 54 Cross-platform mobile (Android, iOS, Web)
Language TypeScript Type-safe development
Navigation Expo Router v6 File-based routing with tabs + stacks
Animations React Native Reanimated v4 60fps gesture-driven animations
Local Database TinyBase + expo-sqlite Offline-first reactive store with SQLite persistence
Backend Convex Serverless functions + real-time database
Authentication Convex Auth + Resend Passwordless email OTP
Payments RevenueCat (react-native-purchases) Subscription management
Push Notifications Expo Push Notifications Silent sync triggers across devices
Recurrence Engine rrule-temporal iCalendar RRULE spec (Google Calendar-level)
State Management React Context + Custom Hooks 24 custom hooks for app logic

System Architecture

The following diagram illustrates how re:mind's client, backend, and external services connect to form a cohesive, real-time system.

graph TB
    subgraph Client["Mobile App (React Native + Expo)"]
        UI["UI Layer<br/>Expo Router + Reanimated"]
        Store["TinyBase Store<br/>(Reactive State)"]
        SQLite["expo-sqlite<br/>(Persistent Storage)"]
        RC["RevenueCat SDK<br/>(Purchases)"]
        Push["Expo Push<br/>(Notifications)"]
    end

    subgraph Backend["Convex Cloud"]
        API["Serverless Functions<br/>(Queries + Mutations)"]
        DB["Real-time Database<br/>(Documents)"]
        Auth["Convex Auth<br/>(Email OTP via Resend)"]
        Webhook["RC Webhook Handler<br/>(Idempotent)"]
        Scheduler["Convex Scheduler<br/>(Background Jobs)"]
    end

    subgraph External["External Services"]
        Resend["Resend<br/>(Email Delivery)"]
        RCCloud["RevenueCat<br/>(Subscription Management)"]
        PlayStore["Google Play<br/>(Billing)"]
        ExpoPush["Expo Push Service"]
    end

    UI --> Store
    Store <--> SQLite
    Store <--> API
    API <--> DB
    Auth --> Resend
    RC <--> RCCloud
    RCCloud --> Webhook
    RCCloud <--> PlayStore
    Webhook --> DB
    Scheduler --> ExpoPush
    Push --> ExpoPush
        

The app uses an offline-first architecture where TinyBase provides a reactive in-memory store synced to SQLite for persistence. All reads and writes happen against TinyBase first, guaranteeing instant UI response regardless of network state.

Convex provides the cloud backend with real-time subscriptions. When data changes server-side, connected clients receive updates automatically through Convex's reactive query system, with no polling required.

The sync layer uses a pull/push model: pull on app launch plus periodic sync, push on each local mutation. This ensures data converges without manual conflict resolution in the vast majority of cases.

Silent push notifications trigger cross-device sync without user-visible notifications. When a user edits a reminder on one device, all other devices receive a silent push that triggers an immediate data pull from Convex.


Offline-First Sync Architecture

The core design principle is that the user should never wait for the network. Every interaction is local-first, with sync happening transparently in the background.

sequenceDiagram
    participant User
    participant App as Mobile App
    participant SQLite as SQLite (Local)
    participant TinyBase as TinyBase Store
    participant Convex as Convex Cloud
    participant OtherDevice as Other Devices

    User->>App: Create/Edit Reminder
    App->>TinyBase: Write to reactive store
    TinyBase->>SQLite: Persist locally
    App-->>User: Instant UI update

    TinyBase->>Convex: Push mutation
    Convex->>Convex: Store in database
    Convex->>OtherDevice: Silent push notification
    OtherDevice->>Convex: Pull updated data
    OtherDevice->>OtherDevice: Update local store
        

Key Design Points


RevenueCat Implementation

Subscription management is handled end-to-end by RevenueCat, with a client-side SDK integration and a server-side webhook handler for reliable state synchronization.

Client-Side Integration

The RevenueCat SDK is initialized using a state machine pattern implemented in revenueCatState.ts. Three transitions govern the SDK lifecycle:

All RevenueCat SDK calls await rcReady() before proceeding. This prevents race conditions where purchase or restore calls execute before the SDK is configured, which would otherwise throw runtime errors.

The subscription screen fetches available offerings from RevenueCat, displays monthly and annual plans with localized pricing, and handles both new purchases and subscription restores. Entitlement checks are performed through the isPro flag exposed by ThemeContext, which gates premium features including unlimited reminders and all five color themes.

Server-Side Webhook Handler

RevenueCat sends server-to-server webhooks for every subscription lifecycle event. The Convex webhook handler processes these with full idempotency and error resilience.

flowchart TD
    A["RevenueCat Event"] --> B{"Auth Header Valid?"}
    B -->|No| C["401 Unauthorized"]
    B -->|Yes| D{"Parse JSON Body"}
    D -->|Malformed| E["400 Bad Request"]
    D -->|Valid| F{"Event ID Seen Before?"}
    F -->|Yes| G["200 OK (Skip - Idempotent)"]
    F -->|No| H{"Event Type?"}
    H -->|INITIAL_PURCHASE\nRENEWAL\nUNCANCELLATION| I["Set status: active"]
    H -->|CANCELLATION| J["Set status: cancelled"]
    H -->|EXPIRATION| K["Set status: expired"]
    H -->|BILLING_ISSUE| L["Keep active + log warning"]
    H -->|PRODUCT_CHANGE| M["Set status: active\nUpdate entitlements"]
    I & J & K & L & M --> N{"Mutation Success?"}
    N -->|No| O["500 (RC will retry)"]
    N -->|Yes| P["Store Event ID"]
    P --> Q["200 OK"]
        

Idempotent Event Processing

The webhook handler is idempotent; duplicate events from RevenueCat retries are safely skipped. Each incoming event's unique ID is checked against a webhookEvents table in Convex. If the event has already been processed, the handler returns 200 OK immediately without re-executing the mutation.

Authorization and Error Handling

Authorization is verified via a shared secret passed in the HTTP header. Invalid requests are rejected with 401 Unauthorized before any processing occurs.

If a mutation fails (database error, transient issue), the handler returns 500 Internal Server Error. RevenueCat's retry mechanism will then re-deliver the event up to 5 times at exponentially increasing intervals: 5, 10, 20, 40, and 80 minutes. This guarantees eventual delivery even during temporary outages.

Subscription State Updates

The user's subscription status and entitlements are updated directly in the Convex database. Because Convex provides real-time subscriptions, the client immediately reflects the new subscription state without requiring a manual refresh or app restart.

Subscription Model

Aspect Detail
Entitlement re:mind:pro
Monthly Product re_mind_pro:monthly-autorenewing (GBP 3.99)
Annual Product re_mind_pro:annual-autorenewing (GBP 29.99)
Free Tier Limit 5 active reminders, 2 themes
Pro Features Unlimited reminders, all 5 themes
PPP Pricing 165 countries with localized prices
Offering default (current)

Key Technical Differentiators

Notable engineering decisions in re:mind:

  1. Offline-First with Real-Time Sync. TinyBase + SQLite + Convex provides local-first writes with cloud sync and real-time subscriptions. Users never wait for the network; the app works identically offline and online.
  2. Google Calendar-Grade Recurrence. Full iCalendar RRULE spec via rrule-temporal, supporting patterns like "every 3rd Wednesday" or "MWF for 10 weeks." This is the same recurrence standard used by Google Calendar.
  3. Per-Occurrence Overrides. Individual instances of recurring reminders can be snoozed, cancelled, edited, or completed independently. Modifying one occurrence does not affect the rest of the series.
  4. Multi-Device Sync via Silent Push. Changes propagate across devices in near-real-time using Expo Push Notifications as sync triggers. No manual refresh needed.
  5. Passwordless Authentication. Email OTP via Convex Auth + Resend eliminates password management entirely.
  6. PPP Pricing at Scale. 165 countries with Purchasing Power Parity pricing, managed through RevenueCat.
  7. 5 Customizable Themes. Dark/light/system modes with 5 color palettes, dynamically applied at runtime. Themes are gated by subscription tier.