Spaces:
Sleeping
Deployment Guide β CourtMitra MissionOS
Target: Vercel (frontend) + Hugging Face Spaces (backend + worker) + Google OAuth (Clerk) + Neon (Postgres)
Architecture Overview
βββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββββββββ
β Vercel (Next.js 15) ββββββββββΆβ Hugging Face Spaces (Docker) β
β - Clerk auth β HTTPS β - NestJS API + Inngest Worker β
β - Hero page β β - Port 7860 β
β - Profile page β β β
β - Mission dashboard β β /api/* β API routes β
β - Static assets β β /api/inngest β Worker functions β
βββββββββββββββββββββββββββ ββββββββββββββββββββββ¬ββββββββββββββββββββββ
β
β postgres://...
βΌ
ββββββββββββββββββββββββββββββ
β Neon Postgres (Cloud) β
β - 55 tables β
β - pgvector + FTS β
β - Legal chunks seeded β
ββββββββββββββββββββββββββββββ
Env Files (Where Secrets Live)
Crucial for your partner: There are TWO separate env files. One is gitignored (real secrets), the other is a committed template.
| File | In Git? | Where | Purpose |
|---|---|---|---|
apps/web/.env.local |
β NO β gitignored | Your laptop / partner's laptop | Frontend local dev β Clerk keys + API URL. You MUST create this and fill in real keys. |
apps/web/.env.local.example |
β YES | In repo as template | Shows exactly what .env.local should contain. |
.env |
β NO β gitignored | Your laptop / partner's laptop | Backend local dev β DB, AI, Inngest, secrets. Copy from .env.example and fill in. |
.env.example |
β YES | In repo as template | Comprehensive backend env template. |
What your partner needs to do before pnpm dev:
- Create
apps/web/.env.local(copy fromapps/web/.env.local.example) - Paste in
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYandCLERK_SECRET_KEYfrom Clerk dashboard - Create
.envat repo root (copy from.env.example) - Fill in
DATABASE_URL,OPENROUTER_API_KEY, etc.
Prerequisites
| Service | Purpose | Free Tier |
|---|---|---|
| Vercel | Frontend hosting | Hobby (unlimited) |
| Clerk | Auth + Google OAuth | 10,000 MAU |
| Hugging Face Spaces | Backend API + Worker | CPU Basic (free) |
| Neon | Postgres database | 500 MB, 200 hrs |
| Inngest | Background workflows | 50k events/mo |
| OpenRouter | LLM API | $5 free credits |
Step 1: Clerk + Google OAuth (10 min)
- Go to dashboard.clerk.com β Create Application.
- Social Connections β enable Google.
- Development: toggle on (uses Clerk's shared OAuth credentials).
- Production: add your own Google Client ID/Secret.
- Copy keys from API Keys tab:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...CLERK_SECRET_KEY=sk_test_...
- Add your Vercel domain to URLs β Allowed Origins:
https://your-project.vercel.apphttp://localhost:3000(for local dev)
Step 2: Neon Postgres (10 min)
- neon.tech β New Project β copy connection string.
- Run migrations from your local machine:
export DATABASE_URL="postgres://courtmitra:password@host.neon.tech/courtmitra" export DIRECT_DATABASE_URL="$DATABASE_URL" pnpm db:migrate pnpm db:seed - Verify: check that
users,missions,route_rules,legal_chunkstables exist.
Step 3: Frontend β Vercel (5 min)
Push repo to GitHub.
Vercel β Add New Project β import repo.
Set Environment Variables:
Variable Value Notes NEXT_PUBLIC_API_BASE_URLhttps://your-hf-space.hf.space/apiYour HF Spaces URL INTERNAL_API_BASE_URLhttps://your-hf-space.hf.space/apiSame as above NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYpk_test_...From Clerk dashboard CLERK_SECRET_KEYsk_test_...From Clerk dashboard NEXT_PUBLIC_CLERK_SIGN_IN_URL/en/sign-inOr /hi/sign-inNEXT_PUBLIC_CLERK_SIGN_UP_URL/en/sign-upOr /hi/sign-upDeploy.
Step 4: Backend β Hugging Face Spaces (20 min)
4a. Create Space
- huggingface.co/spaces β Create new Space.
- Space name:
courtmitra-api - SDK:
Docker - Visibility: Public
- Hardware: Free CPU
4b. Link GitHub Repo
In Space Settings β Repository β link your GitHub repo.
The Dockerfile.hf at repo root builds the monorepo and runs API + Worker in one container.
4c. Set Environment Variables
In Space β Settings β Variables & Secrets:
Required (App won't start without these)
PORT=7860
API_PORT=7860
DATABASE_URL=postgres://courtmitra:password@host.neon.tech/courtmitra
DIRECT_DATABASE_URL=postgres://courtmitra:password@host.neon.tech/courtmitra
APP_BASE_URL=https://your-hf-space.hf.space
API_BASE_URL=https://your-hf-space.hf.space
WEBHOOK_BASE_URL=https://your-hf-space.hf.space
JWT_SECRET=change-me-32-chars-minimum!!
PII_HASH_SALT=change-me-salt
TOKEN_ENCRYPTION_KEY=change-me-32-chars-minimum!!
Required for AI (Intake, route-plan, evidence parsing)
AI_PROVIDER=openrouter
OPENROUTER_API_KEY=sk-or-v1-...
AI_MODEL=google/gemini-2.0-flash-exp:free
AI_VISION_MODEL=google/gemini-2.0-flash-exp:free
Clerk (for JWT verification)
CLERK_SECRET_KEY=sk_test_...
# Optional: for networkless JWT verification (faster, no Clerk API call per request)
CLERK_JWT_KEY=...
# Optional: restrict which domains can use your API
CLERK_AUTHORIZED_PARTIES=https://your-project.vercel.app,http://localhost:3000
Optional (Email actions β Resend)
RESEND_API_KEY=re_...
RESEND_FROM=noreply@yourdomain.com
Optional (Inngest background workflows)
INNGEST_EVENT_KEY=...
INNGEST_SIGNING_KEY=...
INNGEST_BASE_URL=https://api.inngest.com
Optional (Local dev helper β NOT for production)
DEMO_MODE=true
β οΈ Never set
DEMO_MODE=truein production. It bypasses all auth.
Optional (Object storage β for evidence files)
OBJECT_STORAGE_ENDPOINT=https://...
OBJECT_STORAGE_BUCKET=courtmitra
OBJECT_STORAGE_ACCESS_KEY=...
OBJECT_STORAGE_SECRET_KEY=...
If not set, evidence files are stored locally in
/app/.storage(lost on Space restart).
Optional (Other channels)
WHATSAPP_VERIFY_TOKEN=...
WHATSAPP_APP_SECRET=...
WHATSAPP_ACCESS_TOKEN=...
WHATSAPP_PHONE_NUMBER_ID=...
TELEGRAM_BOT_TOKEN=...
Optional (Phase 2 features β not needed for hackathon)
QDRANT_URL=https://...
QDRANT_API_KEY=...
QDRANT_COLLECTION=courtmitra_legal
OPENAI_API_KEY=...
BROWSERBASE_API_KEY=...
BROWSERBASE_PROJECT_ID=...
SUREPASS_API_KEY=...
ENABLE_PORTAL_AUTOMATION=1
4d. Verify
Once built, test:
curl https://your-space.hf.space/api/admin/health
# Expected: {"status":"ok","ts":"..."}
curl https://your-space.hf.space/api/capabilities
# Expected: JSON with adapter capabilities
curl https://your-space.hf.space/api/inngest
# Expected: Inngest registration JSON
Step 5: Wire Frontend β Backend
- In Vercel dashboard, update:
NEXT_PUBLIC_API_BASE_URL=https://your-hf-space.hf.space/api - Redeploy frontend.
Complete Environment Variable Reference
Vercel (Frontend)
| Variable | Required | Description |
|---|---|---|
NEXT_PUBLIC_API_BASE_URL |
β | HF Spaces API URL |
INTERNAL_API_BASE_URL |
β | Same as above (for SSR) |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY |
β | Clerk public key |
CLERK_SECRET_KEY |
β | Clerk secret key |
NEXT_PUBLIC_CLERK_SIGN_IN_URL |
β | /en/sign-in |
NEXT_PUBLIC_CLERK_SIGN_UP_URL |
β | /en/sign-up |
Hugging Face Spaces (Backend)
| Variable | Required | Description |
|---|---|---|
PORT |
β | Must be 7860 |
API_PORT |
β | Must be 7860 |
DATABASE_URL |
β | Neon connection string |
DIRECT_DATABASE_URL |
β | Same as DATABASE_URL |
APP_BASE_URL |
β | HF Spaces URL |
API_BASE_URL |
β | Same |
WEBHOOK_BASE_URL |
β | Same |
JWT_SECRET |
β | Min 32 chars |
PII_HASH_SALT |
β | Any string |
TOKEN_ENCRYPTION_KEY |
β | Min 32 chars |
AI_PROVIDER |
β | openrouter or opencode |
OPENROUTER_API_KEY |
β | If AI_PROVIDER=openrouter |
AI_MODEL |
β | e.g. google/gemini-2.0-flash-exp:free |
AI_VISION_MODEL |
β | e.g. google/gemini-2.0-flash-exp:free |
CLERK_SECRET_KEY |
β | Same as Vercel |
CLERK_JWT_KEY |
β¬ | Optional, for faster JWT verification |
RESEND_API_KEY |
β¬ | For email actions |
RESEND_FROM |
β¬ | Sender email |
INNGEST_EVENT_KEY |
β¬ | For background workflows |
INNGEST_SIGNING_KEY |
β¬ | For background workflows |
LOCAL_STORAGE_DIR |
β¬ | Defaults to /app/.storage |
DEMO_MODE |
β¬ | true = bypass auth (dev only!) |
Quick Smoke Test
After deploying both services:
- Open Vercel URL β click Sign In β choose Google.
- After signing in, you should see your avatar in the top-right.
- On the hero page, click Get Started β create a mission.
- Visit
/{locale}/profileβ you should see:- Your Google name + email + avatar
- Editable phone number
- Language preference dropdown
- Your mission listed under "My Missions"
- The API calls should show
200 OKin browser DevTools Network tab withAuthorization: Bearer <token>header.
For Your Partner: Quick Local Dev Setup
Before they can run pnpm dev, they MUST create two env files:
1. Frontend env: apps/web/.env.local
Create this file (it's gitignored, so it won't exist after clone):
cp apps/web/.env.local.example apps/web/.env.local
Then paste in your Clerk keys:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_... # from Clerk dashboard
CLERK_SECRET_KEY=sk_test_... # from Clerk dashboard
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/en/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/en/sign-up
NEXT_PUBLIC_API_BASE_URL=http://localhost:3001/api
INTERNAL_API_BASE_URL=http://localhost:3001/api
2. Backend env: .env (at repo root)
cp .env.example .env
Fill in at minimum:
DATABASE_URL=postgres://courtmitra:courtmitra@localhost:5432/courtmitra
DIRECT_DATABASE_URL=postgres://courtmitra:courtmitra@localhost:5432/courtmitra
OPENROUTER_API_KEY=sk-or-v1-...
AI_PROVIDER=openrouter
AI_MODEL=google/gemini-2.0-flash-exp:free
AI_VISION_MODEL=google/gemini-2.0-flash-exp:free
CLERK_SECRET_KEY=sk_test_...
JWT_SECRET=change-me-32-chars-minimum!!
PII_HASH_SALT=change-me-salt
TOKEN_ENCRYPTION_KEY=change-me-32-chars-minimum!!
3. Run it
# Terminal 1: start Postgres + Inngest
pnpm infra:up
# Terminal 2: migrate and seed
pnpm db:migrate
pnpm db:seed
# Terminal 3: run all apps
pnpm dev
- Web: http://localhost:3000
- API: http://localhost:3001/api
- Inngest Dev: http://localhost:8288
No Clerk keys yet? Add DEMO_MODE=true to .env to bypass auth for local testing.
Local Development
# 1. Start local Postgres + Inngest
pnpm infra:up
# 2. Copy env and fill in keys
cp .env.example .env
# Edit .env with your keys
# 3. Run everything
pnpm dev
- Web: http://localhost:3000
- API: http://localhost:3001/api
- Inngest Dev Server: http://localhost:8288
For local dev without Clerk keys, add to .env:
DEMO_MODE=true
Troubleshooting
HF Space build fails
- Check Factory Logs in HF dashboard.
- Common:
pnpmnot found β Dockerfile installs viacorepack. - Common: out of disk β free tier has ~50GB. Dockerfile strips
next/reactpackages.
API returns 401 Unauthorized
- Make sure
CLERK_SECRET_KEYis set in HF Space. - Make sure frontend sends
Authorization: Bearer <token>header (check DevTools). - If testing with curl, you need a valid Clerk JWT token.
API returns 500
- Check HF Application Logs.
- Common:
DATABASE_URLnot set β can't connect to Neon. - Common:
OPENROUTER_API_KEYnot set β AI endpoints fail. - Common:
JWT_SECRETtoo short β must be β₯32 characters.
CORS errors from frontend
- API has
app.enableCors({ origin: true })which reflects the request origin. - If still blocked, add your Vercel domain explicitly in
apps/api/src/main.ts.
Clerk sign-in fails
- Verify
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYandCLERK_SECRET_KEYare set in Vercel. - In Clerk Dashboard β Social Connections β Google must be enabled.
- Add your Vercel domain to Clerk Dashboard β URLs β Allowed Origins.
Background workflows not running
- HF free-tier Spaces sleep after inactivity. When the Space sleeps, Inngest can't invoke your worker.
- Fix: Use a paid HF Space (Always On), or use Inngest's
servewith a keep-alive ping. - Alternatively: trigger workflows manually via API for demo purposes.
Database connection errors
- Neon databases have a connection limit. If you see "too many clients", restart the HF Space.
- Use Neon's connection pooling (add
?pgbouncer=trueto connection string).
Known Limitations
| Feature | Status |
|---|---|
| Google OAuth + user profiles | β Works |
| Mission CRUD (user-scoped) | β Works |
| Evidence upload | β Works (if storage configured) |
| Background AI workflows | β οΈ Needs Inngest + always-on Space |
| Email sending | β οΈ Needs Resend key |
| WhatsApp/Telegram | β Needs Meta Business verification (days) |
| Portal automation | β Phase 2 only |
Security Checklist
Before going live:
-
DEMO_MODEis NOT set (or set tofalse) -
JWT_SECRETis β₯32 random characters -
TOKEN_ENCRYPTION_KEYis β₯32 random characters -
CLERK_JWT_KEYis set (optional but recommended for performance) - Database uses SSL (Neon does this by default)
- HF Space is set to private if you don't want public API access
- Vercel domain is in Clerk's Allowed Origins
Good luck at the hackathon! π