Files
Claude-Code-Monitor/docs/DEPLOYMENT.md
T
nntrivi2001 d2fc4a4701 feat: Claude Code Monitor — lanes, pipelines and a merged workspace
Internal SmartGift build of a Claude Code monitoring dashboard.

Lanes: a durable unit of parallel agent work, one per working directory,
tracked across session restarts. Managed lanes are git worktrees the
dashboard provisions and can reset or remove behind a three-check destroy
guard and a counted preflight; adopted lanes are directories you already
own and are never destroyable.

Pipelines: a lane moves through pipeline stages. A stage the agent declares
with evidence renders green; a stage inferred from the tool-event stream
renders dashed amber and never counts as done. Detection is forward-only
within a 30-minute window, and never writes the declared stage.

Workspace: one page at /run with a lane grid, the selected lane's pipeline,
and a full Claude console behind a disclosure.
2026-07-30 14:05:51 +07:00

19 KiB

Deployment Guide

Enterprise deployment strategies for Agent Dashboard across development, staging, and production environments.


Table of Contents


Overview

Agent Dashboard supports multiple deployment modes:

  • Local Development - Hot reload for rapid iteration
  • Docker - Containerized deployment with Docker/Podman
  • PM2 - Process management for production
  • Systemd - System service on Linux
  • Cloud - Deploy to AWS, Azure, GCP, or other cloud providers
graph TB
    subgraph "Deployment Modes"
        Dev[Local Development<br/>npm run dev]
        Docker[Docker Container<br/>docker compose up]
        PM2[PM2 Process Manager<br/>pm2 start]
        Systemd[Systemd Service<br/>systemctl start]
        Cloud[Cloud Platform<br/>Kubernetes, ECS, etc.]
    end
    
    subgraph "Environment"
        DevEnv[Development<br/>Hot reload, verbose logs]
        StagingEnv[Staging<br/>Production build, test data]
        ProdEnv[Production<br/>Optimized, monitoring]
    end
    
    Dev --> DevEnv
    Docker --> DevEnv
    Docker --> StagingEnv
    PM2 --> ProdEnv
    Systemd --> ProdEnv
    Cloud --> ProdEnv
    
    style Dev fill:#3B82F6
    style PM2 fill:#10B981
    style Cloud fill:#F59E0B

Deployment Architecture

Single-Server Architecture

graph TB
    subgraph "Server Host"
        subgraph "Node.js Process"
            Express[Express Server<br/>:4820]
            Static[Static File Serving<br/>client/dist/]
            API[REST API]
            WS[WebSocket Server]
            DB[(SQLite DB<br/>data/dashboard.db)]
        end
    end
    
    subgraph "Clients"
        Browser[Web Browsers]
        MCP[MCP Clients]
    end
    
    subgraph "Claude Code"
        Hooks[Hook Events]
    end
    
    Browser -->|HTTP/WS| Express
    MCP -->|HTTP| API
    Hooks -->|HTTP POST| Express
    
    Express --> Static
    Express --> API
    Express --> WS
    API --> DB
    Express --> DB
    
    style Express fill:#000000,color:#fff
    style DB fill:#003B57,color:#fff

High-Availability Architecture

graph TB
    subgraph "Load Balancer"
        LB[Nginx/HAProxy]
    end
    
    subgraph "Application Servers"
        App1[Node.js Server 1<br/>:4820]
        App2[Node.js Server 2<br/>:4820]
        App3[Node.js Server 3<br/>:4820]
    end
    
    subgraph "Data Layer"
        Redis[Redis<br/>WebSocket pub/sub]
        DB[(PostgreSQL<br/>Shared database)]
    end
    
    LB --> App1
    LB --> App2
    LB --> App3
    
    App1 --> Redis
    App2 --> Redis
    App3 --> Redis
    
    App1 --> DB
    App2 --> DB
    App3 --> DB
    
    style LB fill:#10B981
    style Redis fill:#DC2626
    style DB fill:#2563EB

Local Development

Prerequisites

  • Node.js >= 20.0.0
  • npm >= 9.0.0

