← Product Discovery
🏗️

Swiss AI App Builder — Technical Architecture

System Design, Data Model, AI Pipeline, Security & API Reference

1. System Architecture

1.1 System Context (C4 Level 1)

                          ┌─────────────────┐
                          │   End User       │
                          │  (Browser)       │
                          └────────┬─────────┘
                                   │ HTTPS
                                   ▼
                          ┌─────────────────┐
                          │  Swiss AI App    │
                          │  Builder Platform│
                          └───┬────┬────┬────┘
                              │    │    │
                ┌─────────────┘    │    └──────────────┐
                ▼                  ▼                   ▼
        ┌──────────────┐  ┌──────────────┐   ┌──────────────┐
        │ Stripe       │  │ OAuth        │   │ External LLM │
        │ (Payments)   │  │ Providers    │   │ APIs (BYO)   │
        └──────────────┘  │ (Google,     │   └──────────────┘
                          │  GitHub)     │
                          └──────────────┘
  • End User — Developer/non-technical user building apps via natural language
  • Stripe — Payment processing (CHF billing)
  • OAuth Providers — Google, GitHub for social login
  • External LLM APIs — Optional user-provided API keys (OpenAI, Anthropic) proxied through CH

1.2 Container Diagram (C4 Level 2)

┌─────────────────────────────────────────────────────────────────────────┐
│                        CLOUDFLARE (CDN + WAF + DNS)                     │
└─────────────────────────────┬───────────────────────────────────────────┘
                              │
        ┌─────────────────────┼──────────────────────┐
        ▼                     ▼                      ▼
┌───────────────┐   ┌─────────────────┐    ┌─────────────────────┐
│  Next.js App  │   │  Deployed User  │    │  Hetzner GPU Server │
│  (Vercel)     │   │  Apps (Vercel)  │    │  (Zürich)           │
│               │   │                 │    │                     │
│ ┌───────────┐ │   └─────────────────┘    │ ┌───────────────┐  │
│ │ App Router│ │                           │ │ Ollama        │  │
│ │ API Routes│ │◄─────── Redis ──────────►│ │ (LLM Inference│  │
│ │ SSE Stream│ │   (Upstash/Hetzner)      │ │  Server)      │  │
│ └───────────┘ │                           │ └───────────────┘  │
│               │                           │ ┌───────────────┐  │
│ ┌───────────┐ │                           │ │ BullMQ Worker │  │
│ │ WebSocket │ │                           │ │ (Inference    │  │
│ │ (Preview) │ │                           │ │  Queue)       │  │
│ └───────────┘ │                           │ └───────────────┘  │
└───────┬───────┘                           │ ┌───────────────┐  │
        │                                   │ │ Sandbox Runner│  │
        ▼                                   │ │ (Preview      │  │
┌───────────────┐                           │ │  Containers)  │  │
│  Neon Postgres│                           │ └───────────────┘  │
│  (Platform DB)│                           └─────────────────────┘
│ ┌───────────┐ │
│ │ Tenant DBs│ │
│ │ (Schemas) │ │
│ └───────────┘ │
└───────────────┘

1.3 Containers Overview

ContainerTechnologyPurpose
Web ApplicationNext.js 14 (App Router) on VercelUI + API routes + SSE streaming
Platform DatabaseNeon Postgres (Serverless)Users, apps, billing, conversations
Tenant DatabasesNeon Postgres (schema-per-tenant)User app data (each app gets a schema)
Cache / Queue BrokerRedis (Upstash or self-hosted)Session cache, BullMQ job queue, rate limiting
LLM InferenceOllama on Hetzner GPU (RTX 4090)Code generation via Llama 3.1 8B / DeepSeek Coder V2 16B
Inference WorkerNode.js BullMQ worker on HetznerProcesses AI generation jobs from queue
Sandbox RunnerWebContainers (client-side)Safe preview of generated apps
CDN / WAFCloudflareDDoS protection, SSL termination, DNS
Object StorageCloudflare R2 or Hetzner S3App source code, assets, backups

1.4 Component Diagram (C4 Level 3 — Next.js App)

