"We didn't change money. We changed trust."
A student financial platform where credibility is currency. Ask questions, share knowledge, earn trust — and the AI already knows who to believe.
Live: https://buddy-sand-six.vercel.app
- Go to your Supabase SQL Editor
- Copy and paste the entire contents of
supabase/migrations/001_initial_schema.sql - Click Run
That's it — all tables, views, functions, triggers, and RLS policies are created.
From Supabase Dashboard → Settings → API:
| Key | Where used |
|---|---|
anon / publishable key |
Frontend + backend .env |
service_role key |
Backend .env only — keep secret |
| JWT Secret | Backend .env only — keep secret |
cd backend
# Copy and fill env file
cp .env.example .env
# Edit .env — add SUPABASE_SERVICE_ROLE_KEY, SUPABASE_JWT_SECRET, ANTHROPIC_API_KEY
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Run
uvicorn main:app --reload --port 8000API docs available at: http://localhost:8000/docs
cd frontend
# Copy and fill env file
cp .env.example .env.local
# Edit .env.local — set NEXT_PUBLIC_API_URL
# Install dependencies
npm install
# Run
npm run devApp available at: http://localhost:3000
This project now includes a container-first setup:
backend/Dockerfilefor FastAPI (uvicornon port8000)frontend/Dockerfilemulti-stage build for Next.js (next starton port3000)docker-compose.ymlto run both services together.dockerignorefiles for frontend and backend
- Copy env templates if you haven't already:
cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.env.local- Fill required env values in:
backend/.envfrontend/.env.local
From repository root:
docker compose up --buildThen open:
- Frontend:
http://localhost:3000 - Backend:
http://localhost:8000
docker compose down- Keep one Docker image per service (frontend/backend).
- Build once in CI and tag images with commit SHA.
- Push images to a container registry.
- Deploy by updating image tag in your target environment (Render/Fly/Kubernetes/App Service/etc).
- Inject runtime secrets from platform secret manager (never bake secrets into images).
# Run both suites
python run_tests.py
# Run one suite only
python run_tests.py --backend
python run_tests.py --frontend
# Always exit 0 (print failures as warnings instead of errors)
python run_tests.py --warn-onlyBackend (pytest)
cd backend
pip install -r requirements-test.txt
python -m pytest -vFrontend (vitest)
cd frontend
npm test # single run
npm run test:watch # watch modeThis repository includes two workflows:
-
.github/workflows/ci.yml- Runs on pull requests and pushes to
main - Frontend: lint → test → build
- Backend: pytest →
pip check - Test failures emit a
::warning::annotation but do not block the job
- Runs on pull requests and pushes to
-
.github/workflows/cd.yml- Runs on pushes to
mainand manual dispatch - Quality gate: backend tests → frontend tests → frontend build (tests are warn-only)
- Triggers deployment hooks if secrets are configured
- Runs on pushes to
FRONTEND_DEPLOY_HOOK_URL(optional)BACKEND_DEPLOY_HOOK_URL(optional)
If these secrets are not set, deploy jobs will be skipped safely, while CI still validates code quality.
| Service | Platform | URL |
|---|---|---|
| Frontend (Next.js) | Vercel | https://buddy-sand-six.vercel.app |
| Backend (FastAPI) | Render | connect via render.yaml (see below) |
vercel.json at the repo root explicitly builds the frontend/ Next.js app in this monorepo. Set these in Vercel Dashboard → Project → Settings → Environment Variables:
| Variable | Notes |
|---|---|
NEXT_PUBLIC_SUPABASE_URL |
https://ewyenqtrkdzgckrncwvb.supabase.co |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
publishable key — safe to expose |
ANTHROPIC_API_KEY |
Secret — powers /api/advisor serverless route |
SUPABASE_SERVICE_ROLE_KEY |
Secret — lets advisor cache responses to DB |
NEXT_PUBLIC_API_URL |
URL of your Render backend once deployed (required for backend-powered pages) |
Why not Netlify? Netlify runs serverless functions only. FastAPI with uvicorn is a persistent server and cannot run on Netlify.
render.yaml at the repo root defines the backend web service. To deploy:
- Go to render.com → New → Blueprint
- Connect this repo — Render will detect
render.yamlautomatically - Fill in the secret env vars marked
sync: falsein the Render dashboard:SUPABASE_URL,SUPABASE_ANON_KEY,SUPABASE_SERVICE_ROLE_KEY,SUPABASE_JWT_SECRETANTHROPIC_API_KEYPINECONE_API_KEY(optional — omit to fall back to built-in UK finance snippets)REDIS_URL(optional — from Upstash free tier)
- Once deployed, paste the Render URL into Vercel's
NEXT_PUBLIC_API_URLenv var
buddy/
├── supabase/
│ └── migrations/
│ └── 001_initial_schema.sql ← Run this in Supabase SQL Editor
│
├── run_tests.py ← Run all tests locally (pytest + vitest)
│
├── backend/ ← FastAPI (Python)
│ ├── main.py
│ ├── requirements.txt
│ ├── requirements-test.txt ← Adds pytest + pytest-asyncio
│ ├── pytest.ini
│ ├── .env.example
│ ├── core/
│ │ ├── auth.py ← JWT verification (Supabase tokens)
│ │ ├── config.py ← Settings from .env
│ │ └── database.py ← Supabase client singletons
│ ├── routers/
│ │ ├── auth.py ← /auth/* — profile, onboarding
│ │ ├── posts.py ← /posts/* — questions
│ │ ├── answers.py ← /answers/* — responses
│ │ ├── votes.py ← /votes/* — credibility-weighted voting
│ │ ├── credibility.py ← /credibility/* — scores, history, leaderboard
│ │ ├── challenges.py ← /challenges/* — financial challenges
│ │ ├── goals.py ← /goals/* — savings goals
│ │ ├── tools.py ← /tools/* — utility endpoints
│ │ └── rag.py ← /rag/* — ingest, retrieve, status
│ ├── services/
│ │ ├── credibility_engine.py ← Single source of truth for all cred mutations
│ │ ├── decay.py ← Credibility decay logic
│ │ ├── rag.py ← Pinecone vector search + Claude Haiku sentiment
│ │ └── document_sources.py ← UK financial document fetcher + chunker
│ ├── agents/
│ │ ├── advisor.py ← AI Advisor Agent (Claude)
│ │ └── fact_checker.py ← AI Fact Checker Agent
│ └── tests/
│ ├── conftest.py ← Env var setup for pytest
│ ├── test_credibility_engine.py
│ └── test_rag_service.py
│
└── frontend/ ← Next.js 15 (App Router)
├── __tests__/
│ └── api.test.ts ← API client unit tests (vitest)
├── vitest.config.ts
├── app/
│ ├── page.tsx ← Landing page
│ ├── login/page.tsx ← Magic link login
│ ├── onboarding/page.tsx ← 3-step onboarding wizard
│ ├── auth/
│ │ └── callback/route.ts ← Supabase auth callback
│ ├── api/
│ │ ├── advisor/route.ts ← Serverless AI advisor endpoint
│ │ └── seed/route.ts ← Demo data seeding endpoint
│ └── (app)/ ← Authenticated app shell
│ ├── layout.tsx ← App layout with sidebar/nav
│ ├── dashboard/page.tsx ← Main dashboard
│ ├── feed/
│ │ ├── page.tsx ← Question feed
│ │ └── [id]/page.tsx ← Individual post view
│ ├── ask/page.tsx ← Ask a question
│ ├── leaderboard/page.tsx ← Credibility leaderboard
│ ├── profile/page.tsx ← User profile
│ ├── challenges/page.tsx ← Financial challenges
│ ├── goals/page.tsx ← Savings goals
│ ├── budget/page.tsx ← Budget tracker
│ ├── transactions/page.tsx ← Transaction history
│ ├── accounts/page.tsx ← Connected accounts
│ ├── check/page.tsx ← Fact-check tool
│ └── origin/page.tsx ← Origin story / about
├── components/
│ ├── AnonAuthProvider.tsx ← Anonymous auth context
│ ├── CredibilityBadge.tsx ← Score badge with tier colours
│ ├── layout/
│ │ ├── AppShell.tsx ← App layout wrapper
│ │ ├── Sidebar.tsx ← Navigation sidebar
│ │ └── TopNav.tsx ← Top navigation bar
│ └── ui/
│ ├── Badge.tsx
│ ├── Button.tsx
│ ├── Card.tsx
│ ├── CategoryChip.tsx
│ ├── Modal.tsx
│ ├── ProgressBar.tsx
│ └── Skeleton.tsx
├── lib/
│ ├── api.ts ← Typed backend API client
│ ├── utils.ts ← Shared utility functions
│ └── supabase/
│ ├── client.ts ← Browser client
│ ├── server.ts ← Server component client
│ └── posts.ts ← Post-related Supabase queries
├── middleware.ts ← Session refresh + route protection
└── types/
└── database.ts ← TypeScript types matching DB schema
User enters email → Supabase sends magic link
→ User clicks link → /auth/callback route
→ Code exchanged for session → JWT stored in cookies
→ Middleware refreshes session on every request
→ Backend verifies JWT using SUPABASE_JWT_SECRET
→ Backend uses service_role key for all DB writes
| Tier | Score | Badge |
|---|---|---|
| Newcomer | 0–99 | Grey |
| Learner | 100–299 | Blue |
| Contributor | 300–599 | Green |
| Trusted | 600–899 | Gold |
| Advisor | 900–1199 | Purple |
| Oracle | 1200+ | Cyan |
All credibility changes are recorded as immutable events in credibility_events.
The AI Advisor explicitly weights answers by author credibility and tells users why.
- SQL Editor
- Table Editor
- Auth Users
- API Settings
- Auth Settings — enable Email (magic link)