Skip to main content

Coditect V5 - Automated Pod Provisioning & RBAC Architecture

Last Updated: 2025-10-07 GCP Project: serene-voltage-464305-n2 GKE Cluster: codi-poc-e2-cluster (us-central1-a) Domain: coditect.ai


πŸ“‹ Table of Contents​

  1. Architecture Overview
  2. End-to-End User Flow
  3. Component Architecture
  4. Automated Provisioning System
  5. RBAC & Security Model
  6. Storage Architecture
  7. Implementation Details
  8. Deployment Strategy

πŸ—οΈ Architecture Overview​

High-Level System Diagram​

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ User Browser β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ React Wrapper (coditect.ai) β”‚ β”‚
β”‚ β”‚ β”œβ”€β”€ Header (Logo, Theme, User Menu) β”‚ β”‚
β”‚ β”‚ β”œβ”€β”€ SidePanel (Files, llm Chat, Settings) β”‚ β”‚
β”‚ β”‚ β”œβ”€β”€ theia Embed (iframe to user workspace pod) β”‚ β”‚
β”‚ β”‚ └── Footer (Links, Status) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ HTTPS (JWT auth)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Google Load Balancer β”‚
β”‚ (34.8.51.57 - SSL termination) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Frontend Service β”‚ β”‚ Backend API Service β”‚
β”‚ (React SPA) β”‚ β”‚ (Rust/Actix-web) β”‚
β”‚ Port: 80 β”‚ β”‚ Port: 80 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ FoundationDB β”‚ β”‚ Kubernetes API β”‚
β”‚ (Session/Metadata) β”‚ β”‚ (Pod Provisioning) β”‚
β”‚ 3-node cluster β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Per-User Namespaces β”‚
β”‚ β”œβ”€β”€ user-alice β”‚
β”‚ β”œβ”€β”€ user-bob β”‚
β”‚ └── user-charlie β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ workspace Pod β”‚ β”‚ workspace Pod β”‚
β”‚ (user-alice) β”‚ β”‚ (user-bob) β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ theia Container β”‚ β”‚ β”‚ β”‚ theia Container β”‚ β”‚
β”‚ β”‚ Port: 3000 β”‚ β”‚ β”‚ β”‚ Port: 3000 β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ WebSocket Sidecarβ”‚ β”‚ β”‚ β”‚ WebSocket Sidecarβ”‚ β”‚
β”‚ β”‚ Port: 8765 β”‚ β”‚ β”‚ β”‚ Port: 8765 β”‚ β”‚
β”‚ β”‚ (localhost only) β”‚ β”‚ β”‚ β”‚ (localhost only) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ PVC (50GB) β”‚ β”‚ β”‚ β”‚ PVC (50GB) β”‚ β”‚
β”‚ β”‚ /workspace β”‚ β”‚ β”‚ β”‚ /workspace β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Architectural Principles​

  1. Multi-Tenant Isolation: Each user gets their own Kubernetes namespace
  2. Ephemeral Pods, Persistent Data: Pods can be recreated, data survives in PVCs
  3. Sidecar Pattern: WebSocket gateway runs alongside theia (localhost communication)
  4. Automated Provisioning: Registration triggers namespace + pod + RBAC creation
  5. Centralized Auth: JWT from backend API, validated at every layer
  6. Zero Trust: RBAC enforced at Kubernetes level, not just application level

πŸ”„ End-to-End User Flow​

Registration & Onboarding​

1. User visits coditect.ai/register
└─> Frontend: React registration form

2. User submits email/password
└─> POST /api/v5/auth/register
Backend:
β”œβ”€> Hash password (Argon2)
β”œβ”€> Create user in FoundationDB
β”œβ”€> Assign tenant_id
└─> Return success

3. Backend triggers provisioning controller
└─> Kubernetes Controller watches FDB for new users
β”œβ”€> Create namespace: user-{user_id}
β”œβ”€> Create ServiceAccount
β”œβ”€> Create Role (pod, pvc access)
β”œβ”€> Create RoleBinding (user β†’ role)
β”œβ”€> Create PVC (50GB standard-rwo)
└─> Create Pod (theia + WebSocket sidecar)

4. User logs in
└─> POST /api/v5/auth/login
Backend:
β”œβ”€> Verify password (Argon2)
β”œβ”€> Generate JWT token (exp: 24h)
β”œβ”€> Create session in FDB
└─> Return JWT + session_id

