Skip to main content

CODITECT Development Studio - Technical Design Document (TDD)

Version: 1.0.0
Date: 2026-01-31
Status: Draft


1. Technical Overview

1.1 Technology Stack

LayerTechnologyVersionPurpose
FrontendNext.js14.xReact framework with App Router
React18.xUI library
TypeScript5.xType safety
Tailwind CSS3.xStyling
Zustand4.xState management
Socket.io Client4.xWebSocket communication
BackendCloudflare Workers-Edge compute
Hono3.xWeb framework
Durable Objects-Stateful coordination
R2/GCS-Object storage
ComputeCloudflare Sandboxes-Ephemeral containers
Docker24.xContainer runtime
AI/MLMultiple adapters-Claude, Gemini, Codex, Kimi
OpenAI SDK4.xUnified API interface
DevOpsWrangler3.xWorkers deployment
GitHub Actions-CI/CD

1.2 System Interfaces

// Core Type Definitions

// Tenant Context
interface TenantContext {
orgId: string; // Organization identifier
teamId: string; // Team within org
userId: string; // User identifier
projectId?: string; // Active project
quotas: QuotaConfig; // Resource limits
permissions: string[]; // RBAC permissions
}

// Session
interface Session {
id: string;
tenant: TenantContext;
status: 'creating' | 'running' | 'paused' | 'terminating' | 'terminated';
sandboxId: string;
websocketUrl: string;
createdAt: Date;
expiresAt: Date;
lastActivityAt: Date;
metadata: SessionMetadata;
}

// Chat Message
interface ChatMessage {
id: string;
sessionId: string;
role: 'user' | 'assistant' | 'system' | 'tool';
content: string;
provider?: LLMProvider; // Which LLM generated this
timestamp: Date;
metadata: {
tokens?: { input: number; output: number };
latency?: number;
cost?: number;
tools?: ToolCall[];
};
}

// File System
interface FileNode {
path: string;
name: string;
type: 'file' | 'directory' | 'symlink';
size?: number;
modifiedAt: Date;
children?: FileNode[];
}

// LLM Provider
interface LLMProvider {
id: 'claude' | 'gemini' | 'codex' | 'kimi';
name: string;
endpoint: string;
models: string[];
capabilities: Capability[];
health: 'healthy' | 'degraded' | 'unhealthy';
}

2. Frontend Architecture

2.1 Component Hierarchy

app/
├── layout.tsx # Root layout with providers
├── page.tsx # Main dashboard
├── viz/
│ └── page.tsx # Fullscreen visualization
├── api/
│ └── [...route]/
│ └── route.ts # API proxy

├── components/
│ ├── layout/
│ │ ├── app-shell.tsx # Main app container
│ │ ├── sidebar.tsx # Navigation sidebar
│ │ └── header.tsx # Top header with user menu
│ │
│ ├── chat/
│ │ ├── chat-panel.tsx # Main chat container
│ │ ├── message-list.tsx # Scrollable messages
│ │ ├── message-item.tsx # Individual message
│ │ ├── chat-input.tsx # Message input with attachments
│ │ ├── agent-selector.tsx # LLM provider picker
│ │ └── session-info.tsx # Active session metadata
│ │
│ ├── visualization/
│ │ ├── viz-panel.tsx # Visualization container
│ │ ├── workflow-view.tsx # Mermaid/workflow diagrams
│ │ ├── pitch-deck-view.tsx # Slide presentations
│ │ ├── market-analysis-view.tsx # Charts/graphs
│ │ └── code-preview.tsx # Syntax highlighted code
│ │
│ ├── project/
│ │ ├── file-tree.tsx # Explorer sidebar
│ │ ├── file-editor.tsx # Monaco/CodeMirror editor
│ │ ├── terminal.tsx # Embedded terminal
│ │ └── session-list.tsx # Active/available sessions
│ │
│ └── shared/
│ ├── loading.tsx # Loading states
│ ├── error-boundary.tsx # Error handling
│ ├── command-palette.tsx # CMD+K interface
│ └── theme-toggle.tsx # Dark/light mode

