vibesdk

An open-source vibe coding platform that helps you build your own vibe-coding platform, built entirely on Cloudflare stack

RAW Doc

Architecture Diagrams

VibeSDK Architecture Diagrams

Current architecture

mermaid
flowchart LR
    U[User] <--> UI[VibeSDK UI on Workers]
    UI <--> T[ThinkAgentCloudflare Think + Durable Object]
    T <--> G[Models through AI Gateway]
    T <--> S[SpaceDOworkspace and files]
    S <--> A[Cloudflare Artifactscommits and history]
    S --> B[worker-bundler]
    B --> L[Worker Loader]
    L --> P[Dynamic Worker preview]
    P <--> F[App Facetisolated SQLite]

Think runs the iterative model-and-tool loop. Its explicit tools edit files in SpaceDO, create Artifacts-backed restore points, deploy Dynamic Worker previews, inspect browser logs, and repair errors. SpaceDO is the workspace and file layer; Cloudflare Artifacts is the durable git and version-history layer.

Rollback applies a selected commit tree to the current branch, creates a new commit, and redeploys without rewriting history. Generated applications export an App Durable Object class that SpaceDO hosts as a Facet with isolated SQLite storage.

Legacy architecture reference

The diagrams below document the retired phase-based sandbox architecture. They are retained only as historical context and must not be used to describe the current Think, SpaceDO, Artifacts, Worker Loader, and App Facet implementation.

Presentation-Ready Architecture Diagram

Copy-paste this beautiful diagram directly into your slides

text
/* Detailed source-code truncated for AI context efficiency. */

Detailed System Diagrams

1. Overall System Architecture

text
/* Detailed source-code truncated for AI context efficiency. */

2. Hybrid Agent System Architecture

text
/* Detailed source-code truncated for AI context efficiency. */

3. Authentication & User Management Flow

mermaid
sequenceDiagram
    participant User
    participant Frontend
    participant AuthController
    participant OAuth
    participant D1
    participant JWT
    
    User->>Frontend: Login Request
    Frontend->>AuthController: POST /api/auth/providers
    AuthController->>Frontend: Available auth methods
    
    alt OAuth Flow
        Frontend->>AuthController: POST /api/auth/oauth/github
        AuthController->>OAuth: Redirect to GitHub
        OAuth->>User: Authorization page
        User->>OAuth: Grant permission
        OAuth->>AuthController: Callback with code
        AuthController->>OAuth: Exchange code for tokens
        OAuth->>AuthController: User profile + tokens
        AuthController->>D1: Store/update user
        AuthController->>JWT: Generate tokens
        JWT->>AuthController: Access/Refresh tokens
        AuthController->>Frontend: Set HTTP-only cookies
    else Email/Password Flow
        Frontend->>AuthController: POST /api/auth/login
        AuthController->>D1: Verify credentials
        D1->>AuthController: User data
        AuthController->>JWT: Generate tokens
        JWT->>AuthController: Access/Refresh tokens
        AuthController->>Frontend: Set HTTP-only cookies
    end
    
    Frontend->>User: Login success
    
    Note over AuthController,D1: Session stored in D1JWT tokens in HTTP-only cookiesAutomatic token refresh

4. Sandbox System & Deployment Pipeline

text
/* Detailed source-code truncated for AI context efficiency. */

5. Database Schema & Relationships

