Your daily AI productivity companion — personal finance, task management, focus tools, and on-device AI, all in one React Native app.
d.ai.ly is a full-stack mobile application built with React Native (Expo) on the frontend and a FastAPI + PostgreSQL backend deployed on Render. It combines everyday productivity tools — expense tracking, budgeting, to-dos, reminders, meetings, and a Pomodoro timer — with a live market dashboard and a fully on-device RAG (Retrieval-Augmented Generation) AI pipeline that lets users semantically search and query their notes without sending any data to external AI APIs.
The app is published to the Google Play Store and uses Supabase for authentication (Google OAuth via PKCE) and cloud database sync.
┌─────────────────────────────────────────────────────────────────┐
│ React Native App │
│ (Expo 54 / RN 0.81.5) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌───────────────────────────────┐ │
│ │ Screens │ │ Contexts │ │ Services Layer │ │
│ │ (Expo │ │ (React │ │ │ │
│ │ Router) │ │ Context │ │ ┌──────────┐ ┌───────────┐ │ │
│ └──────────┘ │ API) │ │ │ apiClient│ │supabase │ │ │
│ └──────────┘ │ │ (JWT auth│ │Client.js │ │ │
│ │ │ fetch) │ │(chunked │ │ │
│ │ └────┬─────┘ │SecureStore│ │ │
│ │ │ └─────┬─────┘ │ │
│ │ ┌────▼──────────────▼────┐ │ │
│ │ │ On-Device AI │ │ │
│ │ │ ┌─────────────────┐ │ │ │
│ │ │ │ all-MiniLM-L6 │ │ │ │
│ │ │ │ (384-dim embed) │ │ │ │
│ │ │ └────────┬────────┘ │ │ │
│ │ │ ┌────────▼────────┐ │ │ │
│ │ │ │ OPSQLite │ │ │ │
│ │ │ │ Vector Store │ │ │ │
│ │ │ └────────┬────────┘ │ │ │
│ │ │ ┌────────▼────────┐ │ │ │
│ │ │ │ SmolLM2-135M-q │ │ │ │
│ │ │ │ (ExecuTorch LLM)│ │ │ │
│ │ │ └─────────────────┘ │ │ │
│ │ └────────────────────────┘ │ │
│ └───────────────────────────────┘ │
└──────────────────┬──────────────────────────┬───────────────────┘
│ REST/HTTPS │ Supabase JS SDK
▼ ▼
┌──────────────────────────┐ ┌──────────────────────────┐
│ FastAPI Backend │ │ Supabase │
│ (Render Cloud) │ │ │
│ │ │ • Google OAuth (PKCE) │
│ ┌─────────────────────┐ │ │ • JWKS JWT verification │
│ │ JWT Auth (JWKS) │ │ │ • Auth state sync │
│ └─────────────────────┘ │ └──────────────────────────┘
│ ┌─────────────────────┐ │
│ │ 10 REST Routers │ │
│ │ expenses / income │ │
│ │ budgets / todos │ │
│ │ reminders / notes │ │
│ │ meetings / pomodoro│ │
│ │ user / finance │ │
│ └─────────────────────┘ │
│ ┌─────────────────────┐ │
│ │ PostgreSQL (SQLModel│ │
│ │ + Alembic) │ │
│ └─────────────────────┘ │
│ ┌─────────────────────┐ │
│ │ Finance APIs │ │
│ │ Alpha Vantage │ │
│ │ Yahoo Finance │ │
│ │ CoinGecko │ │
│ └─────────────────────┘ │
└──────────────────────────┘
| Feature | Description |
|---|---|
| Expense Tracker | Log expenses by category, amount, date, payment method, and notes. CSV import supported. |
| Income Tracker | Record income entries with descriptions and dates. |
| Budget Planner | Create category budgets with spend limits and periods. Auto-suggests categories from past expenses. |
| Todo List | Tasks with priority levels, categories, due dates, and completion tracking. |
| Reminders | Timestamped reminders with priority levels and push notifications. |
| Meetings Scheduler | Schedule and track meetings with Google Calendar integration. |
| Pomodoro Timer | Focus sessions (work/break intervals) with session history and stats. |
| Quick Notes + AI | Freeform notes with semantic search and on-device AI question answering. |
| Calculator | Utility calculator accessible from the tools bottom sheet. |
| Widget | Data Source |
|---|---|
| S&P 500 (SPY) live price | Alpha Vantage → Yahoo Finance fallback |
| NASDAQ (QQQ) live price | Alpha Vantage → Yahoo Finance fallback |
| Top gainers / losers | Alpha Vantage |
| Top 3 cryptocurrencies | CoinGecko (by market cap) |
The Quick Notes screen includes a fully on-device AI pipeline — no data leaves the device:
| Stage | Component | Details |
|---|---|---|
| Embedding | all-MiniLM-L6-v2 via ExecuTorch |
384-dimensional sentence vectors |
| Vector Store | OPSQLite (notes_vectors.db) |
Cosine similarity search, upsert/delete/prune |
| LLM | SmolLM2-135M (quantized) via ExecuTorch |
Streamed token generation, max 60 tokens |
| Hallucination filter | Custom post-processing | Clips at first sentence break + repeat-sentence detection |
| Relevance gate | Score threshold (≥ 0.13) | Skips LLM if no note passes the similarity threshold |
User question
│
▼
[Embed question] ──► 384-dim vector
│
▼
[Vector search] ──► Top-5 notes by cosine similarity
│
▼
[Relevance gate] ── score < 0.13? → "no relevant notes" fallback
│
▼
[Build prompt] ──► system + user message with top-3 note excerpts
│
▼
[SmolLM2 generate] ──► streamed tokens (max 60)
│
▼
[Hallucination trim] ──► first sentence only, repeat-loop cut
│
▼
Answer + source chips (clickable back to note)
| Technology | Version | Purpose |
|---|---|---|
| React Native | 0.81.5 | Mobile UI framework |
| Expo | ~54.0.23 | Build toolchain, OTA updates, EAS |
| Expo Router | ~6.0.14 | File-based navigation |
| React | 19.1.0 | UI library (new architecture enabled) |
| Supabase JS | ^2.108.1 | Auth + API client |
| expo-auth-session | ^7.0.8 | OAuth PKCE flow |
| expo-secure-store | ^15.0.7 | Encrypted JWT storage |
| react-native-executorch | ^0.9.0 | On-device ML inference (ExecuTorch) |
| @react-native-rag/executorch | ^0.9.0 | Embeddings + LLM wrappers |
| @react-native-rag/op-sqlite | ^0.9.0 | SQLite vector store |
| @op-engineering/op-sqlite | ^15.2.14 | Native SQLite with libSQL |
| @gorhom/bottom-sheet | ^5.2.6 | Tools bottom sheet |
| react-native-reanimated | ~4.1.1 | Animations |
| react-native-gesture-handler | ~2.28.0 | Swipe-up gesture for tools |
| Technology | Version | Purpose |
|---|---|---|
| Python | 3.11.9 | Runtime (pinned for Render) |
| FastAPI | 0.115.5 | REST API framework |
| SQLModel | 0.0.22 | ORM (built on SQLAlchemy + Pydantic) |
| Alembic | 1.14.0 | Database migrations |
| PostgreSQL | — | Production database |
| python-jose | 3.3.0 | JWT verification (ES256/RS256/HS256) |
| httpx | 0.27.2 | Async HTTP client for finance APIs |
| Supabase Python SDK | 2.10.0 | Server-side Supabase access |
| uvicorn | 0.32.1 | ASGI server |
| Service | Purpose |
|---|---|
| Supabase | Authentication (Google OAuth), JWKS endpoint |
| Render | FastAPI backend hosting (auto-deploy from render.yaml) |
| EAS Build | Android APK/AAB cloud builds |
| Google Play Store | App distribution |
ai-tools-app/
├── app/ # Expo Router screens (file-based routing)
│ ├── (tabs)/ # Tab navigator group
│ │ ├── _layout.js # Tab bar + floating tools button + swipe gesture
│ │ ├── index.js # Home screen
│ │ ├── hub.js # Market dashboard
│ │ ├── quick-notes.js # Notes + RAG AI interface
│ │ ├── expense-tracker.js
│ │ ├── income-tracker.js
│ │ ├── budget-planner.js
│ │ ├── todo-list.js
│ │ ├── reminders.js
│ │ ├── meetings-scheduler.js
│ │ ├── pomodoro-timer.js
│ │ ├── calculator.js
│ │ ├── profile.js
│ │ ├── settings.js
│ │ └── recent-activity.js
│ ├── auth/callback.js # OAuth deep-link handler
│ ├── sign-in.js # Google sign-in screen
│ └── _layout.js # Root layout (AuthProvider, context providers)
│
├── src/
│ ├── context/ # React Context state management
│ │ ├── AuthContext.js # Supabase session, Google OAuth flow
│ │ ├── ExpenseContext.js
│ │ ├── IncomeContext.js
│ │ ├── BudgetContext.js # Budget + category auto-suggest
│ │ ├── TodoContext.js
│ │ ├── RemindersContext.js
│ │ ├── MeetingsContext.js
│ │ ├── QuickNotesContext.js
│ │ ├── PomodoroContext.js
│ │ ├── ThemeContext.js
│ │ └── UserContext.js
│ ├── services/
│ │ ├── supabaseClient.js # Supabase client + ChunkedSecureStore adapter
│ │ ├── apiClient.js # Authenticated fetch wrapper (attaches JWT)
│ │ ├── api.js # Domain API methods
│ │ ├── notificationService.js
│ │ ├── calendarAuth.js # Google Calendar OAuth
│ │ └── ai/
│ │ ├── embeddings.js # all-MiniLM-L6-v2 singleton (ExecuTorch)
│ │ ├── vectorStore.js # OPSQLite CRUD + backfill + orphan prune
│ │ ├── ragAnswer.js # Full RAG pipeline (retrieve → augment → generate)
│ │ └── tools.js # Tool router (keyword intent → tool dispatch)
│ ├── components/ # Shared UI components
│ ├── constants/ # Colors, spacing, theme tokens
│ ├── hooks/ # Custom hooks (CSV parser, Pomodoro stats)
│ └── types/
│
├── server/ # FastAPI backend
│ └── app/
│ ├── main.py # FastAPI app + CORS + router registration
│ ├── auth.py # JWKS-based JWT verification (get_current_user)
│ ├── database.py # SQLModel engine + session dependency
│ ├── models.py # SQLModel table definitions (9 models)
│ ├── schemas.py # Pydantic request/response schemas
│ └── routers/ # One router per resource
│ ├── expenses.py
│ ├── income.py
│ ├── budgets.py
│ ├── todos.py
│ ├── reminders.py
│ ├── meetings.py
│ ├── notes.py
│ ├── pomodoro.py
│ ├── user.py
│ └── finance.py # Market data (Alpha Vantage / Yahoo / CoinGecko)
│
├── render.yaml # Render deployment config
├── eas.json # EAS Build profiles (development/preview/production)
├── app.json # Expo config (bundle IDs, permissions, plugins)
├── playstore-deploy.sh # Automated Play Store deployment script
└── docs/ # Development guides and checklists
| Model | Key Fields |
|---|---|
Expense |
user_id, amount, category, description, date, method, notes |
Income |
user_id, amount, date, description |
Budget |
user_id, category, limit, period, color |
Todo |
user_id, title, description, completed, priority, category, due_date |
Reminder |
user_id, title, description, completed, due_date, priority |
Meeting |
user_id, title, start, end, organizer, description |
Note |
user_id, text |
PomodoroSession |
user_id, start, end, type, completed |
UserProfile |
user_id (unique), name |
All models use UUID primary keys. Every record is scoped to user_id — the backend extracts this from the Supabase JWT, never from the request body.
All endpoints (except /health) require a Bearer <supabase_jwt> header. The backend fetches JWKS from Supabase’s well-known endpoint and verifies the token locally.
| Method | Path | Description |
|---|---|---|
GET |
/health |
Health check |
GET/POST |
/api/expenses |
List / create expenses |
PUT/DELETE |
/api/expenses/{id} |
Update / delete expense |
GET/POST |
/api/income |
List / create income |
PUT/DELETE |
/api/income/{id} |
Update / delete income |
GET/POST |
/api/budgets |
List / create budgets |
PUT/DELETE |
/api/budgets/{id} |
Update / delete budget |
GET/POST |
/api/todos |
List / create todos |
PUT/DELETE |
/api/todos/{id} |
Update / delete todo |
GET/POST |
/api/reminders |
List / create reminders |
PUT/DELETE |
/api/reminders/{id} |
Update / delete reminder |
GET/POST |
/api/meetings |
List / create meetings |
PUT/DELETE |
/api/meetings/{id} |
Update / delete meeting |
GET/POST |
/api/notes |
List / create notes |
PUT/DELETE |
/api/notes/{note_id} |
Update / delete note |
GET/POST |
/api/pomodoro |
List / create sessions |
GET/PUT |
/api/user/profile |
Get / upsert user profile |
GET |
/api/finance/indices |
US market indices (SPY, QQQ) |
GET |
/api/finance/stocks |
Top gainers / losers |
GET |
/api/finance/crypto |
Top 3 cryptos by market cap |
User taps "Continue with Google"
│
▼
supabase.auth.signInWithOAuth({ provider: 'google', ... })
│
▼
Supabase returns OAuth URL
│
▼
expo-web-browser opens auth session (in-app browser tab)
│
▼
Google authenticates → redirects to daily://auth/callback?code=...
│
▼
supabase.auth.exchangeCodeForSession(code)
│
▼
JWT stored in ChunkedSecureStore (handles tokens > 2048 bytes)
│
▼
AuthContext.session updated → app navigates to home screen
│
▼
All API requests include: Authorization: Bearer <jwt>
│
▼
FastAPI verifies JWT via Supabase JWKS (cached in memory)
expo-secure-store has a 2048-byte limit per key. Supabase JWTs exceed this limit. The custom ChunkedSecureStore adapter transparently splits values into 1900-byte chunks, storing them as key__0, key__1, … with a key__count meta-key. Reads reassemble chunks in order.
npm install
cp .env.example .env
Edit .env:
EXPO_PUBLIC_SUPABASE_URL=https://<your-project>.supabase.co
EXPO_PUBLIC_SUPABASE_ANON_KEY=<your-anon-key>
EXPO_PUBLIC_API_URL=http://<your-local-ip>:8000
When using an Android emulator, use your Mac’s LAN IP (e.g.
10.0.0.17) rather thanlocalhost— the emulator cannot reach the host machine vialocalhost.
cd server
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Create server/.env:
DATABASE_URL=postgresql://...
SUPABASE_URL=https://<your-project>.supabase.co
SUPABASE_ANON_KEY=<your-anon-key>
SUPABASE_SERVICE_ROLE_KEY=<your-service-role-key>
ALPHA_VANTAGE_API_KEY=<your-key>
FMP_API_KEY=<your-key>
Run database migrations:
cd server
alembic upgrade head
Start the server:
npm run server
# or directly:
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
npx expo start
# Press 'a' for Android emulator
The render.yaml at the repo root configures automatic deploys:
services:
- type: web
name: daily-api
runtime: python
rootDir: server
buildCommand: pip install -r requirements.txt
startCommand: uvicorn app.main:app --host 0.0.0.0 --port $PORT
healthCheckPath: /health
Push to the main branch → Render auto-builds and deploys. Environment variables are set in the Render dashboard.
# Production build
eas build --platform android --profile production
# Submit to Play Store
eas submit --platform android
Or use the automated deploy script:
./playstore-deploy.sh
The RAG pipeline runs entirely on-device using ExecuTorch — no user notes are ever sent to an external AI service. This means:
The trade-off is model capability: SmolLM2-135M is a 135M-parameter quantized model. The hallucination filter (sentence-level trimming + repeat-sentence detection) compensates for the small model’s tendency to loop or go off-topic.
Standard OAuth implicit flow is blocked on mobile due to the lack of a real browser. The app uses the PKCE (Proof Key for Code Exchange) flow: the authorization code is returned to the daily://auth/callback deep link and exchanged for a session token via supabase.auth.exchangeCodeForSession().
The FastAPI backend does not use the Supabase Python SDK for auth. Instead, it fetches the JWKS (JSON Web Key Set) from Supabase’s .well-known endpoint and verifies tokens locally using python-jose. The JWKS is cached in memory to avoid a network call on every request.
Each feature has its own React Context (e.g., ExpenseContext, TodoContext) rather than a single global store. This keeps state isolated, makes each context independently loadable, and avoids re-rendering unrelated UI on unrelated data changes.
Depends(get_current_user) — no endpoint is accidentally public.user_id is always extracted from the verified JWT, never from the request body — clients cannot spoof data ownership.expo-secure-store (iOS Keychain / Android Keystore), not AsyncStorage.| Version | Highlights |
|---|---|
| 1.3.2 | Current release |
| 1.3.1 | Bug fixes |
| 1.3.0 | RAG pipeline, on-device AI for Quick Notes |
| 1.2.0 | FastAPI backend + PostgreSQL, Google OAuth, finance dashboard |
| 1.1.0 | Budget auto-suggest, income tracker |
| 1.0.x | Initial release: expense tracker, todo, reminders, Pomodoro |