├── lib/
│ ├── store.ts # Zustand store
│ ├── api.ts # API client
│ ├── websocket.ts # WebSocket manager
│ ├── auth.ts # Authentication helpers
│ └── utils.ts # Utilities

├── hooks/
│ ├── use-session.ts # Session management
│ ├── use-chat.ts # Chat state
│ ├── use-files.ts # File operations
│ └── use-websocket.ts # Real-time connection

└── types/
└── index.ts # TypeScript definitions

2.2 State Management (Zustand)

// store.ts - Core state management

import { create } from 'zustand';
import { persist } from 'zustand/middleware';

interface AppState {
// Auth State
user: User | null;
token: string | null;
isAuthenticated: boolean;

// Session State
activeSession: Session | null;
sessions: Session[];

// UI State
sidebarOpen: boolean;
activeTab: 'chat' | 'files' | 'settings';
theme: 'dark' | 'light';

// Chat State
messages: ChatMessage[];
isStreaming: boolean;
selectedProvider: LLMProvider['id'];

// File State
fileTree: FileNode | null;
openFiles: string[];
activeFile: string | null;

// Actions
login: (token: string, user: User) => void;
logout: () => void;
setActiveSession: (session: Session) => void;
addMessage: (message: ChatMessage) => void;
updateMessage: (id: string, updates: Partial<ChatMessage>) => void;
setFileTree: (tree: FileNode) => void;
toggleSidebar: () => void;
}

export const useAppStore = create<AppState>()(
persist(
(set, get) => ({
// Initial state
user: null,
token: null,
isAuthenticated: false,
activeSession: null,
sessions: [],
sidebarOpen: true,
activeTab: 'chat',
theme: 'dark',
messages: [],
isStreaming: false,
selectedProvider: 'claude',
fileTree: null,
openFiles: [],
activeFile: null,

// Actions
login: (token, user) => set({ token, user, isAuthenticated: true }),
logout: () => set({
token: null, user: null, isAuthenticated: false,
activeSession: null, sessions: []
}),
setActiveSession: (session) => set({ activeSession: session }),
addMessage: (message) => set((state) => ({
messages: [...state.messages, message]
})),
updateMessage: (id, updates) => set((state) => ({
messages: state.messages.map(m =>
m.id === id ? { ...m, ...updates } : m
)
})),
setFileTree: (tree) => set({ fileTree: tree }),
toggleSidebar: () => set((state) => ({
sidebarOpen: !state.sidebarOpen
})),
}),
{
name: 'coditect-storage',
partialize: (state) => ({
token: state.token,
theme: state.theme,
sidebarOpen: state.sidebarOpen
}),
}
)
);

2.3 WebSocket Connection Manager

// websocket.ts

import { io, Socket } from 'socket.io-client';
import { useAppStore } from './store';

class WebSocketManager {
private socket: Socket | null = null;
private reconnectAttempts = 0;
private maxReconnectAttempts = 5;

connect(sessionId: string, token: string) {
this.socket = io(process.env.NEXT_PUBLIC_WS_URL!, {
auth: { token },
query: { sessionId },
transports: ['websocket'],
});

this.socket.on('connect', () => {
console.log('WS connected');
this.reconnectAttempts = 0;
});

this.socket.on('message', (data: ChatMessage) => {
useAppStore.getState().addMessage(data);
});

this.socket.on('message_delta', (delta: { id: string; content: string }) => {
const { messages, updateMessage } = useAppStore.getState();
const message = messages.find(m => m.id === delta.id);
if (message) {
updateMessage(delta.id, {
content: message.content + delta.content
});
}
});

this.socket.on('disconnect', () => {
this.attemptReconnect(sessionId, token);
});

this.socket.on('error', (error) => {
console.error('WS error:', error);
});
}

sendMessage(content: string, provider?: string) {
this.socket?.emit('chat', { content, provider });
}

disconnect() {
this.socket?.disconnect();
this.socket = null;
}

private attemptReconnect(sessionId: string, token: string) {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
setTimeout(() => {
this.connect(sessionId, token);
}, 1000 * this.reconnectAttempts);
}
}
}

