---
title: "Open SaaS Architecture Overview"
description: "Open SaaS system design — database schema, authentication, payments, file uploads, and AI integration"
image: "https://tractiongrowth.com/og/open-source/open-saas-wasp-starter/architecture/image.png"
---

# Open SaaS Architecture Overview

# Open SaaS Architecture Overview

<Callout title="For Product Managers">
  This page explains *how Open SaaS is structured* as a software system. Focus on the diagrams and the "Why It Matters" callouts to understand how the technical pieces support the business logic.
</Callout>

Open SaaS uses a full-stack monolith generated by Wasp, with React on the frontend, Node.js server functions on the backend, Prisma and PostgreSQL for data, and third-party services for billing, storage, and AI. This page explains how those layers work together to deliver a SaaS starter that is fast to customize but still production-ready.

## How Open SaaS Architecture Works

Open SaaS is a **full-stack monolith** — frontend and backend deploy as a single unit via the Wasp framework.

```
┌─────────────────────────────────────────────────────────────┐
│                      USERS (Browser)                        │
└──────────────────────────┬──────────────────────────────────┘
                           │
              ┌────────────▼────────────┐
              │   Wasp Application       │
              │                          │
              │  ┌─────────────────────┐ │
              │  │  React Frontend     │ │  ← ShadCN UI + Tailwind
              │  │  (SPA)              │ │     client-side routing
              │  └────────┬────────────┘ │
              │           │ Wasp RPC      │  ← auto-generated, type-safe
              │  ┌────────▼────────────┐ │
              │  │  Node.js Backend    │ │  ← Express under the hood
              │  │  (API + Jobs)       │ │
              │  └────────┬────────────┘ │
              └───────────┼──────────────┘
                          │
           ┌──────────────┼──────────────────┐
           │              │                  │
    ┌──────▼──────┐ ┌────▼──────┐   ┌───────▼──────┐
    │ PostgreSQL   │ │   PgBoss  │   │   External   │
    │  (Prisma)    │ │(Job Queue)│   │   Services   │
    │              │ │           │   │              │
    │ • Users      │ │ • Cron    │   │ • Stripe     │
    │ • Tasks      │ │ • Email   │   │ • LemonSqzy  │
    │ • Files      │ │ • Stats   │   │ • OpenAI     │
    │ • GptResp    │ │           │   │ • AWS S3     │
    │ • DailyStats │ └───────────┘   │ • Analytics  │
    └──────────────┘                 └──────────────┘
```

<Callout type="info" title="Why It Matters">
  Unlike Plane (which has separate frontend, API, and real-time services), Open SaaS is a **monolith** — frontend and backend are one deployable unit. Wasp acts as the "glue" that connects them with auto-generated, type-safe RPC calls. This makes it much simpler to deploy and reason about, at the cost of less horizontal scalability.
</Callout>

## How Wasp Works (The Magic Layer)

Wasp is the key to understanding Open SaaS. The `main.wasp` file is the **single source of truth** for the entire application:

```
main.wasp
  │
  ├── Routes        → which pages exist and their URLs
  ├── Auth          → which auth methods are enabled
  ├── Queries       → read operations (auto-typed frontend → backend)
  ├── Actions       → write operations (auto-typed frontend → backend)
  ├── Jobs          → background tasks (cron or one-off)
  ├── Entity        → which Prisma models to expose
  └── Dependencies  → npm packages used
```

**What Wasp generates:**

* Express routes for every query/action
* React hooks (`useQuery`, `useAction`) for calling them from the frontend
* Full TypeScript types that flow from Prisma schema → backend → frontend
* Auth middleware, session management, and user context
* Job scheduling infrastructure using PgBoss

<Callout type="info" title="For PMs: Why This Matters">
  Wasp eliminates an entire category of bugs — the frontend and backend always agree on what data looks like because types are auto-generated from the database schema. A developer changes the database, and TypeScript immediately flags every place in the frontend that needs updating. This dramatically reduces "integration bugs" where the frontend sends the wrong data format.
</Callout>

## Database Schema

Open SaaS uses PostgreSQL with **Prisma ORM**. The schema is defined in `schema.prisma`.

### Entity Relationship Map

```
┌───────────────────────────────────────┐
│                 User                  │
│  ─────────────────────────────────    │
│  id, email, username                  │
│  isAdmin                              │
│  subscriptionStatus, subscriptionPlan │
│  credits (default: 3)                 │
│  paymentProcessorUserId               │
└───┬──────┬──────┬──────┬──────────────┘
    │      │      │      │
    ▼      ▼      ▼      ▼
  Tasks  Files  GptResp  ContactForm
                  │       Messages
                  │
                  ▼
            DailyStats (aggregated analytics)
```

### User Model (Core Entity)

| Field                    | Type             | Purpose                                |
| ------------------------ | ---------------- | -------------------------------------- |
| `id`                     | UUID             | Primary key                            |
| `email`                  | String (unique)  | Login identifier                       |
| `username`               | String (unique)  | Display name                           |
| `isAdmin`                | Boolean          | Admin access flag                      |
| `subscriptionStatus`     | String?          | `active`, `canceled`, `past_due`, etc. |
| `subscriptionPlan`       | String?          | `hobby`, `pro`, or null                |
| `credits`                | Int (default: 3) | Available AI credits                   |
| `paymentProcessorUserId` | String?          | Stripe/LemonSqueezy customer ID        |
| `datePaid`               | DateTime?        | Last payment date                      |

### Supporting Models

