Deploy CentraKit apps as services in one Vercel project.
CentraKit is deployed as five independently built services in one Vercel project rooted at the
repository root. The root vercel.json owns service definitions, public routing, and cron schedules.
Shared packages continue to build through Turborepo.
Each service installs with pnpm install --filter <app>... and builds with
turbo run build --filter=<app>. Per-service ignoreCommand runs pinned turbo-ignore (with
--turbo-version so the pnpm catalog pin does not break version inference) so unaffected apps skip
the build (for example an api-only change rebuilds api, and docs when OpenAPI or docs/**
inputs change, but not saas / marketing / showcase).
Never list .git in .vercelignore. Selective skips and Turbo remote cache need git metadata on the
build machine. Keep ignoring agent trees, tests, apps/cli, and other non-runtime paths instead.
saas and api both enable the Workflow SDK. Their Turbo build outputs include
app/.well-known/workflow/**, and each pins a distinct WORKFLOW_QUEUE_NAMESPACE (saas / api)
so the shared deployment does not register duplicate __wkf_workflow_* queue consumers.
| Service | App root | First-party domain | Notes |
|---|---|---|---|
saas | apps/saas | app.centrakit.com | Authenticated SaaS surface. |
api | apps/api | api.centrakit.com | REST API and MCP server. |
docs | apps/docs | docs.centrakit.com | Fumadocs site generated from root docs/. |
marketing | apps/marketing | sample.centrakit.com | Public marketing template. |
showcase | apps/showcase | centrakit.com | Component/product UI showcase. |
Use the single project's Vercel-managed environment for all services. Shared values such as the
Supabase connection, Sentry, and one public origin (NEXT_PUBLIC_SITE_URL or the Vercel production domain) are configured once. AI Gateway uses OIDC (VERCEL_OIDC_TOKEN).
Run pnpm env:pull from the repository root to refresh .env.local (development) and
.env.production.local (production).
Turborepo filters build-time variables through each app's turbo.json; add every build-time secret there when an app needs it during build, typecheck, or another task.
Project environment variables are visible to every service. Vercel Services do not provide a general per-service environment allowlist, so treat server-only credentials as project-wide secrets. Use a separate Vercel project when a surface requires an independent credential or deployment-protection boundary.
The first-party project runs Vercel Functions in fra1 (Frankfurt) to match its Supabase
eu-central-1 region. Keep the function and database regions aligned. Static assets remain
globally distributed by Vercel's CDN.
Production domains route by hostname. On the shared preview URL, /api/v1/*, /api/mcp, and
the OAuth protected-resource metadata route select the API service; the catch-all selects SaaS.
Marketing, docs, and showcase previews require a preview domain when they need direct browser
verification.
Cron schedules belong to the project and call the production deployment with GET. Rewrite order
assigns each path to its service:
| Path | Service |
|---|---|
/api/v1/internal/google-calendar-sync | api |
/api/v1/workflows/audit-log/retention | api |
/api/v1/workflows/scheduler/watchdog | api |
/api/v1/internal/channel-webhook-jobs | api |
Keep /api/v1/:path* ahead of the SaaS catch-all and configure the same 32-character minimum
CRON_SECRET for both runtime services.
apps/api/public/openapi.json.packages/api-client.Regenerate committed API artifacts after route handler or schema documentation changes.