VF Tech Stack Source of Truth

VF Tech Stack Source of Truth

Last Verified: 2026-03-04 Purpose: Stop wasting time searching for things that are already configured. Read this before claiming anything is "missing" or "not set up."

Core Principle

If you're about to say "this env var isn't configured" or "I need to find this credential" — READ THIS FILE FIRST. This file is the authoritative record of what's deployed and where.


Deployment Architecture

App Domain Host Root Directory
Website valuefirstteam.com Vercel apps/website
Portal clients.valuefirstteam.com Vercel apps/portal
MCP Server Local Node.js apps/mcp-server

Vercel Environment Variables — Website (valuefirstteam.com)

All confirmed present in Vercel as of Mar 25, 2026:

Variable Purpose Status
HUBSPOT_PRIVATE_APP_KEY VF Team HubSpot API access Active
JWT_SECRET Auth token signing Active
SANITY_API_TOKEN Sanity CMS read/write Active
GOOGLE_SERVICE_ACCOUNT_JSON Google Calendar + Gmail (base64-encoded) Active (added Feb 24)
GEMINI_API_KEY Gemini API for AI features Active
ELEVENLABS_API_KEY ElevenLabs TTS Active
ELEVENLABS_CHRIS_VOICE_ID Chris's voice clone ID Active
MUX_TOKEN_ID Mux video hosting Active
MUX_TOKEN_SECRET Mux video hosting Active

Mux Player Usage:

  • Mux Player is a web component loaded via CDN: <script src="https://cdn.jsdelivr.net/npm/@mux/mux-player@latest"></script>
  • In Astro templates (.astro): use native HTML attributes directly (playback-id, accent-color, class, poster, preload)
  • In React components (.tsx): use dangerouslySetInnerHTML with a template string — do NOT use JSX <mux-player> with React props. React 18 does not map className to class for custom elements.
  • Always set preload="none" and explicit poster="https://image.mux.com/{PLAYBACK_ID}/thumbnail.webp?time=5" to prevent Error NaN metadata display
  • Thumbnail API: https://image.mux.com/{PLAYBACK_ID}/thumbnail.webp?time={SECONDS} (returns 200 with image/webp) | COACH_CHRIS_ENABLED | Feature flag for Coach Chris AI | Active | | KRISP_WEBHOOK_SECRET | Krisp integration webhook | Active | | INNGEST_EVENT_KEY | Inngest event ingestion | Active (added Mar 25) | | INNGEST_SIGNING_KEY | Inngest function signing/verification | Active (added Mar 25) | | SANITY_API_KEY | Sanity API key | Active | | HUBSPOT_PRIVATE_APP_KEY | HubSpot private app key | Active | | PUBLIC_SUPABASE_URL | Supabase project URL (live chat/Q&A) | Active (added Mar 30) | | PUBLIC_SUPABASE_ANON_KEY | Supabase anon/publishable key | Active (added Mar 30) | | SUPABASE_SERVICE_ROLE_KEY | Supabase service role key (server-side) | Active (added Mar 30) |

NEVER say any of these are missing. They are confirmed deployed.

Vercel Environment Variables — Portal (clients.valuefirstteam.com)

Variable Purpose Status
JWT_SECRET Auth token signing Active
HUBSPOT_VF_TEAM_TOKEN VF Team HubSpot access Active
HUBSPOT_CLIENT_{SLUG}_TOKEN Per-client HubSpot tokens Active per client
DEV_BYPASS_AUTH Dev mode flag Active (preview only)

Local Environment Variables (Development)

File Variables Notes
/mnt/d/.env GEMINI_API_KEY Windows line endings (\r\n) — use grep | tr -d '\r' | cut -d= -f2
apps/website/.env SANITY_API_TOKEN, SANITY_DEPLOY_TOKEN Deploy token for Sanity Studio deploys
Root .env HUBSPOT_ACCESS_TOKEN VF Team HubSpot
clients/{slug}/.env HUBSPOT_ACCESS_TOKEN Per-client HubSpot

Service Accounts & Credentials

Google Service Account

  • Key file: /mnt/d/credentials/google-service-account.json
  • Impersonates: chris.carolan@valuefirstteam.com
  • Method: Domain-wide delegation
  • Deployed as: Base64-encoded GOOGLE_SERVICE_ACCOUNT_JSON on Vercel (both website and portal)

Google Workspace CLI (gws) — Universal Google API Access

  • Command: gws (wrapper at /usr/local/bin/gws auto-mints token via service account)
  • Version: 0.9.1
  • Covers: Gmail, Drive, Calendar, Docs, Sheets, Slides, Tasks, and all other Google Workspace APIs
  • Auth: Transparent — wrapper runs scripts/google/gws-token.js to mint a token per invocation
  • Capability report: skills/infrastructure/gws-capability-report.md
# Usage — just type gws, no token prefix needed
gws gmail users messages list --params '{"userId": "me", "maxResults": 5}'
gws drive files list --params '{"pageSize": 5, "fields": "files(id,name,mimeType)"}'
gws calendar events list --params '{"calendarId": "primary", "maxResults": 5}'
gws docs documents get --params '{"documentId": "DOC_ID"}'
gws schema <service.resource.method>   # Discover any API method's parameters