| Model                  | Key Fields                                   | Purpose                       |
| ---------------------- | -------------------------------------------- | ----------------------------- |
| **GptResponse**        | `content` (JSON string), `user`              | Stores AI-generated schedules |
| **Task**               | `description`, `time`, `isDone`, `user`      | User tasks for AI scheduling  |
| **File**               | `name`, `type`, `s3Key`, `user`              | Uploaded file metadata        |
| **DailyStats**         | `date`, `totalViews`, `userCount`, `revenue` | Aggregated analytics          |
| **ContactFormMessage** | `content`, `user`                            | User-submitted contact forms  |

## Authentication System

### How Auth Works

Auth is configured declaratively in `main.wasp`:

```
auth: {
  userEntity: User,
  methods: {
    email: {
      emailVerification: { ... },
      passwordReset: { ... },
    },
    // google: {},   ← uncomment to enable
    // github: {},   ← uncomment to enable
    // discord: {},  ← uncomment to enable
  },
}
```

### Supported Methods

| Method             | Status          | Details                                               |
| ------------------ | --------------- | ----------------------------------------------------- |
| **Email/Password** | Active          | Email verification required, password reset via email |
| **Google OAuth**   | Ready to enable | Uncomment in config + add credentials                 |
| **GitHub OAuth**   | Ready to enable | Same                                                  |
| **Discord OAuth**  | Ready to enable | Same                                                  |

### Admin Designation

Admins are set via environment variable at signup:

```
ADMIN_EMAILS=admin@example.com,cto@example.com
```

When a user signs up with one of these emails, `isAdmin` is automatically set to `true`. Simple, no database migration needed.

## Payment Processing

Open SaaS supports **two payment providers** through an abstraction layer.

### Payment Processor Interface

```
PaymentProcessor
  ├── id: "stripe" | "lemonsqueezy"
  ├── createCheckoutSession()    → redirect user to payment page
  ├── fetchCustomerPortalUrl()   → self-service subscription management
  ├── webhook()                  → handle payment events
  └── webhookMiddlewareConfigFn  → verify webhook signatures
```

<Callout type="info" title="Why Dual Providers?">
  Stripe is the gold standard but isn't available in all countries and requires business verification. Lemon Squeezy is a Merchant of Record — it handles tax compliance globally, making it ideal for solo developers and creators. Supporting both maximizes the template's usefulness worldwide.
</Callout>

### Stripe Flow

1. User clicks "Subscribe" → Checkout Session created
2. Stripe hosts the payment page (PCI compliant)
3. On success: webhook fires `invoice.paid` → Plane updates user's subscription
4. Recurring: Stripe auto-charges → webhook confirms → subscription stays active
5. Cancellation: webhook fires → status updated to `canceled`

### Lemon Squeezy Flow

Same pattern, different events: `order_created`, `subscription_created`, `subscription_updated`, `subscription_cancelled`, `subscription_expired`.

### Webhook Security

Both providers use **HMAC signature verification** to ensure webhook requests are genuine:

* **Stripe:** `Stripe-Signature` header with timestamp and signature
* **Lemon Squeezy:** `X-Signature` header with HMAC-SHA256 (timing-safe comparison)

## File Upload System

### S3 Presigned URL Flow

```
User selects file
    │
    ▼
Frontend calls backend action
    │
    ▼
Backend generates presigned S3 URL (time-limited, secure)
    │
    ▼
Frontend uploads directly to S3 (bypasses backend)
    │
    ▼
Backend stores file metadata in DB (name, type, s3Key)
    │
    ▼
File available for download via S3 key
```

<Callout type="info" title="Why Presigned URLs?">
  The file never passes through the Node.js server — it goes directly from the browser to S3. This means the backend doesn't need to handle large file transfers, keeping it fast and lightweight. The presigned URL is time-limited (expires in minutes) and scoped to a specific S3 key, so it's secure.
</Callout>

## AI Integration

### How the AI Demo Works

Open SaaS includes a demo **AI scheduling assistant**:

1. User creates tasks with descriptions and time estimates
2. User clicks "Generate Schedule"
3. Backend calls OpenAI GPT-3.5-turbo with function calling
4. AI returns a structured schedule (JSON)
5. Schedule is stored in `GptResponse` and displayed to the user
6. 1 credit deducted (or free if user has active subscription)

### Credit System Logic

```
User requests AI operation
    │
    ├── Has active subscription? → Allow (unlimited)
    │
    └── Has credits > 0?
          ├── Yes → Deduct 1 credit (atomic DB operation) → Allow
          └── No  → Return 402 (Payment Required)
```

## Analytics System

| Provider             | Type          | Privacy                    |
| -------------------- | ------------- | -------------------------- |
| **Google Analytics** | Full tracking | Requires cookie consent    |
| **Plausible**        | Privacy-first | No cookies, GDPR compliant |

**Daily Stats Aggregation:** A background job (PgBoss cron) runs daily to aggregate page views, user counts, revenue, and traffic sources into the `DailyStats` table for the admin dashboard.

## Admin Dashboard

The admin panel is built with **ShadCN UI** and provides:

* **User list** with search, subscription status, and admin toggle
* **Daily metrics** — views, signups, revenue, churn
* **Charts** — user growth and revenue trends over time
* **System overview** — key metrics at a glance

Only users with `isAdmin: true` can access the admin routes.

## What's Next

<Cards>
  <Card title="What Is Open SaaS?" description="Product overview, ideal users, included features, and built-in business model." href="/open-source/open-saas-wasp-starter" />

  <Card title="Open SaaS Workflows and Data Flows" description="User signup, payment, AI features, and file uploads traced end-to-end." href="/open-source/open-saas-wasp-starter/workflows" />

  <Card title="How Open SaaS Is Built" description="Repository structure, Wasp config, dependencies, and deployment." href="/open-source/open-saas-wasp-starter/how-it-is-built" />
</Cards>

