courtmitra / DEPLOY.md
Hetansh Waghela
docs: Add partner-facing env file setup to DEPLOY.md
d2d8e95
|
Raw
History Blame Contribute Delete
15.4 kB
# 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`:**
1. Create `apps/web/.env.local` (copy from `apps/web/.env.local.example`)
2. Paste in `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY` from Clerk dashboard
3. Create `.env` at repo root (copy from `.env.example`)
4. Fill in `DATABASE_URL`, `OPENROUTER_API_KEY`, etc.
---
## Prerequisites
| Service | Purpose | Free Tier |
|---------|---------|-----------|
| [Vercel](https://vercel.com) | Frontend hosting | Hobby (unlimited) |
| [Clerk](https://clerk.com) | Auth + Google OAuth | 10,000 MAU |
| [Hugging Face Spaces](https://huggingface.co/spaces) | Backend API + Worker | CPU Basic (free) |
| [Neon](https://neon.tech) | Postgres database | 500 MB, 200 hrs |
| [Inngest](https://inngest.com) | Background workflows | 50k events/mo |
| [OpenRouter](https://openrouter.ai) | LLM API | $5 free credits |
---
## Step 1: Clerk + Google OAuth (10 min)
1. Go to [dashboard.clerk.com](https://dashboard.clerk.com) β†’ Create Application.
2. **Social Connections** β†’ enable **Google**.
- Development: toggle on (uses Clerk's shared OAuth credentials).
- Production: add your own Google Client ID/Secret.
3. Copy keys from **API Keys** tab:
- `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...`
- `CLERK_SECRET_KEY=sk_test_...`
4. Add your Vercel domain to **URLs** β†’ **Allowed Origins**:
- `https://your-project.vercel.app`
- `http://localhost:3000` (for local dev)
---
## Step 2: Neon Postgres (10 min)
1. [neon.tech](https://neon.tech) β†’ New Project β†’ copy **connection string**.
2. Run migrations from your local machine:
```bash
export DATABASE_URL="postgres://courtmitra:password@host.neon.tech/courtmitra"
export DIRECT_DATABASE_URL="$DATABASE_URL"
pnpm db:migrate
pnpm db:seed
```
3. Verify: check that `users`, `missions`, `route_rules`, `legal_chunks` tables exist.
---
## Step 3: Frontend β†’ Vercel (5 min)
1. Push repo to GitHub.
2. Vercel β†’ **Add New Project** β†’ import repo.
3. Set **Environment Variables**:
| Variable | Value | Notes |
|----------|-------|-------|
| `NEXT_PUBLIC_API_BASE_URL` | `https://your-hf-space.hf.space/api` | Your HF Spaces URL |
| `INTERNAL_API_BASE_URL` | `https://your-hf-space.hf.space/api` | Same as above |
| `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` | Or `/hi/sign-in` |
| `NEXT_PUBLIC_CLERK_SIGN_UP_URL` | `/en/sign-up` | Or `/hi/sign-up` |
4. Deploy.
---
## Step 4: Backend β†’ Hugging Face Spaces (20 min)
### 4a. Create Space
1. [huggingface.co/spaces](https://huggingface.co/spaces) β†’ **Create new Space**.
2. **Space name:** `courtmitra-api`
3. **SDK:** `Docker`
4. **Visibility:** Public
5. **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=true` in 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:
```bash
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
1. In Vercel dashboard, update:
```
NEXT_PUBLIC_API_BASE_URL=https://your-hf-space.hf.space/api
```
2. 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:
1. Open Vercel URL β†’ click **Sign In** β†’ choose **Google**.
2. After signing in, you should see your avatar in the top-right.
3. On the hero page, click **Get Started** β†’ create a mission.
4. Visit `/{locale}/profile` β†’ you should see:
- Your Google name + email + avatar
- Editable phone number
- Language preference dropdown
- Your mission listed under "My Missions"
5. The API calls should show `200 OK` in browser DevTools Network tab with `Authorization: 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):
```bash
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)
```bash
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
```bash
# 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
```bash
# 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: `pnpm` not found β†’ Dockerfile installs via `corepack`.
- Common: out of disk β†’ free tier has ~50GB. Dockerfile strips `next`/`react` packages.
### API returns 401 Unauthorized
- Make sure `CLERK_SECRET_KEY` is 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_URL` not set β†’ can't connect to Neon.
- Common: `OPENROUTER_API_KEY` not set β†’ AI endpoints fail.
- Common: `JWT_SECRET` too 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_KEY` and `CLERK_SECRET_KEY` are 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 `serve` with 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=true` to 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_MODE` is NOT set (or set to `false`)
- [ ] `JWT_SECRET` is β‰₯32 random characters
- [ ] `TOKEN_ENCRYPTION_KEY` is β‰₯32 random characters
- [ ] `CLERK_JWT_KEY` is 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! πŸš€*