Hetansh Waghela commited on
Commit
f89cf0c
Β·
1 Parent(s): 76b46a3

fix: Add /profile to protected routes, comprehensive DEPLOY.md

Browse files

- Add /:locale/profile to clerkMiddleware protected routes
- Update DEPLOY.md with complete env var reference for Vercel + HF Spaces
- Add architecture diagram, smoke test steps, troubleshooting section
- Security checklist for production deployment

Files changed (2) hide show
  1. DEPLOY.md +250 -171
  2. apps/web/middleware.ts +1 -0
DEPLOY.md CHANGED
@@ -4,264 +4,274 @@
4
 
5
  ---
6
 
7
- ## Honest Time Estimate
8
 
9
- | Milestone | Time |
10
- |---|---|
11
- | Clerk setup (Google OAuth) + env vars | **10 min** |
12
- | Neon Postgres + run migrations | **10 min** |
13
- | Frontend on Vercel | **5 min** |
14
- | API + Worker on Hugging Face Spaces | **20–30 min** |
15
- | Wire frontend β†’ backend + smoke test | **10 min** |
16
- | **Running frontend + API + Worker + DB + Google Auth** | **~1 hour** (if everything goes smoothly) |
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  ---
19
 
20
  ## Prerequisites
21
 
22
- - GitHub repo pushed and public (or HF Pro for private Spaces)
23
- - Accounts (all have generous free tiers):
24
- - [Vercel](https://vercel.com)
25
- - [Clerk](https://clerk.com)
26
- - [Hugging Face](https://huggingface.co)
27
- - [Neon](https://neon.tech)
28
- - [Inngest](https://inngest.com) (optional β€” for background workflows)
29
- - OpenRouter API key (or OpenCode / OpenAI key) for LLM calls
30
 
31
  ---
32
 
33
  ## Step 1: Clerk + Google OAuth (10 min)
34
 
35
  1. Go to [dashboard.clerk.com](https://dashboard.clerk.com) β†’ Create Application.
36
- 2. In the **Social Connections** section, enable **Google**.
37
- - For development, use Clerk's shared OAuth credentials (just toggle it on).
38
- - For production, add your own Google Client ID/Secret.
39
- 3. Copy your **Publishable Key** and **Secret Key** from Clerk Dashboard β†’ API Keys.
40
- 4. Set these in Vercel (Step 3) and locally:
41
- ```
42
- NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
43
- CLERK_SECRET_KEY=sk_test_...
44
- NEXT_PUBLIC_CLERK_SIGN_IN_URL=/en/sign-in
45
- NEXT_PUBLIC_CLERK_SIGN_UP_URL=/en/sign-up
46
- ```
47
- 5. That's it β€” the frontend has Clerk wired with `SignInButton`, `UserButton`, and `<Show when="signed-in">` components.
48
- 6. **Backend Clerk verification:** The API verifies Clerk JWTs from the `Authorization: Bearer <token>` header. On first API call, it auto-creates a user in the DB and links their Google identity.
49
 
50
  ---
51
 
52
  ## Step 2: Neon Postgres (10 min)
53
 
54
- 1. Go to [neon.tech](https://neon.tech) β†’ New Project β†’ copy the **connection string**.
55
- 2. Save it as `DATABASE_URL` and `DIRECT_DATABASE_URL`.
56
- 3. **Run migrations** against the Neon DB:
57
  ```bash
58
- # At repo root
59
- export DATABASE_URL="postgres://..."
60
- export DIRECT_DATABASE_URL="postgres://..."
61
  pnpm db:migrate
62
  pnpm db:seed
63
  ```
 
64
 
65
  ---
66
 
67
  ## Step 3: Frontend β†’ Vercel (5 min)
68
 
69
- 1. Push this repo to GitHub.
70
- 2. In Vercel β†’ **Add New Project** β†’ import the repo.
71
- 3. Set these **Environment Variables** in the Vercel dashboard:
72
- ```
73
- NEXT_PUBLIC_API_BASE_URL=https://your-hf-space.hf.space/api
74
- NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
75
- CLERK_SECRET_KEY=sk_test_...
76
- NEXT_PUBLIC_CLERK_SIGN_IN_URL=/en/sign-in
77
- NEXT_PUBLIC_CLERK_SIGN_UP_URL=/en/sign-up
78
- ```
79
- 4. Deploy. Vercel will auto-detect Next.js in `apps/web`.
80
 
81
- > **Tip:** `next.config.mjs` already has `output: "standalone"`, so Vercel optimizes automatically.
 
 
 
 
 
 
 
82
 
83
- ---
84
 
85
- ## Step 4: Backend β†’ Hugging Face Spaces (25 min)
86
 
87
- ### 4a. Create the Space
88
 
89
- 1. Go to [huggingface.co/spaces](https://huggingface.co/spaces) β†’ **Create new Space**.
 
90
  2. **Space name:** `courtmitra-api`
91
  3. **SDK:** `Docker`
92
- 4. **Visibility:** Public (free tier)
93
  5. **Hardware:** Free CPU
94
 
95
- ### 4b. Deploy the Dockerfile
96
-
97
- The repo root has `Dockerfile.hf` which builds the monorepo and runs the NestJS API + Inngest Worker in ONE container.
98
 
99
- Push it to HF (or link your GitHub repo in HF Space settings):
100
-
101
- ```bash
102
- git add Dockerfile.hf
103
- git commit -m "Add HF Spaces Dockerfile (API + Worker combined)"
104
- git push origin main
105
- ```
106
-
107
- Then in your HF Space β†’ **Settings** β†’ **Repository** β†’ link your GitHub repo.
108
 
109
  ### 4c. Set Environment Variables
110
 
111
- In your HF Space β†’ **Settings** β†’ **Variables & Secrets**:
112
 
 
113
  ```
114
  PORT=7860
115
  API_PORT=7860
116
- DATABASE_URL=postgres://... (Neon)
117
- DIRECT_DATABASE_URL=postgres://... (Neon)
 
 
 
 
 
 
 
 
 
 
118
  AI_PROVIDER=openrouter
119
  OPENROUTER_API_KEY=sk-or-v1-...
120
  AI_MODEL=google/gemini-2.0-flash-exp:free
121
  AI_VISION_MODEL=google/gemini-2.0-flash-exp:free
122
- APP_BASE_URL=https://your-hf-space.hf.space
123
- API_BASE_URL=https://your-hf-space.hf.space
124
- WEBHOOK_BASE_URL=https://your-hf-space.hf.space
125
- LOCAL_STORAGE_DIR=/app/.storage
126
- JWT_SECRET=hackathon-jwt-secret-change-me
127
- PII_HASH_SALT=hackathon-salt
128
- TOKEN_ENCRYPTION_KEY=hackathon-enc-key-min-32-chars!!
129
  CLERK_SECRET_KEY=sk_test_...
130
- CLERK_JWT_KEY=... (optional β€” for networkless JWT verification)
 
 
 
131
  ```
132
 
133
- Optional (for email actions):
134
  ```
135
  RESEND_API_KEY=re_...
136
  RESEND_FROM=noreply@yourdomain.com
137
  ```
138
 
139
- Optional (for Inngest background workflows):
140
  ```
141
- INNGEST_EVENT_KEY=... (from Inngest Cloud)
142
- INNGEST_SIGNING_KEY=... (from Inngest Cloud)
143
  INNGEST_BASE_URL=https://api.inngest.com
144
  ```
145
 
146
- Optional (for local dev without Clerk):
147
  ```
148
  DEMO_MODE=true
149
  ```
 
150
 
151
- ### 4d. Verify the API
152
-
153
- Once the Space builds, visit:
154
- - `https://your-space.hf.space/api/admin/health` β†’ should return `{"status":"ok"}`
155
- - `https://your-space.hf.space/api/inngest` β†’ should return the Inngest registration JSON
156
-
157
- ---
158
-
159
- ## Step 5: Wire Frontend β†’ Backend
160
-
161
- Update your Vercel environment variable:
162
  ```
163
- NEXT_PUBLIC_API_BASE_URL=https://your-hf-space.hf.space/api
 
 
 
164
  ```
165
- Redeploy the frontend (or Vercel will auto-redeploy on save).
166
 
167
- ---
168
-
169
- ## Architecture (Combined Container)
 
 
 
 
 
170
 
 
171
  ```
172
- β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
173
- β”‚ Hugging Face Spaces β€” Port 7860 β”‚
174
- β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
175
- β”‚ β”‚ Fastify HTTP Server (NestJS) β”‚ β”‚
176
- β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
177
- β”‚ β”‚ β”‚ API Routes β”‚ β”‚ /api/inngest β”‚ β”‚ β”‚
178
- β”‚ β”‚ β”‚ /api/* β”‚ β”‚ (Worker Fns) β”‚ β”‚ β”‚
179
- β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
180
- β”‚ β”‚ β”‚ - missions β”‚ β”‚ - intake β”‚ β”‚ β”‚
181
- β”‚ β”‚ β”‚ - actions β”‚ β”‚ - evidence β”‚ β”‚ β”‚
182
- β”‚ β”‚ β”‚ - users β”‚ β”‚ - route-plan β”‚ β”‚ β”‚
183
- β”‚ β”‚ β”‚ - evidence β”‚ β”‚ - execute β”‚ β”‚ β”‚
184
- β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
185
- β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
186
- β”‚ ↓ β”‚
187
- β”‚ Neon Postgres (Cloud) β”‚
188
- β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
189
  ```
190
 
191
- ---
192
 
193
- ## Quick Smoke Test
 
 
 
194
 
195
- After both are deployed:
 
196
 
197
- 1. Open your Vercel URL β†’ click **Sign In** β†’ choose **Google**.
198
- 2. After signing in, you should see your avatar in the top-right corner.
199
- 3. Click **Get Started** (hero CTA) β†’ describe a problem.
200
- 4. The API call should reach HF Spaces, create a user in the DB, and create a mission.
201
- 5. Visit `/{locale}/profile` β†’ you should see your profile and the mission you just created.
202
- 6. Check `https://your-hf-space.hf.space/api/admin/health` to confirm backend is alive.
203
 
204
  ---
205
 
206
- ## New Features
207
 
208
- | Feature | Status |
209
- |---|---|
210
- | Google OAuth via Clerk | βœ… Frontend + Backend verified |
211
- | User auto-creation on first API call | βœ… Creates `users` + `userIdentities` rows |
212
- | Profile page (`/{locale}/profile`) | βœ… Shows avatar, name, email, editable phone + language |
213
- | My Missions list on profile | βœ… Lists user's own missions |
214
- | Mission ownership | βœ… Users only see their own missions |
215
- | Combined API + Worker container | βœ… One HF Space serves both |
216
- | Demo mode | βœ… `DEMO_MODE=true` bypasses auth for local dev |
217
 
218
  ---
219
 
220
- ## Known Limitations for Hackathon Demo
221
-
222
- | Feature | Status in 1-hour deploy |
223
- |---|---|
224
- | Frontend + Google OAuth on Vercel | βœ… Works |
225
- | API + Worker + DB on HF + Neon | βœ… Works |
226
- | Mission CRUD (user-scoped) | βœ… Works |
227
- | Evidence upload | βœ… Works (if storage configured) |
228
- | Background AI workflows | ⚠️ Needs Inngest Cloud keys for production webhooks |
229
- | Email sending via Resend | ⚠️ Needs Resend key + verified domain |
230
- | WhatsApp / Telegram channels | ❌ Needs Meta Business verification (days) |
231
- | Portal automation / eCourts | ❌ Phase 2 only |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
 
233
  ---
234
 
235
- ## Troubleshooting
236
-
237
- **HF Space build fails:**
238
- - Check **Factory Logs** in HF. If `pnpm` is missing, the Dockerfile installs it via `corepack`.
239
- - If out of disk: HF free tier has ~50GB disk. The Dockerfile removes frontend packages (`next`, `react`) from the API deploy to save space.
240
-
241
- **API returns 401 Unauthorized:**
242
- - Make sure `CLERK_SECRET_KEY` is set in HF Space env vars.
243
- - Make sure the frontend passes `Authorization: Bearer <token>` header. Check browser DevTools Network tab.
244
-
245
- **API returns 500:**
246
- - Verify `DATABASE_URL` is set and migrations ran.
247
- - Verify `OPENROUTER_API_KEY` is set (some endpoints call LLMs).
248
 
249
- **CORS errors from frontend:**
250
- - The API has `app.enableCors({ origin: true })` which reflects the request origin. If blocked, add your Vercel domain to `origin: ["https://your-vercel.app"]` in `apps/api/src/main.ts`.
251
 
252
- **Clerk sign-in fails:**
253
- - Make sure `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY` are set in Vercel.
254
- - In Clerk Dashboard β†’ **Social Connections** β†’ **Google**, ensure it's enabled.
255
- - Add your Vercel domain to Clerk Dashboard β†’ **URLs** β†’ **Allowed Origins**.
256
-
257
- **Inngest events not firing:**
258
- - Make sure `INNGEST_EVENT_KEY` and `INNGEST_SIGNING_KEY` are correct.
259
- - Check the Inngest Cloud dashboard for failed runs.
260
- - HF free-tier Spaces sleep after inactivity. Use a paid Space or keep-alive ping for reliable background processing.
261
 
262
  ---
263
 
264
- ## Local Dev
265
 
266
  ```bash
267
  # 1. Start local Postgres + Inngest
@@ -269,6 +279,7 @@ pnpm infra:up
269
 
270
  # 2. Copy env and fill in keys
271
  cp infra/.env.example .env
 
272
 
273
  # 3. Run everything
274
  pnpm dev
@@ -278,7 +289,75 @@ pnpm dev
278
  - API: http://localhost:3001/api
279
  - Inngest Dev Server: http://localhost:8288
280
 
281
- **Note:** For local dev without Clerk, set `DEMO_MODE=true` in `.env`. The API will skip JWT verification and use a demo user.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
 
283
  ---
284
 
 
4
 
5
  ---
6
 
7
+ ## Architecture Overview
8
 
9
+ ```
10
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
11
+ β”‚ Vercel (Next.js 15) │────────▢│ Hugging Face Spaces (Docker) β”‚
12
+ β”‚ - Clerk auth β”‚ HTTPS β”‚ - NestJS API + Inngest Worker β”‚
13
+ β”‚ - Hero page β”‚ β”‚ - Port 7860 β”‚
14
+ β”‚ - Profile page β”‚ β”‚ β”‚
15
+ β”‚ - Mission dashboard β”‚ β”‚ /api/* β†’ API routes β”‚
16
+ β”‚ - Static assets β”‚ β”‚ /api/inngest β†’ Worker functions β”‚
17
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
18
+ β”‚
19
+ β”‚ postgres://...
20
+ β–Ό
21
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
22
+ β”‚ Neon Postgres (Cloud) β”‚
23
+ β”‚ - 55 tables β”‚
24
+ β”‚ - pgvector + FTS β”‚
25
+ β”‚ - Legal chunks seeded β”‚
26
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
27
+ ```
28
 
29
  ---
30
 
31
  ## Prerequisites
32
 
33
+ | Service | Purpose | Free Tier |
34
+ |---------|---------|-----------|
35
+ | [Vercel](https://vercel.com) | Frontend hosting | Hobby (unlimited) |
36
+ | [Clerk](https://clerk.com) | Auth + Google OAuth | 10,000 MAU |
37
+ | [Hugging Face Spaces](https://huggingface.co/spaces) | Backend API + Worker | CPU Basic (free) |
38
+ | [Neon](https://neon.tech) | Postgres database | 500 MB, 200 hrs |
39
+ | [Inngest](https://inngest.com) | Background workflows | 50k events/mo |
40
+ | [OpenRouter](https://openrouter.ai) | LLM API | $5 free credits |
41
 
42
  ---
43
 
44
  ## Step 1: Clerk + Google OAuth (10 min)
45
 
46
  1. Go to [dashboard.clerk.com](https://dashboard.clerk.com) β†’ Create Application.
47
+ 2. **Social Connections** β†’ enable **Google**.
48
+ - Development: toggle on (uses Clerk's shared OAuth credentials).
49
+ - Production: add your own Google Client ID/Secret.
50
+ 3. Copy keys from **API Keys** tab:
51
+ - `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...`
52
+ - `CLERK_SECRET_KEY=sk_test_...`
53
+ 4. Add your Vercel domain to **URLs** β†’ **Allowed Origins**:
54
+ - `https://your-project.vercel.app`
55
+ - `http://localhost:3000` (for local dev)
 
 
 
 
56
 
57
  ---
58
 
59
  ## Step 2: Neon Postgres (10 min)
60
 
61
+ 1. [neon.tech](https://neon.tech) β†’ New Project β†’ copy **connection string**.
62
+ 2. Run migrations from your local machine:
 
63
  ```bash
64
+ export DATABASE_URL="postgres://courtmitra:password@host.neon.tech/courtmitra"
65
+ export DIRECT_DATABASE_URL="$DATABASE_URL"
 
66
  pnpm db:migrate
67
  pnpm db:seed
68
  ```
69
+ 3. Verify: check that `users`, `missions`, `route_rules`, `legal_chunks` tables exist.
70
 
71
  ---
72
 
73
  ## Step 3: Frontend β†’ Vercel (5 min)
74
 
75
+ 1. Push repo to GitHub.
76
+ 2. Vercel β†’ **Add New Project** β†’ import repo.
77
+ 3. Set **Environment Variables**:
 
 
 
 
 
 
 
 
78
 
79
+ | Variable | Value | Notes |
80
+ |----------|-------|-------|
81
+ | `NEXT_PUBLIC_API_BASE_URL` | `https://your-hf-space.hf.space/api` | Your HF Spaces URL |
82
+ | `INTERNAL_API_BASE_URL` | `https://your-hf-space.hf.space/api` | Same as above |
83
+ | `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` | `pk_test_...` | From Clerk dashboard |
84
+ | `CLERK_SECRET_KEY` | `sk_test_...` | From Clerk dashboard |
85
+ | `NEXT_PUBLIC_CLERK_SIGN_IN_URL` | `/en/sign-in` | Or `/hi/sign-in` |
86
+ | `NEXT_PUBLIC_CLERK_SIGN_UP_URL` | `/en/sign-up` | Or `/hi/sign-up` |
87
 
88
+ 4. Deploy.
89
 
90
+ ---
91
 
92
+ ## Step 4: Backend β†’ Hugging Face Spaces (20 min)
93
 
94
+ ### 4a. Create Space
95
+ 1. [huggingface.co/spaces](https://huggingface.co/spaces) β†’ **Create new Space**.
96
  2. **Space name:** `courtmitra-api`
97
  3. **SDK:** `Docker`
98
+ 4. **Visibility:** Public
99
  5. **Hardware:** Free CPU
100
 
101
+ ### 4b. Link GitHub Repo
102
+ In Space Settings β†’ **Repository** β†’ link your GitHub repo.
 
103
 
104
+ The `Dockerfile.hf` at repo root builds the monorepo and runs API + Worker in one container.
 
 
 
 
 
 
 
 
105
 
106
  ### 4c. Set Environment Variables
107
 
108
+ In Space β†’ **Settings** β†’ **Variables & Secrets**:
109
 
110
+ #### Required (App won't start without these)
111
  ```
112
  PORT=7860
113
  API_PORT=7860
114
+ DATABASE_URL=postgres://courtmitra:password@host.neon.tech/courtmitra
115
+ DIRECT_DATABASE_URL=postgres://courtmitra:password@host.neon.tech/courtmitra
116
+ APP_BASE_URL=https://your-hf-space.hf.space
117
+ API_BASE_URL=https://your-hf-space.hf.space
118
+ WEBHOOK_BASE_URL=https://your-hf-space.hf.space
119
+ JWT_SECRET=change-me-32-chars-minimum!!
120
+ PII_HASH_SALT=change-me-salt
121
+ TOKEN_ENCRYPTION_KEY=change-me-32-chars-minimum!!
122
+ ```
123
+
124
+ #### Required for AI (Intake, route-plan, evidence parsing)
125
+ ```
126
  AI_PROVIDER=openrouter
127
  OPENROUTER_API_KEY=sk-or-v1-...
128
  AI_MODEL=google/gemini-2.0-flash-exp:free
129
  AI_VISION_MODEL=google/gemini-2.0-flash-exp:free
130
+ ```
131
+
132
+ #### Clerk (for JWT verification)
133
+ ```
 
 
 
134
  CLERK_SECRET_KEY=sk_test_...
135
+ # Optional: for networkless JWT verification (faster, no Clerk API call per request)
136
+ CLERK_JWT_KEY=...
137
+ # Optional: restrict which domains can use your API
138
+ CLERK_AUTHORIZED_PARTIES=https://your-project.vercel.app,http://localhost:3000
139
  ```
140
 
141
+ #### Optional (Email actions β€” Resend)
142
  ```
143
  RESEND_API_KEY=re_...
144
  RESEND_FROM=noreply@yourdomain.com
145
  ```
146
 
147
+ #### Optional (Inngest background workflows)
148
  ```
149
+ INNGEST_EVENT_KEY=...
150
+ INNGEST_SIGNING_KEY=...
151
  INNGEST_BASE_URL=https://api.inngest.com
152
  ```
153
 
154
+ #### Optional (Local dev helper β€” NOT for production)
155
  ```
156
  DEMO_MODE=true
157
  ```
158
+ > ⚠️ **Never set `DEMO_MODE=true` in production.** It bypasses all auth.
159
 
160
+ #### Optional (Object storage β€” for evidence files)
 
 
 
 
 
 
 
 
 
 
161
  ```
162
+ OBJECT_STORAGE_ENDPOINT=https://...
163
+ OBJECT_STORAGE_BUCKET=courtmitra
164
+ OBJECT_STORAGE_ACCESS_KEY=...
165
+ OBJECT_STORAGE_SECRET_KEY=...
166
  ```
167
+ > If not set, evidence files are stored locally in `/app/.storage` (lost on Space restart).
168
 
169
+ #### Optional (Other channels)
170
+ ```
171
+ WHATSAPP_VERIFY_TOKEN=...
172
+ WHATSAPP_APP_SECRET=...
173
+ WHATSAPP_ACCESS_TOKEN=...
174
+ WHATSAPP_PHONE_NUMBER_ID=...
175
+ TELEGRAM_BOT_TOKEN=...
176
+ ```
177
 
178
+ #### Optional (Phase 2 features β€” not needed for hackathon)
179
  ```
180
+ QDRANT_URL=https://...
181
+ QDRANT_API_KEY=...
182
+ QDRANT_COLLECTION=courtmitra_legal
183
+ OPENAI_API_KEY=...
184
+ BROWSERBASE_API_KEY=...
185
+ BROWSERBASE_PROJECT_ID=...
186
+ SUREPASS_API_KEY=...
187
+ ENABLE_PORTAL_AUTOMATION=1
 
 
 
 
 
 
 
 
 
188
  ```
189
 
190
+ ### 4d. Verify
191
 
192
+ Once built, test:
193
+ ```bash
194
+ curl https://your-space.hf.space/api/admin/health
195
+ # Expected: {"status":"ok","ts":"..."}
196
 
197
+ curl https://your-space.hf.space/api/capabilities
198
+ # Expected: JSON with adapter capabilities
199
 
200
+ curl https://your-space.hf.space/api/inngest
201
+ # Expected: Inngest registration JSON
202
+ ```
 
 
 
203
 
204
  ---
205
 
206
+ ## Step 5: Wire Frontend β†’ Backend
207
 
208
+ 1. In Vercel dashboard, update:
209
+ ```
210
+ NEXT_PUBLIC_API_BASE_URL=https://your-hf-space.hf.space/api
211
+ ```
212
+ 2. Redeploy frontend.
 
 
 
 
213
 
214
  ---
215
 
216
+ ## Complete Environment Variable Reference
217
+
218
+ ### Vercel (Frontend)
219
+
220
+ | Variable | Required | Description |
221
+ |----------|----------|-------------|
222
+ | `NEXT_PUBLIC_API_BASE_URL` | βœ… | HF Spaces API URL |
223
+ | `INTERNAL_API_BASE_URL` | βœ… | Same as above (for SSR) |
224
+ | `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` | βœ… | Clerk public key |
225
+ | `CLERK_SECRET_KEY` | βœ… | Clerk secret key |
226
+ | `NEXT_PUBLIC_CLERK_SIGN_IN_URL` | βœ… | `/en/sign-in` |
227
+ | `NEXT_PUBLIC_CLERK_SIGN_UP_URL` | βœ… | `/en/sign-up` |
228
+
229
+ ### Hugging Face Spaces (Backend)
230
+
231
+ | Variable | Required | Description |
232
+ |----------|----------|-------------|
233
+ | `PORT` | βœ… | Must be `7860` |
234
+ | `API_PORT` | βœ… | Must be `7860` |
235
+ | `DATABASE_URL` | βœ… | Neon connection string |
236
+ | `DIRECT_DATABASE_URL` | βœ… | Same as `DATABASE_URL` |
237
+ | `APP_BASE_URL` | βœ… | HF Spaces URL |
238
+ | `API_BASE_URL` | βœ… | Same |
239
+ | `WEBHOOK_BASE_URL` | βœ… | Same |
240
+ | `JWT_SECRET` | βœ… | Min 32 chars |
241
+ | `PII_HASH_SALT` | βœ… | Any string |
242
+ | `TOKEN_ENCRYPTION_KEY` | βœ… | Min 32 chars |
243
+ | `AI_PROVIDER` | βœ… | `openrouter` or `opencode` |
244
+ | `OPENROUTER_API_KEY` | βœ… | If `AI_PROVIDER=openrouter` |
245
+ | `AI_MODEL` | βœ… | e.g. `google/gemini-2.0-flash-exp:free` |
246
+ | `AI_VISION_MODEL` | βœ… | e.g. `google/gemini-2.0-flash-exp:free` |
247
+ | `CLERK_SECRET_KEY` | βœ… | Same as Vercel |
248
+ | `CLERK_JWT_KEY` | ⬜ | Optional, for faster JWT verification |
249
+ | `RESEND_API_KEY` | ⬜ | For email actions |
250
+ | `RESEND_FROM` | ⬜ | Sender email |
251
+ | `INNGEST_EVENT_KEY` | ⬜ | For background workflows |
252
+ | `INNGEST_SIGNING_KEY` | ⬜ | For background workflows |
253
+ | `LOCAL_STORAGE_DIR` | ⬜ | Defaults to `/app/.storage` |
254
+ | `DEMO_MODE` | ⬜ | `true` = bypass auth (dev only!) |
255
 
256
  ---
257
 
258
+ ## Quick Smoke Test
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
+ After deploying both services:
 
261
 
262
+ 1. Open Vercel URL β†’ click **Sign In** β†’ choose **Google**.
263
+ 2. After signing in, you should see your avatar in the top-right.
264
+ 3. On the hero page, click **Get Started** β†’ create a mission.
265
+ 4. Visit `/{locale}/profile` β†’ you should see:
266
+ - Your Google name + email + avatar
267
+ - Editable phone number
268
+ - Language preference dropdown
269
+ - Your mission listed under "My Missions"
270
+ 5. The API calls should show `200 OK` in browser DevTools Network tab with `Authorization: Bearer <token>` header.
271
 
272
  ---
273
 
274
+ ## Local Development
275
 
276
  ```bash
277
  # 1. Start local Postgres + Inngest
 
279
 
280
  # 2. Copy env and fill in keys
281
  cp infra/.env.example .env
282
+ # Edit .env with your keys
283
 
284
  # 3. Run everything
285
  pnpm dev
 
289
  - API: http://localhost:3001/api
290
  - Inngest Dev Server: http://localhost:8288
291
 
292
+ **For local dev without Clerk keys**, add to `.env`:
293
+ ```
294
+ DEMO_MODE=true
295
+ ```
296
+
297
+ ---
298
+
299
+ ## Troubleshooting
300
+
301
+ ### HF Space build fails
302
+ - Check **Factory Logs** in HF dashboard.
303
+ - Common: `pnpm` not found β†’ Dockerfile installs via `corepack`.
304
+ - Common: out of disk β†’ free tier has ~50GB. Dockerfile strips `next`/`react` packages.
305
+
306
+ ### API returns 401 Unauthorized
307
+ - Make sure `CLERK_SECRET_KEY` is set in HF Space.
308
+ - Make sure frontend sends `Authorization: Bearer <token>` header (check DevTools).
309
+ - If testing with curl, you need a valid Clerk JWT token.
310
+
311
+ ### API returns 500
312
+ - Check HF **Application Logs**.
313
+ - Common: `DATABASE_URL` not set β†’ can't connect to Neon.
314
+ - Common: `OPENROUTER_API_KEY` not set β†’ AI endpoints fail.
315
+ - Common: `JWT_SECRET` too short β†’ must be β‰₯32 characters.
316
+
317
+ ### CORS errors from frontend
318
+ - API has `app.enableCors({ origin: true })` which reflects the request origin.
319
+ - If still blocked, add your Vercel domain explicitly in `apps/api/src/main.ts`.
320
+
321
+ ### Clerk sign-in fails
322
+ - Verify `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY` are set in Vercel.
323
+ - In Clerk Dashboard β†’ **Social Connections** β†’ **Google** must be enabled.
324
+ - Add your Vercel domain to Clerk Dashboard β†’ **URLs** β†’ **Allowed Origins**.
325
+
326
+ ### Background workflows not running
327
+ - HF free-tier Spaces **sleep after inactivity**. When the Space sleeps, Inngest can't invoke your worker.
328
+ - **Fix:** Use a paid HF Space (Always On), or use Inngest's `serve` with a keep-alive ping.
329
+ - Alternatively: trigger workflows manually via API for demo purposes.
330
+
331
+ ### Database connection errors
332
+ - Neon databases have a **connection limit**. If you see "too many clients", restart the HF Space.
333
+ - Use Neon's **connection pooling** (add `?pgbouncer=true` to connection string).
334
+
335
+ ---
336
+
337
+ ## Known Limitations
338
+
339
+ | Feature | Status |
340
+ |---------|--------|
341
+ | Google OAuth + user profiles | βœ… Works |
342
+ | Mission CRUD (user-scoped) | βœ… Works |
343
+ | Evidence upload | βœ… Works (if storage configured) |
344
+ | Background AI workflows | ⚠️ Needs Inngest + always-on Space |
345
+ | Email sending | ⚠️ Needs Resend key |
346
+ | WhatsApp/Telegram | ❌ Needs Meta Business verification (days) |
347
+ | Portal automation | ❌ Phase 2 only |
348
+
349
+ ---
350
+
351
+ ## Security Checklist
352
+
353
+ Before going live:
354
+ - [ ] `DEMO_MODE` is NOT set (or set to `false`)
355
+ - [ ] `JWT_SECRET` is β‰₯32 random characters
356
+ - [ ] `TOKEN_ENCRYPTION_KEY` is β‰₯32 random characters
357
+ - [ ] `CLERK_JWT_KEY` is set (optional but recommended for performance)
358
+ - [ ] Database uses SSL (Neon does this by default)
359
+ - [ ] HF Space is set to **private** if you don't want public API access
360
+ - [ ] Vercel domain is in Clerk's **Allowed Origins**
361
 
362
  ---
363
 
apps/web/middleware.ts CHANGED
@@ -7,6 +7,7 @@ const intlMiddleware = createMiddleware(routing);
7
  const isProtectedRoute = createRouteMatcher([
8
  "/:locale/ledger",
9
  "/:locale/missions(.*)",
 
10
  ]);
11
 
12
  export default clerkMiddleware(async (auth, req) => {
 
7
  const isProtectedRoute = createRouteMatcher([
8
  "/:locale/ledger",
9
  "/:locale/missions(.*)",
10
+ "/:locale/profile",
11
  ]);
12
 
13
  export default clerkMiddleware(async (auth, req) => {