Knowledge > System Architecture > Infrastructure
Canonical reference for all ChurchWiseAI hosting, domains, external services, environment variables, cron jobs, voice agent deployment, email systems, monitoring, and security controls. This document describes what infrastructure exists and how it connects -- never credentials or secrets.
Multi-Codebase Deployment Architecture
1. Vercel Deployments
All three active codebases deploy to Vercel via GitHub integration. Pushes to the deploy branch trigger automatic production deployments. Feature branches get Vercel preview deploys.
| Property | Codebase | Deploy Branch | Port (Local) | URL | Vercel Account |
|---|
| ChurchWiseAI | churchwiseai-web/ | main | 3002 | churchwiseai.com | churchwiseai-5386 (john@churchwiseai.com) |
| PewSearch | pewsearch/web/ | master | 3000 | pewsearch.com | (same org) |
| ITW | sermon-illustrations/ | master | 3000 | illustratetheword.com | (same org) |
Deploy Flow
feature branch → push → Vercel preview deploy → PR review
↓
merge to main/master
↓
Vercel auto production deploy
Caching / ISR Strategy
| Property | Route Pattern | Cache | SWR |
|---|
| PewSearch | /directory/* | s-maxage=3600 | stale-while-revalidate=86400 |
| PewSearch | /denominations/* | s-maxage=3600 | stale-while-revalidate=86400 |
| PewSearch | /s/* (Pro Website) | s-maxage=3600, ISR revalidate=3600 | stale-while-revalidate=86400 |
| PewSearch | /claim/*, /admin/* | no-store | -- |
| CWA | /admin/* | no-store | -- |
Vercel CLI
Logged in and ready. Key commands:
vercel env ls -- list env vars for a project
vercel env pull -- sync production env vars to .env.local
vercel --prod -- trigger production deployment
vercel logs --tail -- stream deployment logs
echo "value" | vercel env add VAR_NAME production -- set env var (pipe bypasses interactive prompt)
2. Domain Architecture
Five domains serve the three active codebases, with two domains using hostname-based middleware rewrites to serve separate brands from a single codebase.
| Domain | DNS Provider | Codebase | Purpose |
|---|
| churchwiseai.com | Porkbun | churchwiseai-web/ | AI products -- voice agent, chatbot, admin dashboard |
| sermonwise.ai | Porkbun | churchwiseai-web/ (hostname rewrite to /sermons) | AI sermon preparation tools |
| sharewiseai.com | Porkbun | churchwiseai-web/ (hostname rewrite to /social) | AI social media SaaS (Coming Soon) |
| pewsearch.com | Vercel DNS | pewsearch/web/ | Church directory (218K+ visible listings) |
| illustratetheword.com | Porkbun | sermon-illustrations/ | Sermon illustration library |
Middleware Rewrites
ChurchWiseAI middleware (churchwiseai-web/src/middleware.ts):
- sermonwise.ai -- When the
host header contains sermonwise.ai, all paths are rewritten to /sermons/*. For example, sermonwise.ai/titles becomes /sermons/titles internally. Static assets, API routes, and auth paths are excluded from rewriting.
- sharewiseai.com -- Same pattern:
sharewiseai.com/pricing rewrites to /social/pricing. Login redirects adapt based on hostname.
- Supabase PKCE -- Catches
?code= query params on any path and forwards them to /auth/callback with the correct next destination based on hostname.
- Cookie-based session auth -- Validates
cw_session cookie via HMAC-SHA256 hash against church_admin_sessions table. Injects x-church-id, x-identity-id, x-identity-role headers on success.
- Legacy token passthrough --
/admin/[token] URL paths and ?token= query params pass through for backward compatibility.
PewSearch middleware (pewsearch/web/src/middleware.ts):
- Subdomain routing --
{slug}.pewsearch.com rewrites to /s/{slug} for Pro Website vanity URLs. Sets x-is-subdomain header so the layout can hide site chrome.
- Reserved subdomains (
www, api, admin, app) and localhost are excluded.
Future Domains (Acquired, Not Deployed)
| Domain | Vertical | Priority |
|---|
| funeralwiseai.com / memorialwiseai.com | Funeral homes | #1 |
| ShopWiseAI (TBD) | Auto repair shops | #2 |
| veterinarywiseai.com / VetWiseAI | Veterinary clinics | #3 |
| LegalWiseAI (TBD) | Small law firms | #4 |
3. External Services Matrix
All external services used across the portfolio, ordered by criticality.
| Service | Purpose | Used By | Auth Method | Priority |
|---|
| Stripe | Payments, subscriptions, checkout, billing portal | All 3 | API key (secret + publishable) | P0 |
| Supabase | Database + Auth + Storage (ONE shared instance, no staging) | All 3 | Service role key + anon key | P0 |
| Vercel | Hosting, CDN, serverless functions, cron jobs | All 3 | GitHub OAuth | P0 |
| LiveKit | Voice agent SIP gateway + real-time audio session management | churchwiseai-web | API key + secret | P0 |
| Cartesia | TTS (Cartesia Sonic) for voice calls via livekit-plugins-cartesia | churchwiseai-web | API key | P1 |
| Twilio | Phone numbers, SMS sending, inbound SMS webhooks | churchwiseai-web, pewsearch | Account SID + auth token | P0 |
| Resend | Transactional email (magic links, welcome, alerts, notifications) | All 3 | API key | P1 |
| Anthropic (Claude) | Chatbot primary LLM (Haiku 4.5), Care Agent, batch generation via CLI | All | API key + CLI ($200/mo Max plan) | P1 |
| OpenAI | Embeddings (text-embedding-3-small) + chatbot LLM fallback (gpt-4o-mini) | churchwiseai-web | API key | P1 |
| Google AI (Gemini) | Voice agent primary LLM (Gemini 2.5 Flash) | churchwiseai-web | API key | P1 |
| MailerLite | Subscriber CRM, newsletter management | All 3 | JWT key | P2 |
| Google APIs | OAuth (social), Calendar, Drive (knowledge sync), Places | churchwiseai-web | Client ID + secret | P2 |
| Meta | Facebook/Instagram OAuth for ShareWiseAI | churchwiseai-web | App ID + secret | P2 |
| LinkedIn | OAuth for ShareWiseAI | churchwiseai-web | Client ID + secret | P2 |
| X (Twitter) | OAuth for ShareWiseAI | churchwiseai-web | Client ID + secret | P2 |
| TikTok | OAuth for ShareWiseAI | churchwiseai-web | Client key + secret | P2 |
| Pinterest | OAuth for ShareWiseAI | churchwiseai-web | App ID + secret | P2 |
| PostHog | Product analytics, event tracking | All 3 | API key (public) | P3 |
| Cloudflare Turnstile | CAPTCHA on public forms | churchwiseai-web | Site key + secret key | P3 |
| Cal.com | Booking/scheduling embeds | churchwiseai-web | Embed (no API key) | P3 |
| Outscraper | Church data scraping (Google Maps) | PewSearch | API key | P3 |
| TMDB | Movie data for sermon illustrations | ITW | API key | P3 |
| Unsplash | Photo library for illustrations | ITW | Access key | P3 |
| ElevenLabs | TTS (legacy/fallback, mostly unused) | churchwiseai-web | API key | P3 |
| Deepgram | STT for voice agent calls (via livekit-plugins-deepgram) | churchwiseai-web | API key | P1 |
Stripe Account Details
- Account: churchwiseai@gmail.com (ONE account, TWO modes)
- Live mode ID:
acct_1SSPz2FaoK5IPzNo
- Test mode ID:
acct_1SSPzDF8WTm3d6SH
- 24 active products in live mode (post 2026-05-04 VetWiseAI launch); test mode mirrors with a slightly different count due to consolidated annual products (see knowledge/data/pricing.yaml)
- Test mode is CLI default; live mode requires
--api-key flag with full sk_live_ key
- See
C:\dev\PRICING.md for all product/price IDs
Supabase Instance
- Project ID:
wrwkszmobuhvcfjipasi
- URL:
https://wrwkszmobuhvcfjipasi.supabase.co
- Single instance serves both development and production -- no staging database
- 327K records in
unified_rag_content (irreplaceable)
- 218K+ visible church listings in
churches
4. Environment Variables
All env var names grouped by service. Values are never stored in this document.
Supabase
| Variable | Scope | Used By |
|---|
NEXT_PUBLIC_SUPABASE_URL | Public | All 3 |
NEXT_PUBLIC_SUPABASE_ANON_KEY | Public | All 3 |
SUPABASE_SERVICE_ROLE_KEY | Server | All 3 |
Stripe
| Variable | Scope | Used By |
|---|
STRIPE_SECRET_KEY | Server | All 3 |
STRIPE_WEBHOOK_SECRET | Server | All 3 |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | Public | All 3 |
STRIPE_PREMIUM_MONTHLY_PRICE_ID | Server | CWA, PewSearch |
STRIPE_PRO_WEBSITE_MONTHLY_PRICE_ID | Server | PewSearch |
STRIPE_PRICE_STARTER_CHAT | Server | CWA |
STRIPE_PRICE_STARTER_CHAT_ANNUAL | Server | CWA |
STRIPE_PRICE_PRO_CHAT | Server | CWA |
STRIPE_PRICE_PRO_CHAT_ANNUAL | Server | CWA |
STRIPE_PRICE_SUITE_CHAT | Server | CWA |
STRIPE_PRICE_SUITE_CHAT_ANNUAL | Server | CWA |
STRIPE_PRICE_STARTER_VOICE | Server | CWA |
STRIPE_PRICE_PRO_VOICE | Server | CWA |
STRIPE_PRICE_STARTER_BOTH | Server | CWA |
STRIPE_PRICE_PRO_BOTH | Server | CWA |
STRIPE_PRICE_SUITE_BOTH | Server | CWA |
STRIPE_PRICE_AI_STARTER_KIT | Server | CWA |
STRIPE_SERMON_PRO_MONTHLY_PRICE_ID | Server | CWA |
STRIPE_SERMON_PRO_ANNUAL_PRICE_ID | Server | CWA |
STRIPE_SOCIAL_PRO_PRICE_ID | Server | CWA |
STRIPE_SOCIAL_BUSINESS_PRICE_ID | Server | CWA |
STRIPE_SOCIAL_AGENCY_PRICE_ID | Server | CWA |
STRIPE_PREMIUM_ANNUAL_PRICE_ID | Server | ITW |
LLM / AI
| Variable | Scope | Used By |
|---|
OPENAI_API_KEY | Server | CWA, PewSearch, ITW |
ANTHROPIC_API_KEY | Server | CWA, ITW |
GEMINI_API_KEY | Server | CWA |
Voice / Telephony
| Variable | Scope | Used By |
|---|
LIVEKIT_URL | Railway | CWA voice agent worker |
LIVEKIT_API_KEY | Railway | CWA voice agent worker |
LIVEKIT_API_SECRET | Railway | CWA voice agent worker |
CARTESIA_API_KEY | Railway | CWA voice agent TTS |
CARTESIA_FEMALE_VOICE_ID | Railway | CWA voice agent TTS |
CARTESIA_MALE_VOICE_ID | Railway | CWA voice agent TTS |
DEEPGRAM_API_KEY | Railway | CWA voice agent STT |
TWILIO_ACCOUNT_SID | Server | CWA, PewSearch |
TWILIO_AUTH_TOKEN | Server | CWA, PewSearch |
TWILIO_FROM_NUMBER | Server | CWA, PewSearch |
VOICE_AGENT_WS_URL | Server | CWA (legacy) |
ELEVENLABS_API_KEY | Server | CWA (legacy fallback) |
TTS_PROVIDER | Server | CWA |
Email
| Variable | Scope | Used By |
|---|
RESEND_API_KEY | Server | All 3 |
ADMIN_EMAIL | Server | CWA, PewSearch |
ADMIN_PHONE | Server | CWA, PewSearch |
MAILERLITE_API_KEY | Server | CWA, ITW |
MAILERLITE_PROXY_SECRET | Server | All 3 |
MAILERLITE_PROXY_URL | Server | PewSearch, ITW |
MAILERLITE_WEBHOOK_SECRET | Server | CWA |
Social OAuth (ShareWiseAI)
| Variable | Scope | Used By |
|---|
META_APP_ID | Server | CWA |
META_APP_SECRET | Server | CWA |
META_WEBHOOK_VERIFY_TOKEN | Server | CWA |
GOOGLE_CLIENT_ID | Server | CWA |
GOOGLE_CLIENT_SECRET | Server | CWA |
GOOGLE_REDIRECT_URI | Server | CWA |
LINKEDIN_CLIENT_ID | Server | CWA |
LINKEDIN_CLIENT_SECRET | Server | CWA |
X_CLIENT_ID | Server | CWA |
X_CLIENT_SECRET | Server | CWA |
TIKTOK_CLIENT_KEY | Server | CWA |
TIKTOK_CLIENT_SECRET | Server | CWA |
PINTEREST_APP_ID | Server | CWA |
PINTEREST_APP_SECRET | Server | CWA |
SOCIAL_OAUTH_STATE_SECRET | Server | CWA |
SOCIAL_TOKEN_ENCRYPTION_KEY | Server | CWA |
Analytics & Security
| Variable | Scope | Used By |
|---|
NEXT_PUBLIC_POSTHOG_KEY | Public | CWA |
NEXT_PUBLIC_POSTHOG_HOST | Public | CWA |
NEXT_PUBLIC_POSTHOG_API_KEY | Public | PewSearch, ITW |
NEXT_PUBLIC_TURNSTILE_SITE_KEY | Public | CWA |
TURNSTILE_SECRET_KEY | Server | CWA |
Application Secrets
| Variable | Scope | Used By |
|---|
APP_SECRET | Server | CWA (HMAC session hashing) |
ADMIN_SECRET | Server | CWA, PewSearch |
CRON_SECRET | Server | CWA (Vercel cron auth) |
INTERNAL_API_KEY | Server | CWA |
INTERNAL_SOCIAL_KEY | Server | CWA |
DOCS_ADMIN_KEY | Server | CWA |
NEXT_PUBLIC_DOCS_ADMIN_KEY | Public | CWA |
OPS_ALERT_EMAIL | Server | CWA |
OPS_INGEST_KEY | Server | CWA |
STARTER_KIT_DOWNLOAD_SECRET | Server | CWA |
FOUNDER_TOKEN | Server | CWA |
GITHUB_TOKEN | Server | CWA (knowledge sync) |
External Data
| Variable | Scope | Used By |
|---|
GOOGLE_PLACES_API_KEY | Server | CWA, PewSearch |
PLANNING_CENTER_API | Server | PewSearch |
TMDB_API_KEY | Server | ITW |
UNSPLASH_ACCESS_KEY | Server | ITW |
Site Configuration
| Variable | Scope | Used By |
|---|
NEXT_PUBLIC_SITE_URL | Public | All 3 |
NEXT_PUBLIC_SITE_NAME | Public | PewSearch, ITW |
API_URL | Server | CWA |
Test-Only
| Variable | Scope | Used By |
|---|
SERMON_TEST_EMAIL | Server | CWA (dev) |
SERMON_TEST_PASSWORD | Server | CWA (dev) |
5. Cron Jobs
Defined in churchwiseai-web/vercel.json. All run on Vercel Cron and are authenticated via CRON_SECRET bearer token.
| Schedule | Route | Purpose |
|---|
*/15 * * * * (every 15 min) | /api/ops/collect | Collects quota/health checks from Twilio, Resend, and Supabase. Records snapshots to ops_quota_snapshots. Fires P0 alerts for critical conditions (low balance, connection pool, rate limits). |
0 7 * * * (daily at 7:00 UTC) | /api/cron/daily-audit | Security and operational audit. Checks pending founder actions, active customer count, recent voice calls, error rates, and subscription health. Generates WatchTower alerts. |
30 7 * * * (daily at 7:30 UTC) | /api/founder/sync-knowledge | Syncs YAML/Markdown files from the knowledge/ repo to Google Drive as formatted Google Docs. Covers pricing, features, products, policies, vision, and strategy documents. |
30 8 * * * (daily at 8:30 UTC) | /api/cron/knowledge-check | Pricing integrity and knowledge drift check. Verifies pricing.ts tiers match canonical YAML, product_knowledge has required entries, Stripe price env vars are present. Creates P0 founder action item and sends email alert on drift. |
6. Voice Agent Deployment
The voice agent runs on LiveKit Cloud + Railway, not on Vercel or any Cartesia-managed infrastructure.
| Field | Value |
|---|
| Technology | LiveKit Agents SDK (Python 3.11+) |
| SDK Version | livekit-agents~=1.5 |
| Code location | churchwiseai-web/voice-agent-livekit/ |
| Deploy command | Push to GitHub (Railway auto-deploys) |
| Deploy environment | Railway (agent worker) + LiveKit Cloud (SIP gateway) |
| Architecture | Multi-tenant: ONE agent worker serves ALL churches |
| Routing | main.py routes by sip.trunkPhoneNumber from LiveKit room |
| Primary LLM | Gemini 2.5 Flash |
| Fallback / Care LLM | Claude Haiku 4.5 |
| STT | Deepgram (via livekit-plugins-deepgram) |
| TTS | Cartesia Sonic (via livekit-plugins-cartesia) |
Key Files
All files are under churchwiseai-web/voice-agent-livekit/.
| File | Purpose |
|---|
main.py | Entry point -- routes calls by sip.trunkPhoneNumber |
livekit.toml | LiveKit project config (project URL, agent registration) |
session.py | Supabase client, call lifecycle, caching, product knowledge loading |
turn_processor.py | Moderation, noise filtering, farewell detection |
verticals/church/agents.py | Coordinator + Care agent builders |
verticals/church/prompts.py | Per-church prompt templates |
verticals/church/tools.py | Prayer requests, callbacks, visitor capture |
verticals/sales/agents.py | Sales + Demo agent builders |
core/ | Shared tools, RAG, notifications, prompt fragments |
New Church Setup
- Insert row in
church_voice_agents table with church config
- Run
voice-agent-livekit/scripts/setup_sip.py to configure the Twilio → LiveKit SIP trunk for the church phone number
- No redeploy needed -- the multi-tenant agent picks up new churches at call time
Legacy (Do Not Modify)
churchwiseai-web/voice-agent/ -- Old Node.js/Railway voice agent. Left in repo for reference only.
churchwiseai-web/voice-agent-line/ -- Previous Cartesia LINE SDK agent (replaced by voice-agent-livekit/ in March 2026). Left in repo for reference only.
7. Email Infrastructure
Transactional Email (Resend)
All transactional email is sent via Resend. Each property uses its own sender domain.
| Property | From Address | Email Types |
|---|
| ChurchWiseAI | hello@churchwiseai.com | Magic links, welcome emails, onboarding, contact confirmations |
| ChurchWiseAI (Alerts) | alerts@churchwiseai.com | WatchTower operational alerts, error notifications |
| PewSearch | hello@pewsearch.com | Magic links, welcome emails, claim notifications, care broadcasts |
| ITW | hello@illustratetheword.com | Account emails, subscription notifications |
| ShareWiseAI | hello@churchwiseai.com (shared sender) | Social post notifications |
Subscriber CRM (MailerLite)
- Account: JWT key stored in CWA
.env.local
- Access pattern: PewSearch and ITW proxy through CWA's MailerLite API endpoint (
MAILERLITE_PROXY_URL)
- Features used: Subscriber management, newsletter campaigns, audience segmentation (subscriber CRM only — lifecycle automation is handled by the Resend lifecycle email system)
- REST API fully accessible to agents -- never send the founder to the MailerLite dashboard
8. Monitoring and Alerting
Layers
| Layer | Tool | What it covers |
|---|
| Deployment logs | Vercel | Request logs, function errors, build output. Access: vercel logs --tail |
| Product analytics | PostHog | Page views, event tracking, funnels, session recording |
| Operational health | /api/ops/collect cron | Twilio balance, Resend usage, Supabase health. Runs every 15 minutes. Records to ops_quota_snapshots table |
| Daily audit | /api/cron/daily-audit cron | Founder actions, customer count, voice call volume, error rates, subscription health |
| Error reporting | ops-reporter.ts | Classifies errors as P0/P1 by route and message patterns. Records to ops_error_reports table with deduplication (fingerprint hash) |
| Founder dashboard | /founder/[token] | Executive dashboard with pending actions, system health, Drive sync status |
| WatchTower alerts | Daily audit cron | Email/SMS alerts for P0 conditions (quota exhaustion, payment failures, outages) |
Error Severity Classification
- P0 routes:
/api/stripe/, /api/onboard/, /api/chatbot/stream, /api/admin/ -- any error on these paths is P0
- P0 keywords: "quota", "rate limit", "connection pool" in error messages
- P1: Everything else
9. Security Summary
Transport
- HTTPS everywhere -- Vercel provisions and auto-renews SSL certificates for all domains
- HSTS enabled with
max-age=63072000; includeSubDomains; preload
Authentication
| Property | Auth Method | Details |
|---|
| CWA Admin | Cookie-based sessions + legacy URL tokens | cw_session cookie, HMAC-SHA256 hashed, validated against church_admin_sessions table |
| CWA SermonWise | Supabase Auth | PKCE flow, middleware redirects unauthenticated users |
| CWA ShareWiseAI | Supabase Auth | Same PKCE pattern as SermonWise |
| PewSearch Admin | Token-based (URL path) | UUID token in URL, validated against premium_churches.admin_token or church_team_members.access_token |
| ITW | Supabase Auth | Standard Supabase Auth flow |
Database Security
- Row Level Security (RLS) enabled on Supabase tables
- Service role key used server-side only (bypasses RLS for admin mutations)
- Anon key used for client-side queries (subject to RLS policies)
- RBAC with 9 roles:
admin, office_admin, prayer_team, care_team, treasurer, volunteer_coordinator, worship_leader, spiritual_leader, care_leader
- Pastoral data (confidential prayers, callback reasons) redacted for non-pastoral roles
Request Security
- CSRF validation on mutation endpoints (
src/lib/csrf.ts -- origin checking)
- Rate limiting on public-facing endpoints (
src/lib/rate-limit.ts -- in-memory)
- Webhook signature verification for Stripe (
stripe-signature header) and Twilio
- CRON_SECRET bearer token on all Vercel cron endpoints
Content Security Policy
Applied globally via next.config.ts with allowlists for: Stripe (scripts, frames, connect), Supabase (connect, WebSocket), PostHog (scripts, connect), YouTube/Vimeo (frames), Cal.com (scripts, frames), Cloudflare Turnstile (scripts, frames), Google (fonts, connect, frames), social platform APIs (connect). The /embed route uses a relaxed frame-ancestors * policy since it is loaded inside church website iframes.
| Header | Value |
|---|
X-Frame-Options | DENY (except /embed) |
X-Content-Type-Options | nosniff |
Referrer-Policy | strict-origin-when-cross-origin |
Strict-Transport-Security | max-age=63072000; includeSubDomains; preload |
Permissions-Policy | camera=(), microphone=(), geolocation=(), interest-cohort=() |
X-DNS-Prefetch-Control | on |
X-Robots-Tag | noindex (admin pages only) |
See Also
- Parent: System Architecture Overview -- portfolio overview, codebase ownership, tech stack summary
- Related: Database Schema -- table ownership, column reference, RPC functions
- Pricing:
C:\dev\PRICING.md -- all Stripe product/price IDs, billing rules, checkout flows
- Operations: Operations -- day-to-day procedures, runbooks
- Decision Log:
C:\dev\DECISION_LOG.md -- infrastructure decisions with rationale