Skip to main content

Deployment and Environments

SalesArck uses split deployment targets by app role.

Deployment Topology

AppPlatformURLPlan
APIRenderhttps://salesarc-api.onrender.comFree (web service)
WebVercelhttps://salesarc-web.vercel.appFree (static SPA)
WorkersNot yet deployedScaffolded in apps/workers

Deployment Configuration Files

Render (render.yaml)

services:
- type: web
name: salesarc-api
runtime: node
plan: free
buildCommand: pnpm install --frozen-lockfile --prod=false && pnpm turbo run build --filter=@salesarc/api...
startCommand: pnpm --filter @salesarc/api start
healthCheckPath: /health
autoDeploy: true

Environment variables set via Render dashboard:

  • NODE_VERSION=20, NODE_ENV=production, PORT=3000
  • SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, DATABASE_URL
  • CORS_ALLOWED_ORIGINS, API_BASE_URL, FRONTEND_URL
  • ENCRYPTION_KEY, SQUARE_APP_ID, SQUARE_APP_SECRET, SQUARE_ENVIRONMENT
  • SQUARE_WEBHOOK_SIGNATURE_KEY, CRON_SECRET
  • CLOVER_APP_ID, CLOVER_APP_SECRET (when Clover is enabled)
  • RESEND_API_KEY, RESEND_FROM_EMAIL

Note: Cron jobs cannot run on Render free plan. Square polling requires manual invocation or external scheduler.

Vercel (vercel.json)

{
"buildCommand": "pnpm turbo run build --filter=@salesarc/web",
"outputDirectory": "apps/web/dist",
"installCommand": "pnpm install --frozen-lockfile",
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}

Framework: none (static SPA with client-side routing via rewrites).

Fly.io (fly.toml) — Alternative/Future

app = "salesarc-api"
primary_region = "iad"
vm.memory = "512mb"

Present in repo as an alternative deployment target. Not actively used.

Environment Validation

Backend env contract is validated in apps/api/src/lib/env.ts using Zod.

Startup fails fast on invalid configuration.

Core required variables

VariableTypeDescription
DATABASE_URLstringPostgres connection (Supabase Transaction Pooler)
SUPABASE_URLstring (URL)Supabase project URL
SUPABASE_SERVICE_ROLE_KEYstringService role key (server-side only)

Configurable defaults

VariableDefaultDescription
NODE_ENVdevelopmentdevelopment, staging, production
PORT3000HTTP listen port
CORS_ALLOWED_ORIGINS*Comma-separated origins or *
LOG_LEVELinfodebug, info, warn, error
SQUARE_ENVIRONMENTsandboxsandbox or production
CLOVER_ENVIRONMENTsandboxsandbox or production

Conditional requirements

When Square credentials are provided:

  • ENCRYPTION_KEY must be 64 hex chars (openssl rand -hex 32)
  • In production, API_BASE_URL and FRONTEND_URL are required

Optional but operationally critical

VariableDescription
SENTRY_DSNError tracking
RESEND_API_KEYEmail notifications
RESEND_FROM_EMAILSender address (must be verified Resend domain)
CRON_SECRETInternal cron auth (min 16 chars)
SQUARE_WEBHOOK_SIGNATURE_KEYWebhook HMAC verification
CLOVER_WEBHOOK_SECRETClover webhook HMAC verification

Cron and Polling Model

Square poll endpoint:

  • POST /api/v1/internal/cron/square-poll-payments
  • Requires header X-Cron-Secret matching CRON_SECRET env var
  • Not available on Render free plan as a scheduled service
  • Can be invoked externally (e.g., cron-job.org, GitHub Actions, or manual curl)

This route acts as fallback/backfill in addition to webhooks.

Database and Migration Operations

Monorepo scripts:

  • pnpm db:generate — Generate Drizzle migration SQL from schema changes
  • pnpm db:migrate — Apply pending migrations
  • pnpm db:studio — Launch Drizzle Studio (visual DB editor)

Recommended operational pattern:

  • Runtime API uses pooled DATABASE_URL (Supabase Transaction Pooler)
  • Migration tasks use direct DB URL where required by platform

Frontend Runtime Variables

Web app relies on Vite env for runtime integration:

VariableRequiredDescription
VITE_SUPABASE_URLYesSupabase project URL
VITE_SUPABASE_ANON_KEYYesPublic anon key (safe to expose)
VITE_API_BASE_URLNoAPI base override (default: uses Vite proxy in dev)
VITE_APP_ORIGINNoAuth redirect origin override

Missing Supabase URL/key is treated as fatal at startup.

Development Setup

Local development uses:

  • Vite dev server on port 5173 with proxy: /api -> http://localhost:3000
  • API dev server via tsx watch on port 3000
  • Supabase project for auth (shared between local and deployed)
  • Postgres via Supabase (same DB for dev/staging unless configured separately)

Deployment Safety Checklist

Before production deploy:

  1. Run lint, typecheck, and test across workspace
  2. Verify API and web env parity (base URLs, origins)
  3. Ensure Square/Clover environment (sandbox|production) matches credentials
  4. Validate cron secret consistency between scheduler and API
  5. Verify webhook signature key is present for production security
  6. Confirm ENCRYPTION_KEY is 64 hex chars when POS credentials are configured
  7. Verify Resend from-email domain is verified if email notifications are desired

Known Constraints

  • Workers app is scaffolded but not yet queue-backed
  • Webhook processing uses in-process async callback model (setImmediate)
  • Poll fallback is required for resilience until durable queue worker path is complete
  • Render free plan has cold start latency and no built-in cron scheduler
Written byDhruv Doshi