5. User redirected to /ide
└─> Frontend embeds iframe:
src="https://coditect.ai/workspace/{user_id}/theia"

6. Ingress routes traffic:
/workspace/{user_id}/theia β†’ user-{user_id} namespace β†’ theia pod (port 3000)

7. theia loads in iframe, establishes WebSocket
└─> ws://localhost:8765 (sidecar)
WebSocket sidecar:
β”œβ”€> Validates JWT
β”œβ”€> Connects to FoundationDB
└─> Syncs file changes, llm requests, terminal I/O

IDE Session Lifecycle​

Session Start:
1. User opens /ide
2. Frontend validates JWT (decode, check expiry)
3. Frontend fetches session metadata: GET /api/v5/sessions/{session_id}
4. Frontend loads theia iframe with session context
5. theia connects to WebSocket sidecar (localhost:8765)
6. WebSocket establishes FDB connection
7. User files loaded from PVC β†’ displayed in theia

Active Session:
- File edits β†’ WebSocket β†’ FDB (metadata + snapshots)
- terminal commands β†’ Execute in pod β†’ Output via WebSocket
- llm requests β†’ WebSocket β†’ Backend API β†’ LM Studio/Claude
- Real-time sync: Multiple browser tabs share same session

Session End:
- User closes tab β†’ WebSocket disconnect (clean shutdown)
- Session remains in FDB (can resume later)
- Pod stays running (configurable timeout: 30min idle β†’ scale to zero)

Pod Lifecycle Management​

Pod Creation (On-Demand):
- Triggered by: First login after registration
- Process:
1. Controller creates namespace
2. Controller provisions PVC
3. Controller creates pod spec
4. Kubernetes schedules pod
5. Pod initializes (pull image, mount PVC)
6. Health checks pass
7. Ingress routes traffic to pod

Pod Scaling (Idle Timeout):
- After 30min idle: Scale replicas to 0
- Pod deleted, PVC remains
- Next access: Recreate pod from PVC
- Data intact, session restored

Pod Termination:
- User deletes account β†’ Controller deletes namespace
- Cascade delete: Pod, PVC, Secrets, RBAC
- Data purged from FDB (GDPR compliance)

🧩 Component Architecture​

1. Frontend (React Wrapper)​

Purpose: Provide branded UI wrapper around theia IDE

Technology Stack:

  • React 18 + TypeScript
  • Chakra UI (theme system)
  • React Router (routing)
  • Zustand (state management)

Key Components:

src/
β”œβ”€β”€ app.tsx // Main app, routing, auth context
β”œβ”€β”€ components/
β”‚ β”œβ”€β”€ header.tsx // Logo, theme toggle, user menu
β”‚ β”œβ”€β”€ footer.tsx // Links, status indicators
β”‚ β”œβ”€β”€ side-panel.tsx // File tree, llm chat, settings
β”‚ └── theia-embed.tsx // Iframe wrapper for theia
β”œβ”€β”€ pages/
β”‚ β”œβ”€β”€ login-page.tsx // JWT auth login
β”‚ β”œβ”€β”€ register-page.tsx // User registration
β”‚ └── IDEPage.tsx // Main IDE layout
β”œβ”€β”€ hooks/
β”‚ β”œβ”€β”€ useAuth.tsx // JWT validation, refresh
β”‚ β”œβ”€β”€ useSession.tsx // Session management
β”‚ └── use-web-socket.tsx // WebSocket connection
└── theme/
β”œβ”€β”€ chakraTheme.ts // Chakra UI theme
└── theiaTheme.css // theia CSS overrides

Deployment:

  • Build: npm run build β†’ static files
  • Serve: NGINX in coditect-frontend pod
  • Route: coditect.ai β†’ frontend service
  • CDN: Google Cloud CDN (static assets)

2. Backend API (Rust/Actix-web)​

Purpose: Auth, session management, pod orchestration

Technology Stack:

  • Rust 1.90 + Actix-web 4.4
  • FoundationDB 7.1 client
  • JWT (jsonwebtoken crate)
  • Argon2 (password hashing)

API Endpoints:

// Authentication
POST /api/v5/auth/register // Create user
POST /api/v5/auth/login // Get JWT token
POST /api/v5/auth/logout // Invalidate session
POST /api/v5/auth/refresh // Refresh JWT