┌─────────────────────────────────────────────────────────────┐
│                    Next.js Application                       │
│                                                              │
│  ┌─────────────┐  ┌──────────────┐  ┌───────────────────┐  │
│  │ Auth Module  │  │ App Builder  │  │ Admin Dashboard   │  │
│  │ (NextAuth.js)│  │ UI (React)   │  │ (React)           │  │
│  └──────┬──────┘  └──────┬───────┘  └────────┬──────────┘  │
│         │                │                    │              │
│  ┌──────┴────────────────┴────────────────────┴──────────┐  │
│  │                   API Layer (Route Handlers)           │  │
│  │  /api/auth/*    /api/apps/*     /api/chat     (SSE)   │  │
│  │  /api/billing/* /api/templates  /api/admin/*          │  │
│  └──────┬────────────────┬────────────────┬──────────────┘  │
│         │                │                │                  │
│  ┌──────┴──────┐  ┌──────┴──────┐  ┌─────┴───────────┐    │
│  │ Auth Service│  │ App Service │  │ AI Service       │    │
│  │ (NextAuth)  │  │ (CRUD,      │  │ (Prompt assembly,│    │
│  │             │  │  deploy,    │  │  queue dispatch, │    │
│  │             │  │  files)     │  │  stream relay)   │    │
│  └─────────────┘  └─────────────┘  └─────────────────┘    │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐    │
│  │ Billing     │  │ Sandbox     │  │ Deployment      │    │
│  │ Service     │  │ Service     │  │ Service          │    │
│  │ (Stripe)    │  │ (Preview)   │  │ (Vercel API)     │    │
│  └─────────────┘  └─────────────┘  └─────────────────┘    │
│                                                              │
│  ┌───────────────────────────────────────────────────────┐  │
│  │              Data Access Layer (Prisma ORM)           │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

1.5 Data Flow: User Creates App via AI Chat

User          Next.js         Redis/BullMQ      Ollama Worker     Neon DB
 │               │                │                  │               │
 │──POST /chat──►│                │                  │               │
 │               │──save msg─────►│                  │               │
 │               │──enqueue job──►│                  │               │
 │◄──SSE open────│                │                  │               │
 │               │                │──dequeue────────►│               │
 │               │                │                  │──inference──► │
 │               │                │◄──stream tokens──│               │
 │◄──SSE tokens──│◄──pub/sub──────│                  │               │
 │               │                │◄──complete────────│               │
 │               │──save response─┼──────────────────┼──────────────►│
 │               │──parse code────│                  │               │
 │◄──SSE done────│                │                  │               │
 │──preview()───►│ (WebContainer  │                  │               │
 │◄──live app────│  in browser)   │                  │               │

1.6 Technology Choices & Justification

ComponentChoiceJustification
FrontendNext.js 14 (App Router)SSR + API routes in one deploy; Vercel-native
Hosting (Phase 1)VercelZero-config deploys, edge functions, fast iteration
DatabaseNeon PostgresServerless, scales to zero, branching for dev
ORMPrismaType-safe queries, migrations, schema-as-code
AuthNextAuth.js v5Built for Next.js; email + OAuth; JWT sessions
LLM InferenceOllama (self-hosted)Swiss data sovereignty; no API costs per token
GPU ServerHetzner Dedicated (Zürich)Swiss DC; ~€150-200/mo for RTX 4090
Job QueueBullMQ + RedisBattle-tested; priority queues; retries
PaymentsStripeCHF support; subscription + usage-based billing
CDN / DNSCloudflareFree tier; wildcard DNS; SSL; WAF
Code EditorMonaco EditorVSCode engine; syntax highlighting
Object StorageCloudflare R2S3-compatible; no egress fees

1.7 Architecture Decision Records

ADR-001: Next.js Monolith over Microservices

Decision: Single Next.js application handling UI + API + SSE streaming.

Rationale: Solo developer building MVP. Microservices add deployment complexity and operational overhead.

Accepted for Phase 1

ADR-002: Ollama on Dedicated GPU vs. Cloud GPU API

Decision: Self-hosted Ollama on Hetzner dedicated GPU server.

Rationale: Swiss data sovereignty is core. Cloud GPU APIs are mostly US-based. Hetzner has Zürich DC.

Accepted

ADR-003: Neon Postgres over Supabase/PlanetScale

Decision: Neon Postgres for platform and tenant databases.

Rationale: True Postgres with serverless scaling. Schema-per-tenant is native. No unnecessary BaaS features.

Accepted for Phase 1

1.8 Communication Patterns

FlowPatternProtocol
Browser ↔ APISync request/responseHTTPS REST
AI chat streamingServer pushSSE (Server-Sent Events)
Preview hot reloadBidirectionalWebSocket (client-side)
Job dispatchAsync queueBullMQ over Redis
Token streamingPub/subRedis Pub/Sub
Webhooks (Stripe)Async pushHTTPS POST

1.9 SSE Architecture Detail

Browser                    Vercel Edge              Redis           Hetzner Worker
   │                          │                      │                   │
   │──GET /api/chat?stream───►│                      │                   │
   │◄──Content-Type: text/    │                      │                   │
   │   event-stream           │                      │                   │
   │                          │──SUBSCRIBE job:{id}─►│                   │
   │                          │                      │◄──PUBLISH tokens──│
   │◄──data: {token}──────────│◄──message────────────│                   │
   │◄──data: {token}──────────│◄──message────────────│                   │
   │◄──data: [DONE]───────────│◄──message {done}─────│                   │
   │                          │──UNSUBSCRIBE────────►│                   │

2. Data Model

2.1 Entity Relationship Diagram

┌──────────────┐     ┌──────────────────┐     ┌─────────────────┐
│    Users     │     │  Organizations   │     │  OrgMemberships │
├──────────────┤     ├──────────────────┤     ├─────────────────┤
│ id (uuid) PK │◄────│ id (uuid) PK     │◄────│ id (uuid) PK    │
│ email        │     │ name             │     │ userId FK       │
│ passwordHash │     │ slug             │     │ orgId FK        │
│ name         │     │ plan             │     │ role (enum)     │
│ avatarUrl    │     │ stripeCustomerId │     └─────────────────┘
│ provider     │     └──────────────────┘
│ emailVerified│
└──────┬───────┘         ┌───────────────────┐
       │                 │      Apps         │
       └────────────────►│ id (uuid) PK      │
                         │ userId FK         │
                         │ name, slug        │
                         │ status (enum)     │◄── draft|preview|deployed|paused|archived
                         │ dbSchema (text)   │◄── Neon schema name for this app
                         │ settings (jsonb)  │
                         └───────┬───────────┘
                                 │
              ┌──────────────────┼──────────────────┐
              ▼                  ▼                  ▼
    ┌─────────────────┐ ┌───────────────┐ ┌─────────────────┐
    │   AppVersions   │ │ Conversations │ │  Deployments    │
    ├─────────────────┤ ├───────────────┤ ├─────────────────┤
    │ version (int)   │ │ appId FK      │ │ appId FK        │
    │ files (jsonb)   │ │ title         │ │ provider (enum) │◄── vercel|k8s
    │ parentVersionId │ └───────┬───────┘ │ url, subdomain  │
    └─────────────────┘         ▼         │ status (enum)   │◄── building|live|failed
                        ┌───────────────┐ └─────────────────┘
                        │   Messages    │
                        ├───────────────┤
                        │ role (enum)   │◄── user|assistant|system
                        │ content (text)│
                        │ metadata(jsonb│◄── model, tokens, duration
                        └───────────────┘

┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐
│  Subscriptions  │  │    Credits      │  │   AuditLogs     │
├─────────────────┤  ├─────────────────┤  ├─────────────────┤
│ plan (enum)     │  │ balance (int)   │  │ action (text)   │
│ stripeSubId     │  │ type (enum)     │  │ resource (text) │
│ status (enum)   │  │ expiresAt       │  │ metadata (jsonb)│
└─────────────────┘  └─────────────────┘  └─────────────────┘

2.2 Prisma Schema (Key Models)

prisma
model User {
  id             String    @id @default(uuid()) @db.Uuid
  email          String    @unique
  passwordHash   String?
  name           String?
  avatarUrl      String?
  provider       String?   // google, github, email
  providerId     String?
  emailVerified  DateTime?
  createdAt      DateTime  @default(now())
  updatedAt      DateTime  @updatedAt

  apps           App[]
  memberships    OrgMembership[]
  subscription   Subscription?
  credits        Credit[]
  auditLogs      AuditLog[]
  usageLogs      UsageLog[]

  @@index([email])
  @@index([provider, providerId])
}

model App {
  id               String    @id @default(uuid()) @db.Uuid
  userId           String    @db.Uuid
  orgId            String?   @db.Uuid
  name             String
  slug             String
  description      String?
  status           AppStatus @default(DRAFT)
  currentVersionId String?   @db.Uuid
  dbSchema         String?   // Neon schema name: "app_{id_short}"
  settings         Json      @default("{}")
  createdAt        DateTime  @default(now())
  updatedAt        DateTime  @updatedAt

  versions      AppVersion[]
  conversations Conversation[]
  deployments   Deployment[]
  domains       Domain[]

  @@unique([userId, slug])
  @@index([userId])
  @@index([status])
}

model AppVersion {
  id              String   @id @default(uuid()) @db.Uuid
  appId           String   @db.Uuid
  version         Int
  files           Json     // { "path": "content", ... }
  parentVersionId String?  @db.Uuid
  createdAt       DateTime @default(now())

  @@unique([appId, version])
}

model Message {
  id             String      @id @default(uuid()) @db.Uuid
  conversationId String      @db.Uuid
  role           MessageRole // USER | ASSISTANT | SYSTEM
  content        String
  metadata       Json?       // { model, tokensIn, tokensOut, durationMs }
  createdAt      DateTime    @default(now())

  @@index([conversationId, createdAt])
}

2.3 Enums

prisma
enum Plan        { FREE  STARTER  PRO  ENTERPRISE }
enum AppStatus   { DRAFT  PREVIEW  DEPLOYED  PAUSED  ARCHIVED }
enum MessageRole { USER  ASSISTANT  SYSTEM }
enum CreditType  { PURCHASED  GRANTED  EARNED }
enum DeployProvider    { VERCEL  K8S }
enum DeploymentStatus  { BUILDING  LIVE  FAILED  STOPPED }
enum UsageType   { AI_GENERATION  DEPLOYMENT  DB_QUERY }

2.4 Tenant Database Schema Pattern

Each user app that uses a database gets a Postgres schema within the same Neon database.

sql
-- Created when user enables "database" for their app
CREATE SCHEMA IF NOT EXISTS app_a1b2c3d4;

-- Example: user's app has a "todos" table
CREATE TABLE app_a1b2c3d4.todos (
  id SERIAL PRIMARY KEY,
  title TEXT NOT NULL,
  completed BOOLEAN DEFAULT false,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Isolation enforced via connection string with search_path set
-- ?options=-c search_path=app_{id}

2.5 Multi-Tenancy Strategy

┌──────────────────────────────────────────────┐
│              Neon Postgres Instance           │
│                                              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  │
│  │ public   │  │app_a1b2  │  │app_c3d4  │  │
│  │ (platform│  │(user app │  │(user app │  │
│  │  tables) │  │ tables)  │  │ tables)  │  │
│  └──────────┘  └──────────┘  └──────────┘  │
│                                              │
│  Each app schema isolated by search_path    │
│  + dedicated Postgres role + RLS            │
└──────────────────────────────────────────────┘

Isolation Guarantees

  • Each app gets a dedicated Postgres role with access only to its schema
  • Connection string includes ?options=-c search_path=app_{id}
  • RLS policies as defense-in-depth
  • SQL validation before execution (block cross-schema queries)

2.6 Backup & Recovery

ComponentStrategyFrequencyRetention
Platform DBNeon automatic PITRContinuous WAL7-30 days
Tenant schemasSame Neon PITRContinuousSame as platform
App source codeAppVersion.files (JSONB) + R2Per versionIndefinite
Object storage (R2)Cloudflare redundancyAutomaticIndefinite

3. AI Pipeline

3.1 Pipeline Overview

┌────────────┐    ┌──────────────┐    ┌─────────┐    ┌────────────┐    ┌──────────┐
│ User Input │───►│ Prompt       │───►│ BullMQ  │───►│ Ollama     │───►│ Response │
│ (Chat msg) │    │ Assembly     │    │ Queue   │    │ Inference  │    │ Parser   │
└────────────┘    └──────────────┘    └─────────┘    └────────────┘    └────┬─────┘
                                                                           │
                  ┌──────────────┐    ┌─────────┐    ┌────────────┐        │
                  │ Preview      │◄───│ File    │◄───│ Validator  │◄───────┘
                  │ Trigger      │    │ Writer  │    │ (lint/tsc) │
                  └──────────────┘    └─────────┘    └────────────┘

3.2 Code Generation Pipeline Steps

8-Step Pipeline

1. Input Processing
   └─► Validate user message → Check credit balance → Load context (last 20 msgs)

2. Prompt Assembly
   └─► Select system prompt → Inject app template → Inject existing code + DB schema

3. Model Selection
   └─► Simple request → Llama 3.1 8B (fast, ~15 tok/s)
   └─► Complex request → DeepSeek Coder V2 16B (quality, ~8 tok/s)
   └─► User BYO API key → proxy to external provider

4. Inference (Ollama)
   └─► Stream tokens via HTTP → Relay via SSE + Redis pub/sub

5. Response Parsing
   └─► Extract code blocks (```filename.ext ... ```) → Parse file operations

6. Validation
   └─► TypeScript compilation check → ESLint pass → Security scan

7. File System Write
   └─► Write files to app virtual FS → Update app version

8. Preview Trigger
   └─► Push files to client WebContainer → Hot reload

3.3 System Prompts

text
// Base System Prompt (all requests):
You are an expert full-stack web developer. You generate complete, working
Next.js 14 applications using TypeScript, Tailwind CSS, and Prisma.

Rules:
- Output ONLY code files in the format: ```path/to/file.ext\n...code...\n```
- Use Next.js App Router (app/ directory)
- Use TypeScript strictly (no `any`)
- Use Tailwind CSS for styling (no CSS modules)
- Use Prisma for database access when DB is needed
- Include error handling and loading states
- Make UI responsive and accessible
- Never use external APIs unless explicitly asked

3.4 Context Window Management

AllocationBudget %~Tokens (8K model)
System prompt10%~800
Existing code40%~3,200
Conversation history25%~2,000
Current message10%~800
Output reserve15%~1,200

3.5 Model Selection Logic

typescript
function selectModel(input: ProcessedInput): string {
  // User has BYO API key → route externally
  if (input.userApiKey) return 'external';
  
  const hasExistingCode = Object.keys(input.currentFiles).length > 0;
  const complexity = estimateComplexity(input);
  
  // Simple iterations (small changes, bug fixes) → fast model
  if (hasExistingCode && complexity === 'low') {
    return 'llama3.1:8b';  // ~15 tok/s on RTX 4090
  }
  
  // New apps or complex changes → quality model
  return 'deepseek-coder-v2:16b';  // ~8 tok/s on RTX 4090
}

function estimateComplexity(input: ProcessedInput): 'low' | 'medium' | 'high' {
  const msg = input.userMessage.toLowerCase();
  if (msg.length < 100 && /change|fix|update|rename|color|text|typo/i.test(msg))
    return 'low';
  if (/add.*page|new.*feature|database|auth|api.*route|integration/i.test(msg))
    return 'high';
  return 'medium';
}

3.6 Hybrid AI Routing (BYO API Key)

┌──────────┐     ┌──────────────┐     ┌──────────────────┐
│ User     │────►│ AI Service   │────►│ Route Decision   │
│ Request  │     │ (Next.js)    │     │                  │
└──────────┘     └──────────────┘     └────────┬─────────┘
                                               │
                         ┌─────────────────────┼─────────────────┐
                         ▼                     ▼                 ▼
                  ┌──────────────┐    ┌──────────────┐  ┌──────────────┐
                  │ Ollama       │    │ CH Proxy →   │  │ CH Proxy →   │
                  │ (Default)    │    │ OpenAI API   │  │ Anthropic API│
                  │ Hetzner ZH   │    │ (via Hetzner)│  │ (via Hetzner)│
                  └──────────────┘    └──────────────┘  └──────────────┘

🔑 Key Point

User data transits through CH infrastructure even when using external providers. API keys encrypted with AES-256, per-app key derivation via HKDF.

3.7 SSE Event Format

typescript
// SSE events sent to browser:
event: token
data: {"t": "partial token text"}

event: file
data: {"path": "app/page.tsx", "content": "...full file..."}

event: done
data: {"usage": {"tokensIn": 1200, "tokensOut": 3400,
       "model": "deepseek-coder-v2", "durationMs": 12340, "creditsUsed": 5}}

event: error
data: {"code": "GENERATION_FAILED", "message": "Model timed out"}

3.8 Error Handling & Retry Strategy

ErrorHandlingRetry
Ollama unreachableReturn error; fallback to BYO key2 retries, exponential (5s, 15s)
Model OOM (VRAM)Kill inference, retry smaller model1 retry with llama3.1:8b
Generation timeout (>3min)Return partial result + errorNo retry; suggest simplifying
Invalid code outputShow raw output + error1 auto-retry with "fix errors" prompt
Rate limit (queue full)Return 429; show queue positionClient polls for slot

3.9 Credit Cost Calculation

typescript
function calculateCredits(usage: { model: string; tokensIn: number; tokensOut: number }): number {
  const rates = {
    'llama3.1:8b':           { input: 0.001, output: 0.002 },  // ~1 credit per simple request
    'deepseek-coder-v2:16b': { input: 0.003, output: 0.006 },  // ~3-5 credits per complex request
    'external:gpt-4':       { input: 0,     output: 0 },       // BYO key = 0 platform credits
  };
  const rate = rates[usage.model] ?? rates['deepseek-coder-v2:16b'];
  return Math.ceil((usage.tokensIn * rate.input) + (usage.tokensOut * rate.output));
}

4. Sandbox Architecture

4.1 Approach Comparison

CriteriaWebContainers (Client)Docker (Server)Hybrid
LatencyInstant (runs in browser)2-5s container spin-upInstant preview, server for build
Server costZeroHigh (GPU server runs containers)Low
SecurityBrowser sandbox (strong)Container isolation (strong)Both
Node.js supportYes (WASM-based)Full nativeBoth
Database accessLimited (PGlite in-browser)Full Postgres accessBoth
Max app complexityMedium (browser memory)HighHigh
Solo dev feasibility★★★★★★★☆☆☆★★★★☆

ADR-004: WebContainers as Primary Sandbox

Decision: Use WebContainers (StackBlitz API) for app preview. PGlite for database preview. Server-side containers only for production builds.

Accepted — zero server infra for preview, instant hot reload, strong browser sandbox security

4.2 WebContainers + PGlite Architecture

┌─────────────────────────────────────────────────────────────┐
│                     User's Browser                          │
│                                                              │
│  ┌──────────────┐    ┌──────────────────────────────────┐   │
│  │ App Builder  │    │         Preview Panel             │   │
│  │ UI (React)   │    │  ┌────────────────────────────┐  │   │
│  │              │    │  │     WebContainer Instance   │  │   │
│  │ ┌──────────┐ │    │  │  ┌──────┐  ┌───────────┐  │  │   │
│  │ │ Monaco   │─┼────┼─►│  │Node.js│  │ Next.js   │  │  │   │
│  │ │ Editor   │ │    │  │  │(WASM) │  │ Dev Server│  │  │   │
│  │ └──────────┘ │    │  │  └──────┘  └─────┬─────┘  │  │   │
│  │ ┌──────────┐ │    │  │  ┌──────────┐    │        │  │   │
│  │ │ AI Chat  │ │    │  │  │ PGlite   │◄───┘        │  │   │
│  │ │ Panel    │ │    │  │  │(in-browser│             │  │   │
│  │ └──────────┘ │    │  │  │ Postgres) │             │  │   │
│  └──────────────┘    │  │  └──────────┘             │  │   │
│                      │  └────────────────────────────┘  │   │
│                      │  ┌────────────────────────────┐  │   │
│                      │  │   Preview iframe           │  │   │
│                      │  │   (localhost:3000 proxy)    │  │   │
│                      │  └────────────────────────────┘  │   │
│                      └──────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

4.3 Real-Time Preview Flow

SSE Stream          App Builder UI        WebContainer          Preview iframe
    │                    │                     │                      │
    │──file event───────►│                     │                      │
    │  {path, content}   │──writeFile()───────►│                      │
    │                    │                     │──HMR trigger────────►│
    │                    │                     │                      │──re-render
    │──file event───────►│                     │                      │
    │  {path, content}   │──writeFile()───────►│                      │
    │                    │                     │──HMR trigger────────►│
    │                    │                     │                      │──re-render

4.4 Security & Resource Limits

ThreatMitigation
File system accessWebContainer has virtual FS only — no access to user's real FS
Network accessBlocked by default; only localhost dev server allowed
XSS from previewiframe sandbox attribute + separate origin
Memory bombBrowser kills tab if memory exceeds ~512MB
Cookie/storage theftPreview runs on different origin (null or blob:)

4.5 Preview vs. Production

AspectPreview (WebContainer)Production (Deployed)
RuntimeWASM Node.js in browserReal Node.js on Vercel/K8s
DatabasePGlite (in-browser)Neon Postgres
File systemVirtual (in-memory)Real FS / serverless
Environment varsMocked / emptyReal secrets injected
Networklocalhost onlyFull internet access
AuthMocked (always logged in)Real auth flow

5. Deployment Pipeline

5.1 App Lifecycle

  ┌───────┐    ┌─────────┐    ┌──────────┐    ┌────────┐    ┌──────────┐
  │ DRAFT │───►│ PREVIEW │───►│ DEPLOYED │───►│ PAUSED │───►│ ARCHIVED │
  └───────┘    └─────────┘    └──────────┘    └────────┘    └──────────┘
       │             │              │              │
       │             │              │              └──► DEPLOYED (resume)
       │             │              └──► DRAFT (unpublish, edit)
       │             └──► DRAFT (back to editing)
       └──► (delete)

5.2 Phase 1: Vercel Deployment Flow

User clicks          Platform API         Vercel API         Cloudflare
"Deploy"                 │                  │                   │
    │──POST /deploy─────►│                  │                   │
    │                    │── 1. Validate ───►│                   │
    │                    │── 2. Bundle code ►│                   │
    │                    │── 3. Create proj ►│                   │
    │                    │── 4. Upload files►│                   │
    │                    │── 5. Create deploy│──build──►         │
    │                    │── 6. Set env vars►│                   │
    │◄── started ────────│                  │                   │
    │                    │◄── 7. Ready ──────│                   │
    │                    │── 8. CNAME ───────┼──────────────────►│
    │◄── deployed! ──────│  myapp.crea.ch   │                   │

5.3 Subdomain Management

┌─────────────────────────────────────────────────┐
│              Cloudflare DNS                      │
│                                                  │
│  *.crea.ch  →  CNAME  →  cname.vercel-dns.com  │
│  crea.ch    →  A      →  76.76.21.21 (Vercel)  │
│                                                  │
│  SSL: Cloudflare Universal SSL (wildcard)        │
│  + Vercel automatic SSL per deployment           │
└─────────────────────────────────────────────────┘
  • Auto-generated from app slug: my-todo-app → my-todo-app.crea.ch
  • Reserved subdomains: www, api, app, admin, docs, blog, mail, status
  • Validation: lowercase alphanumeric + hyphens, 3-63 chars

5.4 Environment Variables per App

VariableSourceDescription
DATABASE_URLGenerated by platformNeon Postgres URL scoped to app schema
NEXT_PUBLIC_APP_URLComputed from subdomainPublic URL of deployed app
NODE_ENVproductionStandard Node env
APP_SECRETGenerated (random 32 bytes)For signing cookies/tokens
User-definedEncrypted in platform DBUser adds via settings panel

5.5 Phase 2: K8s-Based Deployment

┌──────────────────────────────────────────────────────────┐
│                   K8s Cluster (Swiss DC)                  │
│  ┌──────────────────────────────────────────────────┐    │
│  │  Namespace: user-apps                             │    │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐       │    │
│  │  │ app-abc  │  │ app-def  │  │ app-ghi  │  ...  │    │
│  │  │ Pod      │  │ Pod      │  │ Pod      │       │    │
│  │  │ Next.js  │  │ Next.js  │  │ Next.js  │       │    │
│  │  │ 256MB    │  │ 256MB    │  │ 512MB    │       │    │
│  │  └────┬─────┘  └────┬─────┘  └────┬─────┘       │    │
│  │  ┌────┴──────────────┴──────────────┴──────────┐  │    │
│  │  │         Traefik Ingress Controller          │  │    │
│  │  │         *.crea.ch → route by subdomain      │  │    │
│  │  └─────────────────────────────────────────────┘  │    │
│  └──────────────────────────────────────────────────┘    │
│  ┌─────────────────────┐  ┌──────────────────────────┐   │
│  │  Container Registry │  │  Cert-Manager            │   │
│  │  (Harbor or GHCR)   │  │  (Let's Encrypt wildcard)│   │
│  └─────────────────────┘  └──────────────────────────┘   │
└──────────────────────────────────────────────────────────┘

5.6 Resource Allocation per Deployed App

PlanCPU Req/LimitMemory Req/LimitScale
Free50m / 200m128MB / 256MB1 pod
Starter100m / 500m256MB / 512MB1-2 pods
Pro250m / 1000m512MB / 1GB1-5 pods
EnterpriseCustomCustomAuto

5.7 Cost (Phase 1)

Phase 1 per-app cost: effectively $0

Vercel serverless model — pay only for compute time. Neon schemas are lightweight. Cloudflare DNS + SSL is free. R2 storage ~$0.015/GB/mo.

6. Infrastructure

6.1 Phase 1 Infrastructure

┌─────────────────────────────────────────────────────────────────────────────┐
│                              INTERNET                                       │
└───────────────────────────────────┬─────────────────────────────────────────┘
                                    │
                    ┌───────────────┴───────────────┐
                    │       Cloudflare               │
                    │  • CDN  • WAF  • DNS  • R2     │
                    └───┬───────────┬───────────┬────┘
                        │           │           │
            ┌───────────┘           │           └───────────┐
            ▼                       ▼                       ▼
┌───────────────────┐  ┌───────────────────┐  ┌───────────────────────┐
│  Vercel           │  │  Neon Postgres    │  │  Hetzner Dedicated    │
│  (Frankfurt/ZRH)  │  │  (EU/Frankfurt)   │  │  (Zürich DC)          │
│                   │  │                   │  │                       │
│  • Next.js App    │  │  • Platform DB    │  │  • Ollama (RTX 4090)  │
│  • API Routes     │  │  • Tenant Schemas │  │  • BullMQ Worker      │
│  • Deployed Apps  │  │                   │  │  • Redis              │
│                   │  │                   │  │  • Nginx Proxy        │
└───────────────────┘  └───────────────────┘  └───────────────────────┘

6.2 Phase 1 Cost Breakdown

ServicePlanMonthly Cost (CHF)
VercelPro~22
Neon PostgresScale (Launch)~19
Hetzner GPU (RTX 4090)EX44 or similar~200-350
CloudflareFree (+ R2 usage)~5
Upstash RedisPay-as-you-go~10-20
SentryDeveloperFree
DomainAnnual~5
Total~260-420

6.3 Hetzner GPU Server — Docker Compose

yaml
version: '3.8'
services:
  ollama:
    image: ollama/ollama:latest
    ports: ["127.0.0.1:11434:11434"]
    volumes: [ollama_data:/root/.ollama]
    deploy:
      resources:
        reservations:
          devices: [{ driver: nvidia, count: 1, capabilities: [gpu] }]
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    ports: ["127.0.0.1:6379:6379"]
    volumes: [redis_data:/data]
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
    restart: unless-stopped

  worker:
    build: ./worker
    environment:
      - OLLAMA_URL=http://ollama:11434
      - REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379
      - WORKER_CONCURRENCY=3
    depends_on: [ollama, redis]
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    ports: ["443:443"]
    volumes: [./nginx.conf:/etc/nginx/nginx.conf, ./certs:/etc/nginx/certs]
    restart: unless-stopped

6.4 Phase 2: K8s Cluster Design

┌──────────────────────────────────────────────────────────────────┐
│          K8s Cluster (Swiss DC — Exoscale/Infomaniak)            │
│                                                                   │
│  Node Pools:                                                      │
│  ┌─────────────────┐  ┌──────────────────┐  ┌──────────────────┐ │
│  │ platform-pool   │  │ app-worker-pool  │  │ gpu-pool         │ │
│  │ 2x 4vCPU/8GB   │  │ 2-10x 2vCPU/4GB │  │ 1x GPU node      │ │
│  │                 │  │ (autoscale)      │  │ (RTX 4090)       │ │
│  │ • Platform API  │  │ • User app pods  │  │ • Ollama         │ │
│  │ • Redis         │  │ • Build jobs     │  │ • BullMQ worker  │ │
│  │ • Monitoring    │  │                  │  │                  │ │
│  └─────────────────┘  └──────────────────┘  └──────────────────┘ │
│                                                                   │
│  Namespaces:                                                      │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────────────┐ │
│  │ platform │  │user-apps │  │monitoring│  │ ingress-system   │ │
│  └──────────┘  └──────────┘  └──────────┘  └──────────────────┘ │
└──────────────────────────────────────────────────────────────────┘

6.5 CI/CD Pipeline

┌────────┐   ┌──────┐   ┌──────────┐   ┌─────────┐   ┌────────┐
│  Lint  │──►│ Test │──►│ Type     │──►│ Build   │──►│ Deploy │
│        │   │      │   │ Check    │   │         │   │        │
└────────┘   └──────┘   └──────────┘   └─────────┘   └────────┘
                                                           │
                                            ┌──────────────┼──────────┐
                                            ▼              ▼          ▼
                                       ┌─────────┐  ┌──────────┐ ┌────────┐
                                       │ Vercel  │  │ Prisma   │ │ Hetzner│
                                       │ (app)   │  │ Migrate  │ │(worker)│
                                       └─────────┘  └──────────┘ └────────┘

6.6 Monitoring & Observability

Phase 1 (Lightweight)

  • Sentry (errors) — free tier
  • Vercel Analytics — included in Pro
  • UptimeRobot — free tier
  • Vercel Logs + docker logs
  • nvidia-smi + simple script

Phase 2 (Full Stack on K8s)

  • Prometheus (metrics)
  • Loki (logs)
  • Tempo (traces)
  • Grafana (dashboards + alerting)
  • PagerDuty/Slack alerts

6.7 Key Metrics & Alert Thresholds

MetricSourceAlert Threshold
API response time (p95)Prometheus> 500ms
AI generation time (p95)Custom metric> 120s
Queue depthBullMQ / Redis> 20 jobs
GPU VRAM usagenvidia-smi exporter> 90%
Error rate (5xx)Prometheus> 5%
Deployment success rateCustom metric< 90%

6.8 Disaster Recovery

ScenarioRTORPORecovery
Vercel outage00CDN cached; wait for recovery
Neon outage1h~0Wait for recovery; read-only mode
Hetzner GPU down15min0Disable AI; enable BYO API fallback
Data corruption30minMinutesNeon PITR restore
Security breach1h0Rotate secrets; revoke sessions; audit

7. Security

7.1 Authentication Architecture

┌─────────────┐     ┌──────────────┐     ┌─────────────┐
│   Browser    │────▶│  NextAuth.js │────▶│  PostgreSQL  │
│              │◀────│  /api/auth/* │◀────│  users table │
└─────────────┘     └──────┬───────┘     └─────────────┘
                           │
                    ┌──────┴───────┐
                    │ OAuth 2.0    │
                    │ • Google     │
                    │ • GitHub     │
                    └──────────────┘
  • Passwords hashed with bcrypt (cost factor 12)
  • JWT strategy — httpOnly, Secure, SameSite=Lax
  • Access token: 24h expiry, Refresh token: 30-day (rotated on use)
  • Account lockout after 5 failed attempts (15-min cooldown)

7.2 Authorization — Platform RBAC

RoleApps (own)Apps (team)Team MgmtBillingAdmin
OwnerCRUDCRUD✅✅✅
AdminCRUDCRUD✅❌❌
MemberCRUDRead+Edit❌❌❌
Viewer—Read❌❌❌

7.3 Row-Level Security (Tenant Isolation)

sql
-- Enable RLS on apps table
ALTER TABLE apps ENABLE ROW LEVEL SECURITY;

-- Users can only see their own tenant's apps
CREATE POLICY tenant_isolation ON apps
  USING (tenant_id = current_setting('app.current_tenant_id')::uuid);

-- Set tenant context on every request
CREATE OR REPLACE FUNCTION set_tenant_context(p_tenant_id uuid)
RETURNS void AS $$
BEGIN
  PERFORM set_config('app.current_tenant_id', p_tenant_id::text, true);
END;
$$ LANGUAGE plpgsql;

7.4 API Security

Rate Limiting

PlanRequests/minBurstAI Requests/min
Free10153
Starter304510
Pro10015030
Business300450100
EnterpriseCustomCustomCustom

Generated App Security (Auto-Included)

  • CSRF: Double-submit cookie pattern
  • XSS: React JSX escaping + DOMPurify for any dangerouslySetInnerHTML
  • SQL Injection: Prisma ORM with parameterized queries (never raw SQL)
  • Secure Headers: X-Content-Type-Options, X-Frame-Options, HSTS, Permissions-Policy
  • Input Validation: Zod schemas generated for all API routes

7.5 Network Security

┌─────────────┐          ┌───────────────────────┐
│   Vercel    │          │   Hetzner GPU Server  │
│   Edge      │◀════════▶│                       │
│             │ WireGuard│   ┌─────────────┐     │
│  Next.js    │ Tunnel   │   │   Ollama    │     │
│  App        │  (TLS)   │   │   :11434    │     │
│             │          │   └─────────────┘     │
└─────────────┘          │                       │
                         │   Firewall:           │
                         │   • SSH: key-only     │
                         │   • WireGuard: 51820  │
                         │   • All other: DENY   │
                         └───────────────────────┘

7.6 FADP/nDSG Compliance

Data Inventory

Data CategoryStorage LocationRetentionEncryption
User accountsPostgreSQL (CH)Until deletionAES-256
App source codePostgreSQL (CH)Until deletionAES-256
Conversation historyPostgreSQL (CH)90 daysAES-256
Audit logsPostgreSQL (CH)90 daysAES-256
Payment dataStripe (EU)Per StripeStripe-managed
AI inferenceRAM only (CH)None (transient)N/A

Breach Notification Workflow

Incident Detected
       │
       ▼
  Assess Severity ──▶ Not a breach ──▶ Log & Close
       │
       ▼ (personal data affected)
  Notify FDPIC within 72h
       │
       ▼ (high risk to individuals)
  Notify affected users "as soon as possible"
       │
       ▼
  Post-mortem & remediation

7.7 Security Checklist by Phase

Phase 1 (MVP) ✅

  • NextAuth.js with bcrypt + JWT
  • RLS on all tenant tables
  • Rate limiting (Redis)
  • Zod input validation
  • CORS + CSP headers
  • WireGuard tunnel to GPU server
  • Audit logging
  • Dependabot + npm audit
  • FADP processing register

Phase 2 (Scale) ⬜

  • HashiCorp Vault for secrets
  • K8s network policies
  • Trivy container scanning
  • Snyk integration
  • Annual pen test
  • WAF (ModSecurity / Cloudflare)
  • SOC 2 Type I preparation

8. API Design

8.1 Common Types

typescript
// Standard error response
interface ErrorResponse {
  error: {
    code: string;       // e.g., 'VALIDATION_ERROR', 'NOT_FOUND'
    message: string;    // Human-readable
    details?: unknown;  // Validation errors, etc.
  };
  requestId: string;    // UUID for support/debugging
}

// Paginated response wrapper
interface PaginatedResponse<T> {
  data: T[];
  nextCursor: string | null;
  hasMore: boolean;
}

// Timestamp fields (all models)
interface Timestamps {
  createdAt: string;  // ISO 8601
  updatedAt: string;  // ISO 8601
}

8.2 API Endpoints Overview

MethodEndpointAuthDescription
POST/api/auth/register—Create new account
POST/api/auth/login—Authenticate, get JWT
GET/api/auth/me✅Current user profile
GET/api/apps✅List user apps (paginated)
POST/api/apps✅Create app (with optional AI prompt)
GET/api/apps/:id✅App detail
PATCH/api/apps/:id✅Update app metadata/env vars
DELETE/api/apps/:id✅Soft delete (30-day recovery)
POST/api/apps/:id/chat✅AI chat → SSE stream
GET/api/apps/:id/chat/history✅Conversation history
GET/api/apps/:id/files✅File tree
GET/api/apps/:id/files/:path✅File content
PUT/api/apps/:id/files/:path✅Update file
POST/api/apps/:id/deploy✅Deploy app
GET/api/apps/:id/deployments✅Deployment history
POST/api/apps/:id/domains✅Add custom domain
GET/api/billing/subscription✅Current plan & usage
POST/api/billing/checkout✅Stripe checkout redirect
POST/api/billing/webhookStripe sigStripe webhook handler
GET/api/templates—Browse templates
GET/api/admin/statsOwnerPlatform admin stats

8.3 SSE Chat Streaming

typescript
// POST /api/apps/:id/chat — Request
interface ChatRequest {
  message: string;       // 1-50000 chars
  attachments?: { type: 'image'; url: string }[];
}

// Response: text/event-stream (SSE)
type SSEEvent =
  | { event: 'token';        data: { content: string } }
  | { event: 'file_start';   data: { path: string; action: 'create' | 'update' | 'delete' } }
  | { event: 'file_content'; data: { path: string; content: string } }
  | { event: 'file_end';     data: { path: string } }
  | { event: 'error';        data: { code: string; message: string } }
  | { event: 'done';         data: { messageId: string; version: number; tokensUsed: number } };

Example SSE stream:

event: token
data: {"content":"I'll create a todo app with authentication."}

event: file_start
data: {"path":"src/app/page.tsx","action":"create"}

event: file_content
data: {"path":"src/app/page.tsx","content":"'use client';\nimport { useState } from 'react';..."}

event: file_end
data: {"path":"src/app/page.tsx"}

event: done
data: {"messageId":"msg_abc123","version":2,"tokensUsed":1847}

8.4 Error Codes

CodeHTTPDescription
VALIDATION_ERROR422Request body failed schema validation
NOT_FOUND404Resource does not exist
UNAUTHORIZED401Missing or invalid auth token
FORBIDDEN403Insufficient permissions
EMAIL_EXISTS409Email already registered
PLAN_LIMIT_REACHED402Plan quota exceeded
RATE_LIMITED429Too many requests
ACCOUNT_LOCKED423Too many failed login attempts
INTERNAL_ERROR500Unexpected server error

8.5 Webhook Design

EventTrigger
deployment.completedDeployment goes live
deployment.failedDeployment build fails
app.createdNew app created
app.deletedApp deleted
typescript
// Webhook payload
interface WebhookPayload {
  id: string;              // Webhook delivery ID
  event: string;           // e.g., 'deployment.completed'
  timestamp: string;       // ISO 8601
  data: Record<string, unknown>;
}

// Signature verification (HMAC-SHA256)
// Header: X-Webhook-Signature: sha256=<hex_digest>
function verifyWebhook(payload: string, signature: string, secret: string): boolean {
  const expected = `sha256=${createHmac('sha256', secret).update(payload).digest('hex')}`;
  return timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

// Delivery: 3 retries (10s, 60s, 300s), 10s timeout, expect 2xx

8.6 API Versioning

  • Phase 1 (MVP): No version prefix — /api/auth/login
  • Phase 2: Version prefix — /api/v1/auth/login
  • Old unversioned routes redirect to v1
  • Deprecation via Sunset header (6-month window)