Cue: Technical Documentation
Architecture Overview
iOS app. React Native + Expo. Supabase for backend. RevenueCat for subscriptions.
Production-ready.
Tech Stack
Frontend
| Technology | Purpose |
|---|---|
| React Native | Cross-platform mobile |
| Expo | Dev and build tooling |
| TypeScript | Type-safe JavaScript |
| Zustand | Global state management |
| React Query | Server state and caching |
| Expo Router | File-based navigation |
| AsyncStorage | Local data persistence |
Backend
| Technology | Purpose |
|---|---|
| Supabase | Backend-as-a-Service |
| PostgreSQL | Primary database |
| Supabase Auth | Authentication |
| Edge Functions | Serverless API (Deno) |
| Row Level Security | Data access control |
AI & Services
| Technology | Purpose |
|---|---|
| OpenAI API | GPT-4 for coaching |
| RevenueCat | Subscription management |
| Expo Notifications | Push notifications |
| expo-secure-store | Credential storage |
Architecture Diagram
+------------------+ +-------------------+ +------------------+
| | | | | |
| React Native |<--->| Supabase |<--->| OpenAI API |
| (Expo) App | | (PostgreSQL) | | (GPT-4) |
| | | | | |
+------------------+ +-------------------+ +------------------+
| |
| |
v v
+------------------+ +-------------------+
| | | |
| RevenueCat | | Edge Functions |
| (Subscriptions) | | (Deno) |
| | | |
+------------------+ +-------------------+Database Schema
Core Tables
user_profiles
CREATE TABLE user_profiles (
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
email TEXT NOT NULL,
display_name TEXT,
avatar_url TEXT,
subscription_status TEXT DEFAULT 'free'
CHECK (subscription_status IN ('free', 'premium')),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);user_goals
CREATE TABLE user_goals (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users NOT NULL,
coach_id TEXT NOT NULL,
coach_type TEXT NOT NULL CHECK (coach_type IN ('core', 'custom')),
short_term_goal TEXT,
long_term_goal TEXT,
current_challenge TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(user_id, coach_id)
);custom_coaches
CREATE TABLE custom_coaches (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users NOT NULL,
name TEXT NOT NULL,
emoji TEXT NOT NULL,
description TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);conversations
CREATE TABLE conversations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users NOT NULL,
coach_id TEXT NOT NULL,
pinned BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);messages
CREATE TABLE messages (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
conversation_id UUID REFERENCES conversations ON DELETE CASCADE NOT NULL,
role TEXT NOT NULL CHECK (role IN ('user', 'assistant')),
content TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);notification_settings
CREATE TABLE notification_settings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users NOT NULL,
coach_id TEXT NOT NULL,
enabled BOOLEAN DEFAULT true,
frequency INT DEFAULT 1 CHECK (frequency >= 1 AND frequency <= 7),
times JSONB DEFAULT '["08:00"]'::jsonb,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(user_id, coach_id)
);Row Level Security
All tables implement RLS. Users can only access their own data.
CREATE POLICY "Users can view own conversations" ON conversations
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "Users can insert own conversations" ON conversations
FOR INSERT WITH CHECK (auth.uid() = user_id);Edge Functions
chat-completion
Handles AI coaching responses.
Endpoint: POST /functions/v1/chat-completion
Process:
- Validate authentication
- Check message limits (free: 15/day)
- Load coach prompt + user context
- Call OpenAI with full context
- Store message
- Return response
generate-notification
Generates personalized notification content.
Endpoint: POST /functions/v1/generate-notification
refine-goals
AI-assisted goal refinement.
Endpoint: POST /functions/v1/refine-goals
send-notifications
Cron-triggered notification sender.
RevenueCat Integration
Setup
import Purchases from 'react-native-purchases';
export const initializePurchases = async (userId: string) => {
Purchases.configure({
apiKey: REVENUECAT_IOS_KEY,
});
await Purchases.logIn(userId);
};Entitlement Check
export const checkPremiumStatus = async (): Promise<boolean> => {
const customerInfo = await Purchases.getCustomerInfo();
return customerInfo.entitlements.active['premium'] !== undefined;
};Products
| Product ID | Type | Price |
|---|---|---|
cue_monthly | Subscription | $9.99/month |
cue_annual | Subscription | $59.99/year |
cue_lifetime | Non-consumable | $149.99 |
Authentication Flow
1. App Launch
├── Check Supabase session
│ ├── Valid → Load user → Main App
│ └── Invalid → Auth Screen
2. Sign Up
├── Email/Password registration
├── Create user_profiles record
├── Initialize RevenueCat
└── Begin onboarding
3. Onboarding
├── Select coaches to focus on
├── Set goals per coach
├── Configure notifications
└── Complete → Main AppLocal-First Architecture
Optimistic Updates
- Messages appear instantly before server confirmation
- Failed messages show retry UI
Local Cache
- AsyncStorage for persistence
- Zustand hydration on app launch
- React Query for server state sync
Security
- Supabase Auth with secure sessions
- JWT with auto-refresh
- expo-secure-store for credentials
- RLS on all tables
- HTTPS everywhere
Deployment
EAS Build
# Production
eas build --profile production --platform ios
# Submit
eas submit --platform iosBuilt by Aaryan Sharma.