// Sessions
GET /api/v5/sessions // List user sessions
POST /api/v5/sessions // Create new session
GET /api/v5/sessions/{id} // Get session details
PUT /api/v5/sessions/{id} // Update session
DELETE /api/v5/sessions/{id} // Delete session

// workspaces (Pod Management)
GET /api/v5/workspaces // List user workspaces
POST /api/v5/workspaces // Provision new workspace
GET /api/v5/workspaces/{id} // Get workspace status
DELETE /api/v5/workspaces/{id} // Delete workspace (delete pod)

// Health
GET /health // Health check
GET /health/fdb // FDB connection status

Key Services:

src/
β”œβ”€β”€ main.rs // Server setup, routes
β”œβ”€β”€ handlers/
β”‚ β”œβ”€β”€ auth.rs // Auth endpoints
β”‚ β”œβ”€β”€ sessions.rs // Session CRUD
β”‚ └── workspaces.rs // Pod provisioning
β”œβ”€β”€ services/
β”‚ β”œβ”€β”€ fdb_service.rs // FoundationDB client
β”‚ β”œβ”€β”€ jwt_service.rs // JWT encode/decode
β”‚ β”œβ”€β”€ k8s_service.rs // Kubernetes API client
β”‚ └── provisioner.rs // Automated provisioning logic
β”œβ”€β”€ middleware/
β”‚ β”œβ”€β”€ auth_middleware.rs // JWT validation
β”‚ └── rate_limit.rs // Rate limiting
└── models/
β”œβ”€β”€ user.rs // User model
β”œβ”€β”€ session.rs // Session model
└── workspace.rs // workspace model

