An agentic AI assistant that keeps your data on your phone.
Not a notification mirror. Not a chatbot. It reads your day, works out what matters, and proposes the next move — while every byte you own stays in SQLite on your own device.
┌──────────────────────────────────────────────┐
│ Thu 14 Aug · 08:42 │
│ │
│ Three things today. │
│ One of them is already late. │
│ │
│ NEXT UP │
│ DBMS · 09:00 · LT-3 │
│ │
│ DEADLINES │
│ ● DBMS Assignment 3 due in 6h │
│ ● OS lab record due in 2d │
│ │
│ Block 14:00–15:30 for DBMS Assignment 3? │
│ Because it's due in 6h and untouched. │
│ [ Review ] │
│ │
│ Read: 3 classes, 2 deadlines, 11 notifs │
└──────────────────────────────────────────────┘
Two things in that mock are the whole product thesis:
- It suggests, you approve. The agent never sends, posts, or deletes on its own. Every action is a card you tap.
- It shows its work. The last line names exactly what was read to produce the screen. If Recall can't explain where a claim came from, it doesn't make the claim.
Assistants alert you; they don't act. Knowledge workers switch apps ~1,200×/day and lose ~4 hours a week just reorienting. Meanwhile every "AI assistant" that could close that gap wants your calendar, your inbox and your notifications uploaded to someone else's server.
Recall is a bet that you can have the second thing without paying with the first. The MVP ships free to a student cohort to answer one question:
Do people come back to an agentic assistant every single day?
The bar is >60% week-4 retention. If it misses, the roadmap stops — that's
written into PRD.md as a kill criterion, not a stretch goal. The
long-term vision is a wrist-worn assistant, but no hardware gets built until the
habit loop is proven on a phone.
Most "private" apps ask you to trust a promise. This one is structured so the promise is hard to break by accident:
PHONE — owns every byte of user data server/ — stateless relay
┌──────────────────────────────────────────┐ ┌────────────────────────────────┐
│ UI (expo-router screens, glanceable) │ │ /api/health │
├──────────────────────────────────────────┤ │ /api/chat text / JSON │
│ Agent loop — EXECUTION half │ │ /api/agent tool selection │
│ builds a minimal context slice, │──▶ │
│ runs the chosen tool on local SQLite, │ │ prompts · failover chain │
│ applies the autonomy gate │ │ rate limit · provider keys │
├──────────────────────────────────────────┤ └───────────────┬────────────────┘
│ Services: notifications · calendar · │ ▼
│ voice · local alarms │ NVIDIA NIM / Groq / Gemini
│ Storage: SQLite (structured) · MMKV │
└──────────────────────────────────────────┘
network egress = ONLY the relay, and only the snippet a task needs
The agent loop is split, not moved. The server decides what to do; the phone does it. Tools only ever run against local SQLite, which means:
| Property | How it's enforced |
|---|---|
| No account, no sign-up, no user database | There is no database. server/ stores nothing between requests. |
| Your notes never leave the device | Only the snippet a single task needs is ever sent, and only to the relay. |
| The relay can't build a profile on you | It has no storage, and logs latency and token counts — never message content. |
| Nothing acts without you | Write actions route through an on-device confirmation gate before anything happens. |
| Consent withdrawal actually works | There's nothing server-side to purge, so "delete all" is genuinely all. |
The relay exists for exactly one reason: EXPO_PUBLIC_* environment variables
are inlined into the JS bundle and are extractable from a shipped APK. Provider
keys therefore cannot live in the app. They live in the server's environment,
and the app ships one EXPO_PUBLIC_API_URL and nothing else.
Built for India's DPDP Act 2023 + DPDP Rules 2025: plain-language consent before any processing, in-app export and delete, least-privilege permissions requested in context.
Pre-alpha. It builds, it runs, it has never been in a stranger's hands. No release, no Play Store listing, no APK to download yet.
| Area | State |
|---|---|
| Daily briefing (home screen) | Works. Derived synchronously from SQLite — renders fully with the network down. |
| Local storage — SQLite + MMKV | Works. 5 tables, PRAGMA user_version migrations, 11/11 self-checks. |
| DPDP consent gate + settings | Works. Export, delete, withdraw consent, per-capability autonomy. |
LLM relay (server/) |
Works. 3 endpoints, failover, rate limiting, 6/6 tests. Not yet deployed. |
| Services — notifications, calendar, voice, alarms | Written and exercised; real permission grants unverified on hardware. |
| Agent loop — the on-device half | Not built. Server-side planner exists; the dispatcher and 8 tools don't. |
| Features F2–F7 | Not built. |
| Retention instrumentation | Not built. |
Honest caveats: there are no unit tests for the app (dev screens at /db-test,
/llm-test, /briefing-test, /services-test serve as the suite), there's no
CI yet, and it's been run on exactly one phone — a Xiaomi 23124RN87I on
Android 15.
You need: Node 22.18+, a JDK, the Android SDK, and an Android device or emulator. Expo Go will not work — MMKV and the notification listener are native modules, so you need a dev build.
git clone https://github.com/trh-ds/recall.git
cd recall
npm install # postinstall runs patch-package — see CONTRIBUTING.md
cp .env.example .env # then set EXPO_PUBLIC_API_URL
npx expo run:androidThe relay is a separate npm project with zero runtime dependencies:
cd server
npm install
cp .env.example .env.local # add at least one provider key
npm test # node --test, no network needed
node --env-file=.env.local dev.mjsThen point the app at it in .env:
EXPO_PUBLIC_API_URL=http://10.0.2.2:3000 # Android emulator
EXPO_PUBLIC_API_URL=http://192.168.x.x:3000 # physical device, your LAN IPFree tiers are enough to develop against: NVIDIA NIM, Groq, Google AI Studio. Any one of the three is sufficient — the failover chain skips providers with no key.
The app runs without the relay. The briefing makes no LLM call at all, by design, so you can work on most of the UI with no keys whatsoever.
src/
app/ expo-router screens (+ /db-test, /llm-test, /briefing-test, /services-test)
features/ briefing (the anchor habit), consent gate
db/ SQLite schema + migrations + typed queries, MMKV settings
llm/ thin client to the relay — the single network egress point
services/ notifications, calendar, voice, local alarms, permission flow
components/ glanceable primitives (Card, Eyebrow, Segmented, Button)
constants/ the design system — colour tokens, surface ladder, type scale
server/
api/ health.ts, chat.ts, agent.ts — 1 file = 1 serverless function
lib/ providers, failover, JSON repair, rate limit, request guards
contract.ts wire types, mirrored at src/llm/contract.ts, versioned
plugins/ Expo config plugin (works around a Windows NDK path bug)
patches/ patch-package fixes for an unmaintained native dependency
Contributions are genuinely wanted, and the interesting work is unclaimed. The agent loop — the thing that makes this an agentic assistant rather than a pretty to-do list — is a blank page with a well-defined shape waiting for it.
Start with CONTRIBUTING.md. It covers setup, the traps that
will otherwise cost you an evening, and what a good PR looks like here.
Good places to start, roughly by size:
| Size | Where |
|---|---|
| Small | Any single agent tool — getNextClass, listDeadlines, captureNote. Each is a typed function over SQLite with a clear contract. |
| Small | Test coverage for src/features/briefing.ts — pure formatting logic, no renderer needed. |
| Small | A CI workflow running npm run typecheck, npm run lint, and server/npm test. |
| Medium | The on-device dispatcher: trigger → planAction() → run tool → glanceable result. |
| Medium | F2 deadline reminders, F5 timetable "what's next", F3 voice quick-capture. |
| Large | F6 PDF summarising — needs on-device text extraction (the relay has a 4.5 MB body cap). |
| Large | iOS support. The app is Android-first; nothing in the architecture forbids iOS, but nobody has tried. |
Two rules worth knowing before you open a PR, because they're the ones that get things rejected:
server/never gets a database, a session, or any memory of a user. It relays and forgets. That's the product, not an implementation detail.- Nothing acts without explicit confirmation. Anything that sends, posts, or deletes goes through the autonomy gate.
Both live in full in AGENT.md.
| Doc | What's in it |
|---|---|
AGENT.md |
Architecture, golden rules, tech stack, agent loop design. Read this before writing code. |
PRD.md |
Product spec, feature list F1–F7, target users, success metric, monetisation and the go/no-go gate. |
TODO.md |
Live build status by phase, and every trap already hit so you don't re-hit it. |
CONTRIBUTING.md |
Setup, workflow, code conventions, PR checklist. |
SECURITY.md |
Reporting a vulnerability, and the threat model this project actually holds itself to. |
DESIGN.md |
The design system, and how to change it without breaking glanceability. |
AGENT.md and PRD.md are also loaded automatically by
Claude Code via CLAUDE.md, so an AI
assistant working in this repo gets the same constraints you do.
Apache-2.0. Contributions are accepted under the same terms.