Skip to Content
TechnicalArchitectureArchitecture Overview

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 config

For 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.

Learn more

Last updated on