Privacy-first NUS campus rideshare, implemented as a web-first PWA plus a separate matcher service.
apps/web: Next.js App Router app for OTP login, preferences, private availability submission, tentative groups, acknowledgements, and address revealservices/matcher: separate matcher service that receives plaintext addresses, stores only sealed destination blobs in memory, returns opaque refs, computes compatibility, and releases encrypted address envelopespackages/shared: shared types and constants- Convex for database and auth (OTP via Resend, email reply verification)
- The main web app backend never accepts plaintext addresses.
- The browser submits exact addresses directly to the matcher service.
- The web app stores only opaque destination refs and route descriptor refs.
- Revealed addresses are encrypted to each rider's browser public key and only decrypted in the browser after unanimous acknowledgement.
- Install dependencies:
pnpm install - Set up environment variables:
- Next.js: Copy
apps/web/.env.exampletoapps/web/.env.local - Matcher: Copy
services/matcher/.env.exampletoservices/matcher/.env
- Next.js: Copy
- Set up Convex backend env:
- Copy
apps/web/.env.convex.exampletoapps/web/.env.convex - Fill it in with your local dev values
- Run
cd apps/web && npx convex env set --from-file .env.convex
- Copy
- Start everything with one command:
pnpm dev
- On first run, complete any Convex login prompt shown in the
convexprocess. - Open the web app at
http://localhost:3000.
apps/web/.env.local: browser-visible values onlyapps/web/.env.convex: Convex backend/auth envservices/matcher/.env: matcher-only env
For local dev, that means:
apps/web/.env.localNEXT_PUBLIC_MATCHER_BASE_URL=http://localhost:4001 MATCHER_ADMIN_PREVIEW_SECRET=replace-me
apps/web/.env.convexSITE_URL=http://localhost:3000 MATCHER_BASE_URL=http://localhost:4001 [email protected] # plus JWT_PRIVATE_KEY, JWKS, AUTH_RESEND_KEY, RESEND_FROM_EMAIL, RESEND_INBOUND_ADDRESS
services/matcher/.envMATCHER_ADMIN_PREVIEW_SECRET=replace-me
npx convex dev will automatically write NEXT_PUBLIC_CONVEX_URL into apps/web/.env.local, so you should not manage that value by hand.
Convex Auth requires several environment variables in your Convex deployment, not in Next.js .env.local. Set them via the Convex dashboard or npx convex env set:
| Variable | Purpose | Required |
|---|---|---|
SITE_URL |
Redirect URL after auth (e.g. http://localhost:3000) |
Yes |
JWT_PRIVATE_KEY |
Private key for signing JWTs | Yes |
JWKS |
Public key set (JSON) for verifying JWTs | Yes |
AUTH_RESEND_KEY |
Resend API key for OTP, verification, and inbound emails | Yes |
RESEND_FROM_EMAIL |
Sender address (e.g. Hop <[email protected]>) |
No (has default) |
RESEND_INBOUND_ADDRESS |
Inbound address that receives verification replies (e.g. [email protected]) |
Yes, for email reply flow |
ADMIN_EMAILS |
Comma-separated admin allowlist for /admin access |
No |
Run the key generator and paste the output into Convex dashboard → Deployment Settings → Environment Variables:
cd apps/web && pnpm convex:generate-keysThis uses the jose library to generate JWT_PRIVATE_KEY and JWKS.
cd apps/web
npx convex env set SITE_URL http://localhost:3000
npx convex env set AUTH_RESEND_KEY re_your_resend_api_key
# JWT_PRIVATE_KEY and JWKS: use output from pnpm convex:generate-keysOr copy apps/web/.env.convex.example to apps/web/.env.convex, fill in values, then run:
cd apps/web && npx convex env set --from-file .env.convexThe production site should use a deployed Convex production deployment, not npx convex dev.
- Create a Convex production deployment for this project:
cd apps/web pnpm convex:deploy - Set the production Convex env vars:
cd apps/web npx convex env set --prod --from-file .env.convex
- In Vercel, set these project environment variables:
CONVEX_DEPLOY_KEY— lets the production Vercel build runconvex deployNEXT_PUBLIC_MATCHER_BASE_URL— public matcher base URL for the web appMATCHER_BASE_URL— server-side matcher base URL for Next.js routes and server actionsMATCHER_ADMIN_PREVIEW_SECRET— shared secret for the admin simulator preview endpoint
- Vercel env:
CONVEX_DEPLOY_KEYNEXT_PUBLIC_MATCHER_BASE_URLMATCHER_BASE_URLMATCHER_ADMIN_PREVIEW_SECRET- optionally
NEXT_PUBLIC_CONVEX_URLfor preview deployments
- Convex production env:
SITE_URLJWT_PRIVATE_KEYJWKSAUTH_RESEND_KEYRESEND_FROM_EMAILRESEND_INBOUND_ADDRESSMATCHER_BASE_URLADMIN_EMAILS
- Preview deployments: Vercel runs a normal
next build. SetNEXT_PUBLIC_CONVEX_URLin Vercel preview env if you want previews to connect to Convex. - Production deployments: Vercel runs:
This deploys Convex first, then injects the production Convex URL into the Next.js build automatically.
pnpm exec convex deploy --cmd 'next build' --cmd-url-env-var-name NEXT_PUBLIC_CONVEX_URL
The deploy workflow now:
- runs
lint,test, and non-web package builds in CI - deploys the web app from
apps/web - relies on Vercel's production build command to deploy Convex and build the site with the correct
NEXT_PUBLIC_CONVEX_URL
For first-time users to verify by replying with the passphrase:
-
Resend dashboard → Receiving → Add address
Create inbound address[email protected](add MX records Resend provides). -
Configure webhook
- URL:
https://<your-deployment>.convex.site/resend-inbound - Event:
email.received
- URL:
-
Set Convex env
npx convex env set RESEND_INBOUND_ADDRESS [email protected] npx convex env set RESEND_FROM_EMAIL "Hop <[email protected]>"
- Without
AUTH_RESEND_KEY, OTP and verification emails will fail. - The matcher service intentionally uses coarse route heuristics for the prototype instead of true road-network routing.