text
/* Detailed source-code truncated for AI context efficiency. */
```mermaid
journey
    title User Creates and Deploys an App
    section Getting Started
      Visit Homepage: 5: User
      Enter App Description: 4: User
      Start Generation: 5: User
    section Code Generation
      AI Creates Blueprint: 3: System
      Phase-wise Implementation: 4: System
      Real-time Code Review: 4: System
      Error Detection & Fixing: 3: System
    section Live Preview
      Sandbox Container Starts: 5: System
      Live Preview Available: 5: User
      Runtime Error Detection: 4: System
      Iterative Improvements: 4: User, System
    section Quality Assurance
      Static Analysis: 4: System
      Code Review Cycle: 3: System
      Auto-fix Critical Issues: 4: System
      User Feedback Integration: 5: User
    section Deployment
      Resource Provisioning: 3: System
      Template Parsing: 3: System
      Cloudflare Workers Deploy: 5: System
      Live App URL Generated: 5: User
    section Post-Deployment
      Save to Dashboard: 4: User
      Share with Community: 3: User
      GitHub Export: 4: User

7. Real-time Communication Flow

mermaid
sequenceDiagram
    participant User
    participant Frontend
    participant Agent
    participant AIGateway
    participant Sandbox
    participant WebSocket
    
    User->>Frontend: Send message
    Frontend->>Agent: WebSocket message
    Agent->>WebSocket: Broadcast generation_started
    WebSocket->>Frontend: Real-time update
    
    loop Phase Generation
        Agent->>AIGateway: Phase planning request
        AIGateway->>Agent: Streaming response
        Agent->>WebSocket: Broadcast phase_update
        WebSocket->>Frontend: Live phase progress
    end
    
    loop Code Implementation
        Agent->>AIGateway: Code generation request
        AIGateway->>Agent: SCOF streaming format
        Agent->>WebSocket: Broadcast file_generated
        WebSocket->>Frontend: Live file updates
    end
    
    Agent->>Sandbox: Deploy to container
    Sandbox->>Agent: Preview URL ready
    Agent->>WebSocket: Broadcast preview_ready
    WebSocket->>Frontend: Preview available
    
    loop Quality Assurance
        Sandbox->>Agent: Runtime errors detected
        Agent->>AIGateway: Fix generation request
        AIGateway->>Agent: Fixed code
        Agent->>Sandbox: Update files
        Agent->>WebSocket: Broadcast fixes_applied
        WebSocket->>Frontend: Updated preview
    end
    
    User->>Frontend: Deploy to Cloudflare
    Frontend->>Agent: Deploy request
    Agent->>Sandbox: Trigger deployment
    Sandbox->>Agent: Deployment complete
    Agent->>WebSocket: Broadcast deployment_complete
    WebSocket->>Frontend: Live app URL

8. AI Operations Pipeline

mermaid
flowchart TD
    %% User Input
    Input["๐Ÿ—ฃ๏ธ User PromptNatural Language Request"]
    
    %% Planning Phase
    subgraph "๐Ÿ“‹ Planning Phase"
        TemplateSelection["๐Ÿท๏ธ Template SelectionCloudflare Stack Templates"]
        BlueprintGen["๐Ÿ  Blueprint GenerationPRD + Architecture Design"]
    end
    
    %% State Machine Controller
    subgraph "๐Ÿค– Deterministic State Machine"
        StateMachine["โš™๏ธ Agent State ControllergenerateAllFiles()"]
        
        subgraph "๐Ÿ”„ State Transitions"
            PhaseGenerating["๐Ÿ“ˆ PHASE_GENERATING"]
            PhaseImplementing["๐Ÿš€ PHASE_IMPLEMENTING"]
            Reviewing["๐Ÿ” REVIEWING"]
            Finalizing["โœจ FINALIZING"]
            Idle["โœ… IDLE"]
        end
    end
    
    %% Operations Layer
    subgraph "๐Ÿง  AI Operations"
        PhaseGenOp["๐Ÿ“ˆ Phase GenerationDevelopment Phases"]
        PhaseImplOp["๐Ÿš€ Phase ImplementationFile Generation + SCOF"]
        CodeReviewOp["๐Ÿ” Code ReviewIssue Detection"]
        FastCodeFixerOp["โšก Fast Code FixerQuick Issue Fixes"]
        FileRegenOp["๐Ÿ› ๏ธ File RegenerationSurgical Code Repairs"]
        ScreenshotAnalysisOp["๐Ÿ“ท Screenshot AnalysisVisual Validation"]
        UserConvOp["๐Ÿ’ฌ User ConversationFeedback Processing"]
    end
    
    %% Quality Assurance
    subgraph "๐Ÿ” Quality Assurance"
        ReviewCycles["๐Ÿ”„ Review CyclesUp to 5 iterations"]
        IssueCheck{{"โš ๏ธ Issues Found?"}}
        StaticAnalysis["๐Ÿ“Š Static AnalysisCode Validation"]
    end
    
    %% External Integration
    subgraph "๐Ÿค– AI Gateway"
        AIGateway["๐Ÿšช Cloudflare AI GatewayMulti-Provider Router"]
        
        subgraph "๐ŸŒ AI Providers"
            Gemini["๐Ÿ’Ž Gemini (Primary)"]
            GPT["๐Ÿง  GPT-4"]
            Claude["๐ŸŽญ Claude"]
            Cerebras["๐Ÿงช Cerebras"]
        end
    end
    
    %% Main Flow
    Input --> TemplateSelection
    TemplateSelection --> BlueprintGen
    BlueprintGen --> StateMachine
    
    %% State Machine Flow
    StateMachine --> PhaseGenerating
    PhaseGenerating --> PhaseImplementing
    PhaseImplementing --> Reviewing
    Reviewing --> Finalizing
    Finalizing --> Idle
    
    %% Operations Execution
    PhaseGenerating --> PhaseGenOp
    PhaseImplementing --> PhaseImplOp
    Reviewing --> CodeReviewOp
    
    %% Quality Control Loop
    CodeReviewOp --> ReviewCycles
    ReviewCycles --> IssueCheck
    IssueCheck -->|"โœ… Yes"| FastCodeFixerOp
    IssueCheck -->|"โœ… Yes"| FileRegenOp
    IssueCheck -->|"โŒ No"| StaticAnalysis
    FastCodeFixerOp --> PhaseImplementing
    FileRegenOp --> PhaseImplementing
    
    %% User Interaction
    UserConvOp --> PhaseGenerating
    ScreenshotAnalysisOp --> CodeReviewOp
    
    %% AI Integration
    TemplateSelection --> AIGateway
    BlueprintGen --> AIGateway
    PhaseGenOp --> AIGateway
    PhaseImplOp --> AIGateway
    CodeReviewOp --> AIGateway
    FastCodeFixerOp --> AIGateway
    FileRegenOp --> AIGateway
    ScreenshotAnalysisOp --> AIGateway
    UserConvOp --> AIGateway
    
    AIGateway --> Gemini
    AIGateway --> GPT
    AIGateway --> Claude
    AIGateway --> Cerebras
    
    %% Enhanced Styling
    classDef planning fill:#e3f2fd,stroke:#1976d2,stroke-width:2px,color:#000
    classDef statemachine fill:#fff3e0,stroke:#f57c00,stroke-width:3px,color:#000
    classDef states fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#000
    classDef operations fill:#e8f5e8,stroke:#388e3c,stroke-width:2px,color:#000
    classDef quality fill:#ffebee,stroke:#d32f2f,stroke-width:2px,color:#000
    classDef ai fill:#ff9900,stroke:#cc7a00,stroke-width:3px,color:#fff
    classDef decision fill:#fff8e1,stroke:#f57f17,stroke-width:2px,color:#000
    classDef success fill:#66ff66,stroke:#2e7d32,stroke-width:3px,color:#000
    classDef providers fill:#fce4ec,stroke:#c2185b,stroke-width:2px,color:#000
    
    class Input,TemplateSelection,BlueprintGen planning
    class StateMachine statemachine
    class PhaseGenerating,PhaseImplementing,Reviewing,Finalizing states
    class Idle success
    class PhaseGenOp,PhaseImplOp,CodeReviewOp,FastCodeFixerOp,FileRegenOp,ScreenshotAnalysisOp,UserConvOp operations
    class ReviewCycles,StaticAnalysis quality
    class IssueCheck decision
    class AIGateway ai
    class Gemini,GPT,Claude,Cerebras providers

9. Technology Stack Overview

mermaid
graph TB
    subgraph "Frontend Layer"
        React[React 18]
        Vite[Vite Build Tool]
        TailwindCSS[Tailwind CSS]
        ShadcnUI[shadcn/ui Components]
        ReactRouter[React Router]
    end
    
    subgraph "Backend Layer"
        CFWorkers[Cloudflare Workers]
        AgentsSDK[Cloudflare Agents SDK]
        TypeScript[TypeScript]
        HonoRouter[Hono Router]
        AuthMiddleware[Auth Middleware]
        BaseController[Base Controller]
    end
    
    subgraph "Data Layer"
        D1[Cloudflare D1 SQLite]
        Drizzle[Drizzle ORM]
        KV[Cloudflare KV]
        R2[Cloudflare R2]
        DatabaseService[Database Service]
        UserService[User Service]
        AppService[App Service]
        AuthService[Auth Service]
    end
    
    subgraph "AI & External Services"
        AIGateway[Cloudflare AI Gateway]
        OpenAI[OpenAI GPT-4]
        GitHubAPI[GitHub API]
        OAuth[OAuth Providers]
    end
    
    subgraph "Infrastructure"
        CFContainers[Cloudflare Containers]
        SandboxSDK[Cloudflare Sandbox SDK]
        WebSockets[WebSocket API]
        WorkersAnalytics[Workers Analytics]
    end
    
    React --> CFWorkers
    Vite --> React
    TailwindCSS --> React
    ShadcnUI --> React
    ReactRouter --> React
    
    CFWorkers --> AgentsSDK
    TypeScript --> CFWorkers
    HonoRouter --> CFWorkers
    AuthMiddleware --> CFWorkers
    BaseController --> CFWorkers
    
    CFWorkers --> DatabaseService
    DatabaseService --> Drizzle
    Drizzle --> D1
    DatabaseService --> UserService
    DatabaseService --> AppService
    DatabaseService --> AuthService
    CFWorkers --> KV
    CFWorkers --> R2
    
    CFWorkers --> AIGateway
    AIGateway --> OpenAI
    CFWorkers --> GitHubAPI
    CFWorkers --> OAuth
    
    CFWorkers --> CFContainers
    CFContainers --> SandboxSDK
    CFWorkers --> WebSockets
    CFWorkers --> WorkersAnalytics
    
    style CFWorkers fill:#ff9900
    style AgentsSDK fill:#ff9900
    style D1 fill:#ff9900
    style AIGateway fill:#ff9900
    style CFContainers fill:#ff9900

/* Detailed source-code truncated for AI context efficiency. */

bash
bun install
bun run setup
bun run dev
bun run typecheck
bun run lint
bun run test
bun run build

/* Detailed source-code truncated for AI context efficiency. */

bash
# LLM Providers
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GOOGLE_AI_STUDIO_API_KEY=...

# Authentication
JWT_SECRET=your-secret-key
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...

# Cloudflare
CLOUDFLARE_ACCOUNT_ID=...
CLOUDFLARE_API_TOKEN=...

# Sandbox Service
SANDBOX_SERVICE_URL=https://sandbox.example.com
SANDBOX_SERVICE_TOKEN=...

Setup:

bash
# Install dependencies
npm install

# Setup local D1 database
npm run db:migrate:local

# Start dev servers
npm run dev        # Frontend (Vite)
npm run dev:worker # Backend (Wrangler)

**Common Development Tasks**

Task: Change LLM model for an operation

File: /worker/agents/inferutils/config.ts

typescript
export const AGENT_CONFIG = {
  blueprint: {
    name: GEMINI_2_5_PRO,  // Change this
    reasoning_effort: 'medium',
    max_tokens: 64000,
    temperature: 0.7
  },
  // ... other operations
};

Task: Modify system prompt for conversation agent

File: /worker/agents/operations/UserConversationProcessor.ts

  • Line ~50: System prompt starts
  • Defines Orange AI personality, tool usage rules, behavior

Task: Add new WebSocket message

See "Getting Started - Common Tasks" section below (line 1605)

Task: Debug Durable Object state

In code:

typescript
// In simpleGeneratorAgent.ts
this.logger().info('Current state', { 
  devState: this.state.currentDevState,
  filesCount: Object.keys(this.state.generatedFilesMap).length,
  currentPhase: this.state.currentPhase
});

Via Cloudflare dashboard:

  1. Go to Workers & Pages โ†’ Durable Objects
  2. Find your DO instance
  3. View SQLite database directly

๐ŸŽฏ Core Principles & Non-Negotiable Rules

**1. Strict Type Safety**

  • โŒ NEVER use any type - find or create proper types
  • โœ… All frontend types imported from @/api-types (which re-exports from worker) or shared/types/
  • โœ… Search codebase for existing types before creating new ones
  • โœ… Extend/compose existing types rather than duplicating

Type Import Pattern:

typescript
// โœ… CORRECT - Single source of truth
import { BlueprintType, WebSocketMessage } from '@/api-types';

// โŒ WRONG - Direct worker imports in frontend
import { BlueprintType } from 'worker/agents/schemas';

**2. DRY Principle**

  • Search for similar functionality before implementing
  • Extract reusable utilities, hooks, and components
  • Never copy-paste code - refactor into shared functions

**3. Follow Existing Patterns**

  • Frontend APIs: All defined in /src/lib/api-client.ts
  • Backend Routes: Controllers in worker/api/controllers/, routes in worker/api/routes/
  • Database Services: In worker/database/services/
  • Types: Shared types in shared/types/, API types in src/api-types.ts

**4. File Naming Conventions**

  • React Components: PascalCase.tsx
  • Utilities/Hooks: kebab-case.ts
  • Backend Services: PascalCase.ts
  • Match the naming style of surrounding files

**5. Code Quality Standards**

  • โœ… Production-ready code only - no TODOs or placeholders
  • โœ… Proper TypeScript types with no implicit any
  • โœ… Clean, maintainable code
  • โŒ No hacky workarounds
  • โŒ No overly verbose AI-like comments
  • โŒ No emojis in code (only in markdown docs)

**6. Comments Style**

typescript
// โœ… GOOD - Explains code's purpose
// Calculate exponential backoff with max cap
const delay = Math.min(Math.pow(2, attempt) * 1000, 30000);

// โŒ BAD - Verbose AI narration
// Here we are calculating the delay using exponential backoff...

๐Ÿ—๏ธ Project Architecture

**Tech Stack**

  • Frontend: React 18, TypeScript, Vite, TailwindCSS, React Router v7
  • Backend: Cloudflare Workers, Durable Objects, D1 (SQLite)
  • AI/LLM: OpenAI, Anthropic, Google AI Studio (Gemini)
  • WebSocket: PartySocket for real-time communication
  • Sandbox: Custom container service with CLI tools
  • Templates: Project scaffolding system with template catalog

**Complete Directory Structure**

text
/* Detailed source-code truncated for AI context efficiency. */

๐Ÿ’ฌ Chat View Architecture

**Core Component:** `/src/routes/chat/chat.tsx`

Layout:

  • Left Panel (40%): Chat messages, phase timeline, deployment controls, chat input
  • Right Panel (60%): Editor view, Preview iframe, or Blueprint markdown

**State Management:** `/src/routes/chat/hooks/use-chat.ts`

This hook manages all chat state:

typescript
{
  files: FileType[]              // Generated files
  phaseTimeline: PhaseTimelineItem[] // Phase progress
  messages: ChatMessage[]        // Chat history
  websocket: WebSocket           // Real-time connection
  isGenerating: boolean          // Generation state
  previewUrl: string             // Preview deployment URL
  // ... deployment, blueprint, bootstrap state
}

**WebSocket Message Handler**

Location: /src/routes/chat/utils/handle-websocket-message.ts

Critical Messages:

  • agent_connected - Restore full state on connect
  • conversation_state - Load chat history with deduplication
  • file_generating / file_generated - File generation progress
  • phase_implementing / phase_implemented - Phase progress
  • deployment_completed - Preview URL ready
  • conversation_response - AI message (streaming or complete)
  • generation_stopped - User cancelled, mark phases as "cancelled"

**Phase Timeline**

Component: /src/routes/chat/components/phase-timeline.tsx

Status States:

  • generating - Active (orange spinner)
  • validating - Code review (blue spinner)
  • completed - Success (green checkmark)
  • cancelled - Interrupted (orange X)
  • error - Failed (red alert)

**Message Deduplication**

Problem: Tool execution causes duplicate AI messages

Solution: Multi-layer approach

  1. Backend skips redundant LLM calls (empty tool results)
  2. Frontend utilities (deduplicate-messages.ts) for live and restored messages
  3. System prompt teaches LLM not to repeat

๐Ÿ”ง Backend Architecture

**Durable Objects Pattern**

Each chat session is a Durable Object instance:

typescript
class SimpleCodeGeneratorAgent implements DurableObject {
  // Persisted in SQLite
  private state: CodeGenState;
  
  // In-memory only (ephemeral)
  private currentAbortController?: AbortController;
  private deepDebugPromise: Promise<any> | null = null;
}

/* Detailed source-code truncated for AI context efficiency. */

text
/* Detailed source-code truncated for AI context efficiency. */

/* Detailed source-code truncated for AI context efficiency. */

bash
git clone https://vibesdk.com/git/{agentId}

/* Detailed source-code truncated for AI context efficiency. */

typescript
await git.commit([], 'feat: Add authentication');

**2. reset(ref, options?)**

Aligns with: git reset --hard <commit>

typescript
await git.reset('abc123', { hard: true });
// Moves HEAD to commit, updates working directory
// No new commit created (destructive)

Behavior:

  • Moves HEAD to specified commit
  • Updates working directory (hard: true by default)
  • Does NOT create a new commit
  • Triggers onFilesChangedCallback

**3. log(limit?)**

Query commit history - standard git log.

**4. show(oid)**

Show commit details - files changed in commit.

**5. setOnFilesChangedCallback(callback)**

Register callback to be notified after git operations that change files.

typescript
git.setOnFilesChangedCallback(() => {
  fileManager.syncGeneratedFilesMapFromGit();
});

**6. getAllFilesFromHead()**

Get all files from HEAD commit for syncing.

typescript
const files = await git.getAllFilesFromHead();
// Returns: [{ filePath: string, fileContents: string }]

**FileManager Sync Pattern**

File: /worker/agents/services/implementations/FileManager.ts

FileManager is self-contained - it registers with GitVersionControl during construction and auto-syncs after git operations.

typescript
constructor(stateManager, getTemplateDetailsFunc, git) {
  // Auto-register callback with git
  this.git.setOnFilesChangedCallback(() => {
    this.syncGeneratedFilesMapFromGit();
  });
}

private async syncGeneratedFilesMapFromGit(): Promise<void> {
  // Get all files from HEAD commit
  const gitFiles = await this.git.getAllFilesFromHead();
  
  // Preserve existing file purposes
  const oldMap = this.stateManager.getState().generatedFilesMap;
  
  // Build new map
  const newMap = {};
  for (const file of gitFiles) {
    newMap[file.filePath] = {
      ...file,
      filePurpose: oldMap[file.filePath]?.filePurpose || 'Generated file',
      lastDiff: ''
    };
  }
  
  // Update state
  this.stateManager.setState({ generatedFilesMap: newMap });
}

Flow:

  1. FileManager constructed โ†’ Registers callback with git
  2. User performs operations โ†’ Dual-write continues (map + git)
  3. User calls git reset/checkout โ†’ Git modifies files
  4. Git calls callback โ†’ FileManager.syncGeneratedFilesMapFromGit()
  5. Sync reads from HEAD โ†’ Updates generatedFilesMap
  6. State synchronized โœ…

**Git Tool - Access Control**

Location: /worker/agents/tools/toolkit/git.ts

The git tool has parameterized access control - different commands available in different contexts.

**Tool Creation**

typescript
export function createGitTool(
  agent: CodingAgentInterface,
  logger: StructuredLogger,
  options?: { excludeCommands?: GitCommand[] }
): ToolDefinition<...> {
  const allowedCommands = options?.excludeCommands
    ? allCommands.filter(cmd => !options.excludeCommands!.includes(cmd))
    : allCommands;
  
  // Dynamic enum and description based on allowed commands
  return {
    function: {
      enum: allowedCommands,
        ? "... WARNING: reset is destructive!"
        : "...",
    }
  };
}

**Access by Context**

Context Available Commands File Notes
User Conversations commit, log, show /worker/agents/tools/customTools.ts (line 56) โœ… Safe - no destructive ops
Deep Debugger commit, log, show, reset /worker/agents/tools/customTools.ts (line 71) โš ๏ธ Full access with warnings

User Conversations:

typescript
// Safe version - no reset
createGitTool(agent, logger, { excludeCommands: ['reset'] })

Deep Debugger:

typescript
// Full access - includes reset
createGitTool(session.agent, logger) // No restrictions

**Reset Command - Safety**

Deep debugger prompt warnings:

  • Marked as UNTESTED and DESTRUCTIVE
  • Only use when:
    • User explicitly requests it
    • Tried everything else
    • Absolutely certain it's necessary
  • Must warn user before using
  • Prefer alternatives: regenerate_file, generate_files

**Why This Architecture?**

โœ… Single implementation - DRY principle maintained
โœ… Type-safe - TypeScript enforces valid commands
โœ… Context-aware - Different access in different contexts
โœ… Flexible - Easy to add more restrictions
โœ… Safe default - Users can't accidentally reset commits
โœ… Git CLI semantics - Aligns with actual git behavior

**Removed Methods**

  • inferPurposeFromPath() - Removed as requested
  • revert() - Was creating incorrect "revert commits"
  • restoreCommit() - Renamed to internal helper readFilesFromCommit

**Why These Limits?**

MAX_PHASES = 12:

  • Prevents infinite generation loops
  • Forces focused, efficient implementation
  • Keeps projects manageable

MAX_TOOL_CALLING_DEPTH = 7:

  • Prevents infinite recursion
  • Typically need 2-3 levels max
  • Safety against runaway LLM behavior

MAX_IMAGES_PER_MESSAGE = 2:

  • Balance between utility and cost
  • Vision API costs are high
  • Usually 1-2 images sufficient for context

MAX_LLM_MESSAGES = 200:

  • Prevents conversation from growing unbounded
  • Compactification kicks in before this
  • Typical session has 20-50 messages

**State Machine - Detailed Flow**

States: Defined in CurrentDevState enum

text
/* Detailed source-code truncated for AI context efficiency. */

/* Detailed source-code truncated for AI context efficiency. */

typescript
// Structure in /worker/agents/tools/toolkit/{tool-name}.ts
export function createToolName(agent: CodingAgentInterface, logger: StructuredLogger) {
  return {
    type: 'function',
    function: {
      name: 'tool_name',
      parameters: { /* JSON schema */ }
    },
    implementation: async (args) => {
      // Tool logic here
      return result;
    }
  };
}

**To Add a New Tool:**

  1. Create /worker/agents/tools/toolkit/my-tool.ts
  2. Export createMyTool(agent, logger) function
  3. Import in /worker/agents/tools/customTools.ts
  4. Add to either buildTools() (conversation) or buildDebugTools() (debugging)
  5. Tool automatically available to LLM

**Diagnostic Priority (in system prompt)**

  1. run_analysis first (fast, no user interaction needed)
  2. get_runtime_errors second (focused errors)
  3. get_logs last resort (verbose, cumulative)

Can fix multiple files in parallel - regenerate_file called simultaneously on different files

Concurrency: Cannot run while code generation active - checked via agent.isCodeGenerating()


๐Ÿ”Œ WebSocket Communication

**Connection Flow**

  1. User visits /chat/:chatId
  2. Frontend calls apiClient.connectToAgent(chatId)
  3. API returns websocketUrl
  4. Frontend connects via PartySocket
  5. Backend sends agent_connected with full state
  6. Frontend restores UI

**State Restoration**

typescript
case 'agent_connected': {
  // Backend sends snapshot
  setState(message.state);
  websocket.send({ type: 'get_conversation_state' });
}

case 'conversation_state': {
  // Restore with deduplication
  const deduplicated = deduplicateMessages(message.messages);
  setMessages(prev => [...prev, ...deduplicated]);
}

**Streaming Pattern**

typescript
// Backend sends chunks
ws.send({
  type: 'conversation_response',
  conversationId: 'abc',
  message: 'chunk',
  isStreaming: true,
});

// Frontend updates in place
setMessages(prev => updateOrAppendMessage(prev, id, content));

๐Ÿ› ๏ธ Implementation Patterns

**Adding New API Endpoint**

1. Define types (src/api-types.ts):

typescript
export interface GetFeatureRequest {
  id: string;
}

export interface GetFeatureResponse {
  feature: Feature;
}

2. Add to API client (src/lib/api-client.ts):

typescript
export const apiClient = {
  async getFeature(req: GetFeatureRequest): Promise<GetFeatureResponse> {
    const response = await fetch('/api/features', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(req),
    });
    if (!response.ok) throw new ApiError(response);
    return response.json();
  },
};

3. Create service (worker/database/services/FeatureService.ts):

typescript
export class FeatureService {
  constructor(private env: Env) {}
  
  async getFeature(id: string): Promise<Feature> {
    // Database logic
  }
}

4. Create controller (worker/api/controllers/feature/controller.ts):

typescript
export const featureController = {
  async getFeature(c: Context<AppEnv>) {
    const body = await c.req.json<GetFeatureRequest>();
    const service = new FeatureService(c.env);
    const feature = await service.getFeature(body.id);
    return c.json({ feature });
  },
};

5. Add route (worker/api/routes/feature-routes.ts):

typescript
export const featureRoutes = new Hono<AppEnv>();
featureRoutes.post('/', featureController.getFeature);

6. Register in main router (worker/api/routes/index.ts):

typescript
router.route('/api/features', featureRoutes);

**Creating Custom Hook**

Pattern: /src/hooks/use-{feature}.ts

typescript
export function useFeature(params: FeatureParams) {
  const [data, setData] = useState<FeatureData | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  
  useEffect(() => {
    async function fetch() {
      try {
        const result = await apiClient.getFeature(params);
        setData(result);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    }
    fetch();
  }, [params]);
  
  const refetch = useCallback(() => {
    setLoading(true);
    // ... refetch logic
  }, [params]);
  
  return { data, loading, error, refetch };
}

**Adding LLM Tool**

1. Create tool file (worker/agents/tools/toolkit/my-tool.ts):

typescript
export function createMyToolDefinition() {
  return {
    type: 'function' as const,
    function: {
      name: 'my_tool',
      parameters: {
        type: 'object',
        properties: {
          param: { type: 'string', description: 'Param description' },
        },
        required: ['param'],
      },
    },
  };
}

export async function myToolImplementation(
  args: { param: string },
  context: ToolContext,
  streamCb?: StreamCallback
): Promise<ToolResult> {
  // Check concurrency if needed
  if (context.agent.isCodeGenerating()) {
    return { error: 'GENERATION_IN_PROGRESS' };
  }
  
  // Implementation
  const result = await doWork(args.param);
  
  return { result };
}

2. Register tool (worker/agents/tools/customTools.ts):

typescript
import { createMyToolDefinition, myToolImplementation } from './toolkit/my-tool';

export function buildTools(agent: CodingAgentInterface) {
  return [
    // ... existing tools
    createTool(createMyToolDefinition(), myToolImplementation),
  ];
}

๐Ÿงช Testing Patterns

**Frontend Tests**

  • Component tests in __tests__/ directories
  • Integration tests for hooks
  • E2E tests with Playwright (if applicable)

**Backend Tests**

  • Unit tests for services
  • Integration tests for API endpoints
  • Tool execution tests

๐Ÿ“ Documentation Standards

**Code Comments**

  • Explain WHY, not WHAT (code should be self-documenting)
  • Keep comments brief and to the point
  • Update comments when code changes
  • No emojis in code comments

**Type Documentation**

typescript
// โœ… GOOD
/**
 * Represents a generated file in the chat interface.
 * Contains both metadata and content.
 */
export interface FileType {
  filePath: string;
  fileContents: string;
  isGenerating: boolean;
}

// โŒ BAD
// This is a file type that we use to represent files
export interface FileType { ... }

๐Ÿ” Debugging Guide

**Frontend Debugging**

  • Use React DevTools for component state
  • Check browser console for errors
  • Monitor WebSocket messages in Network tab
  • Use Debug Panel in chat interface

**Backend Debugging**

  • Check Cloudflare Workers logs
  • Use wrangler tail for live logs
  • Add strategic console.log with prefixes: [TOOL_CALL_DEBUG], [WS_DEBUG]
  • Check DO storage for persisted state

**Common Issues**

Empty Deep Debug Transcript:

  • Check max_tokens is sufficient (32000+)
  • Verify tool calls are completing
  • Check for abort signals

Duplicate Messages:

  • Verify deduplication utilities are used
  • Check backend history management
  • Ensure tool results aren't causing re-calls

WebSocket Disconnects:

  • Check retry logic in use-chat.ts
  • Verify DO isn't being evicted prematurely
  • Check for abort controller issues

๐Ÿ“ฆ Deployment

**Frontend**

  • Built with Vite
  • Deployed as static assets
  • Served by Cloudflare Pages or Workers

**Backend**

  • Deployed via Wrangler
  • Durable Objects for stateful agents
  • D1 for persistent database
  • KV for caching (if used)

**Database Migrations**

bash
# Generate migration
npm run db:generate

# Apply migrations (local)
npm run db:migrate:local

# Apply migrations (production)
npm run db:migrate:remote

๐Ÿ”„ Continuous Improvement

**Keep This Document Updated**

When you:

  • Add new features or components
  • Discover undocumented patterns
  • Find inaccuracies or outdated info
  • Learn domain-specific knowledge
  • Identify new best practices

Update sections:

  • Add to relevant section
  • Create new section if needed
  • Mark outdated info with โš ๏ธ and correction
  • Add examples for clarity
  • Keep it concise but complete

**Document Structure**

This guide is organized by:

  1. Core principles (rules that never change)
  2. Architecture (how things are structured)
  3. Patterns (how to implement features)
  4. Examples (concrete implementations)

Keep this structure when adding content.


๐Ÿ”Œ WebSocket Communication - Complete Reference

**WebSocket Message Types**

Location: /worker/agents/constants.ts

**Request Messages (Frontend โ†’ Backend):**

typescript
WebSocketMessageRequests:
- GENERATE_ALL: 'generate_all'           // Start code generation
- DEPLOY: 'deploy'                       // Deploy to Cloudflare Workers
- PREVIEW: 'preview'                     // Deploy to sandbox preview
- STOP_GENERATION: 'stop_generation'     // Cancel current operation
- RESUME_GENERATION: 'resume_generation' // Resume paused generation
- USER_SUGGESTION: 'user_suggestion'     // User message (conversational AI)
- CLEAR_CONVERSATION: 'clear_conversation' // Reset chat history
- GET_CONVERSATION_STATE: 'get_conversation_state' // Request history
- GET_MODEL_CONFIGS: 'get_model_configs' // Request model info
- CAPTURE_SCREENSHOT: 'capture_screenshot' // Capture preview screenshot
- GITHUB_EXPORT: 'github_export'         // DEPRECATED - use OAuth flow

**Response Messages (Backend โ†’ Frontend):**

text
/* Detailed source-code truncated for AI context efficiency. */

**WebSocket Message Flow Examples**

**1. Code Generation Flow:**

text
User clicks "Generate" button
  โ†“
Frontend: GENERATE_ALL
  โ†“
Backend: generation_started
  โ†“
[For each phase]
  Backend: phase_generating (LLM thinking)
  Backend: phase_generated (plan ready)
  Backend: phase_implementing (files starting)
  [For each file]
    Backend: file_generating
    Backend: file_chunk_generated (streaming)
    Backend: file_generated
  Backend: deployment_started
  Backend: deployment_completed (preview URL)
  Backend: code_reviewing (static analysis)
  Backend: code_reviewed (results)
  Backend: phase_implemented (phase done)
  โ†“
Backend: generation_complete

**2. User Conversation Flow:**

text
User types message โ†’ clicks send
  โ†“
Frontend: USER_SUGGESTION { message, images? }
  โ†“
Backend: conversation_response { isStreaming: true } (chunks)
  โ†“
[If tool calls]
  Backend: conversation_response { tool: { name, status: 'start' } }
  Backend: conversation_response { tool: { name, status: 'success', result } }
  โ†“
Backend: conversation_response { isStreaming: false } (final)

**3. Abort Generation Flow:**

text
User clicks abort button
  โ†“
Frontend: STOP_GENERATION
  โ†“
Backend: 
  - Calls agent.cancelCurrentInference()
  - Aborts active AbortController
  - Sets shouldBeGenerating = false
  โ†“
Backend: generation_stopped
  โ†“
Frontend:
  - Marks active phases as 'cancelled'
  - Shows orange X icon
  - Disables abort button

**Critical State Flags**

**`shouldBeGenerating` Flag**

Purpose: Persistent intent to generate code, survives page refreshes.

When set to true:

  • User clicks "Generate" button
  • User resumes generation

When set to false:

  • User clicks "Stop" button
  • Generation completes successfully
  • Generation fails permanently

Why it matters:

  • On page refresh, if shouldBeGenerating=true and no active generation โ†’ restart
  • Prevents abandoned generation sessions
  • Used by frontend to show "generating" vs "cancelled" phases

๐Ÿค– Conversational AI System ("Orange")

**Purpose**

Orange is the AI interface between users and the development agent. It handles:

  • User questions and discussions
  • Feature/bug requests (via queue_request tool)
  • Immediate debugging (via deep_debug tool)
  • Web searches for information

**System Prompt Philosophy**

CRITICAL: Orange speaks AS IF it's the developer:

  • โœ… "I'll add that feature"
  • โœ… "I'm fixing that bug"
  • โŒ NEVER: "The team will...", "The agent will..."

Two Options for User Requests:

  1. Immediate Action (deep_debug):

    • For active bugs needing instant fixes
    • Transfers control to autonomous debug agent
    • Returns transcript after completion
    • User sees real-time progress
  2. Queued Implementation (queue_request):

    • For features or non-urgent fixes
    • Relays to development agent
    • Implemented in next phase
    • Tell user: "I'll have that in the next phase or two"

**Available Tools**

Location: /worker/agents/tools/customTools.ts โ†’ buildTools()

typescript
1. queue_request: Queue modification requests
2. get_logs: Fetch sandbox logs (USE SPARINGLY)
3. deep_debug: Autonomous debugging (immediate fixes)
4. git: Version control (commit, log, show) - Safe version without reset
5. wait_for_generation: Wait for code generation
6. wait_for_debug: Wait for debug session
7. deploy_preview: Redeploy sandbox
8. clear_conversation: Clear chat history
9. rename_project: Rename the project
10. alter_blueprint: Modify blueprint fields
11. web_search: Search the web
12. feedback: Submit platform feedback

Note: User conversations get safe git tool (no reset command). Deep debugger gets full git tool (includes reset with warnings).

**Tool Call Rendering**

Pattern:

typescript
// Tool calls appear as expandable UI in chat messages
conversation_response {
  tool: {
    name: 'deep_debug',
    status: 'start' | 'success' | 'error',
    args: { issue: "..."},
    result: "transcript or error"
  }
}

Frontend displays:

  • Tool name with icon
  • Status indicator (spinner/check/alert)
  • Expandable arguments
  • Expandable result (for deep_debug, shows full transcript)

**Conversation History Management**

Two-Tier Storage:

  1. Running History (Compact):

    • Used for LLM context
    • Size-limited for token efficiency
    • Can be archived/summarized
    • Stored in compact_conversations table
  2. Full History:

    • Complete conversation log
    • Used for UI restoration
    • Never truncated
    • Stored in full_conversations table

Compactification:

typescript
COMPACTIFICATION_CONFIG:
- MAX_TURNS: 40 conversation turns
- MAX_ESTIMATED_TOKENS: 100,000 tokens
- PRESERVE_RECENT_MESSAGES: 10 messages always kept
- CHARS_PER_TOKEN: 4 (estimation)

Update Pattern:

typescript
addConversationMessage(message) {
  // Update or append to both histories
  if (exists) {
    // Update existing (for streaming)
    runningHistory[index] = message;
  } else {
    // Append new message
    runningHistory.push(message);
  }
  // Same for fullHistory
  save();
}

๐Ÿ› ๏ธ Tool System Architecture

**Tool Definition Pattern**

Location: /worker/agents/tools/toolkit/{tool-name}.ts

typescript
// 1. Define tool schema
export function createMyToolDefinition() {
  return {
    type: 'function' as const,
    function: {
      name: 'my_tool',
      parameters: {
        type: 'object',
        properties: {
          param: { 
            type: 'string', 
          }
        },
        required: ['param'],
      },
    },
  };
}

// 2. Implement tool logic
export async function myToolImplementation(
  args: { param: string },
  context: ToolContext,
  streamCb?: StreamCallback
): Promise<ToolResult> {
  // Validation
  if (!args.param) {
    return { error: 'Missing required parameter' };
  }
  
  // Concurrency checks (if needed)
  if (context.agent.isCodeGenerating()) {
    return { error: 'GENERATION_IN_PROGRESS' };
  }
  
  // Execute tool logic
  const result = await doWork(args.param);
  
  // Stream progress if callback provided
  streamCb?.('Processing...');
  
  return { result };
}

**Tool Registration**

For Conversation Tools:

typescript
// File: /worker/agents/tools/customTools.ts
import { createMyToolDefinition, myToolImplementation } from './toolkit/my-tool';

export function buildTools(
  agent: CodingAgentInterface,
  logger: StructuredLogger,
  toolRenderer: RenderToolCall,
  streamCb: (chunk: string) => void
): ToolDefinition[] {
  return [
    // ... existing tools
    createTool(createMyToolDefinition(), myToolImplementation),
  ];
}

For Debug Tools:

typescript
export function buildDebugTools(
  session: DebugSession,
  logger: StructuredLogger
): ToolDefinition[] {
  return [
    createReadFilesTool(session.agent, logger),
    createRunAnalysisTool(session.agent, logger),
    createRegenerateFileTool(session.agent, logger),
    // ... more debug-specific tools
  ];
}

**Tool Lifecycle Hooks**

typescript
const tool = {
  function: {...},
  implementation: async (args) => {...},
  
  // Optional hooks for UI feedback
  onStart: (args) => {
    toolRenderer({ 
      name: 'my_tool', 
      status: 'start', 
      args 
    });
  },
  
  onComplete: (args, result) => {
    toolRenderer({ 
      name: 'my_tool', 
      status: 'success', 
      args,
      result: JSON.stringify(result)
    });
  }
};

๐Ÿ—„๏ธ Database Schema Overview

**Core Tables**

Location: /worker/database/schema.ts

**1. Users Table**

typescript
users {
  id: text (PK)
  email: text (unique)
  username: text (unique, nullable)
  displayName: text
  avatarUrl: text
  provider: 'github' | 'google' | 'email'
  providerId: text
  passwordHash: text (for email provider)
  
  // Security
  emailVerified: boolean
  failedLoginAttempts: number
  lockedUntil: timestamp
  
  // Preferences
  theme: 'light' | 'dark' | 'system'
  timezone: text
  
  // Status
  isActive: boolean
  isSuspended: boolean
  
  // Timestamps
  createdAt, updatedAt, lastActiveAt, deletedAt
}

**2. Apps Table**

typescript
apps {
  id: text (PK)
  title: text
  iconUrl: text
  
  // Generation
  originalPrompt: text
  finalPrompt: text
  framework: text
  
  // Ownership
  userId: text (FK โ†’ users, nullable for anonymous)
  sessionToken: text (for anonymous)
  
  // Visibility
  visibility: 'private' | 'public'
  status: 'generating' | 'completed'
  
  // Deployment
  deploymentId: text
  githubRepositoryUrl: text
  
  // Metadata
  isArchived: boolean
  isFeatured: boolean
  version: number
  parentAppId: text (for forks)
  screenshotUrl: text
  
  // Timestamps
  createdAt, updatedAt, lastDeployedAt
}

**3. Sessions Table**

typescript
sessions {
  id: text (PK)
  userId: text (FK โ†’ users)
  
  // Session data
  deviceInfo: text
  userAgent: text
  ipAddress: text
  
  // Security
  isRevoked: boolean
  accessTokenHash: text
  refreshTokenHash: text
  
  // Timing
  expiresAt: timestamp
  createdAt: timestamp
  lastActivity: timestamp
}

**4. Stars & Favorites**

typescript
stars {
  id: text (PK)
  userId: text (FK โ†’ users)
  appId: text (FK โ†’ apps)
  starredAt: timestamp
  
  // Unique constraint on (userId, appId)
}

favorites {
  id: text (PK)
  userId: text (FK โ†’ users)
  appId: text (FK โ†’ apps)
  createdAt: timestamp
  
  // Unique constraint on (userId, appId)
}

**5. Analytics Tables**

typescript
appViews {
  id: text (PK)
  appId: text (FK โ†’ apps)
  userId: text (FK โ†’ users, nullable)
  sessionId: text
  viewedAt: timestamp
  
  // Indexes for fast counting
}

userModelConfigs {
  id: text (PK)
  userId: text (FK โ†’ users)
  agentActionName: text
  
  // Model overrides
  modelName: text
  maxTokens: number
  temperature: number
  reasoningEffort: 'low' | 'medium' | 'high'
  fallbackModel: text
  
  // Unique per user+action
}

**Database Service Pattern**

typescript
// File: /worker/database/services/DomainService.ts
export class DomainService {
  private db: D1Database;
  
  constructor(env: Env) {
    this.db = env.DB;
  }
  
  async getItem(id: string): Promise {
    // Use Drizzle ORM for type safety
    const result = await this.db
      .select()
      .from(itemsTable)
      .where(eq(itemsTable.id, id))
      .get();
    
    if (!result) throw new ApiError(404, 'Not found');
    return result;
  }
  
  async createItem(data: CreateInput): Promise {
    // Insert with validation
    const id = generateId();
    await this.db
      .insert(itemsTable)
      .values({ id, ...data });
    
    return this.getItem(id);
  }
}

๐Ÿ“ธ Image Attachment System

**Supported Formats**

Location: /worker/types/image-attachment.ts

typescript
SUPPORTED_IMAGE_MIME_TYPES = [
  'image/png',
  'image/jpeg',
  'image/webp',
]

MAX_IMAGE_SIZE_BYTES = 10 * 1024 * 1024; // 10MB
MAX_IMAGES_PER_MESSAGE = 2;

**Image Flow**

text
1. User uploads/drags image
   โ†“
2. Frontend: Validate size/type
   โ†“
3. Frontend: Convert to base64
   โ†“
4. Frontend: Show preview
   โ†“
5. User sends message
   โ†“
6. Frontend โ†’ Backend: USER_SUGGESTION { message, images: [...] }
   โ†“
7. Backend: Upload to R2 storage
   โ†“
8. Backend: Pass to LLM with vision model
   โ†“
9. LLM: Analyze image + generate response

**Image Types**

typescript
// Raw upload from user
interface ImageAttachment {
  id: string;
  filename: string;
  mimeType: SupportedImageMimeType;
  base64Data: string; // Without data URL prefix
  size: number;
  dimensions?: { width: number; height: number };
}

// After R2 upload
interface ProcessedImageAttachment {
  mimeType: SupportedImageMimeType;
  base64Data?: string; // Optional, may be cleared after upload
  r2Key: string; // R2 storage key
  publicUrl: string; // Public URL
  hash: string; // Content hash
}

**Frontend Validation**

Location: /src/hooks/use-image-upload.ts

typescript
Validation checks:
1. File type in SUPPORTED_IMAGE_MIME_TYPES
2. File size โ‰ค MAX_IMAGE_SIZE_BYTES
3. Total images โ‰ค MAX_IMAGES_PER_MESSAGE

Rejection behavior:
- Show error toast
- Don't add to preview
- Log validation failure

**Backend Validation**

Location: /worker/agents/core/websocket.ts

typescript
case USER_SUGGESTION:
  if (images && images.length > MAX_IMAGES_PER_MESSAGE) {
    sendError(`Maximum ${MAX_IMAGES_PER_MESSAGE} images allowed`);
    return;
  }
  
  for (const image of images) {
    if (image.size > MAX_IMAGE_SIZE_BYTES) {
      sendError(`Image exceeds ${MAX_IMAGE_SIZE_BYTES / 1024 / 1024}MB`);
      return;
    }
  }

๐Ÿ” Authentication Guards

Location: /src/hooks/useAuthGuard.ts and useActionGuard.ts

Purpose: Protect actions requiring authentication (star, fork, etc.)

Flow:

  1. User clicks protected action (not authenticated)
  2. Guard shows auth modal
  3. User logs in via GitHub/Google OAuth
  4. OAuth callback creates session
  5. Redirects back with ?action=star parameter
  6. Frontend detects parameter, executes pending action
  7. Clears action parameter

Options: requireFullAuth (reject anonymous), actionContext ("to star this app"), onSuccess callback


๐Ÿš€ GETTING STARTED - Common Tasks

**Adding a New LLM Tool**

Steps:

  1. Create tool file: /worker/agents/tools/toolkit/my-new-tool.ts
  2. Structure:
typescript
import { CodingAgentInterface } from 'worker/agents/services/implementations/CodingAgent';
import { StructuredLogger } from '../../../logger';

export function createMyNewTool(agent: CodingAgentInterface, logger: StructuredLogger) {
  return {
    type: 'function' as const,
    function: {
      name: 'my_new_tool',
      parameters: {
        type: 'object',
        properties: {
          input: {
            type: 'string',
          }
        },
        required: ['input']
      }
    },
    implementation: async (args: { input: string }) => {
      logger.info('Tool called', { args });
      // Your logic here
      return { result: 'success' };
    }
  };
}

/* Detailed source-code truncated for AI context efficiency. */

typescript
export class UserService extends BaseService {
  // Existing methods...
  
  async getNewMethod(userId: string): Promise<ResultType> {
    // Use 'fresh' for user's own data
    const readDb = this.getReadDb('fresh');
    
    const result = await readDb
      .select()
      .from(schema.users)
      .where(eq(schema.users.id, userId));
      
    if (!result) throw new Error('Not found');
    return result;
  }
}

Call from controller:

typescript
const userService = new UserService(c.env);
const data = await userService.getNewMethod(userId);

/* Detailed source-code truncated for AI context efficiency. */

typescript
// User-based (authenticated)
user:abc123

// Token-based (JWT hash)
token:sha256_hash_16_chars

// IP-based (anonymous)
ip:192.168.1.1

**Rate Limit Types**

Location: /worker/services/rate-limit/config.ts

  1. API_ENDPOINT - HTTP endpoint rate limit
  2. LLM_REQUEST - LLM inference rate limit
  3. AUTH_ATTEMPT - Login/signup attempts
  4. APP_CREATION - New app creation
  5. GITHUB_EXPORT - GitHub push operations

**Configuration Structure**

typescript
interface DORateLimitConfig {
  limit: number;        // Max requests per period
  period: number;       // Time window in seconds
  burst?: number;       // Burst allowance
  burstWindow?: number; // Burst time window
  bucketSize: number;   // Bucket size for sliding window
  dailyLimit?: number;  // Optional daily cap
}

**Usage**

typescript
// In API route
const allowed = await RateLimitService.enforce(
  env,
  request,
  RateLimitType.API_ENDPOINT,
  user
);

if (!allowed) {
  throw new RateLimitExceededError('Too many requests');
}

**LLM Model-Specific Rates**

Different models have different rate increments:

  • GPT-4o: 10 units
  • GPT-4o-mini: 1 unit
  • Claude Sonnet: 15 units
  • Gemini Pro: 20 units
  • Gemini Flash: 5 units

Why? Expensive models consume more quota to prevent abuse.


GitHub Service

Location: /worker/services/github/GitHubService.ts

Purpose: Export generated apps to GitHub repositories

**Key Operations**

1. Create Repository

typescript
static async createUserRepository(options: {
  token: string;           // User's GitHub PAT
  name: string;            // Repo name
  description?: string;
  private: boolean;        // Public or private
  auto_init?: boolean;     // Create with README
})

2. Push Generated Code

typescript
static async pushCodeToRepository({
  token,
  owner,
  repo,
  gitObjects,           // Agent's git objects
  templateDetails,      // Template base
  appQuery,            // Original user prompt
  branch = 'main'
})

Process:

  1. Build git repo with GitCloneService (rebases on template)
  2. Push all commits to GitHub via Octokit
  3. Add README with app description + Cloudflare deploy button
  4. Return repository URL

3. Add Deploy to Cloudflare Button

typescript
static async addCloudflareDeployButton({
  token,
  owner,
  repo,
  templateName
})

Appends markdown button to README for one-click Cloudflare deployment.


OAuth Service

Location: /worker/services/oauth/

Providers: Google, GitHub

**Base Pattern** (`base.ts`)

All providers extend BaseOAuthProvider:

typescript
abstract class BaseOAuthProvider {
  abstract getAuthorizationUrl(params): string;
  abstract exchangeCodeForToken(code, verifier): Promise<TokenResponse>;
  abstract getUserInfo(token): Promise;
}

**OAuth Flow**

Step 1: Generate Auth URL

typescript
const provider = OAuthProviderFactory.create('google', env);
const { url, state, codeVerifier } = await provider.getAuthorizationUrl({
  redirectUri: 'https://app.com/auth/callback',
  state: csrfToken,
  scopes: ['openid', 'email', 'profile']
});

// Store state + verifier in oauthStates table
// Redirect user to url

Step 2: Handle Callback

typescript
// Verify state (CSRF protection)
const storedState = await db.getOAuthState(state);
if (!storedState || storedState.used) throw new Error('Invalid state');

// Exchange code for token
const tokenData = await provider.exchangeCodeForToken(
  code,
  storedState.codeVerifier
);

// Get user info
const userInfo = await provider.getUserInfo(tokenData.access_token);

// Create or update user
const user = await authService.findOrCreateOAuthUser({
  provider: 'google',
  providerId: userInfo.id,
  email: userInfo.email,
  displayName: userInfo.name
});

// Create session
const session = await sessionService.createSession(user);

Step 3: Cleanup

typescript
// Mark state as used
await db.markOAuthStateUsed(state);

// Cleanup expired states (runs periodically)
await db.cleanupExpiredOAuthStates();

**PKCE (Proof Key for Code Exchange)**

Purpose: Prevent authorization code interception

Flow:

  1. Generate random codeVerifier (128 chars)
  2. Create codeChallenge = SHA256(codeVerifier)
  3. Send challenge in auth URL
  4. Store verifier in oauthStates table
  5. Send verifier in token exchange
  6. Provider verifies: SHA256(verifier) == challenge

Google Implementation:

  • Uses code_challenge_method=S256
  • Requires openid scope

GitHub Implementation:

  • Standard OAuth 2.0 (no PKCE)
  • Uses state for CSRF only

Analytics Service

Location: /worker/services/analytics/

Purpose: Track app views, stars, user activity

**Database Service**

File: /worker/database/services/AnalyticsService.ts

Key Operations:

1. Track View

typescript
await analyticsService.trackView(appId, userId, ipAddress);
  • Creates view record
  • Deduplicates by IP (1 view per IP per day)
  • Updates app.viewsCount

2. Star App

typescript
const result = await analyticsService.toggleStar(appId, userId);
// result: { starred: true } or { starred: false }
  • Adds/removes star
  • Updates app.starsCount
  • Returns new state

3. Get Activity Stats

typescript
const stats = await analyticsService.getUserActivity(userId, days = 30);
// Returns: appsCreated, totalViews, totalStars, recentActivity[]

**Ranking Impact**

Views and stars affect app rankings:

  • Popular: (views ร— 1) + (stars ร— 3) DESC
  • Trending: (recent_activity ร— 1000000 + recency_bonus) DESC

Cache Service

Location: /worker/services/cache/

Purpose: Cache expensive operations

**Cache Strategies**

1. Git Packfile Cache

typescript
// Cache generated packfiles for git clone
await cacheService.set(
  `git:packfile:${agentId}`,
  packfileBuffer,
  3600 // 1 hour TTL
);

2. Static Analysis Cache

typescript
// Cache TypeScript analysis results
await cacheService.set(
  `analysis:${fileHash}`,
  analysisResults,
  300 // 5 min TTL
);

3. Template Cache

typescript
// Cache template file trees
await cacheService.set(
  `template:${templateName}`,
  templateFiles,
  86400 // 24 hours
);

**Implementation**

Uses Cloudflare Cache API:

typescript
const cache = caches.default;
await cache.put(request, response);
const cached = await cache.match(request);

CSRF Protection

Location: /worker/services/csrf/

Purpose: Prevent cross-site request forgery

**Token Generation**

typescript
// Generate token for OAuth state
const csrfToken = await crypto.subtle.digest(
  'SHA-256',
  crypto.getRandomValues(new Uint8Array(32))
);

**Validation**

typescript
// In OAuth callback
if (callbackState !== storedState.state) {
  throw new SecurityError('CSRF token mismatch');
}

**Storage**

CSRF tokens stored in oauthStates table:

  • state column = CSRF token
  • expiresAt = 10 minutes
  • used flag prevents replay

User Secrets Store (Durable Object)

Location: /worker/services/secrets/

Purpose: Secure, encrypted storage for user API keys and secrets with key rotation support

**Architecture**

Storage: Durable Object with SQLite backend

  • One DO instance per user (userId as DO ID)
  • XChaCha20-Poly1305 encryption (AEAD)
  • Hierarchical key derivation: MEK โ†’ UMK โ†’ DEK
  • Key rotation metadata tracking

Core Components:

  1. UserSecretsStore (UserSecretsStore.ts) - Main DO class
  2. KeyDerivation (KeyDerivation.ts) - PBKDF2-based key derivation
  3. EncryptionService (EncryptionService.ts) - XChaCha20-Poly1305 encryption
  4. Types (types.ts) - Type definitions

**Key Features**

1. Hierarchical Key Derivation

text
Master Encryption Key (MEK)
    โ†“ PBKDF2 with userId salt
User Master Key (UMK)
    โ†“ PBKDF2 with secret-specific salt
Data Encryption Key (DEK) - unique per secret

2. Encryption

  • Algorithm: XChaCha20-Poly1305 (AEAD)
  • Unique salt per secret (16 bytes)
  • Unique nonce per encryption (24 bytes)
  • Authentication tag for integrity verification

3. Key Rotation

  • Tracks master key fingerprint (SHA-256)
  • Detects key changes automatically
  • Re-encrypts all secrets with new key
  • Maintains rotation statistics

4. Security Features

  • Access counting (tracks how many times secret accessed)
  • Secret expiration timestamps
  • Soft deletion (90-day retention)
  • Key preview masking (shows first/last 4 chars)

**Database Schema**

Tables:

sql
-- Main secrets table
CREATE TABLE secrets (
    id TEXT PRIMARY KEY,
    user_id TEXT NOT NULL,
    name TEXT NOT NULL,
    secret_type TEXT NOT NULL,
    encrypted_value BLOB NOT NULL,
    nonce BLOB NOT NULL,
    salt BLOB NOT NULL,
    key_preview TEXT NOT NULL,
    metadata TEXT,
    access_count INTEGER DEFAULT 0,
    created_at INTEGER NOT NULL,
    updated_at INTEGER NOT NULL,
    expires_at INTEGER,
    is_active INTEGER DEFAULT 1,
    key_fingerprint TEXT NOT NULL
);

-- Key rotation tracking
CREATE TABLE key_rotation_metadata (
    id INTEGER PRIMARY KEY DEFAULT 1,
    current_key_fingerprint TEXT NOT NULL,
    last_rotation_at INTEGER NOT NULL,
    rotation_count INTEGER DEFAULT 0
);

**API Methods (RPC - No Exceptions)**

Critical: All DO RPC methods return null or boolean on error, never throw exceptions.

typescript
// Store new secret
async storeSecret(request: StoreSecretRequest): Promise<SecretMetadata | null>

// Get decrypted value
async getSecretValue(secretId: string): Promise<SecretWithValue | null>

// List secrets (metadata only)
async listSecrets(): Promise<SecretMetadata[]>

// Update secret
async updateSecret(secretId: string, updates: UpdateSecretRequest): Promise<SecretMetadata | null>

// Delete secret (soft delete)
async deleteSecret(secretId: string): Promise

// Get key rotation info
async getKeyRotationInfo(): Promise<KeyRotationInfo>

**Type Definitions**

typescript
interface StoreSecretRequest {
    name: string;
    secretType: 'api_key' | 'oauth_token' | 'webhook_secret' | 'encryption_key' | 'other';
    value: string;
    metadata?: Record<string, unknown>;
    expiresAt?: number;
}

interface SecretMetadata {
    id: string;
    userId: string;
    name: string;
    secretType: string;
    keyPreview: string;
    metadata?: Record<string, unknown>;
    accessCount: number;
    createdAt: number;
    updatedAt: number;
    expiresAt?: number;
}

interface SecretWithValue {
    value: string;
    metadata: SecretMetadata;
}

interface KeyRotationInfo {
    currentKeyFingerprint: string;
    lastRotationAt: number;
    rotationCount: number;
    totalSecrets: number;
    secretsRotated: number;
}

**Usage Example**

typescript
// Get DO stub
const id = env.UserSecretsStore.idFromName(user.id);
const store = env.UserSecretsStore.get(id);

// Store secret
const metadata = await store.storeSecret({
    name: 'OpenAI API Key',
    secretType: 'api_key',
    value: 'sk-...',
    metadata: { provider: 'openai' }
});

if (!metadata) {
    throw new Error('Failed to store secret');
}

// Retrieve decrypted value
const secret = await store.getSecretValue(metadata.id);

if (!secret) {
    throw new Error('Secret not found or expired');
}

console.log(secret.value); // Decrypted value
console.log(secret.metadata.accessCount); // Incremented on each access

// List all secrets (no values)
const secrets = await store.listSecrets();

// Update secret
const updated = await store.updateSecret(metadata.id, {
    name: 'OpenAI API Key (Production)',
    expiresAt: Date.now() + 86400000 // 24 hours
});

// Delete secret
const deleted = await store.deleteSecret(metadata.id);

**Controller Integration**

Location: /worker/api/controllers/user-secrets/controller.ts

typescript
// Example: Get secret value
static async getSecretValue(
    request: Request,
    env: Env,
    ctx: ExecutionContext,
    context: RouteContext
): Promise<ControllerResponse<ApiResponse>> {
    const user = context.user!;
    const secretId = context.pathParams.secretId;
    
    const stub = this.getUserSecretsStub(env, user.id);
    const result = await stub.getSecretValue(secretId);
    
    if (!result) {
        return UserSecretsController.createErrorResponse(
            'Secret not found or has expired',
            404
        );
    }
    
    return UserSecretsController.createSuccessResponse(result);
}

**Key Rotation Process**

Automatic Detection:

  1. On DO initialization, checks current master key fingerprint
  2. Compares with stored fingerprint in database
  3. If different, triggers key rotation

Re-encryption:

typescript
async performKeyRotation() {
    // 1. Fetch all active secrets
    const secrets = this.ctx.storage.sql.exec(`
        SELECT * FROM secrets WHERE is_active = 1
    `);
    
    // 2. Decrypt with old key, encrypt with new key
    for (const secret of secrets) {
        const decrypted = await this.decrypt(secret.encrypted_value, ...);
        const encrypted = await this.encrypt(decrypted);
        // 3. Update in database atomically
    }
    
    // 4. Update rotation metadata
}

**Security Considerations**

โœ… Good Practices:

  • Master key stored in Worker environment variable
  • Unique salt per secret
  • AEAD encryption with integrity verification
  • Key rotation support
  • Soft deletion for recovery
  • Access tracking for audit

โš ๏ธ Important Notes:

  • DO RPC methods return null/boolean instead of throwing exceptions
  • Master key must be 64 hex characters (32 bytes)
  • Expired secrets automatically filtered from results
  • Soft deleted secrets retained for 90 days

**Testing**

Location: /test/worker/services/secrets/

Comprehensive test suite with 90+ tests (3 test files):

  • KeyDerivation.test.ts - 17 unit tests for key derivation
  • EncryptionService.test.ts - 18 unit tests for encryption/decryption
  • UserSecretsStore.test.ts - 55+ E2E tests for full DO lifecycle

Run tests:

bash
npm test test/worker/services/secrets
# Or with Bun:
bun run test:bun test/worker/services/secrets

Test Coverage:

  • CRUD operations
  • Encryption/decryption
  • Key rotation
  • Expiration handling
  • Concurrency (10 parallel operations)
  • Large scale (20+ secrets, 5KB values)
  • Data integrity verification
  • Error handling

**Configuration**

Wrangler Configuration:

jsonc
{
  "durable_objects": {
    "bindings": [
      {
        "name": "UserSecretsStore",
        "class_name": "UserSecretsStore"
      }
    ]
  },
  "migrations": [
    {
      "tag": "v3",
      "new_sqlite_classes": ["UserSecretsStore"]
    }
  ]
}

๐ŸŽจ FRONTEND RENDERING PATTERNS

Component Architecture

**Atomic Design Structure**

Hierarchy:

text
1. Primitives (ui/) - shadcn/ui base components
   โ†“
2. Shared (shared/) - App-specific reusable components
   โ†“
3. Features (routes/) - Page-specific components
   โ†“
4. Pages (routes/*.tsx) - Full page views

**Example: Button Hierarchy**

typescript
// 1. Primitive: /components/ui/button.tsx
export const Button = forwardRef<HTMLButtonElement, ButtonProps>((
  { className, variant, size, ...props },
  ref
) => {
  return (
    
  );
});

// 2. Shared: /components/shared/AppCard.tsx
export function AppCard({ app }: { app: App }) {
  return (
    <Card>
      <CardHeader>
        <h3>{app.title}</h3>
      </CardHeader>
      <CardFooter>
         navigate(`/app/${app.id}`)}>
          View App
        
      </CardFooter>
    </Card>
  );
}

// 3. Feature: /routes/apps/apps-list.tsx
export function AppsList() {
  const { apps } = useApps();
  
  return (
    
      {apps.map(app => <AppCard key={app.id} app={app} />)}
    
  );
}

State Management Patterns

**1. Local State (useState)**

Use for: UI-only state (modals, dropdowns, form inputs)

typescript
const [isOpen, setIsOpen] = useState(false);
const [selectedFile, setSelectedFile] = useState<FileType | null>(null);

**2. Server State (Custom Hooks)**

Use for: Data from API

typescript
// /hooks/use-apps.ts
export function useApps(filters?: AppFilters) {
  const [apps, setApps] = useState<App[]>([]);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    apiClient.getApps(filters).then(setApps);
  }, [filters]);
  
  return { apps, loading, refetch };
}

// Usage
const { apps, loading } = useApps({ sortBy: 'popular' });

**3. Global State (Context)**

Use for: Cross-component shared state (auth, theme)

typescript
// /contexts/auth-context.tsx
const AuthContext = createContext<AuthContextType>(null!);

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  
  return (
    <AuthContext.Provider value={{ user, setUser }}>
      {children}
    </AuthContext.Provider>
  );
}

// Usage
const { user } = useAuth();

**4. WebSocket State (use-chat hook)**

Use for: Real-time agent state

Location: /src/routes/chat/hooks/use-chat.ts

Pattern:

typescript
export function useChat(chatId: string) {
  // Local state
  const [messages, setMessages] = useState<ChatMessage[]>([]);
  const [files, setFiles] = useState<FileType[]>([]);
  const [isGenerating, setIsGenerating] = useState(false);
  
  // WebSocket connection
  const [websocket, setWebSocket] = useState<WebSocket | null>(null);
  
  // Message handler
  useEffect(() => {
    if (!websocket) return;
    
    websocket.onmessage = (event) => {
      const message = JSON.parse(event.data);
      handleWebSocketMessage(message, {
        setMessages,
        setFiles,
        setIsGenerating,
        // ... other setters
      });
    };
  }, [websocket]);
  
  return {
    messages,
    files,
    isGenerating,
    websocket,
    sendMessage: (text) => {
      websocket?.send(JSON.stringify({ type: 'USER_MESSAGE', text }));
    }
  };
}

Rendering Optimization

**1. useMemo for Expensive Computations**

typescript
const sortedFiles = useMemo(() => {
  return files.sort((a, b) => a.path.localeCompare(b.path));
}, [files]);

**2. useCallback for Event Handlers**

typescript
const handleFileClick = useCallback((fileId: string) => {
  setSelectedFile(files.find(f => f.id === fileId));
}, [files]);

**3. React.memo for Pure Components**

typescript
export const FileTreeNode = memo(({ file, onSelect }: Props) => {
  return (
     onSelect(file.id)}>
      {file.name}
    
  );
});

**4. Virtual Scrolling for Large Lists**

typescript
// For 1000+ items
import { useVirtualizer } from '@tanstack/react-virtual';

const virtualizer = useVirtualizer({
  count: apps.length,
  getScrollElement: () => containerRef.current,
  estimateSize: () => 200, // Card height
});

Data Fetching Patterns

**1. Single Resource**

typescript
// /hooks/use-app.ts
export function useApp(appId?: string) {
  const [app, setApp] = useState<App | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  
  useEffect(() => {
    if (!appId) return;
    
    setLoading(true);
    apiClient.getApp(appId)
      .then(setApp)
      .catch(setError)
      .finally(() => setLoading(false));
  }, [appId]);
  
  return { app, loading, error, refetch };
}

**2. Paginated List**

typescript
// /hooks/use-apps.ts
export function useApps(filters?: AppFilters) {
  const [apps, setApps] = useState<App[]>([]);
  const [hasMore, setHasMore] = useState(true);
  const currentPageRef = useRef(1);
  const isLoadingMoreRef = useRef(false);
  
  const fetchApps = useCallback(async (loadMore = false) => {
    if (isLoadingMoreRef.current) return;
    isLoadingMoreRef.current = true;
    
    const page = loadMore ? currentPageRef.current + 1 : 1;
    const result = await apiClient.getApps({ ...filters, page });
    
    if (loadMore) {
      setApps(prev => [...prev, ...result.apps]);
    } else {
      setApps(result.apps);
    }
    
    setHasMore(result.hasMore);
    currentPageRef.current = page;
    isLoadingMoreRef.current = false;
  }, [filters]);
  
  const loadMore = () => fetchApps(true);
  
  return { apps, hasMore, loadMore };
}

**3. Infinite Scroll**

typescript
const { apps, hasMore, loadMore } = useApps();

const observerRef = useRef();
const sentinelRef = useRef<HTMLDivElement>(null);

useEffect(() => {
  observerRef.current = new IntersectionObserver(entries => {
    if (entries[0].isIntersecting && hasMore) {
      loadMore();
    }
  });
  
  if (sentinelRef.current) {
    observerRef.current.observe(sentinelRef.current);
  }
  
  return () => observerRef.current?.disconnect();
}, [hasMore, loadMore]);

return (
  
    {apps.map(app => <AppCard key={app.id} app={app} />)}
     {/* Sentinel element */}
  
);

Form Handling

**Controlled Components**

typescript
const [formData, setFormData] = useState({
  title: '',
  isPublic: true
});

const handleChange = (field: keyof typeof formData) => (
  e: React.ChangeEvent<HTMLInputElement>
) => {
  setFormData(prev => ({ ...prev, [field]: e.target.value }));
};

const handleSubmit = async (e: FormEvent) => {
  e.preventDefault();
  await apiClient.createApp(formData);
};

return (
  <form onSubmit={handleSubmit}>
    
    Create
  </form>
);

**Form Validation**

typescript
const [errors, setErrors] = useState<Record<string, string>>({});

const validate = () => {
  const newErrors: Record<string, string> = {};
  
  if (!formData.title) {
    newErrors.title = 'Title is required';
  }
  if (formData.title.length < 3) {
    newErrors.title = 'Title must be at least 3 characters';
  }
  
  setErrors(newErrors);
  return Object.keys(newErrors).length === 0;
};

const handleSubmit = async (e: FormEvent) => {
  e.preventDefault();
  if (!validate()) return;
  
  await apiClient.createApp(formData);
};

**Simple Modal State**

typescript
const [isOpen, setIsOpen] = useState(false);

return (
  <>
     setIsOpen(true)}>Open Modal
    
    <Dialog open={isOpen} onOpenChange={setIsOpen}>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Modal Title</DialogTitle>
        </DialogHeader>
        {/* Modal content */}
      </DialogContent>
    </Dialog>
  </>
);
typescript
const [selectedApp, setSelectedApp] = useState<App | null>(null);

return (
  <>
    {apps.map(app => (
       setSelectedApp(app)}>
        Edit
      
    ))}
    
    {selectedApp && (
      <EditAppModal
        app={selectedApp}
        onClose={() => setSelectedApp(null)}
      />
    )}
  </>
);

Error Handling

**Error Boundary**

typescript
// /components/ErrorBoundary.tsx
export class ErrorBoundary extends Component {
  state = { hasError: false, error: null };
  
  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error };
  }
  
  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    console.error('Error caught by boundary:', error, errorInfo);
    // Send to Sentry
  }
  
  render() {
    if (this.state.hasError) {
      return <ErrorFallback error={this.state.error} />;
    }
    return this.props.children;
  }
}

**API Error Handling**

typescript
try {
  const app = await apiClient.getApp(appId);
  setApp(app);
} catch (error) {
  if (error instanceof ApiError) {
    if (error.status === 404) {
      setError('App not found');
    } else if (error.status === 403) {
      setError('Access denied');
    } else {
      setError('Something went wrong');
    }
  }
}

/* Detailed source-code truncated for AI context efficiency. */

text
/* Detailed source-code truncated for AI context efficiency. */

/* Detailed source-code truncated for AI context efficiency. */

text
โ†’ createInstance(templateName, projectName, webhookUrl)
   โ† { instanceId, url, status }
   โ†’ Save instanceId to state
  1. File Synchronization

    text
    โ†’ Collect all files from generatedFilesMap
    โ†’ Format as { path, content, encoding: 'utf-8' }[]
    โ†’ writeFiles(instanceId, files, "Deploy generated code")
    โ† { success: true, filesWritten: 42 }
  2. Package.json Sync

    text
    โ†’ Check if package.json changed
    โ†’ If changed: executeCommands(['npm install'])
    โ†’ Wait for completion (timeout: 60s)
    โ†’ Cache new package.json in state
  3. Bootstrap Commands (if needed)

    text
    โ†’ Execute commandsHistory (previously run user commands)
    โ†’ Validates/filters dangerous commands
    โ†’ Runs: npm install, setup scripts, etc.
  4. Start Dev Server

    text
    โ†’ Already running from instance creation
    โ†’ Or trigger via command if stopped
    โ†’ Monitor startup logs
  5. Health Check Loop

    text
    โ†’ setInterval(30s): ping sandbox
    โ†’ Check status endpoint
    โ†’ If unhealthy: log warning, may reset
  6. Return Preview URL

    text
    โ†’ Send URL to frontend via WebSocket
    โ†’ User can open in iframe or new tab
    โ†’ App is live and interactive

Redeployment (Incremental Updates)

When files change after initial deploy:

  1. Diff Detection

    • Compare file hashes in generatedFilesMap
    • Only sync changed files
  2. Partial Sync

    text
    โ†’ writeFiles(instanceId, [changedFiles])
    โ† Hot reload triggered automatically
  3. No Full Rebuild

    • Dev server hot reloads changes
    • Fast iteration (< 1s typically)

Deployment Errors & Recovery

Common errors:

  1. Timeout (60s)

    • Cause: npm install too slow, network issues
    • Recovery: Reset sessionId, retry with fresh instance
  2. Instance Not Found

    • Cause: Container crashed or evicted
    • Recovery: Create new instance, redeploy all files
  3. Command Execution Failed

    • Cause: Invalid package.json, dependency conflicts
    • Recovery: Show error to user, allow editing
  4. Health Check Failed

    • Cause: Dev server crashed, port conflict
    • Recovery: Reset session on next deploy attempt

๐Ÿค– LLM INFERENCE SYSTEM

Overview

Location: /worker/agents/inferutils/

Centralized inference engine that all operations use to call LLMs.

Key features:

  • Multi-provider support (OpenAI, Anthropic via Cloudflare AI Gateway)
  • Streaming responses
  • Tool calling with recursive execution
  • Retry logic with exponential backoff
  • Cancellation support (AbortController)
  • Token tracking

Inference Flow

text
Operation (PhaseImplementation, UserConversationProcessor, etc.)
        โ†“
   getOperationOptions() โ†’ InferenceContext
        โ†“
   executeInference(args, context)
        โ†“
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚   Retry Loop (max 3 attempts)  โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                 โ†“
            infer(args)
                 โ†“
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚  OpenAI SDK (via AI Gateway)   โ”‚
   โ”‚  - Model selection              โ”‚
   โ”‚  - Token streaming              โ”‚
   โ”‚  - Tool call parsing            โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                 โ†“
          Tool calls present?
                 โ”‚
         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        Yes              No
         โ”‚                โ”‚
         โ†“                โ†“
  Execute tools    Return response
  Recursive infer

Model Selection

Location: /worker/agents/inferutils/config.ts

Available models:

  1. GPT-4o - Fast, good for most tasks
  2. GPT-4o-mini - Cheapest, simple operations
  3. Claude 3.5 Sonnet - Best for complex reasoning
  4. Gemini 2.0 Flash - Fast, experimental
  5. Gemini 2.5 Pro - Highest quality, deep debugging

Selection by operation:

  • Blueprint generation: GPT-4o
  • Phase planning: GPT-4o
  • File generation: GPT-4o
  • Conversation: GPT-4o-mini
  • Deep debugging: Gemini 2.5 Pro (reasoning_effort: high)
  • Code review: GPT-4o

Streaming

When enabled:

  • User conversation responses
  • Deep debugger output
  • Real-time code generation feedback

How it works:

  1. LLM sends Server-Sent Events (SSE)
  2. infer() yields chunks via async generator
  3. Operation accumulates + forwards to WebSocket
  4. Frontend renders progressively

Tool Calling

Recursive execution:

  1. LLM response includes tool_calls array
  2. infer() executes each tool in parallel
  3. Results collected
  4. Filtered (empty/null results skipped)
  5. If results exist: call LLM again with tool outputs
  6. Repeat until LLM provides final response

Max depth: Configurable per operation


Retry Logic

Triggers retry:

  • Rate limit errors (429)
  • Network timeouts
  • Temporary API failures (5xx)

Does NOT retry:

  • Cancelled operations (AbortError)
  • Invalid API key (401)
  • Malformed requests (400)

Backoff: Exponential (1s, 2s, 4s)


Cancellation

Each operation gets AbortSignal:

text
User clicks stop button
     โ†“
WebSocket: STOP_GENERATION
     โ†“
agent.cancelCurrentInference()
     โ†“
AbortController.abort()
     โ†“
OpenAI SDK cancels HTTP request
     โ†“
infer() throws InferError('cancelled')
     โ†“
No retry, immediate propagation

Nested operations: Share same AbortController
Tool calls: All cancelled together


๐Ÿ” AUTHENTICATION & AUTHORIZATION SYSTEM

Overview

The auth system implements a comprehensive JWT-based authentication with OAuth 2.0 social login (Google, GitHub), session management, API keys, and security auditing. All auth operations are centralized through services that interact with D1 database.

Core Components:

  1. AuthService - Main authentication orchestrator
  2. SessionService - JWT session management with D1 persistence
  3. JWTUtils - Token creation, verification, signing
  4. OAuth Providers - Google & GitHub implementations with PKCE
  5. Middleware - Route protection and token extraction
  6. Security - Password hashing, rate limiting, audit logs

Auth Architecture Flow

text
/* Detailed source-code truncated for AI context efficiency. */

/* Detailed source-code truncated for AI context efficiency. */

text
/* Detailed source-code truncated for AI context efficiency. */

BaseService Pattern

Location: /worker/database/services/BaseService.ts

**Purpose**

Provides common database functionality to all domain services:

  • Database connection management
  • Read replica access (D1 Sessions API)
  • Type-safe where condition building
  • Error handling patterns
  • Logging

**Implementation**

typescript
abstract class BaseService {
  protected logger = createLogger(this.constructor.name);
  protected db: DatabaseService;
  protected env: Env;
  
  constructor(env: Env) {
    this.db = createDatabaseService(env);
    this.env = env;
  }
  
  // Direct database access (primary)
  protected get database() {
    return this.db.db;
  }
  
  // Read replica access (optimized latency)
  protected getReadDb(strategy: 'fast' | 'fresh' = 'fast') {
    return this.db.getReadDb(strategy);
  }
  
  // Build type-safe WHERE conditions
  protected buildWhereConditions(
    conditions: (SQL | undefined)[]
  ): SQL | undefined {
    const validConditions = conditions.filter(
      (c): c is SQL => c !== undefined
    );
    if (validConditions.length === 0) return undefined;
    if (validConditions.length === 1) return validConditions[0];
    return and(...validConditions);
  }
  
  // Standard error handling
  protected handleDatabaseError(
    error: unknown,
    operation: string,
    context?: Record<string, unknown>
  ): never {
    this.logger.error(`Database error in ${operation}`, { error, context });
    throw error;
  }
}

D1 Sessions API - Read Replicas

**What is D1 Sessions?**

Cloudflare D1 Sessions API provides read replicas for D1 databases distributed globally. This dramatically reduces latency for read queries by serving them from the nearest replica.

**Strategies**

Location: /worker/database/database.ts

typescript
class DatabaseService {
  getReadDb(strategy: 'fast' | 'fresh' = 'fast') {
    if (strategy === 'fast') {
      // Lowest latency - may be slightly stale
      return drizzle(this.env.DB, { ... });
    } else {
      // Latest data - may have higher latency
      return drizzle(this.env.DB.withSession({ strategy: 'fresh' }), { ... });
    }
  }
}

**When to Use Each Strategy**

**'fast' Strategy (Default)**

Use for:

  • Public app listings
  • Public app details
  • Analytics and stats
  • Search results
  • Any read-only public data

Benefits:

  • Lowest latency (served from nearest replica)
  • Suitable for data that can tolerate slight staleness (few seconds)
  • Most queries should use this

Example:

typescript
// Public apps - use fast replicas
async getPublicApps(options: PublicAppQueryOptions) {
  const readDb = this.getReadDb('fast');  // โ† Use fast strategy
  
  const apps = await readDb
    .select()
    .from(schema.apps)
    .where(eq(schema.apps.visibility, 'public'));
}

**'fresh' Strategy**

Use for:

  • User's own data (own apps, favorites)
  • Immediately after writes (read-after-write)
  • Auth/session validation
  • Account settings
  • Any data where staleness is unacceptable

Benefits:

  • Latest data from primary or recent replica
  • Ensures user sees their own changes immediately

Example:

typescript
// User's own apps - use fresh data
async getUserApps(userId: string) {
  const readDb = this.getReadDb('fresh');  // โ† Use fresh strategy
  
  const apps = await readDb
    .select()
    .from(schema.apps)
    .where(eq(schema.apps.userId, userId));
}

**NEVER Use Read Replicas For:**

โŒ Write operations - Always use primary (this.database)
โŒ Auth validation - Use primary to avoid security issues
โŒ Immediately after INSERT/UPDATE - Read from primary
โŒ Critical consistency - Password changes, payments, etc.


Drizzle ORM Patterns

**Basic Queries**

**SELECT**

typescript
// Simple select
const users = await db
  .select()
  .from(schema.users)
  .where(eq(schema.users.email, email));

// Select specific columns
const users = await db
  .select({
    id: schema.users.id,
    email: schema.users.email
  })
  .from(schema.users);

// With JOIN
const apps = await db
  .select({
    app: schema.apps,
    userName: schema.users.displayName
  })
  .from(schema.apps)
  .leftJoin(schema.users, eq(schema.apps.userId, schema.users.id));

**INSERT**

typescript
// Insert one
const [user] = await db
  .insert(schema.users)
  .values({
    id: generateId(),
    email: '[email protected]',
    displayName: 'User',
    createdAt: new Date()
  })
  .returning();

// Insert many
await db
  .insert(schema.apps)
  .values([
    { id: id1, title: 'App 1', ... },
    { id: id2, title: 'App 2', ... }
  ]);

**UPDATE**

typescript
await db
  .update(schema.users)
  .set({ 
    displayName: 'New Name',
    updatedAt: new Date()
  })
  .where(eq(schema.users.id, userId));

**DELETE**

typescript
// Hard delete
await db
  .delete(schema.sessions)
  .where(eq(schema.sessions.id, sessionId));

// Soft delete (preferred)
await db
  .update(schema.users)
  .set({ deletedAt: new Date() })
  .where(eq(schema.users.id, userId));

**Complex Queries**

**Aggregations**

typescript
// COUNT
const result = await db
  .select({ count: sql<number>`COUNT(*)` })
  .from(schema.apps)
  .where(eq(schema.apps.visibility, 'public'));

const total = result[0].count;

// SUM, AVG
const stats = await db
  .select({
    totalViews: sql<number>`SUM(${schema.appViews.id})`,
    avgViews: sql<number>`AVG(view_count)`
  })
  .from(schema.apps);

**Subqueries**

typescript
// Subquery in WHERE
const apps = await db
  .select()
  .from(schema.apps)
  .where(
    inArray(
      schema.apps.id,
      db.select({ id: schema.favorites.appId })
        .from(schema.favorites)
        .where(eq(schema.favorites.userId, userId))
    )
  );

**Conditional WHERE Clauses**

typescript
// Use BaseService.buildWhereConditions()
const conditions: WhereCondition[] = [];

if (framework) {
  conditions.push(eq(schema.apps.framework, framework));
}

if (search) {
  conditions.push(
    or(
      sql`LOWER(${schema.apps.title}) LIKE ${`%${search}%`}`,
      sql`LOWER(${schema.apps.description}) LIKE ${`%${search}%`}`
    )
  );
}

const whereClause = this.buildWhereConditions(conditions);

const apps = await db
  .select()
  .from(schema.apps)
  .where(whereClause);

/* Detailed source-code truncated for AI context efficiency. */

typescript
// Add to websocket.ts handleWebSocketMessage()
logger.info('Received WebSocket message', { type: message.type, data: message });

**Issue: LLM not calling tools**

Common causes:

  1. Tool description unclear โ†’ LLM doesn't know when to use it
  2. Tool not registered in buildTools() or buildDebugTools()
  3. Parameter schema too complex โ†’ simplify
  4. System prompt doesn't mention tool

Fix:

  • Keep tool description to 2-3 clear lines
  • Make parameters simple (prefer strings over complex objects)
  • Add tool to relevant system prompt

**Issue: Database query returning stale data**

Cause: Using read replica for data that needs to be fresh

Fix:

typescript
// WRONG - uses fast replica
const readDb = this.getReadDb('fast');

// RIGHT - uses fresh data
const readDb = this.getReadDb('fresh');

// OR use primary for critical consistency
const result = await this.database.select()...

**Issue: "Rate limit exceeded" during development**

Quick fix:

typescript
// In UserConversationProcessor.ts or codeDebugger.ts
// Temporarily increase max_tokens or reduce frequency

Better fix: Use cheaper model for testing

typescript
// In config.ts
conversationalResponse: {
  name: GEMINI_2_5_FLASH,  // Fast & cheap
  max_tokens: 4000,
}

**Issue: Type errors after schema change**

Steps:

  1. Regenerate Drizzle types: npm run db:generate
  2. Restart TypeScript server in IDE
  3. Check migration applied: npm run db:migrate:local

**Issue: Sandbox deployment failing**

Check logs:

typescript
// In DeploymentManager.ts, enable verbose logging
this.logger.info('Deployment attempt', { 
  sessionId: this.getSessionId(),
  filesCount: files.length 
});

Common causes:

  1. Sandbox service unreachable
  2. Invalid template name
  3. sessionId mismatch (check agent.state.sessionId)
  4. npm install timeout โ†’ increase timeout or split commands

**Issue: Agent state not persisting**

Verify:

  1. Check DO storage: Cloudflare dashboard โ†’ Durable Objects
  2. Ensure setState() called after changes
  3. Check for exceptions in state serialization

Test:

typescript
const currentState = this.getState();
this.logger().info('State before save', { currentState });
this.setState(newState);
this.logger().info('State after save', { newState });

**Where to Look for Logs**

Local development:

  • Frontend: Browser console
  • Worker: Terminal where npm run dev:worker is running
  • Durable Objects: Same terminal, prefixed with DO ID

Production:

  • Cloudflare dashboard โ†’ Workers & Pages โ†’ Logs
  • Real-time logs via wrangler tail
  • Sentry (if configured)

**Useful Debug Snippets**

Log all WebSocket messages:

typescript
// In websocket.ts
logger.info('[WS_IN]', { type: message.type, keys: Object.keys(message) });

Log all tool calls:

typescript
// In customTools.ts executeToolWithDefinition()
logger.info('[TOOL_CALL]', { name: toolDef.function.name, args });

Log state transitions:

typescript
// In simpleGeneratorAgent.ts launchStateMachine()
logger.info('[STATE_TRANSITION]', { 
  from: currentDevState, 
  to: executionResults.currentDevState 
});

๐Ÿ”’ RATE LIMITING

Overview

Location: /worker/middleware/rate-limiter.ts

Rate limiting protects API endpoints from abuse using token bucket algorithm with Durable Object storage.


Implementation

Middleware: Applied to all API routes except health checks

Strategy:

  • Token bucket algorithm - Tokens refill over time
  • Per-user basis - Keyed by userId (authenticated) or IP (anonymous)
  • Durable Object storage - Distributed rate limit state
  • Graceful degradation - Falls back on DO errors

Rate Limits

User Type Requests Window Burst
Authenticated 100 1 minute 150
Anonymous 20 1 minute 30
API Keys 300 1 minute 400

Burst: Maximum requests in short burst before throttling


Response Headers

http
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1698765432

On rate limit exceeded:

http
HTTP/1.1 429 Too Many Requests
Retry-After: 42
Content-Type: application/json

{
  "error": "Rate limit exceeded",
  "retryAfter": 42
}

Frontend Handling

Location: /src/routes/chat/utils/message-helpers.ts

typescript
export function handleRateLimitError(
  error: RateLimitExceededError,
  setMessages: (fn: (prev: ChatMessage[]) => ChatMessage[]) => void
) {
  const retryAfter = error.retryAfter || 60;
  const message = `Rate limit exceeded. Please wait ${retryAfter} seconds.`;
  
  setMessages(prev => [
    ...prev,
    createAIMessage('rate-limit', message)
  ]);
}

Usage:

typescript
catch (error) {
  if (error instanceof RateLimitExceededError) {
    handleRateLimitError(error, setMessages);
    return;
  }
  // ... other error handling
}

Bypassing for Internal Tools

Some endpoints bypass rate limiting:

  • Health checks (/health, /api/health)
  • WebSocket connections (rate limited separately)
  • Internal service-to-service calls (authenticated with service tokens)

Configuration:

typescript
// In rate-limiter.ts
const EXEMPT_PATHS = ['/health', '/api/health'];

Monitoring

Cloudflare Analytics:

  • 429 response rate
  • Peak request times
  • Top rate-limited IPs

Custom Logs:

typescript
logger.warn('Rate limit exceeded', {
  userId: ctx.userId,
  ip: ctx.ip,
  path: ctx.path,
  remaining: 0
});

/* Detailed source-code truncated for AI context efficiency. */

json
POST /api/auth/register
{
  "email": "[email protected]",
  "password": "SecurePassword123!",
  "name": "Test User"
}

POST /api/auth/login
{
  "email": "[email protected]", 
  "password": "SecurePassword123!"
}

2. OAuth Authentication

  • Google OAuth: GET /api/auth/oauth/google
  • GitHub OAuth: GET /api/auth/oauth/github

3. Session-Based Authentication

  • Uses secure HTTP-only cookies
  • Sessions are automatically maintained across requests
  • CSRF protection via X-CSRF-Token header

๐Ÿ“ฑ Core API Workflows

1. Create a New App with AI

bash
# 1. Login or use OAuth
POST /api/auth/login

# 2. Start code generation
POST /api/agent
{
  "query": "Create a React todo app with TypeScript and Tailwind CSS",
  "agentMode": "smart",
  "language": "typescript", 
  "frameworks": ["react", "tailwindcss"],
  "selectedTemplate": "react-typescript"
}

# 3. Connect to WebSocket for real-time updates
GET /api/agent/{agentId}/ws (WebSocket)

# 4. Deploy preview when ready
GET /api/agent/{agentId}/preview

2. Browse and Interact with Apps

bash
# Get public apps (no auth required)
GET /api/apps/public?page=1&limit=20&sort=stars&order=desc

# Get app details (no auth required)
GET /api/apps/{appId}

# Star an app (requires auth)
POST /api/apps/{appId}/star

# Fork an app (requires auth) 
POST /api/apps/{appId}/fork

3. Configure AI Models

bash
# Get available models and providers
GET /api/model-configs/byok-providers

# Update model configuration for specific agent action
PUT /api/model-configs/planner
{
  "modelName": "claude-3-5-sonnet-20241022",
  "maxTokens": 4096,
  "temperature": 0.7,
  "reasoningEffort": "medium"
}

# Test model configuration
POST /api/model-configs/test
{
  "agentActionName": "planner",
  "useUserKeys": true
}

4. Manage API Keys and Secrets

bash
# Get secret templates
GET /api/secrets/templates

# Store an API key
POST /api/secrets
{
  "templateId": "openai_api_key",
  "name": "My OpenAI API Key",
  "envVarName": "OPENAI_API_KEY",
  "value": "sk-your-api-key-here"
}

# Create custom model provider  
POST /api/user/providers
{
  "name": "My Custom OpenAI Provider",
  "baseUrl": "https://api.openai.com/v1",
  "apiKey": "sk-your-key",
  "models": [...]
}

๐Ÿ”ง Advanced Features

Environment Variables

The collection uses these automatically managed variables:

Variable Description Auto-populated
csrf_token CSRF protection token โœ…
user_id Current user ID โœ…
session_id Current session ID โœ…
agent_id Current agent/app ID โœ…
app_id Current app ID โœ…
provider_id Model provider ID Manual
secret_id Secret ID Manual

Request Automation

  • CSRF Tokens: Automatically fetched and included
  • Session Management: Cookies handled transparently
  • Variable Population: IDs extracted from responses
  • Error Handling: Test scripts validate responses

WebSocket Testing

For WebSocket endpoints like agent communication:

  1. Use a WebSocket client (wscat, Postman WebSocket, etc.)
  2. Connect to: ws://localhost:8787/api/agent/{agentId}/ws
  3. Include authentication cookies
  4. Send/receive real-time messages during code generation

๐Ÿ› ๏ธ Development Setup

Local Development

  1. Start Wrangler Dev Server:

    bash
    cd /path/to/vibesdk
    bun run dev
  2. Update Environment:

    • Set baseUrl to http://localhost:8787
    • Ensure .dev.vars contains required environment variables
  3. Test Authentication:

    • OAuth may require ngrok for localhost callback URLs
    • Email auth works directly with localhost

Production Testing

  1. Update baseUrl to your production domain
  2. Ensure OAuth apps are configured with correct callback URLs
  3. Test with real OAuth credentials

๐Ÿ“š API Documentation

Authentication Levels

  • Public: No authentication required
  • Authenticated: Requires valid session
  • Owner Only: Requires ownership of the resource

Common Parameters

Parameter Description Example
page Page number for pagination 1
limit Items per page 20
sort Sort field createdAt, stars
order Sort order asc, desc
period Time period filter today, week, month, all
search Search query "todo app"

Response Format

All API responses follow this structure:

json
{
  "success": true,
  "data": { ... },
  "message": "Optional message",
  "pagination": {  // For paginated responses
    "page": 1,
    "limit": 20, 
    "total": 100,
    "totalPages": 5
  }
}

Error Responses

json
{
  "success": false,
  "error": "Error message",
  "code": "ERROR_CODE",
  "details": { ... }  // Optional additional details
}

/* Detailed source-code truncated for AI context efficiency. */

bash
# Bun is recommended. Install it first if needed.
curl -fsSL https://bun.sh/install | bash
# Then install dependencies and run setup
bun install
bun run setup

/* Detailed source-code truncated for AI context efficiency. */

bash
Enter your custom domain (or press Enter to skip): myapp.com
โœ… Custom domain set: myapp.com
Use remote Cloudflare resources (KV, D1, R2, etc.)? (Y/n): 
Configure for production deployment? (Y/n):

Without Custom Domain:

bash
Enter your custom domain (or press Enter to skip): [press Enter]
โš ๏ธ  No custom domain provided.
   โ€ข Remote Cloudflare resources: Not available
   โ€ข Production deployment: Not available
   โ€ข Only local development will be configured

Continue with local-only setup? (Y/n):

/* Detailed source-code truncated for AI context efficiency. */

text
openid user-details.read ai.read ai.write aig.read aig.run aig.write offline_access

The scopes and the Cloudflare OAuth endpoint URLs are hardcoded in the worker
(worker/services/oauth/cloudflare-connect.ts) and are not configurable โ€” just make
sure the OAuth client is authorized for all of these scopes, or the authorization
request fails with invalid_scope.

2. Set the environment variables

Add the client credentials to .dev.vars (and .prod.vars for production):

bash
CLOUDFLARE_OAUTH_CLIENT_ID="<your-oauth-client-id>"        # required for Login with Cloudflare
CLOUDFLARE_OAUTH_CLIENT_SECRET="<your-oauth-client-secret>"
CF_OAUTH_ENCRYPTION_KEY="<32-byte base64 key>"             # required for AI Gateway; encrypts the token cookie

/* Detailed source-code truncated for AI context efficiency. */

bash
cp .dev.vars.example .dev.vars

2. Configure Required Variables

bash
# Essential
CLOUDFLARE_API_TOKEN="your-api-token"
CLOUDFLARE_ACCOUNT_ID="your-account-id"

# Security
JWT_SECRET="generated-secret"

# Domain (optional)
CUSTOM_DOMAIN="your-domain.com"

3. Create Cloudflare Resources

Create required resources in your Cloudflare account:

  • KV Namespace for VibecoderStore
  • D1 Database named vibesdk-db
  • R2 Bucket named vibesdk-templates

4. Update `wrangler.jsonc`

Update resource IDs in wrangler.jsonc with the IDs from step 3.

Starting Development

After setup is complete:

bash
# Set up database
bun run db:migrate:local

# Start development server
bun run dev

/* Detailed source-code truncated for AI context efficiency. */

dockerfile
# Add your company's Root CA certificate for corporate network access
COPY your-root-ca.pem /usr/local/share/ca-certificates/your-root-ca.crt
RUN update-ca-certificates

# Set SSL environment variables for cloudflared and other tools
ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
ENV NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/your-root-ca.crt
ENV CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt

โš ๏ธ Security Warning: Never commit corporate CA certificates to public repositories. Use .gitignore to exclude certificate files and only use this for local development.

Getting Help

  1. Check the setup report for specific issues and suggestions
  2. Review the Cloudflare Workers documentation
  3. Ensure all prerequisites are met

Production Deployment

If you configured remote deployment during setup, you'll have a .prod.vars file ready for production. Deploy with:

bash
bun run deploy

This will:

  • Build the application
  • Update Cloudflare resources
  • Deploy to Cloudflare Workers
  • Apply database migrations
  • Configure custom domain routing (if specified)

Production-Only Setup

If you only set up for local development initially, you can configure production later:

  1. Run setup again and choose "yes" for remote deployment configuration
  2. Provide production domain when prompted
  3. Deploy using bun run deploy

Manual Production Setup

Alternatively, create .prod.vars manually based on .dev.vars but with:

  • Production domain in CUSTOM_DOMAIN
  • Production API keys and secrets
  • ENVIRONMENT="prod"

Next Steps

Once setup is complete:

  1. Start developing with bun run dev
  2. Visit http://localhost:5173 to access VibeSDK
  3. Try generating your first AI-powered application
  4. Deploy to production when ready with bun run deploy

File Structure After Setup

The setup script creates and modifies these files:

text
vibesdk/
โ”œโ”€โ”€ .dev.vars              # Local development environment variables
โ”œโ”€โ”€ .prod.vars             # Production environment variables (if configured)
โ”œโ”€โ”€ wrangler.jsonc         # Updated with resource IDs and domain
โ”œโ”€โ”€ vite.config.ts         # Updated for remote/local bindings
โ”œโ”€โ”€ migrations/            # Database migration files
โ””โ”€โ”€ templates/             # Template repository (downloaded)

Summary

The VibeSDK setup script provides a comprehensive, intelligent configuration experience:

**Key Features:**

  • Simplified domain setup - One-time domain configuration with clear feature implications
  • Intelligent AI provider selection - Multi-provider support with automatic configuration
  • AI Gateway automation - Automatic token setup and configuration
  • Custom provider support - Dynamic API key generation and worker configuration updates
  • Production-ready - Both local development and production deployment configuration
  • User-friendly defaults - Y/n prompts with clear default indicators

**What Gets Configured:**

  • Cloudflare resources (KV, D1, R2, AI Gateway, dispatch namespaces)
  • Environment variables (.dev.vars and .prod.vars)
  • Worker configuration (wrangler.jsonc, worker-configuration.d.ts)
  • Database setup and migrations
  • Template deployment
  • ARM64 compatibility

The setup script handles everything from basic Cloudflare resource creation to advanced AI provider configuration, making it easy to get started regardless of your Cloudflare plan or AI provider preferences.

For any issues during setup, check the troubleshooting section above or refer to the comprehensive status report the script provides at the end.

Important Caveats & Known Issues

**Legacy tunnel and container configuration**

USE_TUNNEL_FOR_PREVIEW, SandboxDockerfile, and container instance settings belong to the retired sandbox preview path. Current generated-app previews run as Dynamic Workers loaded by SpaceDO. Do not troubleshoot the current preview path as a Docker or cloudflared tunnel unless you are intentionally running legacy tooling.

**"Deploy to Cloudflare" Button Limitations (Chat Interface)**

The "Deploy to Cloudflare" button in the chat interface (for generated apps) has specific requirements for local development:

Note: This refers to the deployment button within the VibeSDK platform's chat interface, not the GitHub repository deploy button.

Requirements:

  1. Custom domain must be properly configured during setup
  2. Initial deployment - Project must be deployed at least once to your Cloudflare account
  3. Remote dispatch bindings - wrangler.jsonc must have remote dispatch namespace enabled
  4. Dispatch worker - A dispatch worker must be running in your account

Why These Requirements?

  • The deploy feature uses Cloudflare's dispatch namespace system
  • Dispatch requires a running worker in your account to handle deployment requests
  • Local-only development isn't yet supported for this in vibesdk

Current Status: Making "Deploy to Cloudflare" work completely in local-only mode is not yet implemented.

**Dynamic Worker preview troubleshooting**

For current previews, verify the SpaceDO Durable Object binding, Worker Loader binding, branch deployment, and signed preview URL. Verify the Artifacts namespace only when ENABLE_ARTIFACTS="true". Build failures originate in @cloudflare/worker-bundler; application runtime failures should be inspected through browser console logs. If an issue persists, open a GitHub issue with the setup report and deployment error.