What SaaSFoundry gives you
SaaSFoundry is two systems delivered through one project:
- a prebuilt SaaS foundation with authentication, tenant-aware RBAC, a typed API, a React application and production runtime;
- a development harness that connects coding agents to your specifications, ticket board, repository rules and validation workflow.
You can generate either system independently or combine them. Optional capabilities remain explicit: an inventory is useful only if it distinguishes what is always present from what you chose to install.
full = SaaS foundation + development harness
stack = SaaS foundation only
harness = development harness added to an existing codebaseCompare the three setup paths or inspect the generated topologies.
At a glance
| Capability | full | stack | harness | Optional |
|---|---|---|---|---|
| Authentication, tenant model and scoped RBAC | ✓ | ✓ | — | — |
| NestJS API, PostgreSQL and Prisma | ✓ | ✓ | — | — |
| React application and bilingual UI foundation | ✓ | ✓ | — | — |
| Workflow, core skills and integration rules | ✓ | — | ✓ | — |
| Manifest and update lifecycle | ✓ | ✓ | ✓ | — |
| MailerSend, S3, Analytics and PWA | When selected | When selected | — | ✓ |
| SRS centralization and external tool skills | When selected | — | When selected | ✓ |
The exact selection is recorded in .saasfoundry.json, so the CLI and coding agents read the same project contract.
{
"profile": "full",
"structure": "monorepo",
"modules": {
"harness": { "version": 1 },
"email": { "provider": "mailersend", "version": 1 }
}
}Learn how that contract evolves in Updating projects.
SaaS foundation
Authentication and session lifecycle
The generated API includes signup, signin, signout, current-user, account confirmation and password-reset flows. NestJS Passport strategies validate short-lived access tokens and refresh tokens; the browser receives both through httpOnly cookies instead of exposing tokens to application JavaScript.
async signIn(dto: SignInDto, response: Response) {
const { accessToken, refreshToken, userId } = await authService.signIn(dto)
authService.setAuthCookies(response, accessToken, refreshToken)
return { userId }
}Passwords are hashed with bcrypt, authenticated routes use JwtAuthGuard, and refresh-token state can be invalidated on signout. The web application includes the corresponding signin, signup, confirmation and reset screens.
Continue with RBAC and tenancy to see how identity becomes authorization.
Tenant, account and entity model
The data model represents a platform, customer accounts, organizations and nested entities instead of leaving multi-tenancy as an exercise for the first product team. Account status, membership, invitations and reactivation requests are part of the generated lifecycle.
Platform
└─ Account
├─ Users and role assignments
├─ Organization profile
└─ Entities (including parent/child hierarchy)The generated UI includes platform administration, account settings, users, roles, entities, invitations, profile and reactivation views. Read RBAC and tenancy for the enforcement model.
Scoped RBAC
Authorization is evaluated at PLATFORM, ACCOUNT or ENTITY scope. Roles combine modules, visible sub-modules and action permissions, while database constraints reject incoherent assignments.
@RequireAccess({
module: 'ACCOUNT_ADMINISTRATION',
subModule: 'USERS'
})
findUsers() {}
@RequirePermissions(['ACCOUNT_USER_MANAGEMENT'], 'ACCOUNT_ADMINISTRATION')
updateUser() {}The frontend hides unavailable routes and actions for clarity, but the NestJS guard remains authoritative. See the complete RBAC and tenancy guide.
Invitations and account reactivation
Account administrators can invite users into a scoped role; invitees accept a signed, expiring token. Deactivated customers can submit reactivation requests for platform review instead of requiring an improvised support-only flow.
invite created → token issued → user accepts → scoped role assigned
account inactive → reactivation requested → platform review → account activeWith the email provider disabled, rendered messages remain visible in development logs. Installing Email sends the same flows through MailerSend.
Internationalization
The React application is wired with i18next, browser language detection and YAML resources. English and French namespaces are seeded for authentication, navigation, account administration, dashboard, platform, profile and error pages.
# apps/web/src/locales/fr/auth.yml
signIn:
title: Connexion
submit: Se connecterNew domains extend the same namespace in both locale directories. Locale-aware preferences are persisted on the user model.
PostgreSQL and Prisma
The API uses Prisma 7 with the PostgreSQL driver adapter and a multi-file schema split by domain. SQL functions, triggers and seed data protect role-scope invariants and install the initial modules and system roles.
export class PrismaService extends PrismaClient {
constructor(env: EnvConfig) {
super({
adapter: new PrismaPg({ connectionString: env.get('DATABASE_URL') })
})
}
}prisma/schema/
├── schema.prisma
├── accounts.prisma
├── invitations.prisma
├── modules.prisma
├── organizations.prisma
└── users.prismaThe local development and test configurations use PostgreSQL too, reducing the gap between tests and production.
Typed API contract
NestJS 11 controllers describe operations with Swagger decorators. Request contracts use Zod 4 through nestjs-zod; startup generates a cleaned OpenAPI document at docs/openapi.json.
export const createEntitySchema = z.object({
name: z.string().trim().min(1),
parentId: z.string().uuid().optional()
})
export class CreateEntityDto extends createZodDto(createEntitySchema) {}In a monorepo, Orval turns that document into the shared api-client, including React Query hooks. Shared validation, types and configuration packages keep both applications on the same contract.
Zod schema → NestJS DTO → OpenAPI → generated API client → React QuerySee Project structure and Monorepo vs multirepo.
React application
The generated frontend uses React 19, React Router 7, Vite, Tailwind CSS 4, Radix primitives, ShadCN-style components, TanStack React Query and React Hook Form. Public and protected routes, query/error providers, layouts, navigation and responsive primitives are already connected.
const router = createBrowserRouter([...publicRoutes, ...privateRoutes])
root.render(
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
</QueryClientProvider>
)In monorepo mode, reusable UI primitives live in packages/ui-primitives; multirepo projects vendor the same primitives into the web repository.
Developer experience and quality gates
Each generated application includes TypeScript, flat ESLint configuration, Prettier, Jest or Vitest, API E2E tests and Playwright browser tests. Husky hooks validate commit messages, quick pre-commit checks and the heavier pre-push gate.
.husky/
├── prepare-commit-msg
├── commit-msg
├── pre-commit
└── pre-pushDocker starts the development database, and dedicated test Compose configuration isolates API database tests. Generated GitHub Actions are topology-aware and can limit jobs to the surfaces changed by a pull request.
The development tools guide explains the local loop.
Production runtime
The API and web projects include multi-stage Dockerfiles. Nginx serves the compiled SPA and proxies API traffic, while health checks, environment validation and Winston rotating logs make failures observable.
FROM node:22-alpine AS builder
RUN npm run build
FROM nginx:alpine AS runner
COPY --from=builder /app/dist /usr/share/nginx/html
HEALTHCHECK CMD wget --quiet --tries=1 --spider http://localhost:80/ || exit 1The CLI validates the generated manifest before lifecycle operations. Numbered manifest migrations and ordered module migrations upgrade owned configuration without silently overwriting user-owned code.
Read Updating projects for preview, three-way comparison, conflicts and recovery.
Development harness
One project contract
.saasfoundry.json records topology, ports, branches, tools, modules, agent profiles and managed-file baselines. CLI commands and generated skills consume it instead of duplicating project facts in prompts.
sf status --claude-friendly --no-networkThat command gives a coding agent a deterministic, offline summary before it changes the project. See Project structure.
Complexity-adaptive delivery workflow
The harness ships two guarded presets. The team workflow separates functional feature testing from code review; the Solo workflow combines its human gate with PR review. Interactive setup can also define and save a custom status sequence as an advanced extension point; it does not generate arbitrary status documents or guards.
Backlog → Ready → In progress → AI testing
→ Human testing → In review → DoneSolo: Backlog → In progress → AI testing → In review → DoneIn the team preset, Human testing means feature testing and In review means code review. Complexity changes the depth of analysis, planning, testing and review inside the configured phases.
bug direct fix + regression proof
low lightweight implementation
medium structured analysis and approved plan
complex deep analysis and adversarial reviewTransitions are executed through the configured board adapter. GitHub Projects provides the complete v1 contract; Jira and Linear adapters are experimental. Notion is the complete v1 SRS backend, not a full workflow tracker. Start with Workflow system.
Skills and integration grammar
Core sf-* skills teach agents how to inspect project state, respect ticket gates, wire backend and frontend layers, and use the selected board tool. They are project instructions, not a separate application hidden outside the repository.
.agents/skills/
├── sf-workflow/
├── sf-integration-rules/
├── sf-tool-github-projects/
└── sf-srs/ # when selectedThe integration rules cover Prisma model → NestJS service/controller → shared contract → React Query hook → route/form → RBAC permission, preventing a feature from being implemented in only one layer.
Read Skills system and Connect your tools.
Specification-to-delivery traceability
When the SRS capability is selected, requirements stay in the configured source of truth and are reconciled with native board tickets. The harness proposes conversational additions, waits for explicit approval, and preserves the Epic/FR/DS/TC relationship.
requirement → approved SRS update → reconciled ticket
→ guarded implementation → test evidence → PRV1 ships the Notion backend; other backends are adapter targets, not advertised as implemented. See One SRS source of truth and the SRS module.
Optional capabilities
Optional means supported and installable, not enabled in every scaffold. Select them during sf new or add compatible modules later with sf update.
Transactional email
The Email module activates MailerSend for account confirmation, password reset and invitations. Without it, the same templates and flows remain locally testable through development logging.
sf update --add-modules email \
--mailersend-api-key "$MAILERSEND_KEY" \
--mailersend-sender-email noreply@example.comS3-compatible storage
The Storage module adds uploads, pre-signed URLs, organization logo handling and either a local MinIO service or existing S3-compatible credentials.
sf update --add-modules storage --s3-setup dockerAnalytics and installability
Analytics adds production-only, privacy-friendly Umami loading. PWA adds the web manifest, branded icons and service-worker integration required for an installable application.
sf update --add-modules analytics,pwaExternal tools
Optional tool skills connect the harness to services such as Notion, Jira/Confluence, Figma or live library documentation. A declared integration still depends on credentials and host capabilities; SaaSFoundry reports unsupported combinations instead of pretending every connector is universally available.
sf modules list
sf modules info storage
sf update --dry-runSee Connect your tools for the support matrix and failure-safe behavior.