Deployment:

  • Container: us-central1-docker.pkg.dev/.../coditect-v5-api:latest
  • Replicas: 3 (auto-scaling 3-10)
  • Resources: 512Mi-1Gi memory, 500m-1000m CPU
  • Health checks: /health endpoint
  • Route: coditect.ai/api/v5/* β†’ backend service

3. Provisioning Controller (Kubernetes Operator)​

Purpose: Automate namespace, pod, and RBAC creation

Technology: Rust + kube-rs (Kubernetes client)

Reconciliation Loop:

// Pseudo-code
async fn reconcile_user(user: User) -> Result<()> {
let ns_name = format!("user-{}", user.id);

// 1. Create namespace
if !namespace_exists(&ns_name).await? {
create_namespace(&ns_name).await?;
}

// 2. Create service account
create_service_account(&ns_name, "workspace-sa").await?;

// 3. Create RBAC
create_role(&ns_name, "workspace-access").await?;
create_role_binding(&ns_name, "workspace-sa", "workspace-access").await?;

// 4. Create PVC
if !pvc_exists(&ns_name, "workspace-storage").await? {
create_pvc(&ns_name, "workspace-storage", "50Gi").await?;
}

// 5. Create pod
if !pod_exists(&ns_name, "workspace").await? {
create_workspace_pod(&ns_name, &user).await?;
}

// 6. Wait for pod ready
wait_for_pod_ready(&ns_name, "workspace").await?;

// 7. Update user status in FDB
fdb_client.set_user_workspace_ready(user.id, true).await?;

Ok(())
}

Trigger Mechanisms:

  1. Watch FDB for new users (polling every 5s)
  2. Kubernetes CRD (custom resource: workspaceRequest)
  3. HTTP webhook from backend API

Deployment:

  • Single pod in coditect-app namespace
  • ServiceAccount with cluster-admin (or scoped RBAC)
  • Watches all user-* namespaces
  • Logs to Cloud Logging

4. workspace Pod (theia + WebSocket Sidecar)​

Purpose: Per-user IDE environment with real-time sync

Pod Specification:

apiVersion: v1
kind: Pod
metadata:
name: workspace
namespace: user-{user_id}
labels:
app: workspace
user_id: "{user_id}"
spec:
serviceAccountName: workspace-sa

containers:
# Main theia container
- name: theia
image: us-central1-docker.pkg.dev/serene-voltage-464305-n2/coditect/t2-workspace-theia:latest
ports:
- containerPort: 3000
name: theia-http
volumeMounts:
- name: workspace-storage
mountPath: /workspace
env:
- name: USER_ID
value: "{user_id}"
- name: WEBSOCKET_URL
value: "ws://localhost:8765"
resources:
requests:
memory: "2Gi"
cpu: "1000m"
limits:
memory: "4Gi"
cpu: "2000m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10
periodSeconds: 5

# WebSocket sidecar
- name: websocket-gateway
image: us-central1-docker.pkg.dev/serene-voltage-464305-n2/coditect/websocket-gateway:latest
ports:
- containerPort: 8765
name: websocket
env:
- name: FDB_CLUSTER_STRING
value: "coditect:production@foundationdb-0.fdb-cluster.coditect-app.svc.cluster.local:4500"
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: jwt-secret
key: secret
- name: USER_ID
value: "{user_id}"
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "512Mi"
cpu: "500m"

volumes:
- name: workspace-storage
persistentVolumeClaim:
claimName: workspace-storage

Sidecar Benefits:

  • βœ… Localhost communication: No network policies needed
  • βœ… Security: WebSocket not exposed outside pod
  • βœ… Simplicity: No service mesh required
  • βœ… Performance: Zero network latency (same pod)

πŸ€– Automated Provisioning System​

Provisioning Workflow Diagram​

User Registration
β”‚
β–Ό
Backend API
β”œβ”€> Create user in FDB
└─> Emit provisioning event
β”‚
β–Ό
Provisioning Controller
(watches FDB/CRD/webhook)
β”‚
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Step 1: Create Namespace β”‚
β”‚ user-{user_id} β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Step 2: Create RBAC β”‚
β”‚ β”œβ”€> ServiceAccount β”‚
β”‚ β”œβ”€> Role (pod/pvc access) β”‚
β”‚ └─> RoleBinding β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Step 3: Create PVC β”‚
β”‚ 50GB standard-rwo β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Step 4: Create Pod β”‚
β”‚ β”œβ”€> theia container β”‚
β”‚ └─> WebSocket sidecar β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Step 5: Wait for Ready β”‚
β”‚ Poll pod status (max 2min) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Step 6: Update FDB β”‚
β”‚ Mark workspace as ready β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β–Ό
Success! βœ…

Implementation: Kubernetes API Calls​

// src/services/provisioner.rs
use kube::{Api, Client};
use k8s_openapi::api::core::v1::{Namespace, Pod, PersistentVolumeClaim, ServiceAccount};
use k8s_openapi::api::rbac::v1::{Role, RoleBinding};

pub struct Provisioner {
client: Client,
}

impl Provisioner {
pub async fn provision_workspace(&self, user_id: &str, user_email: &str) -> Result<()> {
let ns_name = format!("user-{}", user_id);

// Step 1: Create namespace
self.create_namespace(&ns_name).await?;

// Step 2: Create RBAC
self.create_service_account(&ns_name).await?;
self.create_role(&ns_name).await?;
self.create_role_binding(&ns_name, user_email).await?;

// Step 3: Create PVC
self.create_pvc(&ns_name).await?;

// Step 4: Create pod
self.create_workspace_pod(&ns_name, user_id).await?;

// Step 5: Wait for pod ready
self.wait_for_pod_ready(&ns_name, "workspace", Duration::from_secs(120)).await?;

Ok(())
}

async fn create_namespace(&self, name: &str) -> Result<()> {
let ns_api: Api<Namespace> = Api::all(self.client.clone());
let ns = Namespace {
metadata: ObjectMeta {
name: Some(name.to_string()),
labels: Some(BTreeMap::from([
("app".to_string(), "coditect".to_string()),
("managed-by".to_string(), "provisioner".to_string()),
])),
..Default::default()
},
..Default::default()
};

ns_api.create(&PostParams::default(), &ns).await?;
Ok(())
}

// ... (other create functions similar)
}

Deprovisioning (Account Deletion)​

pub async fn deprovision_workspace(&self, user_id: &str) -> Result<()> {
let ns_name = format!("user-{}", user_id);

// Delete namespace (cascade deletes pod, pvc, secrets, rbac)
let ns_api: Api<Namespace> = Api::all(self.client.clone());
ns_api.delete(&ns_name, &DeleteParams::default()).await?;

// Wait for namespace deletion (finalizers)
self.wait_for_namespace_deleted(&ns_name).await?;

// Purge FDB data (GDPR compliance)
fdb_client.delete_user_data(user_id).await?;

Ok(())
}

πŸ” RBAC & Security Model​

Multi-Tenant Isolation​

Principle: Each user operates in isolated namespace with scoped RBAC

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Kubernetes Cluster β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Namespace: user-alice β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ ServiceAccount: workspace-sa β”‚ β”‚
β”‚ β”‚ Role: workspace-access β”‚ β”‚
β”‚ β”‚ RoleBinding: workspace-sa β†’ workspace-access β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ Resources alice can access: β”‚ β”‚
β”‚ β”‚ β”œβ”€> pods (get, list, create, delete) β”‚ β”‚
β”‚ β”‚ β”œβ”€> pvcs (get, list) β”‚ β”‚
β”‚ β”‚ └─> secrets (get) - only in user-alice namespaceβ”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ ❌ CANNOT access: β”‚ β”‚
β”‚ β”‚ β”œβ”€> Other namespaces (user-bob, user-charlie) β”‚ β”‚
β”‚ β”‚ β”œβ”€> Cluster-level resources β”‚ β”‚
β”‚ β”‚ └─> coditect-app namespace (system services) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Namespace: user-bob β”‚ β”‚
β”‚ β”‚ (Isolated from alice) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

RBAC Manifests​

ServiceAccount:

apiVersion: v1
kind: ServiceAccount
metadata:
name: workspace-sa
namespace: user-{user_id}

Role (scoped to namespace):

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: workspace-access
namespace: user-{user_id}
rules:
# Pod management
- apiGroups: [""]
resources: ["pods", "pods/log", "pods/exec"]
verbs: ["get", "list", "create", "delete", "watch"]

# PVC access (read-only)
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list"]

# Secret access (read JWT for validation)
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["jwt-secret"]
verbs: ["get"]

RoleBinding:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: workspace-sa-binding
namespace: user-{user_id}
subjects:
- kind: ServiceAccount
name: workspace-sa
namespace: user-{user_id}
roleRef:
kind: Role
name: workspace-access
apiGroup: rbac.authorization.k8s.io

Identity Mapping​

Google IAM to Kubernetes RBAC:

User Email (user@example.com)
β”‚
β”œβ”€> Backend API: Validate JWT
β”‚ └─> JWT contains: user_id, email, tenant_id
β”‚
β”œβ”€> Kubernetes: Map to ServiceAccount
β”‚ └─> ServiceAccount: workspace-sa in user-{user_id} namespace
β”‚
└─> RBAC: Enforce via RoleBinding
└─> RoleBinding: workspace-sa β†’ workspace-access role

πŸ’Ύ Storage Architecture​

Persistent Volume Claims (Per-User)​

Purpose: User files, IDE settings, project data

PVC Specification:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: workspace-storage
namespace: user-{user_id}
spec:
accessModes:
- ReadWriteOnce # Single pod can mount (not shared)
storageClassName: standard-rwo # GCE Persistent Disk (SSD)
resources:
requests:
storage: 50Gi

Storage Lifecycle:

User Registration β†’ PVC Created (50GB)
β”‚
β–Ό
Pod Mounts PVC β†’ /workspace directory
β”‚
β–Ό
User edits files β†’ Writes to /workspace β†’ Persists to GCE PD
β”‚
β–Ό
Pod deleted (idle) β†’ PVC remains intact
β”‚
β–Ό
Pod recreated β†’ Remounts same PVC β†’ Files restored
β”‚
β–Ό
Account deleted β†’ PVC deleted β†’ Data purged

Storage Classes​

Available Options:

StorageClassBackendPerformanceCostUse Case
standard-rwoStandard PDGoodLowDefault (recommended)
premium-rwoSSD PDExcellentMediumPower users, large projects
filestore-rwxGoogle Filestore (NFS)GoodHighMulti-pod access (future)

Cost Estimation (50GB per user):

Standard PD: $0.04/GB/month = $2.00/user/month
Premium SSD: $0.17/GB/month = $8.50/user/month

For 1000 users:
Standard: $2,000/month
Premium: $8,500/month

FoundationDB Storage​

Purpose: Session metadata, file snapshots, llm history

Data Model:

Key Structure:
tenant/{tenant_id}/user/{user_id}/profile
tenant/{tenant_id}/user/{user_id}/sessions/{session_id}
tenant/{tenant_id}/user/{user_id}/files/{file_path}/metadata
tenant/{tenant_id}/user/{user_id}/files/{file_path}/snapshots/{timestamp}
tenant/{tenant_id}/user/{user_id}/llm_history/{conversation_id}/messages

Example Keys:
tenant/org-123/user/alice-456/profile
tenant/org-123/user/alice-456/sessions/session-789
tenant/org-123/user/alice-456/files/workspace/main.rs/metadata
tenant/org-123/user/alice-456/files/workspace/main.rs/snapshots/1696809600
tenant/org-123/user/alice-456/llm_history/conv-abc/messages

Data Separation:

  • Hot data (active sessions): FoundationDB (low latency)
  • Warm data (file content): PVC (block storage)
  • Cold data (snapshots, archives): Cloud Storage (object storage)

πŸ› οΈ Implementation Details​

Technology Choices​

ComponentTechnologyRationale
FrontendReact 18 + Chakra UIMature ecosystem, V4 compatibility
BackendRust + Actix-webPerformance, safety, async
DatabaseFoundationDB 7.1ACID transactions, multi-tenant
StorageGCE Persistent DiskReliable, cost-effective
Container Runtimecontainerd (GKE)Industry standard
ProvisioningRust + kube-rsType safety, performance
WebSocketRust + Tokio-tungsteniteAsync, efficient

Container Images​

1. Frontend (coditect-frontend:latest):

FROM node:20-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80

2. Backend API (coditect-v5-api:latest):

FROM rust:1.90 as builder
RUN apt-get update && apt-get install -y clang libclang-dev
RUN curl -LO https://github.com/apple/foundationdb/releases/download/7.1.27/foundationdb-clients_7.1.27-1_amd64.deb && \
dpkg -i foundationdb-clients_7.1.27-1_amd64.deb
WORKDIR /app
COPY cargo.toml ./
RUN mkdir src && echo "fn main() {}" > src/main.rs && cargo build --release && rm -rf src
COPY src ./src
RUN cargo build --release

FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && \
curl -LO https://github.com/apple/foundationdb/releases/download/7.1.27/foundationdb-clients_7.1.27-1_amd64.deb && \
dpkg -i foundationdb-clients_7.1.27-1_amd64.deb && \
rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /app/target/release/api-server /app/api-server
COPY fdb.cluster /app/fdb.cluster
ENV FDB_CLUSTER_FILE=/app/fdb.cluster
EXPOSE 8080
CMD ["/app/api-server"]

3. theia workspace (t2-workspace-theia:latest):

FROM theia/theia:1.65.0
USER root
RUN apt-get update && apt-get install -y \
git curl wget vim nano build-essential python3 python3-pip nodejs npm
USER theia
WORKDIR /workspace
EXPOSE 3000
CMD ["node", "/theia/packages/cli/lib/backend/main.js", "--hostname=0.0.0.0", "--port=3000"]

4. WebSocket Gateway (websocket-gateway:latest):

FROM rust:1.90 as builder
# (Same FDB setup as backend)
WORKDIR /app
COPY websocket-gateway/ .
RUN cargo build --release

FROM debian:bookworm-slim
# (Same FDB runtime as backend)
WORKDIR /app
COPY --from=builder /app/target/release/websocket-gateway /app/websocket-gateway
EXPOSE 8765
CMD ["/app/websocket-gateway"]

πŸš€ Deployment Strategy​

Phase 1: Backend API Deployment βœ… (COMPLETE)​

Status: Backend API deployed to GKE, pods currently CrashLoopBackOff (FDB connection issue)

Next Steps: Debug FDB connectivity from pods

Phase 2: Provisioning Controller (NEXT)​

Tasks:

  1. Build controller Docker image
  2. Deploy to GKE (single pod in coditect-app namespace)
  3. Grant ServiceAccount cluster-admin (or scoped RBAC)
  4. Test manual provisioning (kubectl apply)
  5. Integrate with backend API (webhook)

Timeline: 2-3 days

Phase 3: Frontend Integration​

Tasks:

  1. Build React wrapper with theia embed
  2. Implement JWT authentication flow
  3. Add session management UI
  4. Deploy frontend to GKE
  5. Update Ingress routes

Timeline: 3-4 days

Phase 4: E2E Testing & Production​

Tasks:

  1. Test full user registration β†’ provisioning β†’ IDE access flow
  2. Load testing (100 concurrent users)
  3. Security audit (RBAC verification)
  4. Monitoring setup (Cloud Logging, Prometheus)
  5. Production rollout (blue-green deployment)

Timeline: 3-5 days


πŸ“š References​

Internal Documents​

External References (GCP/GKE)​

Research Documents​


Document Version: 1.0 Last Updated: 2025-10-07 Maintained By: Coditect Engineering Team