Setup

# Clone repository
git clone https://github.com/your-org/agent-dashboard.git
cd agent-dashboard

# Install dependencies
npm run setup

# Start development servers
npm run dev

Development Architecture

sequenceDiagram
    participant Dev as Developer
    participant Server as Dev Server<br/>:4820 (watch mode)
    participant Client as Vite Dev<br/>:5173 (HMR)
    participant Browser
    
    Dev->>Server: Edit server/*.js
    Server->>Server: Auto-reload
    Server-->>Dev: Ready
    
    Dev->>Client: Edit client/src/*
    Client->>Client: HMR rebuild
    Client->>Browser: Hot update
    Browser->>Browser: Re-render
    
    Note over Browser: State preserved!

Running Components Separately

# Terminal 1: Server only
npm run dev:server

# Terminal 2: Client only
npm run dev:client

# Terminal 3: MCP server (optional)
npm run mcp:dev

Production Deployment

Build Process

graph TB
    Source[Source Code] --> Install[Install Dependencies<br/>npm ci --production]
    Install --> BuildClient[Build Client<br/>npm run build]
    BuildClient --> Bundle[Bundled Assets<br/>client/dist/]
    Bundle --> Deploy[Deploy to Server]
    
    Deploy --> Server[Start Server<br/>node server/index.js]
    
    style BuildClient fill:#646CFF
    style Server fill:#10B981

Production Checklist

# 1. Install dependencies (production only)
npm ci --production
cd client && npm ci --production && cd ..

# 2. Build client
npm run build

# 3. Set environment variables
export NODE_ENV=production
export PORT=4820
export DASHBOARD_DB_PATH=/var/lib/agent-dashboard/dashboard.db

# 4. Create data directory
mkdir -p /var/lib/agent-dashboard

# 5. Start server
node server/index.js

Environment Variables

# Server
DASHBOARD_PORT=4820                    # Server port
NODE_ENV=production                    # Environment mode

# Network exposure (SECURITY — GHSA-gr74-4xfh-6jw9)
# The server binds 127.0.0.1 by default and is NOT network-reachable. It reads
# transcripts, exports all data, and can spawn `claude`, so only widen the bind
# deliberately — and require a token when you do.
DASHBOARD_HOST=127.0.0.1               # set 0.0.0.0 ONLY if you must expose it
DASHBOARD_TOKEN=                        # required on /api/* + WS when set; use with a non-loopback host
DASHBOARD_ALLOWED_HOSTS=                # extra Host names (comma-sep) for a LAN bind

# Database
DASHBOARD_DB_PATH=/var/lib/agent-dashboard/dashboard.db

# Logging
LOG_LEVEL=info                         # debug | info | warn | error

Reverse-proxy / Docker exposure: the app binds loopback, so publish it to a network only through a proxy you control that adds TLS + auth, or set DASHBOARD_HOST=0.0.0.0 with DASHBOARD_TOKEN. A -p 4820:4820 Docker mapping assumes a trusted host network — do not expose it publicly without a token and a proxy.


Docker Deployment

# docker-compose.yml
version: '3.8'

services:
  agent-dashboard:
    build: .
    ports:
      - "4820:4820"
    volumes:
      - ./data:/app/data
    environment:
      - NODE_ENV=production
      - PORT=4820
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:4820/api/sessions"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

Build & Run

# Build image
docker compose build

# Start container
docker compose up -d

# View logs
docker compose logs -f

# Stop container
docker compose down

Multi-Stage Dockerfile

# Build stage
FROM node:22-alpine AS builder

WORKDIR /app

# Install dependencies
COPY package*.json ./
COPY client/package*.json ./client/
RUN npm ci && cd client && npm ci

# Build client
COPY client ./client
RUN cd client && npm run build

# Production stage
FROM node:22-alpine

WORKDIR /app

# Copy built artifacts
COPY --from=builder /app/client/dist ./client/dist
COPY --from=builder /app/node_modules ./node_modules
COPY server ./server
COPY package.json ./

# Create data directory
RUN mkdir -p /app/data

EXPOSE 4820

HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
  CMD node -e "require('http').get('http://localhost:4820/api/sessions', (res) => process.exit(res.statusCode === 200 ? 0 : 1))"

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

Container Architecture

graph TB
    subgraph "Docker Host"
        subgraph "Container"
            App[Node.js App<br/>:4820]
            Volume[Volume Mount<br/>/app/data]
        end
    end
    
    Host[Host Filesystem<br/>./data] -->|Bind Mount| Volume
    App --> Volume
    
    Client[External Clients] -->|Port 4820| App
    
    style App fill:#2496ED,color:#fff
    style Volume fill:#FFA500

Cloud Deployment

AWS (Elastic Beanstalk)

# Install EB CLI
pip install awsebcli

# Initialize
eb init -p node.js agent-dashboard

# Create environment
eb create production

# Deploy
eb deploy

# Open in browser
eb open

Kubernetes

# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: agent-dashboard
spec:
  replicas: 3
  selector:
    matchLabels:
      app: agent-dashboard
  template:
    metadata:
      labels:
        app: agent-dashboard
    spec:
      containers:
      - name: agent-dashboard
        image: agent-dashboard:latest
        ports:
        - containerPort: 4820
        env:
        - name: NODE_ENV
          value: "production"
        volumeMounts:
        - name: data
          mountPath: /app/data
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: agent-dashboard-pvc

---
apiVersion: v1
kind: Service
metadata:
  name: agent-dashboard
spec:
  selector:
    app: agent-dashboard
  ports:
  - protocol: TCP
    port: 80
    targetPort: 4820
  type: LoadBalancer

Kubernetes Architecture

graph TB
    subgraph "Kubernetes Cluster"
        subgraph "LoadBalancer Service"
            LB[Load Balancer<br/>:80]
        end
        
        subgraph "Pods"
            Pod1[agent-dashboard-1<br/>:4820]
            Pod2[agent-dashboard-2<br/>:4820]
            Pod3[agent-dashboard-3<br/>:4820]
        end
        
        subgraph "Storage"
            PVC[PersistentVolumeClaim]
            PV[PersistentVolume]
        end
    end
    
    LB --> Pod1
    LB --> Pod2
    LB --> Pod3
    
    Pod1 --> PVC
    Pod2 --> PVC
    Pod3 --> PVC
    PVC --> PV
    
    style LB fill:#10B981
    style PV fill:#F59E0B

Process Management

PM2 (Production Process Manager)

# Install PM2
npm install -g pm2

# Start application
pm2 start server/index.js --name agent-dashboard

# Start with environment
pm2 start server/index.js --name agent-dashboard --env production

# View logs
pm2 logs agent-dashboard

# Monitor
pm2 monit

# Restart
pm2 restart agent-dashboard

# Stop
pm2 stop agent-dashboard

# Auto-start on system boot
pm2 startup
pm2 save

PM2 Ecosystem File

// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'agent-dashboard',
    script: './server/index.js',
    instances: 2,
    exec_mode: 'cluster',
    env: {
      NODE_ENV: 'development',
      PORT: 4820
    },
    env_production: {
      NODE_ENV: 'production',
      PORT: 4820,
      DASHBOARD_DB_PATH: '/var/lib/agent-dashboard/dashboard.db'
    },
    max_memory_restart: '500M',
    error_file: '/var/log/agent-dashboard/error.log',
    out_file: '/var/log/agent-dashboard/out.log',
    time: true
  }]
};
# Start with ecosystem file
pm2 start ecosystem.config.js --env production

Systemd Service (Linux)

# /etc/systemd/system/agent-dashboard.service
[Unit]
Description=Agent Dashboard
After=network.target

[Service]
Type=simple
User=agent-dashboard
WorkingDirectory=/opt/agent-dashboard
Environment=NODE_ENV=production
Environment=PORT=4820
Environment=DASHBOARD_DB_PATH=/var/lib/agent-dashboard/dashboard.db
ExecStart=/usr/bin/node server/index.js
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
# Enable and start service
sudo systemctl enable agent-dashboard
sudo systemctl start agent-dashboard

# Check status
sudo systemctl status agent-dashboard

# View logs
sudo journalctl -u agent-dashboard -f

Monitoring & Logging

Health Checks

# Server health check
curl http://localhost:4820/api/sessions

# Expected: {"sessions": [...]}

Logging Strategy

graph TB
    subgraph "Application Logs"
        App[Express Server] --> Console[Console Output]
        Console --> Stdout[stdout/stderr]
    end
    
    subgraph "Log Aggregation"
        Stdout --> PM2[PM2 Logs]
        Stdout --> Systemd[Systemd Journal]
        Stdout --> Docker[Docker Logs]
    end
    
    subgraph "Monitoring Tools"
        PM2 --> File[Log Files]
        Systemd --> Journalctl[journalctl]
        Docker --> DockerLogs[docker logs]
    end
    
    subgraph "Analysis"
        File --> Splunk[Splunk/ELK]
        Journalctl --> Splunk
        DockerLogs --> Splunk
    end
    
    style App fill:#000000,color:#fff
    style Splunk fill:#10B981

Monitoring Metrics

// Add to server/index.js for metrics endpoint
app.get('/metrics', (req, res) => {
  const metrics = {
    uptime: process.uptime(),
    memory: process.memoryUsage(),
    cpu: process.cpuUsage(),
    sessions: db.prepare('SELECT COUNT(*) as count FROM sessions').get(),
    agents: db.prepare('SELECT COUNT(*) as count FROM agents').get(),
    websocket_clients: wss.clients.size
  };
  res.json(metrics);
});

Backup & Recovery

Backup Strategy

graph TB
    subgraph "Backup Process"
        DB[(SQLite DB)] --> Backup[Backup Script]
        Backup --> Local[Local Storage<br/>./backups/]
        Backup --> S3[AWS S3]
        Backup --> Cloud[Cloud Storage]
    end
    
    subgraph "Schedule"
        Cron[Cron Job<br/>Daily at 2 AM]
    end
    
    subgraph "Retention"
        Daily[Daily: 7 days]
        Weekly[Weekly: 4 weeks]
        Monthly[Monthly: 12 months]
    end
    
    Cron --> Backup
    Local --> Daily
    S3 --> Weekly
    Cloud --> Monthly
    
    style DB fill:#003B57,color:#fff
    style S3 fill:#FF9900

Backup Script

#!/bin/bash
# scripts/backup.sh

BACKUP_DIR="/var/backups/agent-dashboard"
DB_PATH="/var/lib/agent-dashboard/dashboard.db"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/dashboard_$TIMESTAMP.db"

# Create backup directory
mkdir -p "$BACKUP_DIR"

# Create backup (online backup with VACUUM INTO)
sqlite3 "$DB_PATH" "VACUUM INTO '$BACKUP_FILE'"

# Compress backup
gzip "$BACKUP_FILE"

# Upload to S3 (optional)
aws s3 cp "$BACKUP_FILE.gz" s3://my-backups/agent-dashboard/

# Delete old backups (keep last 7 days)
find "$BACKUP_DIR" -name "dashboard_*.db.gz" -mtime +7 -delete

echo "Backup completed: $BACKUP_FILE.gz"

Restore Process

#!/bin/bash
# scripts/restore.sh

BACKUP_FILE=$1
DB_PATH="/var/lib/agent-dashboard/dashboard.db"

if [ -z "$BACKUP_FILE" ]; then
  echo "Usage: ./restore.sh <backup_file.db.gz>"
  exit 1
fi

# Stop application
systemctl stop agent-dashboard

# Decompress backup
gunzip -c "$BACKUP_FILE" > /tmp/restore.db

# Restore database
cp /tmp/restore.db "$DB_PATH"
chown agent-dashboard:agent-dashboard "$DB_PATH"

# Start application
systemctl start agent-dashboard

echo "Restore completed from $BACKUP_FILE"

Security Hardening

Security Checklist

graph TB
    subgraph "Network Security"
        Firewall[Firewall Rules<br/>Allow :4820 only from trusted IPs]
        TLS[TLS/SSL<br/>HTTPS + WSS]
        CORS[CORS Configuration<br/>Restrict origins]
    end
    
    subgraph "Application Security"
        Validation[Input Validation]
        Prepared[Prepared Statements<br/>SQL injection prevention]
        Sanitize[Output Sanitization]
    end
    
    subgraph "System Security"
        User[Dedicated User<br/>Non-root]
        Perms[File Permissions<br/>640 for DB]
        SELinux[SELinux/AppArmor]
    end
    
    style Firewall fill:#10B981
    style Prepared fill:#10B981
    style User fill:#10B981

TLS Configuration (Nginx Reverse Proxy)

# /etc/nginx/sites-available/agent-dashboard
server {
    listen 443 ssl http2;
    server_name dashboard.example.com;
    
    ssl_certificate /etc/letsencrypt/live/dashboard.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/dashboard.example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    
    location / {
        proxy_pass http://localhost:4820;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    
    location /ws {
        proxy_pass http://localhost:4820/ws;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name dashboard.example.com;
    return 301 https://$server_name$request_uri;
}

Performance Tuning

Node.js Optimization

# Increase memory limit
NODE_OPTIONS="--max-old-space-size=4096" node server/index.js

# Enable V8 optimizations
node --optimize-for-size server/index.js

SQLite Tuning

// server/db.js - Add these pragmas
db.pragma('journal_mode = WAL');       // Write-Ahead Logging
db.pragma('synchronous = NORMAL');     // Faster writes
db.pragma('cache_size = -64000');      // 64MB cache
db.pragma('temp_store = MEMORY');      // Temp tables in memory
db.pragma('mmap_size = 30000000000');  // Memory-mapped I/O
db.pragma('page_size = 4096');         // Optimal page size

Nginx Tuning

# /etc/nginx/nginx.conf
worker_processes auto;
worker_connections 4096;

http {
    # Enable compression
    gzip on;
    gzip_comp_level 6;
    gzip_types text/plain text/css application/json application/javascript;
    
    # Client body buffer
    client_body_buffer_size 128k;
    
    # Keepalive
    keepalive_timeout 65;
    keepalive_requests 100;
    
    # Proxy buffering
    proxy_buffering on;
    proxy_buffer_size 4k;
    proxy_buffers 8 4k;
}

Troubleshooting

Common Issues

graph TB
    subgraph "Issue Categories"
        Startup[Startup Failures]
        Connection[Connection Errors]
        Performance[Performance Issues]
        Data[Data Inconsistencies]
    end
    
    subgraph "Diagnostics"
        Logs[Check Logs]
        Health[Health Checks]
        Metrics[Monitor Metrics]
        DB[Database Integrity]
    end
    
    Startup --> Logs
    Connection --> Health
    Performance --> Metrics
    Data --> DB
    
    style Logs fill:#F59E0B

Issue Resolution Guide

Issue Symptoms Solution
Port already in use EADDRINUSE: address already in use :::4820 lsof -i :4820 then kill process
Database locked database is locked Check for long-running queries, increase timeout
WebSocket connection fails Clients can't connect Check firewall, verify WebSocket upgrade headers
High memory usage >500MB RAM Enable memory limits, check for leaks
Slow queries API responses >100ms Add indexes, use EXPLAIN QUERY PLAN

Debug Mode

# Enable verbose logging
DEBUG=* node server/index.js

# SQLite query logging
NODE_ENV=development node server/index.js

Summary

This deployment guide covers:

  • Multiple deployment modes - Local, Docker, PM2, Systemd, Cloud
  • Production best practices - Environment variables, health checks, logging
  • Process management - PM2, systemd service files
  • Monitoring & logging - Metrics endpoint, log aggregation
  • Backup & recovery - Automated backups, restore procedures
  • Security hardening - TLS, CORS, firewall rules
  • Performance tuning - Node.js, SQLite, Nginx optimizations
  • Troubleshooting - Common issues and resolutions

For architecture details, see ARCHITECTURE.md.