When to use gws: Ad-hoc queries, exploring Google APIs, any read operation, quick one-liners. When to use purpose-built scripts: Complex operations with MIME encoding, state management, or sharing logic.

Available Google Scripts (Local)

Script Purpose
scripts/google/send-email.js Send email via Gmail
scripts/google/get-inbox.js Read Gmail inbox
scripts/google/get-calendar.js Read Google Calendar
scripts/google/sync-transcripts.js Sync Drive transcripts
scripts/google/create-draft.js Create Gmail draft
scripts/google/create-doc.js Create Google Doc (with sharing)
scripts/google/create-calendar-event.js Create calendar event
scripts/google/gws-wrapper.sh gws CLI wrapper (auto-mints token)
scripts/google/gws-token.js Token minter for gws

NEVER say "I don't have Gmail/Calendar/Drive access." The service account is ALWAYS available locally. Use gws for ad-hoc queries or purpose-built scripts for complex operations. On Vercel, it's the GOOGLE_SERVICE_ACCOUNT_JSON env var.

Sanity CMS

  • Project ID: 0efm0pow
  • Dataset: production
  • API Token: In apps/website/.env as SANITY_API_TOKEN
  • Deploy Token: In apps/website/.env as SANITY_DEPLOY_TOKEN (for Studio deploys only)
  • CLI Query Tool: scripts/sanity/query.js (6 presets + custom GROQ, auto-loads token)
  • NEVER use curl to query Sanity. Always use node scripts/sanity/query.js.

Supabase (Live Chat/Q&A + Community)

  • Project: lrdlfxqdoxzoxgpobbui
  • URL: https://lrdlfxqdoxzoxgpobbui.supabase.co
  • REST API: https://lrdlfxqdoxzoxgpobbui.supabase.co/rest/v1/
  • Credentials: apps/website/.env (PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY)
  • Access method: REST API (PostgREST) with service role key. NEVER use direct PostgreSQL connections from WSL2 — DB host is IPv6-only, WSL2 cannot reach it.
  • Migrations: Apply via Supabase Dashboard SQL Editor (not programmatic migration runners)
  • Schema: 9 tables (members, spaces, threads, posts, reactions, mentions, notifications, space_follows, thread_follows)
  • Required headers: apikey: {key}, Authorization: Bearer {key}, Accept: application/json

HubSpot

  • VF Team Portal: 40810431
  • Local MCP: mcp__hubspot__hubspot-* (22 tools, supports ALL objects — use when available)
  • CLI Tool: node scripts/hubspot/api.js — unified CLI, zero deps, auto-loads token, supports ALL objects (use when MCP didn't start)
  • Cloud MCP: mcp__claude_ai_HubSpot__* (does NOT support custom objects — NEVER use for VF Team)
  • Rule: If local MCP is available, use it. If not, use CLI. Never say HubSpot is unavailable.
  • Chris Owner ID: 474813558
  • Ryan Owner ID: 85787138

Build & Deploy

Operation Command Duration
Website build cd apps/website && npx pnpm build ~5-6 minutes
Portal build cd apps/portal && npx pnpm build ~3 minutes
Sanity Studio deploy cd apps/website/studio && npm install && SANITY_AUTH_TOKEN=$SANITY_DEPLOY_TOKEN npx sanity deploy ~1 minute
Git push triggers Vercel deploy git -C /mnt/d/Projects/value-first-operations push ~2-3 minutes

Chris prefers build-and-push to production. No dev server testing.


Content Infrastructure

System Location Notes
Content Vault /mnt/d/data/content-vault.db 34MB SQLite, schema v2, FTS5 search
Content Census /mnt/d/data/content-census-report.md 1,003 episodes, 114 articles, 1,496 canonical
Sanity CMS sanity.io project 0efm0pow Shows, episodes, articles, glossary terms
HubSpot Listings VF Team portal Intelligence reports, planning docs, ideal week, decision queue

Anti-Patterns (Things That Waste Time)

Bad Pattern Correct Action
Searching for GOOGLE_SERVICE_ACCOUNT_JSON It's on Vercel. Read this file.
Searching for SANITY_API_TOKEN location It's in apps/website/.env. Read this file.
Running hubspot-list-properties Read skills/hubspot/property-index/{object}.json instead.
Using curl for Sanity queries Use node scripts/sanity/query.js.
Using cloud HubSpot MCP for VF Team Use local MCP (mcp__hubspot__hubspot-*) if available, otherwise CLI (node scripts/hubspot/api.js).
Saying "I don't have access to Gmail" Use gws or scripts/google/*.js. Always available.
Writing a new script for a Google API query Use gws <service> <resource> <method> — it covers ALL Google APIs.
Claiming env vars aren't configured Check this file first. Then check Vercel dashboard.
Attempting direct PostgreSQL connection to Supabase Use REST API at /rest/v1/ with service role key. WSL2 is IPv6-blocked.