export const wsManager = new WebSocketManager();

3. Backend Architecture

3.1 Worker Implementation

// src/worker.ts - CODITECT-CORE Worker

import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { authMiddleware } from './middleware/auth';
import { tenantMiddleware } from './middleware/tenant';
import { rateLimitMiddleware } from './middleware/rate-limit';

import { authRouter } from './routes/auth';
import { sessionsRouter } from './routes/sessions';
import { chatRouter } from './routes/chat';
import { filesRouter } from './routes/files';
import { teamsRouter } from './routes/teams';

const app = new Hono<{ Bindings: Env }>();

// Global middleware
app.use('*', cors({
origin: ['https://studio.coditect.dev', 'http://localhost:3000'],
credentials: true,
}));

app.use('/api/v1/*', rateLimitMiddleware);

// Public routes
app.route('/auth', authRouter);

// Protected routes
app.use('/api/v1/*', authMiddleware);
app.use('/api/v1/*', tenantMiddleware);

app.route('/api/v1/sessions', sessionsRouter);
app.route('/api/v1/chat', chatRouter);
app.route('/api/v1/files', filesRouter);
app.route('/api/v1/teams', teamsRouter);

// Health check
app.get('/health', (c) => c.json({
status: 'healthy',
version: '1.0.0',
timestamp: new Date().toISOString()
}));

export default app;

3.2 Session Management (Durable Object)

// src/durable-objects/session.ts

export class SessionDurableObject implements DurableObject {
private state: DurableObjectState;
private sessions: Map<string, SessionState> = new Map();

constructor(state: DurableObjectState) {
this.state = state;
// Restore from storage
this.state.blockConcurrencyWhile(async () => {
const stored = await this.state.storage.list<SessionState>();
stored.forEach((value, key) => {
this.sessions.set(key, value);
});
});
}

async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
const path = url.pathname;

if (path === '/create') {
return this.createSession(request);
}

if (path === '/get') {
const sessionId = url.searchParams.get('id');
return this.getSession(sessionId!);
}

if (path === '/update') {
return this.updateSession(request);
}

if (path === '/heartbeat') {
const sessionId = url.searchParams.get('id');
return this.heartbeat(sessionId!);
}

return new Response('Not found', { status: 404 });
}

private async createSession(request: Request): Promise<Response> {
const config: SessionConfig = await request.json();
const sessionId = crypto.randomUUID();

const session: SessionState = {
id: sessionId,
tenant: config.tenant,
status: 'creating',
createdAt: Date.now(),
lastActivityAt: Date.now(),
sandboxId: null,
};

this.sessions.set(sessionId, session);
await this.state.storage.put(sessionId, session);

// Trigger sandbox creation asynchronously
this.state.waitUntil(this.spawnSandbox(sessionId, config));

return Response.json({ sessionId, status: 'creating' });
}

private async spawnSandbox(sessionId: string, config: SessionConfig): Promise<void> {
try {
// Call Sandbox API to create container
const sandboxResponse = await fetch(
`${SANDBOX_API_URL}/create`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sessionId,
tenant: config.tenant,
resources: config.resources,
mountPath: `/${config.tenant.orgId}/${config.tenant.teamId}/${config.tenant.userId}/projects/${config.projectId}`,
}),
}
);

const { sandboxId, endpoint } = await sandboxResponse.json();

// Update session state
const session = this.sessions.get(sessionId)!;
session.status = 'running';
session.sandboxId = sandboxId;
session.websocketUrl = endpoint;
session.lastActivityAt = Date.now();

await this.state.storage.put(sessionId, session);

} catch (error) {
console.error('Failed to spawn sandbox:', error);
const session = this.sessions.get(sessionId)!;
session.status = 'error';
session.error = String(error);
await this.state.storage.put(sessionId, session);
}
}

private async heartbeat(sessionId: string): Promise<Response> {
const session = this.sessions.get(sessionId);
if (!session) {
return new Response('Session not found', { status: 404 });
}

session.lastActivityAt = Date.now();
await this.state.storage.put(sessionId, session);

return Response.json({ status: session.status });
}
}

