# DevBox Implementation Plan: CLI to Microservices ## Phase 1: CLI Refactoring ### 1.1 New CLI Structure ``` devbox/ ├── core/ # Local operations only │ ├── docker.sh # Docker management │ ├── network.sh # Network setup │ ├── validation.sh # Environment validation │ └── utils.sh # Common utilities ├── api/ # API communication │ ├── client.sh # HTTP client wrapper │ ├── auth.sh # Authentication handling │ ├── review.sh # Code review API calls │ ├── cicd.sh # CI/CD API calls │ └── ai.sh # AI service API calls ├── auth/ # Authentication management │ ├── login.sh # Login functionality │ ├── token.sh # Token management │ └── permissions.sh # Permission checking ├── config/ # Configuration management │ ├── settings.sh # Settings management │ ├── profiles.sh # Profile management │ └── defaults.sh # Default configurations └── commands/ # Command implementations ├── init.sh # Local init command ├── start.sh # Local start command ├── review.sh # Review command (API-based) ├── package.sh # Package command (API-based) └── deploy.sh # Deploy command (API-based) ``` ### 1.2 API Client Implementation ```bash # api/client.sh #!/usr/bin/env bash # API client configuration API_BASE_URL="${DEVBOX_API_URL:-https://api.devbox.com}" API_VERSION="v1" API_TIMEOUT=30 # HTTP client with authentication api_request() { local method="$1" local endpoint="$2" local data="$3" local token="$4" local url="${API_BASE_URL}/api/${API_VERSION}${endpoint}" local headers=( "Content-Type: application/json" "User-Agent: DevBox-CLI/1.0" ) if [[ -n "$token" ]]; then headers+=("Authorization: Bearer $token") fi local curl_opts=( "-X" "$method" "-H" "${headers[0]}" "-H" "${headers[1]}" "--connect-timeout" "$API_TIMEOUT" "--max-time" "$API_TIMEOUT" "-s" ) if [[ -n "$token" ]]; then curl_opts+=("-H" "${headers[2]}") fi if [[ -n "$data" ]]; then curl_opts+=("-d" "$data") fi curl "${curl_opts[@]}" "$url" } # API response handling handle_api_response() { local response="$1" local expected_status="$2" if [[ -z "$response" ]]; then log_error "No response from API" return 1 fi local status=$(echo "$response" | jq -r '.status // .error // "unknown"') local message=$(echo "$response" | jq -r '.message // .error // "Unknown error"') if [[ "$status" == "$expected_status" ]] || [[ "$status" == "success" ]]; then echo "$response" return 0 else log_error "API Error: $message" return 1 fi } ``` ### 1.3 Authentication Implementation ```bash # auth/login.sh #!/usr/bin/env bash devbox_auth_login() { local username="$1" local password="$2" local api_key="$3" log_info "Authenticating with DevBox API..." # Prepare login payload local login_data=$(cat << EOF { "username": "$username", "password": "$password", "api_key": "$api_key" } EOF ) # Make login request local response=$(api_request "POST" "/auth/login" "$login_data") if handle_api_response "$response" "success"; then local token=$(echo "$response" | jq -r '.token') local refresh_token=$(echo "$response" | jq -r '.refresh_token') local permissions=$(echo "$response" | jq -r '.permissions') # Store tokens securely store_auth_tokens "$token" "$refresh_token" store_permissions "$permissions" log_info "Authentication successful" return 0 else return 1 fi } # auth/token.sh #!/usr/bin/env bash store_auth_tokens() { local token="$1" local refresh_token="$2" # Store in secure location local config_dir="$HOME/.devbox" mkdir -p "$config_dir" # Encrypt tokens before storing echo "$token" | openssl enc -aes-256-cbc -salt -out "$config_dir/.token" -pass pass:"$DEVBOX_ENCRYPTION_KEY" echo "$refresh_token" | openssl enc -aes-256-cbc -salt -out "$config_dir/.refresh_token" -pass pass:"$DEVBOX_ENCRYPTION_KEY" # Set file permissions chmod 600 "$config_dir/.token" "$config_dir/.refresh_token" } get_auth_token() { local config_dir="$HOME/.devbox" if [[ -f "$config_dir/.token" ]]; then openssl enc -aes-256-cbc -d -in "$config_dir/.token" -pass pass:"$DEVBOX_ENCRYPTION_KEY" 2>/dev/null fi } refresh_auth_token() { local refresh_token=$(openssl enc -aes-256-cbc -d -in "$HOME/.devbox/.refresh_token" -pass pass:"$DEVBOX_ENCRYPTION_KEY" 2>/dev/null) if [[ -n "$refresh_token" ]]; then local refresh_data=$(cat << EOF { "refresh_token": "$refresh_token" } EOF ) local response=$(api_request "POST" "/auth/refresh" "$refresh_data") if handle_api_response "$response" "success"; then local new_token=$(echo "$response" | jq -r '.token') local new_refresh_token=$(echo "$response" | jq -r '.refresh_token') store_auth_tokens "$new_token" "$new_refresh_token" return 0 fi fi return 1 } ``` ### 1.4 Permission Checking ```bash # auth/permissions.sh #!/usr/bin/env bash check_permission() { local required_permission="$1" local user_permissions="$2" # Check if user has wildcard permission if [[ "$user_permissions" == *"*"* ]]; then return 0 fi # Check specific permission if [[ "$user_permissions" == *"$required_permission"* ]]; then return 0 fi return 1 } require_permission() { local permission="$1" local operation="$2" local user_permissions=$(get_user_permissions) if ! check_permission "$permission" "$user_permissions"; then log_error "Permission denied: $permission required for $operation" log_error "Contact your administrator to request access" return 1 fi return 0 } get_user_permissions() { local config_dir="$HOME/.devbox" if [[ -f "$config_dir/.permissions" ]]; then cat "$config_dir/.permissions" fi } ``` ### 1.5 API-based Commands ```bash # commands/review.sh #!/usr/bin/env bash devbox_review_command() { local component="$1" local options="$2" log_info "Starting code review for $component..." # Check permissions if ! require_permission "code_review:read" "code review"; then exit 1 fi # Get authentication token local token=$(get_auth_token) if [[ -z "$token" ]]; then log_error "Not authenticated. Run 'devbox auth login' first." exit 1 fi # Prepare review request local review_data=$(prepare_review_data "$component" "$options") # Make API request local response=$(api_request "POST" "/review/analyze" "$review_data" "$token") if handle_api_response "$response" "success"; then local report_url=$(echo "$response" | jq -r '.report_url') local report_id=$(echo "$response" | jq -r '.report_id') log_info "Code review completed successfully" log_info "Report ID: $report_id" log_info "View report at: $report_url" # Open report in browser if possible if command -v xdg-open &>/dev/null; then xdg-open "$report_url" elif command -v open &>/dev/null; then open "$report_url" fi return 0 else return 1 fi } prepare_review_data() { local component="$1" local options="$2" # Get changed files local changed_files=$(get_changed_files "$component") # Prepare file contents local files_data="[]" while IFS= read -r file; do if [[ -f "$file" ]]; then local content=$(cat "$file" | jq -R -s .) local file_info=$(cat << EOF { "path": "$file", "content": $content } EOF ) files_data=$(echo "$files_data" | jq ". += [$file_info]") fi done <<< "$changed_files" # Create review request cat << EOF { "component": "$component", "files": $files_data, "options": $options } EOF } ``` ## Phase 2: Microservices Implementation ### 2.1 API Gateway (Nginx) ```nginx # gateway/nginx.conf events { worker_connections 1024; } http { upstream auth_service { server auth-service:8001; } upstream review_service { server review-service:8002; } upstream cicd_service { server cicd-service:8003; } upstream ai_service { server ai-service:8004; } # Rate limiting limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; server { listen 80; server_name api.devbox.com; # SSL configuration (for production) # listen 443 ssl; # ssl_certificate /etc/nginx/ssl/cert.pem; # ssl_certificate_key /etc/nginx/ssl/key.pem; # Security headers add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; # Rate limiting limit_req zone=api burst=20 nodelay; # Authentication endpoints location /api/v1/auth/ { proxy_pass http://auth_service; 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; } # Code review endpoints location /api/v1/review/ { auth_request /auth/validate; proxy_pass http://review_service; 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; } # CI/CD endpoints location /api/v1/cicd/ { auth_request /auth/validate; proxy_pass http://cicd_service; 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; } # AI service endpoints location /api/v1/ai/ { auth_request /auth/validate; proxy_pass http://ai_service; 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; } # Authentication validation location = /auth/validate { internal; proxy_pass http://auth_service/api/v1/auth/validate; proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_set_header X-Original-URI $request_uri; } } } ``` ### 2.2 Authentication Service (Python/FastAPI) ```python # services/auth/main.py from fastapi import FastAPI, HTTPException, Depends, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from pydantic import BaseModel import jwt import datetime from typing import List, Optional import redis import os app = FastAPI(title="DevBox Auth Service") security = HTTPBearer() # Configuration JWT_SECRET = os.getenv("JWT_SECRET", "your-secret-key") JWT_ALGORITHM = "HS256" JWT_EXPIRY = 3600 # 1 hour REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379") # Redis connection redis_client = redis.from_url(REDIS_URL) # Models class LoginRequest(BaseModel): username: str password: str api_key: Optional[str] = None class TokenResponse(BaseModel): token: str refresh_token: str permissions: List[str] expires_in: int class PermissionCheck(BaseModel): permission: str user_id: str # Permission definitions PERMISSIONS = { "developer": [ "code_review:read", "cicd:build", "ai:chat", "ai:analyze" ], "senior_developer": [ "code_review:read", "code_review:write", "cicd:build", "cicd:deploy", "ai:chat", "ai:analyze", "ai:generate" ], "devops": [ "cicd:build", "cicd:deploy", "cicd:admin", "deployment:read", "deployment:write" ], "admin": ["*"] } @app.post("/api/v1/auth/login", response_model=TokenResponse) async def login(request: LoginRequest): # Validate credentials (implement your authentication logic) if not validate_credentials(request.username, request.password): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials" ) # Get user role and permissions user_role = get_user_role(request.username) permissions = PERMISSIONS.get(user_role, []) # Generate tokens token = create_access_token(request.username, permissions) refresh_token = create_refresh_token(request.username) # Store refresh token in Redis redis_client.setex( f"refresh_token:{request.username}", JWT_EXPIRY * 24, # 24 hours refresh_token ) return TokenResponse( token=token, refresh_token=refresh_token, permissions=permissions, expires_in=JWT_EXPIRY ) @app.post("/api/v1/auth/refresh") async def refresh_token(refresh_token: str): try: payload = jwt.decode(refresh_token, JWT_SECRET, algorithms=[JWT_ALGORITHM]) username = payload.get("sub") # Verify refresh token in Redis stored_token = redis_client.get(f"refresh_token:{username}") if not stored_token or stored_token.decode() != refresh_token: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid refresh token" ) # Generate new tokens user_role = get_user_role(username) permissions = PERMISSIONS.get(user_role, []) new_token = create_access_token(username, permissions) new_refresh_token = create_refresh_token(username) # Update stored refresh token redis_client.setex( f"refresh_token:{username}", JWT_EXPIRY * 24, new_refresh_token ) return { "token": new_token, "refresh_token": new_refresh_token, "expires_in": JWT_EXPIRY } except jwt.ExpiredSignatureError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Refresh token expired" ) except jwt.InvalidTokenError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid refresh token" ) @app.get("/api/v1/auth/validate") async def validate_token(credentials: HTTPAuthorizationCredentials = Depends(security)): try: payload = jwt.decode(credentials.credentials, JWT_SECRET, algorithms=[JWT_ALGORITHM]) username = payload.get("sub") permissions = payload.get("permissions", []) if not username: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token" ) return { "user_id": username, "permissions": permissions, "valid": True } except jwt.ExpiredSignatureError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Token expired" ) except jwt.InvalidTokenError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token" ) def create_access_token(username: str, permissions: List[str]) -> str: payload = { "sub": username, "permissions": permissions, "exp": datetime.datetime.utcnow() + datetime.timedelta(seconds=JWT_EXPIRY), "iat": datetime.datetime.utcnow() } return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM) def create_refresh_token(username: str) -> str: payload = { "sub": username, "type": "refresh", "exp": datetime.datetime.utcnow() + datetime.timedelta(days=1), "iat": datetime.datetime.utcnow() } return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM) def validate_credentials(username: str, password: str) -> bool: # Implement your authentication logic here # This could be database lookup, LDAP, etc. return True # Placeholder def get_user_role(username: str) -> str: # Implement role lookup logic return "developer" # Placeholder ``` ### 2.3 Code Review Service (Python/FastAPI) ```python # services/review/main.py from fastapi import FastAPI, HTTPException, Depends, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from pydantic import BaseModel import openai import redis import json import os from typing import List, Dict, Any import uuid from datetime import datetime app = FastAPI(title="DevBox Code Review Service") security = HTTPBearer() # Configuration OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379") # Initialize OpenAI openai.api_key = OPENAI_API_KEY # Redis connection redis_client = redis.from_url(REDIS_URL) # Models class FileContent(BaseModel): path: str content: str class ReviewRequest(BaseModel): component: str files: List[FileContent] options: Dict[str, Any] class ReviewResponse(BaseModel): report_id: str report_url: str status: str @app.post("/api/v1/review/analyze", response_model=ReviewResponse) async def analyze_code( request: ReviewRequest, credentials: HTTPAuthorizationCredentials = Depends(security) ): # Validate permissions if not has_permission(credentials.credentials, "code_review:read"): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Permission denied: code_review:read required" ) # Generate report ID report_id = str(uuid.uuid4()) # Prepare files content for OpenAI files_content = "" for file in request.files: files_content += f"\n\n=== File: {file.path} ===\n" files_content += file.content # Generate review prompt prompt = generate_review_prompt(request.component, files_content) try: # Call OpenAI API response = openai.ChatCompletion.create( model="gpt-4", messages=[ {"role": "user", "content": prompt} ], max_tokens=4000, temperature=0.1 ) review_content = response.choices[0].message.content # Parse and structure the review structured_review = parse_review_content(review_content, request.component) # Store review in Redis review_data = { "report_id": report_id, "component": request.component, "review": structured_review, "timestamp": datetime.utcnow().isoformat(), "files_analyzed": len(request.files) } redis_client.setex( f"review:{report_id}", 86400, # 24 hours json.dumps(review_data) ) # Generate report URL report_url = f"https://api.devbox.com/reports/{report_id}" return ReviewResponse( report_id=report_id, report_url=report_url, status="completed" ) except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Review failed: {str(e)}" ) @app.get("/api/v1/review/reports/{report_id}") async def get_report( report_id: str, credentials: HTTPAuthorizationCredentials = Depends(security) ): # Validate permissions if not has_permission(credentials.credentials, "code_review:read"): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Permission denied: code_review:read required" ) # Get report from Redis report_data = redis_client.get(f"review:{report_id}") if not report_data: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Report not found" ) return json.loads(report_data) def generate_review_prompt(component: str, files_content: str) -> str: return f""" You are an expert code reviewer with deep knowledge of software engineering best practices, security, performance, and maintainability. Please review the following code changes for the {component} component. **Review Guidelines:** 1. **Security**: Identify potential security vulnerabilities, input validation issues, authentication/authorization problems 2. **Performance**: Look for inefficient algorithms, memory leaks, database query issues, scalability concerns 3. **Code Quality**: Check for code smells, maintainability issues, readability problems, naming conventions 4. **Best Practices**: Verify adherence to language-specific best practices, design patterns, SOLID principles 5. **Error Handling**: Assess error handling, exception management, logging practices 6. **Testing**: Evaluate test coverage, test quality, mocking practices 7. **Documentation**: Check for proper documentation, comments, API documentation **Review Format:** For each issue found, provide: - **Severity**: CRITICAL, WARNING, INFO, or SUGGESTION - **Title**: Brief description of the issue - **Description**: Detailed explanation of the problem - **Line Number**: Specific line where the issue occurs (if applicable) - **Code Snippet**: Relevant code section (if applicable) - **Suggestion**: Specific recommendation for improvement **Code to Review:** {files_content} Please provide a comprehensive review focusing on the most important issues first. Be specific and actionable in your recommendations. """ def parse_review_content(content: str, component: str) -> Dict[str, Any]: # This is a simplified parser - you might want to enhance it return { "component": component, "raw_content": content, "issues": [], # Parse structured issues from content "summary": { "critical_issues": 0, "warnings": 0, "suggestions": 0 } } def has_permission(token: str, required_permission: str) -> bool: # Implement permission checking logic # This would decode the JWT and check permissions return True # Placeholder ``` ## Phase 3: Migration Script ```bash # migration/migrate.sh #!/usr/bin/env bash migrate_to_microservices() { log_info "Starting DevBox migration to microservices architecture..." # Backup current CLI backup_current_cli # Install new lightweight CLI install_new_cli # Migrate configuration migrate_configuration # Test new setup test_new_setup log_info "Migration completed successfully!" } backup_current_cli() { local backup_dir="$HOME/.devbox/backup/$(date +%Y%m%d_%H%M%S)" mkdir -p "$backup_dir" log_info "Backing up current CLI to $backup_dir" # Copy current CLI files cp -r /usr/local/bin/devbox "$backup_dir/" cp -r "$HOME/.devbox" "$backup_dir/config" log_info "Backup completed" } install_new_cli() { log_info "Installing new lightweight CLI..." # Download new CLI curl -L -o /tmp/devbox-new "https://api.devbox.com/download/cli/latest" chmod +x /tmp/devbox-new # Install new CLI sudo mv /tmp/devbox-new /usr/local/bin/devbox log_info "New CLI installed" } migrate_configuration() { log_info "Migrating configuration..." local old_config="$HOME/.devbox" local new_config="$HOME/.devbox-v2" # Create new config structure mkdir -p "$new_config" # Migrate basic settings if [[ -f "$old_config/config.yaml" ]]; then cp "$old_config/config.yaml" "$new_config/" fi # Set up API configuration cat > "$new_config/api.yaml" << EOF api: url: "https://api.devbox.com" version: "v1" timeout: 30 auth: token_file: "$new_config/.token" refresh_token_file: "$new_config/.refresh_token" permissions_file: "$new_config/.permissions" EOF log_info "Configuration migrated" } test_new_setup() { log_info "Testing new setup..." # Test basic commands if devbox --version; then log_info "✓ CLI installation test passed" else log_error "✗ CLI installation test failed" return 1 fi # Test authentication if devbox auth status; then log_info "✓ Authentication test passed" else log_warn "⚠ Authentication not configured (expected for new setup)" fi log_info "New setup test completed" } ``` ## Benefits of This Implementation ### 1. **Security** - Sensitive code protected in microservices - Role-based access control - Encrypted token storage - Audit trails for all operations ### 2. **Maintainability** - Independent service updates - Centralized configuration - Easy feature rollouts - Better error tracking ### 3. **Scalability** - Services can scale independently - Load balancing support - Geographic distribution - Resource optimization ### 4. **User Experience** - Seamless migration - Same familiar commands - Enhanced features - Better performance This implementation provides a solid foundation for transforming DevBox into a secure, scalable, and maintainable platform while preserving the user experience.