Skip to Content
TechnicalDeployment

Deployment

This guide covers how Sustentus applications are deployed. Each app in the monorepo deploys independently to Vercel as its own project, with its own environment variables and custom domain. Shared packages are built and bundled with the apps that consume them — they are not deployed separately.

For the full pipeline from commit to production (pre-commit, CI, and Vercel preview/production builds), see CI/CD pipeline.

Per-app Vercel configuration

Each app is a separate Vercel project. Set the root directory and build command, then add the app’s environment variables in the project settings.

AppRoot directoryBuild commandOutput directory
Agentapps/agentcd ../.. && pnpm agent:build.next
Marketingapps/marketingcd ../.. && pnpm marketing:build.next
Docsapps/docscd ../.. && pnpm docs:build.next
Storybookapps/storybookcd ../.. && pnpm storybook:buildstorybook-static

Install command is pnpm install for all projects.

Continuous deployment

Vercel deploys automatically: pushing to a PR branch publishes a preview build, and merging to main promotes to production. To roll back, open Deployments, find a previous deployment, and click Promote to Production.

Custom domains

In Project Settings > Domains, add the domain and configure DNS; the SSL certificate is provisioned automatically.

The web app is served at two domains on the one project: platform.sustentus.com and demo.sustentus.com. The second is the public demo front door — the app decides which host it is answering on from the DEMO_HOSTS allowlist, which is read at build time, so setting or changing it requires a redeploy. See Demo environment for the full runbook.

Environment variables

Manage variables in the Vercel dashboard or via the CLI:

vercel env add CLERK_SECRET_KEY production vercel env pull .env.local

Variables by app

Marketing:

# Required NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_... CLERK_SECRET_KEY=sk_... # Optional NEXT_PUBLIC_POSTHOG_KEY=phc_... NEXT_PUBLIC_POSTHOG_HOST=https://...

Agent:

# Required NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_... CLERK_SECRET_KEY=sk_... AI_GATEWAY_API_KEY=... MONGODB_URI=mongodb+srv://... MONGODB_DATABASE_NAME=sustentus-... # Optional PLATFORM_APP_URL=https://... AGENT_AUTHORIZED_PARTIES=https://ai.sustentus.com,https://platform.sustentus.com

The two Clerk variables must point at the same Clerk instance as the platform — that is what makes a platform session valid in the agent app on a shared root domain. Without them the app builds and then fails on every request, because the Clerk middleware cannot initialise. The Vercel project is agentic-interface, rooted at apps/agent.

AI_GATEWAY_API_KEY is the Vercel AI Gateway credential, and it is needed on two projects — agentic-interface (the assistant and its availability probe) and web (the BRD agent and the onboarding concierge) — for Production and Preview on each. Declaring it in turbo.jsonglobalEnv governs what turbo passes into build tasks under strict env mode and nothing else; a serverless function reads its own Vercel project’s runtime environment, which turbo.json does not populate. A missing key is silent rather than loud: the SDK falls back to the deployment’s OIDC identity and, failing that, throws, which the availability probe reports as unknown — and unknown renders nothing. Read the variable directly (vercel env ls --scope sustentus) rather than inferring it from a quiet UI. See AI Gateway for the account, the balance, and the smoke-test procedure.

The two Mongo variables are newer: since agentic-tenant-context the agent resolves the caller’s tenant and reads that tenant’s data through @sustentus/services/server, so it opens a database connection like the platform does. They carry the same runtime-not-build-time caveat as the gateway key — both are in turbo.jsonglobalEnv already, which governs the build and populates nothing at request time — so set them on agentic-interface for Production and Preview, pointing at the same databases the web project uses. Without them a tenant user’s context resolves to “no workspace” and a staff or partner caller is told their tenant directory cannot be listed: honest empty states rather than a crash, which is exactly why the misconfiguration is quiet. Read the variables directly rather than inferring them from a working page.

PLATFORM_APP_URL is the origin the platform app (web) is served from: since agent-shell-layout-polish the agent’s header carries a labelled link back to the platform, and this variable is its target. It is parsed with the same rules as the console’s CONSOLE_APP_URL — a bare host is completed to https, http for localhost — and it is a runtime read with the same caveat as the variables above, so set it on agentic-interface for Production and Preview. Unset, it fails safe: the header simply renders no platform link, which is why this misconfiguration is quiet too.

AGENT_AUTHORIZED_PARTIES is the agent app’s counterpart to the console’s CONSOLE_AUTHORIZED_PARTIES, added by ai-foundation-hygiene: a comma-separated allowlist of the origins whose Clerk sessions this deployment will accept, passed to clerkMiddleware as authorizedParties. It is deliberately opt-in and empty by default. Sharing authentication across subdomains of one root domain is exactly what lets a platform session sign the user in here without satellite configuration, and it is the same property a compromised sibling subdomain would abuse — naming the origins shuts that door in production, while leaving it unset keeps preview deployments working, since they are served from per-commit hostnames no allowlist can predict. Set it on Production only, and note that until you do, the variable buys nothing: the code reads it, the security benefit starts when a value exists. The same is true of CONSOLE_AUTHORIZED_PARTIES on tenant-management.

Documentation and Storybook: none required (static documentation / component showcase).

Scheduled jobs (cron)

The web app runs scheduled work through Vercel cron jobs , declared in apps/web/vercel.json. Crons run only against the production deployment.

SchedulePathJob
0 1 * * */api/cron/demo-resetPurges every isDemo: true tenant back to the approved storyline, so the demo opens clean each morning.
0 2 * * */api/cron/csm-portfolio-snapshotWrites the daily per-CSM portfolio snapshot so the go live trend is a real day-over-day delta.

The order is deliberate: the reset runs an hour before the snapshot, so each morning’s snapshot describes the freshly reset world rather than the residue of yesterday’s walkthrough.

Cron routes are protected by a shared secret. Set CRON_SECRET for the web app; Vercel sends it as Authorization: Bearer <CRON_SECRET> on each cron request, and the route rejects anything else with 401, so it is unreachable from a normal user session.

vercel env add CRON_SECRET production

Build verification

Test a production build locally before deploying:

pnpm marketing:build cd apps/marketing pnpm start # Visit http://localhost:3001

Static export (docs)

The documentation site can be exported as a static site by setting output: "export" in apps/docs/next.config.ts; pnpm docs:build then emits to apps/docs/out/ for upload to any static host.

Storybook hosting

pnpm storybook:build outputs to apps/storybook/storybook-static/, which can be deployed to any static host. Chromatic  additionally provides Storybook hosting, visual testing, and component review:

pnpm add -D chromatic --filter=@sustentus/storybook npx chromatic --project-token=<token>

Learn More

Last updated on