Architecture Overview
Sustentus is a Turborepo monorepo (pnpm workspaces) housing eight applications and two shared packages. The architecture promotes code reuse, consistent tooling, and independent deployment of each app to Vercel.
Monorepo structure
sustentus/
├── apps/
│ ├── web/ # Main platform — Next.js 16 (App Router, Turbopack), port 3000
│ ├── agent/ # Agentic layer — Next.js 16, port 3007
│ ├── console/ # Tenant management console — Next.js 16, port 3006
│ ├── dashboards/ # Role-based dashboards — Next.js 16, port 3005
│ ├── marketing/ # Marketing site — Next.js 15, port 3001
│ ├── docs/ # This documentation site — Nextra
│ ├── help/ # Help & support centre — Nextra
│ └── storybook/ # Component docs for @sustentus/ui — Storybook 10, port 3002
├── packages/
│ ├── ui/ # Shared shadcn (New York) components → @sustentus/ui
│ └── services/ # Business logic, DB, AI, email → @sustentus/services
├── package.json # Root workspace scripts
├── pnpm-workspace.yaml # Workspace definition (apps/*, packages/*)
├── turbo.json # Turborepo task orchestration
└── tsconfig.json # Base TypeScript configFor the full data-flow picture, see the system diagram.
Architecture principles
Workspace isolation
Each app and package has its own package.json, build config, and dev server, and can be developed
and deployed independently.
Shared packages
@sustentus/ui and @sustentus/services are consumed via the workspace protocol
("@sustentus/ui": "workspace:*"). @sustentus/services is import-scoped — apps import from a
specific entrypoint (/server, /shared, /ai, /email, /db, …), never the root in apps/web.
Build orchestration
Turborepo builds packages before the apps that depend on them, caches task outputs, and runs independent tasks in parallel.
Type safety
TypeScript 5 in strict mode throughout, each package extending the root tsconfig.json.
Where AI and the API live
There is no separate API app. apps/web (Next.js App Router) hosts both the platform UI and its
server surface — route handlers and Server Actions — and the runtime AI (the BRD agent) runs there
via @sustentus/services/ai. Business logic and data access live in @sustentus/services.
apps/agent is the agentic layer’s own app, deployed separately and gated on an authenticated Clerk
session. It is not an API app. It exists so agentic features have a surface of their own,
independent of the classic screens.
Since ai-foundation-hygiene the split between the two is explicit: @sustentus/services/ai is a
connector to the AI SDK and provider — defineAgent, the model constants, generateStructured,
the tenant-tool primitives and canned replay — and agent logic belongs to the app that owns it.
The agent app’s assistant, its prompt and the registers built on it live in apps/agent/lib/ai/;
the BRD and onboarding agents still in packages/services/src/ai/ are apps/web’s legacy surfaces,
frozen there rather than treated as the pattern to copy. That boundary is what lets the agentic
layer’s prompts change without rebuilding the platform app.
It does read tenant data, on tightly drawn terms. The assistant holds read-only, tenant-scoped
tools built with defineTenantTool (today: the catalogue) from @sustentus/services/server, called
from route handlers only. Each tool closes over the tenant resolved from the session and its input
schema carries no tenant id, so a tool cannot be pointed at another tenant by anything the
conversation says — one tenant at a time, chosen by the platform rather than by the model.