A save-it-for-later app for the things you want to come back to. Drop in a link, an image, or a note and Amber fetches the content, writes a title, a short summary, and tags it — then files it into the right collection for you. Everything shows up as a warm masonry feed.
Built with Expo (SDK 58) + Convex backend + AI classification via AI SDK.
- Capture anything — links, photos, screenshots, and plain notes, from inside the app (share into Amber from any other app).
- Automatic organization — every saved item is analyzed by an LLM that generates a title, a one-line description, and tags, and sorts it into matching spaces (themed collections).
- Smart spaces — create a new space and Amber retroactively pulls in your existing items that belong in it.
- Full-text search across everything you've saved.
- Subject stickers lift the subject out of a photo into a transparent die-cut sticker.
- Offline-first feel — the feed is persisted locally and paints instantly on cold launch, then syncs live.
- Adaptive light/dark theming with a warm amber palette.
| Layer | What |
|---|---|
| App | Expo SDK 58, React Native 0.88 (New Architecture), Expo Router (typed) |
| Backend | Convex — reactive DB, file storage, server actions |
| Auth | Clerk |
| AI | Vercel AI SDK → Gemini 3.1 Flash-Lite via the AI Gateway |
| Data/cache | TanStack Query + Convex adapter, persisted to MMKV |
| UI | react-native-unistyles, Reanimated, Skia, FlashList, Vision Camera |
| Native | Local Expo modules (subject lift, progressive blur) + a share extension |
- Bun (this project uses bun, not npm/yarn)
- Xcode (iOS) and/or Android Studio — Amber uses custom native modules, so it runs in a dev client, not Expo Go.
- Accounts: a Convex project and a Clerk app.
bun installCreate a .env file in the project root:
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
EXPO_PUBLIC_CONVEX_URL=https://<your-deployment>.convex.cloudSet the backend secrets on your Convex deployment (Convex dashboard or bunx convex env set):
bunx convex env set AI_GATEWAY_API_KEY <your-vercel-ai-gateway-key>
bunx convex env set CLERK_JWT_ISSUER_DOMAIN https://<your-clerk-instance>.clerk.accounts.devconvex/auth.config.ts reads the Clerk JWT issuer from CLERK_JWT_ISSUER_DOMAIN, so set it per
deployment (dev and prod each get their own instance's domain). The Clerk JWT template must use
applicationID: "convex".
bunx convex devThis deploys the functions in convex/, watches for changes, and keeps convex/_generated/
in sync.
bunx expo run:ios # or: bunx expo run:androidThis builds the dev client and starts Metro. For subsequent runs you can just start Metro:
bun startAmber declares its App Intents in app-intents/ and connects them to the signed-in app
through src/lib/app-intents.tsx. See Expo's SDK 58 App Intents docs
and introduction.
Build with Xcode 27 for the iOS 27 Notes schema and shortcut phrases. Image and link actions
also work in Shortcuts on iOS 18+. Swift changes require a new native build. Deploy the Convex
schema and functions before distributing this build so the captureReceipts table and replay
mutation are available.
Native captures save without opening Amber. Failed captures wait until you open and unlock the app. Retries share a request ID, so a lost response does not create another save. Enabling the app lock removes published saves and spaces from Siri and Spotlight. Signing out also removes capture credentials and pending captures on this device.
Run bun run test:convex for capture replay and authorization tests. Before release, test
Siri and Shortcuts with the app running and closed, including an offline capture followed by
reopening Amber. Also run a populated shortcut saved by the previous build after upgrading.
| Command | What it does |
|---|---|
bun start |
Start the Metro dev server |
bunx expo run:ios |
Build and run the iOS dev client |
bunx expo run:android |
Build and run the Android dev client |
bunx convex dev |
Run/deploy the Convex backend and codegen |
bun run lint |
Lint (ESLint, eslint-config-expo) |
bunx tsc --noEmit |
Typecheck (strict) |
convex/ Backend: schema, queries/mutations, AI processing pipeline
schema.ts items, spaces, spaceItems tables
items.ts item CRUD + search
spaces.ts space CRUD + item membership
ai.ts content extraction + LLM classification (Node action)
model/auth.ts requireUserId — every function derives the user from Clerk
src/
app/ Expo Router routes (auth gate, native tabs, modals, detail screens)
components/ Masonry feed, item cards, detail view, etc.
lib/ Convex/TanStack client, onboarding, image saving
unistyles.ts Themes, palette, typography
modules/ Local native Expo modules
subject-lift/ iOS Vision subject cutout
progressive-blur/ SwiftUI gradient blur
When you save something, the client inserts an item as processing and Convex schedules a
background action. That action fetches the page (extracting the readable article with Mozilla
Readability), or reads the image/note, and asks the model for a title, description, tags, and
which of your spaces it belongs to. Once done the item flips to ready and streams into your
feed live. Space names returned by the model are mapped back to real spaces, and creating a new
space triggers a pass over your existing items to backfill it.
Auth is enforced server-side: every Convex function resolves the user from the Clerk identity, never from a client-supplied id.
See LICENSE.