Deployment Guide
Deployment Guide
The Groundtruth Platform consists of three services that must be deployed and configured together.
Service Overview
| Service | Platform | Purpose |
|---|---|---|
| Web App | Vercel | Next.js frontend + API routes |
| Engine | Railway | Python FastAPI engine (long-running crew execution) |
| Database + Auth + Storage | Supabase | PostgreSQL, authentication, file storage |
Supporting services:
| Service | Platform | Purpose |
|---|---|---|
| Billing | Stripe | Subscriptions, checkout, usage metering |
| Caching + Rate Limiting | Upstash Redis | API response cache, sliding-window rate limits |
| Resend | Transactional notifications | |
| Observability | Sentry | Error tracking, performance monitoring |
1. Supabase (Database + Auth + Storage)
Supabase is the foundation. Set it up first since other services depend on its connection strings and API keys.
Create Project
- Create a new project at supabase.com
- Note the following from Settings > API:
- Project URL (
NEXT_PUBLIC_SUPABASE_URL) - Anon key (
NEXT_PUBLIC_SUPABASE_ANON_KEY) - Service role key (
SUPABASE_SERVICE_ROLE_KEY)
- Project URL (
- Note the connection string from Settings > Database:
- Direct connection:
DATABASE_URL(for migrations) - Connection pooler (port 6543): for the Railway engine (use Transaction mode)
- Direct connection:
Apply Migrations
From your local machine (or CI):
cd apps/web
DATABASE_URL="postgresql://postgres:PASSWORD@db.PROJECT.supabase.co:5432/postgres" npx prisma migrate deployThis creates all 20 tables, indexes, and RLS policies.
Seed Data
cd apps/web
DATABASE_URL="postgresql://..." npx prisma db seedThis populates the AgentConfig table with 20 agents and creates system engagement templates.
Create Storage Bucket
- Go to Storage in the Supabase dashboard
- Create a new bucket named
engagement-attachments - Set it to Private (access controlled by service role key)
Configure Auth
- Go to Authentication > URL Configuration
- Set the Site URL to your Vercel deployment URL (e.g.,
https://your-app.vercel.app) - Add redirect URLs:
https://your-app.vercel.app/auth/callbackhttp://localhost:3000/auth/callback(for local development)
- Enable desired auth providers (email/password is enabled by default)
SSO Configuration (Enterprise Plan Only)
For tenants on the enterprise plan, SAML SSO is configured per-tenant via the /api/tenant/sso API endpoint. Supabase Pro plan includes SAML support.
2. Vercel (Next.js Web App)
Connect Repository
- Import your GitHub repository at vercel.com
- Set the Root Directory to
apps/web - Framework Preset: Next.js
- Build Command:
npx prisma generate && next build - Install Command:
npm install
Environment Variables
Set these in the Vercel dashboard under Settings > Environment Variables:
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://PROJECT.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
SUPABASE_SERVICE_ROLE_KEY=eyJ...
# Database
DATABASE_URL=postgresql://postgres:PASSWORD@db.PROJECT.supabase.co:5432/postgres
# Stripe
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
# Engine
RAILWAY_ENGINE_URL=https://your-engine.up.railway.app
# Upstash Redis
UPSTASH_REDIS_REST_URL=https://REGION.upstash.io
UPSTASH_REDIS_REST_TOKEN=AX...
# Resend
RESEND_API_KEY=re_...
# Sentry
NEXT_PUBLIC_SENTRY_DSN=https://...@sentry.io/...
SENTRY_AUTH_TOKEN=sntrys_...Deployment
Vercel auto-deploys on push to main. Each pull request gets a preview deployment.
3. Railway (Python Engine)
The Python engine runs as a persistent service (not serverless) because engagement runs take 10-60 minutes, well beyond serverless timeout limits.
Deploy
- Create a new project at railway.app
- Connect your GitHub repository
- Set the Root Directory to
packages/engine - Railway detects the Python project automatically via
requirements.txt - The
railway.tomlfile ininfrastructure/configures the deployment
Environment Variables
Set these in the Railway dashboard:
# Database (use Supabase connection pooler URL, port 6543)
DATABASE_URL=postgresql://postgres:PASSWORD@PROJECT.supabase.co:6543/postgres
# LLM API keys
OPENAI_API_KEY=sk-...
XAI_API_KEY=xai-...
ANTHROPIC_API_KEY=sk-ant-...
GOOGLE_API_KEY=AIza...
# CORS
ALLOWED_ORIGINS=https://your-app.vercel.app
# Sentry
SENTRY_DSN=https://...@sentry.io/...
ENVIRONMENT=production
SENTRY_TRACES_SAMPLE_RATE=0.1Important Notes
- Use the Supabase connection pooler URL (port 6543) for the engine, not the direct connection. This prevents connection exhaustion under load.
- The engine must be stateless -- no local filesystem state. All persistence goes through the
DatabaseStorageAdapterto PostgreSQL. - Railway provides a public URL automatically. Set this as
RAILWAY_ENGINE_URLin Vercel.
4. Stripe (Billing)
Create Products and Prices
Create three subscription products in the Stripe dashboard (or via API):
| Plan | Monthly Price | Features |
|---|---|---|
| Starter | $99/mo | Basic engagement execution |
| Professional | $299/mo | Advanced features, higher limits |
| Enterprise | Custom | SSO, custom branding, dedicated support |
Configure Webhook
- Go to Developers > Webhooks in the Stripe dashboard
- Add endpoint:
https://your-app.vercel.app/api/billing/webhook - Select events:
checkout.session.completedcustomer.subscription.updatedcustomer.subscription.deletedinvoice.paidinvoice.payment_failed
- Copy the signing secret as
STRIPE_WEBHOOK_SECRET
Customer Portal
- Go to Settings > Billing > Customer portal
- Enable the customer portal
- Configure allowed actions (cancel subscription, update payment method)
5. Upstash Redis (Caching + Rate Limiting)
- Create a database at upstash.com
- Choose a region close to your Vercel deployment
- Copy the REST URL and token:
UPSTASH_REDIS_REST_URLUPSTASH_REDIS_REST_TOKEN
The platform uses Redis for:
- API response caching: Agent configs, engagement lists, tenant settings
- Rate limiting: Sliding-window algorithm via
@upstash/ratelimit
If Redis is unavailable, the platform degrades gracefully to no-cache and in-memory rate limiting.
6. Resend (Email)
- Create an account at resend.com
- Verify your sending domain
- Generate an API key:
RESEND_API_KEY
Email notifications are sent for:
- Engagement started, completed, or failed
- Deliverable ready for review
- Client portal comment received
- Team member invited
7. Sentry (Observability)
Next.js Project
- Create a project at sentry.io for the Next.js app
- Set
NEXT_PUBLIC_SENTRY_DSNin Vercel - Generate an auth token for source map uploads:
SENTRY_AUTH_TOKEN
Python Engine Project
- Create a separate Sentry project for the Python engine
- Set
SENTRY_DSNin Railway - The engine uses
sentry-sdk[fastapi]for automatic request instrumentation
Deployment Checklist
Before going live, verify:
- Supabase migrations applied, agents seeded, storage bucket created
- Supabase Auth redirect URLs configured
- Vercel deploying successfully with all env vars
- Railway engine running and reachable from Vercel (
RAILWAY_ENGINE_URL) - Stripe products/prices created, webhook endpoint configured
- Health checks passing:
GET https://your-app.vercel.app/api/healthGET https://your-engine.up.railway.app/health
- Redis connected (check admin health dashboard)
- Sentry receiving events from both services
- Email delivery working (send a test)
Cost Summary
| Service | Monthly Cost |
|---|---|
| Supabase Pro | $25 |
| Vercel Pro | $20 |
| Railway | $5-20 (usage-based) |
| Upstash Redis | $0-10 |
| Resend | $0-20 |
| Stripe | 2.9% + $0.30 per transaction |
| Sentry | Free tier (5K events) or $26/mo |
| Total | ~$50-95/month (before Stripe fees) |
Related Documentation
- Environments & Dev Setup -- dev branches, Supabase branching, environment matrix
- Architecture Overview -- system design and data flow
- Local Development -- running locally
- Engine Architecture -- Python engine details