3.3 LLM Router with Circuit Breaker

// src/services/llm-router.ts

interface RoutingStrategy {
priority: LLMProvider['id'][];
fallbackOnError: boolean;
fallbackOnTimeout: boolean;
timeoutMs: number;
}

class CircuitBreaker {
private failures = 0;
private lastFailureTime: number | null = null;
private state: 'closed' | 'open' | 'half-open' = 'closed';

constructor(
private readonly threshold = 5,
private readonly resetTimeoutMs = 30000
) {}

canExecute(): boolean {
if (this.state === 'closed') return true;

if (this.state === 'open') {
if (Date.now() - (this.lastFailureTime || 0) > this.resetTimeoutMs) {
this.state = 'half-open';
return true;
}
return false;
}

return true; // half-open
}

recordSuccess() {
this.failures = 0;
this.state = 'closed';
}

recordFailure() {
this.failures++;
this.lastFailureTime = Date.now();

if (this.failures >= this.threshold) {
this.state = 'open';
}
}
}

export class LLMRouter {
private adapters: Map<LLMProvider['id'], LLMAdapter> = new Map();
private circuitBreakers: Map<LLMProvider['id'], CircuitBreaker> = new Map();

constructor() {
// Initialize adapters
this.adapters.set('claude', new ClaudeAdapter());
this.adapters.set('gemini', new GeminiAdapter());
this.adapters.set('codex', new CodexAdapter());
this.adapters.set('kimi', new KimiAdapter());

// Initialize circuit breakers
this.adapters.forEach((_, id) => {
this.circuitBreakers.set(id, new CircuitBreaker());
});
}

async route(
prompt: string,
context: ChatMessage[],
strategy: RoutingStrategy = {
priority: ['claude', 'gemini', 'kimi', 'codex'],
fallbackOnError: true,
fallbackOnTimeout: true,
timeoutMs: 30000,
}
): Promise<ChatMessage> {

for (const providerId of strategy.priority) {
const cb = this.circuitBreakers.get(providerId)!;

if (!cb.canExecute()) {
console.log(`Circuit breaker open for ${providerId}`);
continue;
}

const adapter = this.adapters.get(providerId)!;

try {
const startTime = Date.now();

const response = await Promise.race([
adapter.generate(prompt, context),
new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), strategy.timeoutMs)
),
]);

const latency = Date.now() - startTime;
cb.recordSuccess();

return {
id: crypto.randomUUID(),
role: 'assistant',
content: response.content,
provider: providerId,
timestamp: new Date(),
metadata: {
latency,
tokens: response.tokens,
cost: response.cost,
},
};

} catch (error) {
console.error(`${providerId} failed:`, error);
cb.recordFailure();

if (!strategy.fallbackOnError && !strategy.fallbackOnTimeout) {
throw error;
}

// Continue to next provider
}
}

throw new Error('All LLM providers unavailable');
}
}

4. Storage Implementation

4.1 R2 File Operations

// src/services/file-service.ts

