┌─────────────┐ ┌──────────────┐ ┌─────────────────┐
│ Vercel │────▶│ Railway │────▶│ Supabase │
│ (Next.js) │ WS │ (Go API) │ │ (PostgreSQL + │
│ prism-web/ │◀────│ prism/ │ │ pgvector) │
└─────────────┘ └──────┬───────┘ └─────────────────┘
│
┌──────┴───────┐
│ External │
│ APIs │
├──────────────┤
│ OpenAI │ TTS, GPT-4o analysis, embeddings
│ Deepgram │ Speech-to-text
└──────────────┘
Frontend (Vercel): Next.js 15 + React 19 + Tailwind v4 + Framer Motion Backend (Railway): Go HTTP/WebSocket server, eino LLM orchestration Database (Supabase): PostgreSQL 15+ with pgvector for semantic search
| Tool | Purpose | Install |
|---|---|---|
railway |
Backend deploy | npm i -g @railway/cli |
vercel |
Frontend deploy | npm i -g vercel |
supabase |
Database setup | brew install supabase/tap/supabase |
jq |
JSON parsing | brew install jq / apt install jq |
go >= 1.21 |
Backend build | golang.org/dl |
node >= 18 |
Frontend build | nodejs.org |
pnpm |
Package manager | npm i -g pnpm |
| Key | Required | Purpose |
|---|---|---|
OPENAI_API_KEY |
Recommended | TTS voice output, GPT-4o post-session analysis, pgvector embeddings |
DEEPGRAM_API_KEY |
Optional | Voice input (speech-to-text). Without it, text-only mode |
The server starts in degraded mode without API keys — all features degrade gracefully.
./scripts/setup.shThis interactive script walks through:
- Prerequisites check
- API key collection with format validation
- Supabase project creation + pgvector
- Railway backend deployment with env vars
- Vercel frontend deployment with env vars
- Health endpoint verification
- Summary with all URLs
# Create project
supabase projects create prism --region us-east-1
# Get connection string from Dashboard → Settings → Database
# Format: postgres://postgres.[ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres
# Enable pgvector (run in SQL Editor)
CREATE EXTENSION IF NOT EXISTS vector;Migrations run automatically on server startup.
cd prism
# Initialize and link
railway login
railway init --name prism-backend
# Set environment variables
railway variables set \
DATABASE_URL="postgres://..." \
OPENAI_API_KEY="sk-..." \
DEEPGRAM_API_KEY="..." \
PORT=8080
# Deploy
railway up
# Get the public URL
railway domainThe Go server auto-runs migrations on startup and initializes all available services.
cd prism-web
# Install dependencies
pnpm install
# Link and configure
vercel link
vercel env add BACKEND_URL production # Railway URL (https://...)
vercel env add NEXT_PUBLIC_API_URL production # Same Railway URL
# Deploy
vercel --prodThe Next.js app proxies /api/* and /ws/* to the backend via rewrites in next.config.ts.
# Health check (should show all services)
curl https://your-railway-url.up.railway.app/api/health | jq
# Expected response:
# {
# "status": "ok",
# "service": "prism",
# "services": {
# "database": true,
# "tts": true,
# "stt": true,
# "embeddings": true,
# "analyzer": true
# }
# }For reproducible infrastructure provisioning:
cd infra/terraform
# Copy and fill in variables
cp terraform.tfvars.example terraform.tfvars
# Edit terraform.tfvars with your values
terraform init
terraform plan
terraform applyThis provisions Supabase and Vercel. Railway is deployed via CLI (community Terraform provider is immature).
See infra/terraform/ for full configuration.
For self-hosting on a Mac Mini with minikube + Cloudflare Tunnels:
cd infra/k8s
# Automated setup
./scripts/local-setup.sh
# Or manual:
minikube start --driver=docker --cpus=4 --memory=8g
minikube addons enable ingress
# Create .env file with your secrets
cat > .env << 'EOF'
DATABASE_URL=postgres://postgres:password@postgres.prism.svc.cluster.local:5432/prism?sslmode=disable
OPENAI_API_KEY=sk-...
DEEPGRAM_API_KEY=...
POSTGRES_PASSWORD=your-secure-password
EOF
kubectl create namespace prism
kubectl create secret generic prism-secrets --from-env-file=.env -n prism
kubectl apply -k .See infra/k8s/ for full manifests and Cloudflare Tunnel configuration.
| Variable | Service | Required | Description |
|---|---|---|---|
DATABASE_URL |
Backend | Yes | PostgreSQL connection string |
OPENAI_API_KEY |
Backend | Recommended | Enables TTS, analysis, embeddings |
DEEPGRAM_API_KEY |
Backend | Optional | Enables voice input (STT) |
PORT |
Backend | No | HTTP port (default: 8080) |
BACKEND_URL |
Frontend | Yes | Backend URL for Next.js rewrites |
NEXT_PUBLIC_API_URL |
Frontend | Production | Direct backend URL (bypasses rewrites) |
NEXT_PUBLIC_BASE_URL |
Frontend | No | Frontend URL for OpenGraph tags |
-
curl $BACKEND/api/healthreturns{"status":"ok"} -
services.databaseistrue - Frontend loads at Vercel URL
- Can start an interview session (WebSocket connects)
- Voice input works (if Deepgram key set)
- TTS audio plays (if OpenAI key set)
- Dashboard shows sessions
- Topics page shows mastery heatmap
- Ensure
NEXT_PUBLIC_API_URLis set in Vercel env vars - Railway must have a public domain assigned (
railway domain) - Check CORS: backend allows all origins by default
- Verify
DATABASE_URLformat includes?sslmode=requirefor Supabase - Check Railway logs:
railway logs - Ensure pgvector extension is enabled:
SELECT * FROM pg_extension WHERE extname = 'vector';
- TTS requires
OPENAI_API_KEY— check for "TTS client initialized" in logs - STT requires
DEEPGRAM_API_KEY— check for "Deepgram STT configured" in logs - Browser must grant microphone permission
- Mobile Safari requires user gesture to unlock AudioContext
- Check which
servicesarefalse - Missing API keys cause graceful degradation, not failure
- Only
database: falseindicates a real problem
- Must run from
prism-web/directory - Requires
pnpm installfirst - Next.js 15 requires Node.js >= 18
# Start PostgreSQL
docker compose up -d
# Start backend (auto-runs migrations)
cd prism && go run ./cmd/server
# Start frontend (proxies to localhost:8080)
cd prism-web && pnpm dev