WIP: Photon Trail is actively under development. The core architecture is in place (auth, Plaid ingestion, DB, AI categorization), but features and polish are still evolving.
An AI-assisted personal finance dashboard: connect accounts with Plaid, store transactions in Neon/Postgres, and use OpenAI to suggest clean, human-friendly categories with confidence scores.
Add your images to docs/screenshots/ and replace these placeholders.
- Plaid Link connection flow (no access tokens sent to the client).
- Postgres-backed persistence (Neon) with Drizzle ORM + migrations.
- Google OAuth sign-in (NextAuth) with DB sessions.
- AI categorization endpoint that validates responses with Zod before writing suggestions.
- Mobile-first dashboard UX with filters and inline category overrides.
- Framework: Next.js 14 App Router (React 18, TypeScript)
- UI: Tailwind CSS +
@stargazers-stella/cosmic-ui+sonner - Data: Neon Postgres + Drizzle ORM +
drizzle-kit - Auth: NextAuth (Google) + Drizzle adapter
- Integrations: Plaid (
react-plaid-link+ server routes) - AI: OpenAI SDK (
openai) - Fetching: SWR
- Testing: Vitest
- User signs in with Google (NextAuth + DB sessions).
- Plaid Link creates a link token; the client completes linking and returns a public token.
- Server exchanges the public token and stores an encrypted access token at rest.
- A sync route pulls transactions incrementally using Plaid cursors.
- An AI route batches transactions, requests category suggestions, validates JSON via Zod, and upserts suggestions.
Prereqs: Node 20+ recommended, a Postgres database (Neon or local), Plaid sandbox keys.
cd Photon-Trail
npm install
cp .env.example .env.localRun migrations + seed:
npm run db:generate
npm run db:migrate
npm run db:seedStart dev server:
npm run devOpen http://localhost:3000.
Core env vars (see .env.example):
| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL |
Yes | Postgres connection string (sslmode=require for Neon) |
NEXTAUTH_URL |
Yes | App base URL |
NEXTAUTH_SECRET |
Yes | Session encryption/signing |
GOOGLE_CLIENT_ID |
Optional | Google OAuth |
GOOGLE_CLIENT_SECRET |
Optional | Google OAuth |
PLAID_CLIENT_ID |
Optional | Plaid API |
PLAID_SECRET |
Optional | Plaid API |
PLAID_ENV |
Optional | sandbox/development/production |
PLAID_ENCRYPTION_KEY |
Yes (for Plaid) | Encrypt Plaid access tokens at rest (32+ chars) |
OPENAI_API_KEY |
Optional | Enable AI categorization |
OPENAI_MODEL |
Optional | Defaults to gpt-4o-mini |
AI_RATE_LIMIT_PER_MINUTE |
Optional | Simple throttling |
- App & routes:
app/ - Auth:
lib/auth.ts,app/api/auth/[...nextauth]/route.ts - DB:
lib/db.ts, schemadb/schema.ts, migrationsdrizzle/ - Plaid:
lib/plaid.ts, API routes underapp/api/plaid/* - AI:
lib/ai.ts,app/api/transactions/ai/route.ts - UI:
components/dashboard/*,components/plaid-link-button.tsx
- Never store Plaid access tokens in the client; this project encrypts them at rest server-side.
- Never commit
.env.local; use Vercel/hosted secrets in production.
- Generate migrations locally (
npm run db:generate) and commitdrizzle/. - Run migrations against production (
npm run db:migrate) beforenext build. - Set Plaid + OpenAI keys in Vercel, and ensure webhook URLs match your deployment domain.



