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 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)

  1. Go to 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 β†’ New Project β†’ copy connection string.
  2. 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
    
  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 β†’ 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:

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):

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

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

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! πŸš€