Deployment
client-os targets three platforms: Render for the API, Vercel for the web app, and Supabase Cloud for the database and auth. This page summarizes what's involved — for the full, authoritative detail, read the two source documents directly:
docs/PROD-DEPLOY.md— the first-production-deploy checklist.docs/RENDER-RUNBOOK.md— operational reference for the deployed API service.
The core gotcha: a git push alone doesn't bring prod up
Neither Render nor Vercel run this repo's @cogs/config file-loader
(server-preload.cjs) at runtime. That means every environment variable the
app reads — not just secrets, but the whole type: 'public' set too — has
to be entered directly into each platform's own environment settings.
Committed .env.prod files in this repo are not read on either
platform; they exist for local reference only.
First deploy, in order
- Supabase Cloud — confirm the real project ref, grab the anon key and
JWKS/issuer URLs, and confirm email confirmations are ON (local dev turns
them off for frictionless testing; that setting doesn't travel to the
cloud project). This repo has no custom access-token hook and no
RLS — org-scoping is enforced entirely at the app layer
(
apps/api/src/auth/middleware.ts), so the usual "register the access-token hook" step doesn't apply here. - Migrate the prod database —
pnpm --filter @client/db db:deployagainst the Supabase session pooler (:5432,sslmode=require; the transaction pooler on:6543isn't reachable from Render's egress). Never run the demo seed scripts against a cloud connection. - Render (API) — set
ENV=prod,NODE_ENV=production, and every var the API's runtime actually reads (seerender.yaml's annotated list). Never setAUTH_DEV_SECRETorSUPABASE_SERVICE_ROLE_KEY— neither is read by the API's production code path, and the former is explicitly rejected underNODE_ENV=production. - Vercel (web) — set
ENV=prod,NODE_ENV=production,API_URL(the live Render URL, not alocalhostleftover), and the Supabase public URL/key, all in the dashboard's Production environment variables. - Verify — API health check,
/and/loginrender, and a full login round-trip confirmingGET /api/mereturns a non-emptyrolesarray (this repo's equivalent of "the JWT carries org context").
Operating the live API
docs/RENDER-RUNBOOK.md is the day-2 reference once the API is live:
service identity and dashboard links, the confirmed DATABASE_URL pooler
host (watch for aws-1 vs the correct aws-0 — a real incident that caused
every authenticated route to 500), why a plain Render /restart doesn't pick
up new env vars (only a real deploy does), and a post-deploy verification
checklist including a reminder that Vercel env var changes require an
actual rebuild — reading a correct value back from the Vercel API is not
proof the live page is serving it. Always curl the live payload to confirm.
Automation
scripts/deploy-env.mjs dry-runs (and, with --apply, executes) pushing the
per-app .env.prod.local values to both platforms:
node scripts/deploy-env.mjs --stage prod # dry-run, prints the plan
node scripts/deploy-env.mjs --stage prod --apply # writes to both platforms
The database migration and the deploy trigger itself are intentionally left as manual, deliberate human actions — they're not scripted.