import { S3Client, ListObjectsV2Command, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';

export class FileService {
private r2: S3Client;
private bucket: string;

constructor(accountId: string, accessKeyId: string, secretAccessKey: string) {
this.r2 = new S3Client({
region: 'auto',
endpoint: `https://${accountId}.r2.cloudflarestorage.com`,
credentials: { accessKeyId, secretAccessKey },
});
this.bucket = 'coditect-storage';
}

async listFiles(tenant: TenantContext, path: string): Promise<FileNode[]> {
const prefix = `${tenant.orgId}/${tenant.teamId}/${tenant.userId}/projects/${tenant.projectId}/${path}`;

const command = new ListObjectsV2Command({
Bucket: this.bucket,
Prefix: prefix,
Delimiter: '/',
});

const response = await this.r2.send(command);

const files: FileNode[] = [];

// Directories
response.CommonPrefixes?.forEach((prefix) => {
const name = prefix.Prefix!.split('/').slice(-2)[0];
files.push({
path: prefix.Prefix!,
name,
type: 'directory',
modifiedAt: new Date(),
});
});

// Files
response.Contents?.forEach((object) => {
if (object.Key === prefix) return; // Skip the prefix itself
const name = object.Key!.split('/').pop()!;
files.push({
path: object.Key!,
name,
type: 'file',
size: object.Size,
modifiedAt: object.LastModified!,
});
});

return files;
}

async readFile(tenant: TenantContext, path: string): Promise<string> {
const key = `${tenant.orgId}/${tenant.teamId}/${tenant.userId}/projects/${tenant.projectId}/${path}`;

const command = new GetObjectCommand({
Bucket: this.bucket,
Key: key,
});

const response = await this.r2.send(command);
const body = await response.Body?.transformToString();

return body || '';
}

async writeFile(tenant: TenantContext, path: string, content: string): Promise<void> {
const key = `${tenant.orgId}/${tenant.teamId}/${tenant.userId}/projects/${tenant.projectId}/${path}`;

const command = new PutObjectCommand({
Bucket: this.bucket,
Key: key,
Body: content,
ContentType: this.getContentType(path),
});

await this.r2.send(command);
}

private getContentType(path: string): string {
const ext = path.split('.').pop()?.toLowerCase();
const types: Record<string, string> = {
js: 'application/javascript',
ts: 'application/typescript',
json: 'application/json',
html: 'text/html',
css: 'text/css',
md: 'text/markdown',
};
return types[ext || ''] || 'text/plain';
}
}

4.2 Git Backup Service

// src/services/git-service.ts

import { Octokit } from '@octokit/rest';

export class GitBackupService {
private octokit: Octokit;

constructor(token: string) {
this.octokit = new Octokit({ auth: token });
}

async createCheckpoint(
owner: string,
repo: string,
sessionId: string,
files: { path: string; content: string }[]
): Promise<string> {
const branch = `coditect/session-${sessionId}`;

// Get default branch
const { data: repoData } = await this.octokit.repos.get({ owner, repo });
const baseBranch = repoData.default_branch;

// Get base commit
const { data: refData } = await this.octokit.git.getRef({
owner,
repo,
ref: `heads/${baseBranch}`,
});
const baseSha = refData.object.sha;

// Create new branch
await this.octokit.git.createRef({
owner,
repo,
ref: `refs/heads/${branch}`,
sha: baseSha,
});

// Create blobs for each file
const treeEntries = await Promise.all(
files.map(async (file) => {
const { data: blob } = await this.octokit.git.createBlob({
owner,
repo,
content: Buffer.from(file.content).toString('base64'),
encoding: 'base64',
});
return {
path: file.path,
mode: '100644' as const,
type: 'blob' as const,
sha: blob.sha,
};
})
);

// Create tree
const { data: tree } = await this.octokit.git.createTree({
owner,
repo,
base_tree: baseSha,
tree: treeEntries,
});

// Create commit
const { data: commit } = await this.octokit.git.createCommit({
owner,
repo,
message: `CODITECT checkpoint: ${new Date().toISOString()}`,
tree: tree.sha,
parents: [baseSha],
});

// Update branch
await this.octokit.git.updateRef({
owner,
repo,
ref: `heads/${branch}`,
sha: commit.sha,
});

return commit.sha;
}
}

5. Security Implementation

5.1 Authentication Middleware

// src/middleware/auth.ts

import { MiddlewareHandler } from 'hono';
import { verify } from 'jose';

export const authMiddleware: MiddlewareHandler = async (c, next) => {
const authHeader = c.req.header('Authorization');

if (!authHeader?.startsWith('Bearer ')) {
return c.json({ error: 'Unauthorized' }, 401);
}

const token = authHeader.slice(7);

try {
const secret = new TextEncoder().encode(c.env.JWT_SECRET);
const { payload } = await verify(token, secret);

c.set('user', {
userId: payload.sub,
email: payload.email,
orgId: payload.orgId,
});

await next();
} catch (error) {
return c.json({ error: 'Invalid token' }, 401);
}
};

5.2 Tenant Isolation Middleware

// src/middleware/tenant.ts

export const tenantMiddleware: MiddlewareHandler = async (c, next) => {
const user = c.get('user');
const orgId = c.req.header('X-Organization-ID');
const teamId = c.req.header('X-Team-ID');
const projectId = c.req.query('projectId');

// Validate user has access to org
const hasOrgAccess = await checkOrgAccess(user.userId, orgId);
if (!hasOrgAccess) {
return c.json({ error: 'Forbidden' }, 403);
}

// Validate team membership
if (teamId) {
const hasTeamAccess = await checkTeamAccess(user.userId, teamId);
if (!hasTeamAccess) {
return c.json({ error: 'Forbidden' }, 403);
}
}

// Set tenant context
c.set('tenant', {
orgId,
teamId: teamId || 'default',
userId: user.userId,
projectId,
});

await next();
};

6. Deployment Configuration

6.1 Wrangler Configuration

# wrangler.toml

name = "coditect-core"
main = "src/worker.ts"
compatibility_date = "2026-01-31"

# Durable Objects
[[durable_objects.bindings]]
name = "SESSION_DO"
class_name = "SessionDurableObject"

[[migrations]]
tag = "v1"
new_classes = ["SessionDurableObject"]

# R2 Buckets
[[r2_buckets]]
binding = "STORAGE"
bucket_name = "coditect-storage"

# KV Namespaces
[[kv_namespaces]]
binding = "CONFIG"
id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# Secrets (set via wrangler secret)
# JWT_SECRET
# GITHUB_TOKEN
# ANTHROPIC_API_KEY
# OPENAI_API_KEY
# etc.

[env.staging]
name = "coditect-core-staging"
routes = [{ pattern = "staging-api.coditect.dev/*", zone_name = "coditect.dev" }]

[env.production]
name = "coditect-core"
routes = [{ pattern = "api.coditect.dev/*", zone_name = "coditect.dev" }]

6.2 Dockerfile for UI

# Dockerfile

# Build stage
FROM node:20-alpine AS builder

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

# Production stage
FROM node:20-alpine AS runner

WORKDIR /app

ENV NODE_ENV=production

COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

EXPOSE 3000

ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

CMD ["node", "server.js"]

7. Monitoring & Observability

7.1 Logging

// src/lib/logger.ts

interface LogEntry {
timestamp: string;
level: 'debug' | 'info' | 'warn' | 'error';
message: string;
context: {
requestId: string;
userId?: string;
sessionId?: string;
[key: string]: any;
};
}

export class Logger {
constructor(private requestId: string) {}

private log(level: LogEntry['level'], message: string, meta: Record<string, any> = {}) {
const entry: LogEntry = {
timestamp: new Date().toISOString(),
level,
message,
context: {
requestId: this.requestId,
...meta,
},
};

console.log(JSON.stringify(entry));
}

debug(msg: string, meta?: Record<string, any>) { this.log('debug', msg, meta); }
info(msg: string, meta?: Record<string, any>) { this.log('info', msg, meta); }
warn(msg: string, meta?: Record<string, any>) { this.log('warn', msg, meta); }
error(msg: string, meta?: Record<string, any>) { this.log('error', msg, meta); }
}

7.2 Metrics

MetricTypeDescription
sessions_activeGaugeCurrently active sessions
session_durationHistogramSession lifetime in seconds
llm_requests_totalCounterTotal LLM requests by provider
llm_latencyHistogramLLM response latency
file_operationsCounterFile read/write operations
errors_totalCounterTotal errors by type

8. Testing Strategy

8.1 Test Layers

LayerToolCoverage Target
UnitVitest80%
IntegrationPlaywrightCritical paths
E2EPlaywrightUser workflows
ContractPactAPI contracts
Loadk61000 concurrent users

8.2 Critical Test Scenarios

  1. Session Lifecycle: Create → Connect → Execute → Terminate
  2. LLM Fallback: Primary fails → Fallback succeeds
  3. File Operations: List → Read → Edit → Save → Checkpoint
  4. Multi-tenant Isolation: User A cannot access User B files
  5. Reconnection: WebSocket drops → Reconnect → State synced

Document Owner: Engineering Team
Last Updated: 2026-01-31