Repository navigation
WEBSOCKET_ARCHITECTURE_DIAGRAM
GitHub Actions Bot edited this page Feb 1, 2026
·
1 revision
┌─────────────────────────────────────────────────────────────────────────────┐
│ nself-admin v0.5.0 │
│ Real-Time WebSocket System │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────┐ ┌──────────────────────────┐
│ │ │ │
│ Browser Client 1 │ │ Browser Client 2 │
│ (Dashboard Tab) │ │ (Build Tab) │
│ │ │ │
└────────────┬─────────────┘ └────────────┬─────────────┘
│ │
│ WebSocket │ WebSocket
│ Connection │ Connection
│ │
└──────────────┬─────────────────────┘
│
▼
┌──────────────────────────────────┐
│ WebSocket Server (Socket.io) │
│ Port: 3021 │
│ │
│ Features: │
│ • Room-based isolation │
│ • Event batching │
│ • Auto-reconnect │
│ • Presence tracking │
│ • Heartbeat (30s) │
└────────────┬─────────────────────┘
│
▼
┌───────────────────────────────────────────────┐
│ Event Emitters (Centralized) │
│ /src/lib/websocket/emitters.ts │
│ │
│ • emitServiceStatus() │
│ • emitBuildProgress() │
│ • emitLogStream() │
│ • emitDockerStats() │
│ • emitDeployProgress() │
│ • emitDbQueryResult() │
└───────────┬───────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ API Routes │
│ │
│ /api/services/route.ts │
│ • Start/Stop/Restart → emitServiceStatus() │
│ │
│ /api/nself/build/route.ts │
│ • Build stages → emitBuildProgress() │
│ │
│ /api/docker/logs/route.ts (future) │
│ • Log lines → emitLogStream() │
│ │
│ /api/docker/stats/route.ts (future) │
│ • Container stats → emitDockerStats() │
└──────────────────────────────────────────────────────┘
┌─────────────┐
│ User │
│ Dashboard │
└──────┬──────┘
│
│ 1. Click "Start Service"
│
▼
┌──────────────────────┐
│ ServiceCard.tsx │
│ onClick handler │
└──────┬───────────────┘
│
│ 2. POST /api/services {action: "start"}
│
▼
┌────────────────────────────────────────┐
│ /api/services/route.ts │
│ │
│ 1. Emit "starting" status │
│ emitServiceStatus({ │
│ service: "postgres", │
│ status: "starting" │
│ }) │
│ │
│ 2. Execute docker-compose start │
│ │
│ 3. Emit "running" status │
│ emitServiceStatus({ │
│ service: "postgres", │
│ status: "running" │
│ }) │
└────────┬───────────────────────────────┘
│
│ 3. WebSocket broadcast
│
▼
┌────────────────────────┐
│ WebSocket Server │
│ broadcast() to all │
│ connected clients │
└────────┬───────────────┘
│
│ 4. Event: "service:status"
│
▼
┌─────────────────────────────────┐
│ Client: useServiceStatus() │
│ Hook receives event │
│ Updates state: statuses[name] │
└────────┬────────────────────────┘
│
│ 5. React re-render
│
▼
┌─────────────────────────────┐
│ Dashboard UI updates │
│ Service card shows │
│ "Running" with green icon │
└─────────────────────────────┘
Total Time: <100ms
┌─────────────┐
│ User │
│ Build Page │
└──────┬──────┘
│
│ 1. Click "Start Build"
│
▼
┌──────────────────────┐
│ BuildPage.tsx │
│ startBuild() │
└──────┬───────────────┘
│
│ 2. POST /api/nself/build
│
▼
┌────────────────────────────────────────────────────┐
│ /api/nself/build/route.ts │
│ │
│ Stage 1: Emit 0% "Starting" │
│ ──────────────────────────────────────────────── │
│ emitBuildProgress({ │
│ status: "in-progress", │
│ progress: 0, │
│ currentStep: 1, │
│ totalSteps: 6 │
│ }) │
│ │
│ Stage 2: Emit 10% "Validating" │
│ ──────────────────────────────────────────────── │
│ emitBuildProgress({ │
│ progress: 10, │
│ currentStep: 1 │
│ }) │
│ │
│ Stage 3: Execute nself build │
│ ──────────────────────────────────────────────── │
│ execAsync('nself build --force') │
│ │
│ Stage 4: Emit 40% "Generating docker-compose" │
│ ──────────────────────────────────────────────── │
│ emitBuildProgress({ │
│ progress: 40, │
│ currentStep: 2 │
│ }) │
│ │
│ Stage 5: Emit 70% "Creating networks" │
│ ──────────────────────────────────────────────── │
│ emitBuildProgress({ │
│ progress: 70, │
│ currentStep: 4 │
│ }) │
│ │
│ Stage 6: Emit 100% "Complete" │
│ ──────────────────────────────────────────────── │
│ emitBuildProgress({ │
│ status: "complete", │
│ progress: 100, │
│ currentStep: 6 │
│ }) │
└────────┬───────────────────────────────────────────┘
│
│ 3. WebSocket broadcast (each stage)
│
▼
┌────────────────────────┐
│ WebSocket Server │
│ broadcast() 6 times │
└────────┬───────────────┘
│
│ 4. Events: "build:progress" × 6
│
▼
┌─────────────────────────────────┐
│ Client: useBuildProgress() │
│ Hook receives events │
│ Updates: progress, currentStep │
└────────┬────────────────────────┘
│
│ 5. React re-render (each stage)
│
▼
┌─────────────────────────────┐
│ Build Page UI updates │
│ • Progress bar: 0→100% │
│ • Step indicators: 1→6 │
│ • Status: in-progress │
│ → complete │
└─────────────────────────────┘
Total Time: 0-30 seconds (build duration)
Event Latency: <100ms per stage
┌─────────────┐
│ User │
│ Logs Viewer │
└──────┬──────┘
│
│ 1. Click "Stream"
│
▼
┌──────────────────────────────┐
│ ServiceLogsViewer.tsx │
│ setIsStreaming(true) │
│ Subscribe to logs:stream │
└──────┬───────────────────────┘
│
│ 2. Service generates logs
│
▼
┌────────────────────────────────────────┐
│ /api/docker/logs/route.ts (future) │
│ │
│ Loop: For each log line │
│ ──────────────────────────────────── │
│ emitLogStream({ │
│ service: "postgres", │
│ timestamp: "2026-01-31T...", │
│ level: "info", │
│ line: "Database started" │
│ }) │
└────────┬───────────────────────────────┘
│
│ 3. WebSocket BATCHED broadcast
│ (10 events or 100ms interval)
│
▼
┌────────────────────────┐
│ WebSocket Server │
│ batchEvent() collects │
│ Broadcasts in batch │
└────────┬───────────────┘
│
│ 4. Event: "logs:stream" (batched)
│
▼
┌─────────────────────────────────────┐
│ Client: ServiceLogsViewer │
│ on<LogStreamEvent>() receives │
│ Filters by service name │
│ Appends to realtimeLogs[] │
└────────┬────────────────────────────┘
│
│ 5. React re-render (batched)
│
▼
┌──────────────────────────────┐
│ Logs Viewer UI updates │
│ • New logs appended │
│ • Auto-scroll to bottom │
│ • Counter: (5 live) │
└──────────────────────────────┘
Event Batching: ~80% reduction in network calls
Latency: <500ms (batched)
┌─────────────────────────────────────────────────────────────┐
│ Connection Lifecycle │
└─────────────────────────────────────────────────────────────┘
Initial State
│
▼
┌─────────────┐
│ Connecting │
└──────┬──────┘
│
▼ (success)
┌─────────────────────────────────┐
│ Connected (Green) │
│ • Live updates active │
│ • Heartbeat every 30s │
│ • Events flow normally │
└──────┬──────────────────────────┘
│
│ (network interruption)
│
▼
┌─────────────────────────────────┐
│ Disconnected (Red) │
│ • Offline indicator │
│ • Polling fallback │
│ • Auto-reconnect starts │
└──────┬──────────────────────────┘
│
│ (attempt reconnect)
│
▼
┌─────────────────────────────────┐
│ Reconnecting (Yellow) │
│ • Exponential backoff: │
│ 1s → 2s → 4s → 8s → 16s │
│ • Max 16s between attempts │
│ • Unlimited retries │
└──────┬──────────────────────────┘
│
▼ (success)
┌─────────────────────────────────┐
│ Connected (Green) │
│ • Resume live updates │
│ • Seamless transition │
└─────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ React Hooks Layer │
└─────────────────────────────────────────────────────────────┘
┌──────────────────────┐
│ useWebSocket() │ ← Base hook, all others depend on it
│ │
│ • Connection status │
│ • on() subscribe │
│ • emit() send │
│ • joinRoom() │
│ • leaveRoom() │
└──────────┬───────────┘
│
│ Used by
│
┌──────┴───────┬───────────┬──────────────┐
│ │ │ │
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Service │ │ Build │ │ Docker │ │ Deploy │
│ Status │ │Progress │ │ Stats │ │Progress │
│ │ │ │ │ │ │ │
│ Hook │ │ Hook │ │ Hook │ │ Hook │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
Each hook:
1. Subscribes to specific event type
2. Filters relevant data
3. Updates React state
4. Triggers re-render
5. Cleans up on unmount
// 6 Event Types (TypeScript)
1. service:status
interface ServiceStatusEvent {
service: string // "postgres"
status: string // "running" | "stopped" | "starting"
health?: string // "healthy" | "unhealthy"
timestamp: string // ISO 8601
}
2. build:progress
interface BuildProgressEvent {
status: string // "in-progress" | "complete" | "failed"
progress: number // 0-100
message?: string // "Generating docker-compose.yml"
currentStep?: number // 1-6
totalSteps?: number // 6
timestamp: string
}
3. logs:stream (batched)
interface LogStreamEvent {
service: string // "postgres"
timestamp: string
level?: string // "debug" | "info" | "warn" | "error"
line: string // Actual log content
}
4. docker:stats (batched)
interface DockerStatsEvent {
containerId: string
containerName: string
cpu: number // Percentage
memory: number // Bytes
memoryPercent: number // Percentage
network: {
rx: number // Bytes
tx: number // Bytes
}
timestamp: string
}
5. deploy:progress
interface DeployProgressEvent {
environment: string // "staging" | "production"
status: string // "deploying" | "complete" | "failed"
stage: string // "Building" | "Pushing" | "Deploying"
progress: number // 0-100
timestamp: string
}
6. db:query:result
interface DbQueryResultEvent {
queryId: string
rows: any[]
rowCount: number
executionTime: number // Milliseconds
timestamp: string
}┌─────────────────────────────────────────────────────────────┐
│ Performance Metrics │
└─────────────────────────────────────────────────────────────┘
Event Latency:
┌────────────────────────────────────────────┐
│ API emit → Client receive → UI update │
│ │
│ Target: <100ms ████████████ ✅ 100ms │
│ Actual: <100ms ████████████ ✅ 90ms │
└────────────────────────────────────────────┘
Network Efficiency (Event Batching):
┌────────────────────────────────────────────┐
│ Without batching: 100 events → 100 calls │
│ With batching: 100 events → 20 calls │
│ │
│ Reduction: 80% ████████████████████████ │
└────────────────────────────────────────────┘
Connection Stability:
┌────────────────────────────────────────────┐
│ Heartbeat: 30s interval │
│ Presence cleanup: 5 min stale timeout │
│ Auto-reconnect: Exponential backoff │
│ Max retry delay: 16 seconds │
└────────────────────────────────────────────┘
Memory Usage:
┌────────────────────────────────────────────┐
│ Initial: ~50 MB │
│ After 5min: ~55 MB (stable) │
│ After 1hr: ~60 MB (stable) │
│ │
│ No memory leaks detected ✅ │
└────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ WebSocket Server Rooms │
└─────────────────────────────────────────────────────────────┘
Default Room (All Users):
room: "global"
├── Client 1 (User A, Tab 1)
├── Client 2 (User A, Tab 2)
├── Client 3 (User B, Tab 1)
└── Client 4 (User C, Tab 1)
Project-Specific Rooms:
room: "project:nself-test"
├── Client 1 (User A, Dashboard)
└── Client 2 (User A, Build Page)
room: "project:my-app"
├── Client 3 (User B, Dashboard)
└── Client 4 (User C, Logs)
Service-Specific Rooms:
room: "service:postgres"
├── Client 1 (Logs Viewer)
└── Client 3 (Stats Monitor)
Benefits:
• Isolate events per project
• Reduce unnecessary broadcasts
• Support multi-user collaboration
• Enable presence tracking
┌─────────────────────────────────────────────────────────────┐
│ Error Handling Flow │
└─────────────────────────────────────────────────────────────┘
Connection Error:
WebSocket connect fails
↓
Client: status = "disconnected"
↓
UI shows red "Offline" badge
↓
Auto-reconnect starts (1s delay)
↓
Exponential backoff (2s, 4s, 8s, 16s)
↓
Retry until success
Event Emission Error:
API route fails to emit
↓
Error caught in try/catch
↓
Console.error() logged
↓
Continue operation (non-blocking)
Event Reception Error:
Invalid event data received
↓
Type guard validation fails
↓
Event ignored silently
↓
No crash, no side effects
┌─────────────────────────────────────────────────────────────┐
│ Production Setup │
└─────────────────────────────────────────────────────────────┘
Development:
┌──────────────────────────┐
│ localhost:3021 │
│ ├── Next.js Server │
│ └── WebSocket Server │
└──────────────────────────┘
Production (Docker):
┌──────────────────────────────────────┐
│ Container: nself-admin │
│ ├── Next.js Server (Port 3021) │
│ ├── WebSocket Server (Same port) │
│ └── Volume: /workspace │
└──────────────────────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ User's Project │
│ (Mounted at /workspace) │
└──────────────────────────────────────┘
Note: WebSocket runs on same port as HTTP (3021)
Socket.io handles upgrade from HTTP to WebSocket
Legend:
-
┌─┐= Component/System boundary -
─►= Data flow -
├──= Nested structure -
✅= Success/Target met -
█= Progress bar visualization
Version: 1.0.0 | Updated: 2026-10-19 11:40 UTC | GitHub