Files
agents/tools/config-validate.md
Seth Hobson ce7a5938c1 Consolidate workflows and tools from commands repository
Repository Restructure:
- Move all 83 agent .md files to agents/ subdirectory
- Add 15 workflow orchestrators from commands repo to workflows/
- Add 42 development tools from commands repo to tools/
- Update README for unified repository structure

This prepares the repository for unified plugin marketplace integration.
The commands repository functionality is now fully integrated, providing
complete workflow orchestration and development tooling alongside agents.

Directory Structure:
- agents/    - 83 specialized AI agents
- workflows/ - 15 multi-agent orchestration commands
- tools/     - 42 focused development utilities

No breaking changes to agent functionality - all agents remain accessible
with same names and behavior. Adds workflow and tool commands for enhanced
multi-agent coordination capabilities.
2025-10-08 08:25:17 -04:00

49 KiB

model
model
claude-sonnet-4-0

Configuration Validation

You are a configuration management expert specializing in validating, testing, and ensuring the correctness of application configurations. Create comprehensive validation schemas, implement configuration testing strategies, and ensure configurations are secure, consistent, and error-free across all environments.

Context

The user needs to validate configuration files, implement configuration schemas, ensure consistency across environments, and prevent configuration-related errors. Focus on creating robust validation rules, type safety, security checks, and automated validation processes.

Requirements

$ARGUMENTS

Instructions

1. Configuration Analysis

Analyze existing configuration structure and identify validation needs:

Configuration Scanner

import os
import yaml
import json
import toml
import configparser
from pathlib import Path
from typing import Dict, List, Any, Set

class ConfigurationAnalyzer:
    def analyze_project(self, project_path: str) -> Dict[str, Any]:
        """
        Analyze project configuration files and patterns
        """
        analysis = {
            'config_files': self._find_config_files(project_path),
            'config_patterns': self._identify_patterns(project_path),
            'security_issues': self._check_security_issues(project_path),
            'consistency_issues': self._check_consistency(project_path),
            'validation_coverage': self._assess_validation(project_path),
            'recommendations': []
        }
        
        self._generate_recommendations(analysis)
        return analysis
    
    def _find_config_files(self, project_path: str) -> List[Dict]:
        """Find all configuration files in project"""
        config_patterns = [
            '**/*.json', '**/*.yaml', '**/*.yml', '**/*.toml',
            '**/*.ini', '**/*.conf', '**/*.config', '**/*.env*',
            '**/*.properties', '**/config.js', '**/config.ts'
        ]
        
        config_files = []
        for pattern in config_patterns:
            for file_path in Path(project_path).glob(pattern):
                if not self._should_ignore(file_path):
                    config_files.append({
                        'path': str(file_path),
                        'type': self._detect_config_type(file_path),
                        'size': file_path.stat().st_size,
                        'environment': self._detect_environment(file_path)
                    })
        
        return config_files
    
    def _check_security_issues(self, project_path: str) -> List[Dict]:
        """Check for security issues in configurations"""
        issues = []
        
        # Patterns that might indicate secrets
        secret_patterns = [
            r'(api[_-]?key|apikey)',
            r'(secret|password|passwd|pwd)',
            r'(token|auth)',
            r'(private[_-]?key)',
            r'(aws[_-]?access|aws[_-]?secret)',
            r'(database[_-]?url|db[_-]?connection)'
        ]
        
        for config_file in self._find_config_files(project_path):
            content = Path(config_file['path']).read_text()
            
            for pattern in secret_patterns:
                if re.search(pattern, content, re.IGNORECASE):
                    # Check if it's a placeholder or actual secret
                    if self._looks_like_real_secret(content, pattern):
                        issues.append({
                            'file': config_file['path'],
                            'type': 'potential_secret',
                            'pattern': pattern,
                            'severity': 'high'
                        })
        
        return issues
    
    def _check_consistency(self, project_path: str) -> List[Dict]:
        """Check configuration consistency across environments"""
        inconsistencies = []
        
        # Group configs by base name
        config_groups = defaultdict(list)
        for config in self._find_config_files(project_path):
            base_name = self._get_base_config_name(config['path'])
            config_groups[base_name].append(config)
        
        # Check each group for inconsistencies
        for base_name, configs in config_groups.items():
            if len(configs) > 1:
                keys_by_env = {}
                for config in configs:
                    env = config.get('environment', 'default')
                    keys = self._extract_config_keys(config['path'])
                    keys_by_env[env] = keys
                
                # Find missing keys
                all_keys = set()
                for keys in keys_by_env.values():
                    all_keys.update(keys)
                
                for env, keys in keys_by_env.items():
                    missing = all_keys - keys
                    if missing:
                        inconsistencies.append({
                            'config_group': base_name,
                            'environment': env,
                            'missing_keys': list(missing),
                            'severity': 'medium'
                        })
        
        return inconsistencies

2. Schema Definition and Validation

Implement configuration schema validation:

JSON Schema Validator

// config-validator.ts
import Ajv from 'ajv';
import ajvFormats from 'ajv-formats';
import ajvKeywords from 'ajv-keywords';
import { JSONSchema7 } from 'json-schema';

interface ValidationResult {
  valid: boolean;
  errors?: Array<{
    path: string;
    message: string;
    keyword: string;
    params: any;
  }>;
}

export class ConfigValidator {
  private ajv: Ajv;
  private schemas: Map<string, JSONSchema7> = new Map();
  
  constructor() {
    this.ajv = new Ajv({
      allErrors: true,
      verbose: true,
      strict: false,
      coerceTypes: true
    });
    
    // Add formats support
    ajvFormats(this.ajv);
    ajvKeywords(this.ajv);
    
    // Add custom formats
    this.addCustomFormats();
  }
  
  private addCustomFormats() {
    // URL format with protocol validation
    this.ajv.addFormat('url-https', {
      type: 'string',
      validate: (data: string) => {
        try {
          const url = new URL(data);
          return url.protocol === 'https:';
        } catch {
          return false;
        }
      }
    });
    
    // Environment variable reference
    this.ajv.addFormat('env-var', {
      type: 'string',
      validate: /^\$\{[A-Z_][A-Z0-9_]*\}$/
    });
    
    // Semantic version
    this.ajv.addFormat('semver', {
      type: 'string',
      validate: /^\d+\.\d+\.\d+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/
    });
    
    // Port number
    this.ajv.addFormat('port', {
      type: 'number',
      validate: (data: number) => data >= 1 && data <= 65535
    });
    
    // Duration format (e.g., "5m", "1h", "30s")
    this.ajv.addFormat('duration', {
      type: 'string',
      validate: /^\d+[smhd]$/
    });
  }
  
  registerSchema(name: string, schema: JSONSchema7): void {
    this.schemas.set(name, schema);
    this.ajv.addSchema(schema, name);
  }
  
  validate(configData: any, schemaName: string): ValidationResult {
    const validate = this.ajv.getSchema(schemaName);
    
    if (!validate) {
      throw new Error(`Schema '${schemaName}' not found`);
    }
    
    const valid = validate(configData);
    
    if (!valid && validate.errors) {
      return {
        valid: false,
        errors: validate.errors.map(error => ({
          path: error.instancePath || '/',
          message: error.message || 'Validation error',
          keyword: error.keyword,
          params: error.params
        }))
      };
    }
    
    return { valid: true };
  }
  
  generateSchema(sampleConfig: any): JSONSchema7 {
    // Auto-generate schema from sample configuration
    const schema: JSONSchema7 = {
      type: 'object',
      properties: {},
      required: []
    };
    
    for (const [key, value] of Object.entries(sampleConfig)) {
      schema.properties![key] = this.inferSchema(value);
      
      // Make all top-level properties required by default
      if (schema.required && !key.startsWith('optional_')) {
        schema.required.push(key);
      }
    }
    
    return schema;
  }
  
  private inferSchema(value: any): JSONSchema7 {
    if (value === null) {
      return { type: 'null' };
    }
    
    if (Array.isArray(value)) {
      return {
        type: 'array',
        items: value.length > 0 ? this.inferSchema(value[0]) : {}
      };
    }
    
    if (typeof value === 'object') {
      const properties: Record<string, JSONSchema7> = {};
      const required: string[] = [];
      
      for (const [k, v] of Object.entries(value)) {
        properties[k] = this.inferSchema(v);
        if (v !== null && v !== undefined) {
          required.push(k);
        }
      }
      
      return {
        type: 'object',
        properties,
        required
      };
    }
    
    // Infer format from value patterns
    if (typeof value === 'string') {
      if (value.match(/^https?:\/\//)) {
        return { type: 'string', format: 'uri' };
      }
      if (value.match(/^\d{4}-\d{2}-\d{2}$/)) {
        return { type: 'string', format: 'date' };
      }
      if (value.match(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i)) {
        return { type: 'string', format: 'uuid' };
      }
    }
    
    return { type: typeof value as JSONSchema7['type'] };
  }
}

// Example schemas
export const schemas = {
  database: {
    type: 'object',
    properties: {
      host: { type: 'string', format: 'hostname' },
      port: { type: 'integer', format: 'port' },
      database: { type: 'string', minLength: 1 },
      user: { type: 'string', minLength: 1 },
      password: { type: 'string', minLength: 8 },
      ssl: {
        type: 'object',
        properties: {
          enabled: { type: 'boolean' },
          ca: { type: 'string' },
          cert: { type: 'string' },
          key: { type: 'string' }
        },
        required: ['enabled']
      },
      pool: {
        type: 'object',
        properties: {
          min: { type: 'integer', minimum: 0 },
          max: { type: 'integer', minimum: 1 },
          idleTimeout: { type: 'string', format: 'duration' }
        }
      }
    },
    required: ['host', 'port', 'database', 'user', 'password'],
    additionalProperties: false
  },
  
  api: {
    type: 'object',
    properties: {
      server: {
        type: 'object',
        properties: {
          host: { type: 'string', default: '0.0.0.0' },
          port: { type: 'integer', format: 'port', default: 3000 },
          cors: {
            type: 'object',
            properties: {
              enabled: { type: 'boolean' },
              origins: {
                type: 'array',
                items: { type: 'string', format: 'uri' }
              },
              credentials: { type: 'boolean' }
            }
          }
        },
        required: ['port']
      },
      auth: {
        type: 'object',
        properties: {
          jwt: {
            type: 'object',
            properties: {
              secret: { type: 'string', minLength: 32 },
              expiresIn: { type: 'string', format: 'duration' },
              algorithm: {
                type: 'string',
                enum: ['HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512']
              }
            },
            required: ['secret', 'expiresIn']
          }
        }
      },
      rateLimit: {
        type: 'object',
        properties: {
          windowMs: { type: 'integer', minimum: 1000 },
          max: { type: 'integer', minimum: 1 },
          message: { type: 'string' }
        }
      }
    },
    required: ['server', 'auth']
  }
};

3. Environment-Specific Validation

Validate configurations across environments:

Environment Validator

# environment_validator.py
from typing import Dict, List, Set, Any
import os
import re

class EnvironmentValidator:
    def __init__(self):
        self.environments = ['development', 'staging', 'production']
        self.environment_rules = self._define_environment_rules()
    
    def _define_environment_rules(self) -> Dict[str, Dict]:
        """Define environment-specific validation rules"""
        return {
            'development': {
                'allow_debug': True,
                'require_https': False,
                'allow_wildcards': True,
                'min_password_length': 8,
                'allowed_log_levels': ['debug', 'info', 'warn', 'error']
            },
            'staging': {
                'allow_debug': True,
                'require_https': True,
                'allow_wildcards': False,
                'min_password_length': 12,
                'allowed_log_levels': ['info', 'warn', 'error']
            },
            'production': {
                'allow_debug': False,
                'require_https': True,
                'allow_wildcards': False,
                'min_password_length': 16,
                'allowed_log_levels': ['warn', 'error'],
                'require_encryption': True,
                'require_backup': True
            }
        }
    
    def validate_config(self, config: Dict, environment: str) -> List[Dict]:
        """Validate configuration for specific environment"""
        if environment not in self.environment_rules:
            raise ValueError(f"Unknown environment: {environment}")
        
        rules = self.environment_rules[environment]
        violations = []
        
        # Check debug settings
        if not rules['allow_debug'] and config.get('debug', False):
            violations.append({
                'rule': 'no_debug_in_production',
                'message': 'Debug mode is not allowed in production',
                'severity': 'critical',
                'path': 'debug'
            })
        
        # Check HTTPS requirements
        if rules['require_https']:
            urls = self._extract_urls(config)
            for url_path, url in urls:
                if url.startswith('http://') and 'localhost' not in url:
                    violations.append({
                        'rule': 'require_https',
                        'message': f'HTTPS required for {url_path}',
                        'severity': 'high',
                        'path': url_path,
                        'value': url
                    })
        
        # Check log levels
        log_level = config.get('logging', {}).get('level')
        if log_level and log_level not in rules['allowed_log_levels']:
            violations.append({
                'rule': 'invalid_log_level',
                'message': f"Log level '{log_level}' not allowed in {environment}",
                'severity': 'medium',
                'path': 'logging.level',
                'allowed': rules['allowed_log_levels']
            })
        
        # Check production-specific requirements
        if environment == 'production':
            violations.extend(self._validate_production_requirements(config))
        
        return violations
    
    def _validate_production_requirements(self, config: Dict) -> List[Dict]:
        """Additional validation for production environment"""
        violations = []
        
        # Check encryption settings
        if not config.get('security', {}).get('encryption', {}).get('enabled'):
            violations.append({
                'rule': 'encryption_required',
                'message': 'Encryption must be enabled in production',
                'severity': 'critical',
                'path': 'security.encryption.enabled'
            })
        
        # Check backup configuration
        if not config.get('backup', {}).get('enabled'):
            violations.append({
                'rule': 'backup_required',
                'message': 'Backup must be configured for production',
                'severity': 'high',
                'path': 'backup.enabled'
            })
        
        # Check monitoring
        if not config.get('monitoring', {}).get('enabled'):
            violations.append({
                'rule': 'monitoring_required',
                'message': 'Monitoring must be enabled in production',
                'severity': 'high',
                'path': 'monitoring.enabled'
            })
        
        return violations
    
    def _extract_urls(self, obj: Any, path: str = '') -> List[tuple]:
        """Recursively extract URLs from configuration"""
        urls = []
        
        if isinstance(obj, dict):
            for key, value in obj.items():
                new_path = f"{path}.{key}" if path else key
                urls.extend(self._extract_urls(value, new_path))
        elif isinstance(obj, list):
            for i, item in enumerate(obj):
                new_path = f"{path}[{i}]"
                urls.extend(self._extract_urls(item, new_path))
        elif isinstance(obj, str) and re.match(r'^https?://', obj):
            urls.append((path, obj))
        
        return urls

# Cross-environment consistency checker
class ConsistencyChecker:
    def check_consistency(self, configs: Dict[str, Dict]) -> List[Dict]:
        """Check configuration consistency across environments"""
        issues = []
        
        # Get all unique keys across environments
        all_keys = set()
        env_keys = {}
        
        for env, config in configs.items():
            keys = self._flatten_keys(config)
            env_keys[env] = keys
            all_keys.update(keys)
        
        # Check for missing keys
        for env, keys in env_keys.items():
            missing_keys = all_keys - keys
            if missing_keys:
                issues.append({
                    'type': 'missing_keys',
                    'environment': env,
                    'keys': list(missing_keys),
                    'severity': 'medium'
                })
        
        # Check for type inconsistencies
        for key in all_keys:
            types_by_env = {}
            for env, config in configs.items():
                value = self._get_nested_value(config, key)
                if value is not None:
                    types_by_env[env] = type(value).__name__
            
            unique_types = set(types_by_env.values())
            if len(unique_types) > 1:
                issues.append({
                    'type': 'type_mismatch',
                    'key': key,
                    'types': types_by_env,
                    'severity': 'high'
                })
        
        return issues

4. Configuration Testing Framework

Implement configuration testing:

Config Test Suite

// config-test.ts
import { describe, it, expect, beforeEach } from '@jest/globals';
import { ConfigValidator } from './config-validator';
import { loadConfig } from './config-loader';

interface ConfigTestCase {
  name: string;
  config: any;
  environment: string;
  expectedValid: boolean;
  expectedErrors?: string[];
}

export class ConfigTestSuite {
  private validator: ConfigValidator;
  
  constructor() {
    this.validator = new ConfigValidator();
  }
  
  async runTests(testCases: ConfigTestCase[]): Promise<TestResults> {
    const results: TestResults = {
      passed: 0,
      failed: 0,
      errors: []
    };
    
    for (const testCase of testCases) {
      try {
        const result = await this.runTestCase(testCase);
        if (result.passed) {
          results.passed++;
        } else {
          results.failed++;
          results.errors.push({
            testName: testCase.name,
            errors: result.errors
          });
        }
      } catch (error) {
        results.failed++;
        results.errors.push({
          testName: testCase.name,
          errors: [error.message]
        });
      }
    }
    
    return results;
  }
  
  private async runTestCase(testCase: ConfigTestCase): Promise<TestResult> {
    // Load and validate config
    const validationResult = this.validator.validate(
      testCase.config,
      testCase.environment
    );
    
    const result: TestResult = {
      passed: validationResult.valid === testCase.expectedValid,
      errors: []
    };
    
    // Check expected errors
    if (testCase.expectedErrors && validationResult.errors) {
      for (const expectedError of testCase.expectedErrors) {
        const found = validationResult.errors.some(
          error => error.message.includes(expectedError)
        );
        
        if (!found) {
          result.passed = false;
          result.errors.push(`Expected error not found: ${expectedError}`);
        }
      }
    }
    
    return result;
  }
}

// Jest test examples
describe('Configuration Validation', () => {
  let validator: ConfigValidator;
  
  beforeEach(() => {
    validator = new ConfigValidator();
  });
  
  describe('Database Configuration', () => {
    it('should validate valid database config', () => {
      const config = {
        host: 'localhost',
        port: 5432,
        database: 'myapp',
        user: 'dbuser',
        password: 'securepassword123'
      };
      
      const result = validator.validate(config, 'database');
      expect(result.valid).toBe(true);
    });
    
    it('should reject invalid port number', () => {
      const config = {
        host: 'localhost',
        port: 70000, // Invalid port
        database: 'myapp',
        user: 'dbuser',
        password: 'securepassword123'
      };
      
      const result = validator.validate(config, 'database');
      expect(result.valid).toBe(false);
      expect(result.errors?.[0].path).toBe('/port');
    });
    
    it('should require SSL in production', () => {
      const config = {
        host: 'prod-db.example.com',
        port: 5432,
        database: 'myapp',
        user: 'dbuser',
        password: 'securepassword123',
        ssl: { enabled: false }
      };
      
      const envValidator = new EnvironmentValidator();
      const violations = envValidator.validate_config(config, 'production');
      
      expect(violations).toContainEqual(
        expect.objectContaining({
          rule: 'ssl_required_in_production'
        })
      );
    });
  });
  
  describe('API Configuration', () => {
    it('should validate CORS settings', () => {
      const config = {
        server: {
          port: 3000,
          cors: {
            enabled: true,
            origins: ['https://example.com', 'https://app.example.com'],
            credentials: true
          }
        },
        auth: {
          jwt: {
            secret: 'a'.repeat(32),
            expiresIn: '1h',
            algorithm: 'HS256'
          }
        }
      };
      
      const result = validator.validate(config, 'api');
      expect(result.valid).toBe(true);
    });
    
    it('should reject short JWT secrets', () => {
      const config = {
        server: { port: 3000 },
        auth: {
          jwt: {
            secret: 'tooshort',
            expiresIn: '1h'
          }
        }
      };
      
      const result = validator.validate(config, 'api');
      expect(result.valid).toBe(false);
      expect(result.errors?.[0].path).toBe('/auth/jwt/secret');
    });
  });
});

5. Runtime Configuration Validation

Implement runtime validation and hot-reloading:

Runtime Config Validator

// runtime-validator.ts
import { EventEmitter } from 'events';
import * as chokidar from 'chokidar';
import { ConfigValidator } from './config-validator';

export class RuntimeConfigValidator extends EventEmitter {
  private validator: ConfigValidator;
  private currentConfig: any;
  private watchers: Map<string, chokidar.FSWatcher> = new Map();
  private validationCache: Map<string, ValidationResult> = new Map();
  
  constructor() {
    super();
    this.validator = new ConfigValidator();
  }
  
  async initialize(configPath: string): Promise<void> {
    // Load initial config
    this.currentConfig = await this.loadAndValidate(configPath);
    
    // Setup file watcher for hot-reloading
    this.watchConfig(configPath);
  }
  
  private async loadAndValidate(configPath: string): Promise<any> {
    try {
      // Load config
      const config = await this.loadConfig(configPath);
      
      // Validate config
      const validationResult = this.validator.validate(
        config,
        this.detectEnvironment()
      );
      
      if (!validationResult.valid) {
        this.emit('validation:error', {
          path: configPath,
          errors: validationResult.errors
        });
        
        // In development, log errors but continue
        if (this.isDevelopment()) {
          console.error('Configuration validation errors:', validationResult.errors);
          return config;
        }
        
        // In production, throw error
        throw new ConfigValidationError(
          'Configuration validation failed',
          validationResult.errors
        );
      }
      
      this.emit('validation:success', { path: configPath });
      return config;
    } catch (error) {
      this.emit('validation:error', { path: configPath, error });
      throw error;
    }
  }
  
  private watchConfig(configPath: string): void {
    const watcher = chokidar.watch(configPath, {
      persistent: true,
      ignoreInitial: true
    });
    
    watcher.on('change', async () => {
      console.log(`Configuration file changed: ${configPath}`);
      
      try {
        const newConfig = await this.loadAndValidate(configPath);
        
        // Check if config actually changed
        if (JSON.stringify(newConfig) !== JSON.stringify(this.currentConfig)) {
          const oldConfig = this.currentConfig;
          this.currentConfig = newConfig;
          
          this.emit('config:changed', {
            oldConfig,
            newConfig,
            changedKeys: this.findChangedKeys(oldConfig, newConfig)
          });
        }
      } catch (error) {
        this.emit('config:error', { error });
      }
    });
    
    this.watchers.set(configPath, watcher);
  }
  
  private findChangedKeys(oldConfig: any, newConfig: any): string[] {
    const changed: string[] = [];
    
    const findDiff = (old: any, new_: any, path: string = '') => {
      // Check all keys in old config
      for (const key in old) {
        const currentPath = path ? `${path}.${key}` : key;
        
        if (!(key in new_)) {
          changed.push(`${currentPath} (removed)`);
        } else if (typeof old[key] === 'object' && typeof new_[key] === 'object') {
          findDiff(old[key], new_[key], currentPath);
        } else if (old[key] !== new_[key]) {
          changed.push(currentPath);
        }
      }
      
      // Check for new keys
      for (const key in new_) {
        if (!(key in old)) {
          const currentPath = path ? `${path}.${key}` : key;
          changed.push(`${currentPath} (added)`);
        }
      }
    };
    
    findDiff(oldConfig, newConfig);
    return changed;
  }
  
  validateValue(path: string, value: any): ValidationResult {
    // Use cached schema for performance
    const cacheKey = `${path}:${JSON.stringify(value)}`;
    if (this.validationCache.has(cacheKey)) {
      return this.validationCache.get(cacheKey)!;
    }
    
    // Extract schema for specific path
    const schema = this.getSchemaForPath(path);
    if (!schema) {
      return { valid: true }; // No schema defined for this path
    }
    
    const result = this.validator.validateValue(value, schema);
    this.validationCache.set(cacheKey, result);
    
    return result;
  }
  
  async shutdown(): Promise<void> {
    // Close all watchers
    for (const watcher of this.watchers.values()) {
      await watcher.close();
    }
    
    this.watchers.clear();
    this.validationCache.clear();
  }
}

// Type-safe configuration access
export class TypedConfig<T> {
  constructor(
    private config: T,
    private validator: RuntimeConfigValidator
  ) {}
  
  get<K extends keyof T>(key: K): T[K] {
    const value = this.config[key];
    
    // Validate on access in development
    if (process.env.NODE_ENV === 'development') {
      const result = this.validator.validateValue(String(key), value);
      if (!result.valid) {
        console.warn(`Invalid config value for ${String(key)}:`, result.errors);
      }
    }
    
    return value;
  }
  
  getOrDefault<K extends keyof T>(key: K, defaultValue: T[K]): T[K] {
    return this.config[key] ?? defaultValue;
  }
  
  require<K extends keyof T>(key: K): NonNullable<T[K]> {
    const value = this.config[key];
    
    if (value === null || value === undefined) {
      throw new Error(`Required configuration '${String(key)}' is missing`);
    }
    
    return value as NonNullable<T[K]>;
  }
}

6. Configuration Migration

Implement configuration migration and versioning:

Config Migration System

# config_migration.py
from typing import Dict, List, Callable, Any
from abc import ABC, abstractmethod
import semver

class ConfigMigration(ABC):
    """Base class for configuration migrations"""
    
    @property
    @abstractmethod
    def version(self) -> str:
        """Target version for this migration"""
        pass
    
    @property
    @abstractmethod
    def description(self) -> str:
        """Description of what this migration does"""
        pass
    
    @abstractmethod
    def up(self, config: Dict) -> Dict:
        """Apply migration to config"""
        pass
    
    @abstractmethod
    def down(self, config: Dict) -> Dict:
        """Revert migration from config"""
        pass
    
    def validate(self, config: Dict) -> bool:
        """Validate config after migration"""
        return True

class ConfigMigrator:
    def __init__(self):
        self.migrations: List[ConfigMigration] = []
    
    def register_migration(self, migration: ConfigMigration):
        """Register a migration"""
        self.migrations.append(migration)
        # Sort by version
        self.migrations.sort(key=lambda m: semver.VersionInfo.parse(m.version))
    
    def migrate(self, config: Dict, target_version: str) -> Dict:
        """Migrate config to target version"""
        current_version = config.get('_version', '0.0.0')
        
        if semver.compare(current_version, target_version) == 0:
            return config  # Already at target version
        
        if semver.compare(current_version, target_version) > 0:
            # Downgrade
            return self._downgrade(config, current_version, target_version)
        else:
            # Upgrade
            return self._upgrade(config, current_version, target_version)
    
    def _upgrade(self, config: Dict, from_version: str, to_version: str) -> Dict:
        """Upgrade config from one version to another"""
        result = config.copy()
        
        for migration in self.migrations:
            if (semver.compare(migration.version, from_version) > 0 and
                semver.compare(migration.version, to_version) <= 0):
                
                print(f"Applying migration to v{migration.version}: {migration.description}")
                result = migration.up(result)
                
                if not migration.validate(result):
                    raise ValueError(f"Migration to v{migration.version} failed validation")
                
                result['_version'] = migration.version
        
        return result
    
    def _downgrade(self, config: Dict, from_version: str, to_version: str) -> Dict:
        """Downgrade config from one version to another"""
        result = config.copy()
        
        # Apply migrations in reverse order
        for migration in reversed(self.migrations):
            if (semver.compare(migration.version, to_version) > 0 and
                semver.compare(migration.version, from_version) <= 0):
                
                print(f"Reverting migration from v{migration.version}: {migration.description}")
                result = migration.down(result)
                
                # Update version to previous migration's version
                prev_version = self._get_previous_version(migration.version)
                result['_version'] = prev_version
        
        return result
    
    def _get_previous_version(self, version: str) -> str:
        """Get the version before the given version"""
        for i, migration in enumerate(self.migrations):
            if migration.version == version:
                return self.migrations[i-1].version if i > 0 else '0.0.0'
        return '0.0.0'

# Example migrations
class MigrationV1_0_0(ConfigMigration):
    @property
    def version(self) -> str:
        return '1.0.0'
    
    @property
    def description(self) -> str:
        return 'Initial configuration structure'
    
    def up(self, config: Dict) -> Dict:
        # Add default structure
        return {
            '_version': '1.0.0',
            'app': config.get('app', {}),
            'database': config.get('database', {}),
            'logging': config.get('logging', {'level': 'info'})
        }
    
    def down(self, config: Dict) -> Dict:
        # Remove version info
        result = config.copy()
        result.pop('_version', None)
        return result

class MigrationV1_1_0(ConfigMigration):
    @property
    def version(self) -> str:
        return '1.1.0'
    
    @property
    def description(self) -> str:
        return 'Split database config into read/write connections'
    
    def up(self, config: Dict) -> Dict:
        result = config.copy()
        
        # Transform single database config to read/write split
        if 'database' in result and not isinstance(result['database'], dict):
            old_db = result['database']
            result['database'] = {
                'write': old_db,
                'read': old_db.copy()  # Same as write initially
            }
        
        return result
    
    def down(self, config: Dict) -> Dict:
        result = config.copy()
        
        # Revert to single database config
        if 'database' in result and 'write' in result['database']:
            result['database'] = result['database']['write']
        
        return result

class MigrationV2_0_0(ConfigMigration):
    @property
    def version(self) -> str:
        return '2.0.0'
    
    @property
    def description(self) -> str:
        return 'Add security configuration section'
    
    def up(self, config: Dict) -> Dict:
        result = config.copy()
        
        # Add security section with defaults
        if 'security' not in result:
            result['security'] = {
                'encryption': {
                    'enabled': True,
                    'algorithm': 'AES-256-GCM'
                },
                'tls': {
                    'minVersion': '1.2',
                    'ciphers': ['TLS_AES_256_GCM_SHA384', 'TLS_AES_128_GCM_SHA256']
                }
            }
        
        return result
    
    def down(self, config: Dict) -> Dict:
        result = config.copy()
        result.pop('security', None)
        return result

7. Configuration Security

Implement secure configuration handling:

Secure Config Manager

// secure-config.ts
import * as crypto from 'crypto';
import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
import { KeyVaultSecret, SecretClient } from '@azure/keyvault-secrets';

interface EncryptedValue {
  encrypted: true;
  value: string;
  algorithm: string;
  iv: string;
  authTag?: string;
}

export class SecureConfigManager {
  private secretsCache: Map<string, any> = new Map();
  private encryptionKey: Buffer;
  
  constructor(private options: SecureConfigOptions) {
    this.encryptionKey = this.deriveKey(options.masterKey);
  }
  
  private deriveKey(masterKey: string): Buffer {
    return crypto.pbkdf2Sync(masterKey, 'config-salt', 100000, 32, 'sha256');
  }
  
  encrypt(value: any): EncryptedValue {
    const algorithm = 'aes-256-gcm';
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv(algorithm, this.encryptionKey, iv);
    
    const stringValue = JSON.stringify(value);
    let encrypted = cipher.update(stringValue, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    
    const authTag = cipher.getAuthTag();
    
    return {
      encrypted: true,
      value: encrypted,
      algorithm,
      iv: iv.toString('hex'),
      authTag: authTag.toString('hex')
    };
  }
  
  decrypt(encryptedValue: EncryptedValue): any {
    const decipher = crypto.createDecipheriv(
      encryptedValue.algorithm,
      this.encryptionKey,
      Buffer.from(encryptedValue.iv, 'hex')
    );
    
    if (encryptedValue.authTag) {
      decipher.setAuthTag(Buffer.from(encryptedValue.authTag, 'hex'));
    }
    
    let decrypted = decipher.update(encryptedValue.value, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    
    return JSON.parse(decrypted);
  }
  
  async processConfig(config: any): Promise<any> {
    const processed = {};
    
    for (const [key, value] of Object.entries(config)) {
      if (this.isEncryptedValue(value)) {
        // Decrypt encrypted values
        processed[key] = this.decrypt(value as EncryptedValue);
      } else if (this.isSecretReference(value)) {
        // Fetch from secret manager
        processed[key] = await this.fetchSecret(value as string);
      } else if (typeof value === 'object' && value !== null) {
        // Recursively process nested objects
        processed[key] = await this.processConfig(value);
      } else {
        processed[key] = value;
      }
    }
    
    return processed;
  }
  
  private isEncryptedValue(value: any): boolean {
    return typeof value === 'object' && 
           value !== null && 
           value.encrypted === true;
  }
  
  private isSecretReference(value: any): boolean {
    return typeof value === 'string' && 
           (value.startsWith('secret://') || 
            value.startsWith('vault://') ||
            value.startsWith('aws-secret://'));
  }
  
  private async fetchSecret(reference: string): Promise<any> {
    // Check cache first
    if (this.secretsCache.has(reference)) {
      return this.secretsCache.get(reference);
    }
    
    let secretValue: any;
    
    if (reference.startsWith('secret://')) {
      // Google Secret Manager
      secretValue = await this.fetchGoogleSecret(reference);
    } else if (reference.startsWith('vault://')) {
      // Azure Key Vault
      secretValue = await this.fetchAzureSecret(reference);
    } else if (reference.startsWith('aws-secret://')) {
      // AWS Secrets Manager
      secretValue = await this.fetchAWSSecret(reference);
    }
    
    // Cache the secret
    this.secretsCache.set(reference, secretValue);
    
    return secretValue;
  }
  
  private async fetchGoogleSecret(reference: string): Promise<any> {
    const secretName = reference.replace('secret://', '');
    const client = new SecretManagerServiceClient();
    
    const [version] = await client.accessSecretVersion({
      name: `projects/${this.options.gcpProject}/secrets/${secretName}/versions/latest`
    });
    
    const payload = version.payload?.data;
    if (!payload) {
      throw new Error(`Secret ${secretName} has no payload`);
    }
    
    return JSON.parse(payload.toString());
  }
  
  validateSecureConfig(config: any): ValidationResult {
    const errors: string[] = [];
    
    const checkSecrets = (obj: any, path: string = '') => {
      for (const [key, value] of Object.entries(obj)) {
        const currentPath = path ? `${path}.${key}` : key;
        
        // Check for plaintext secrets
        if (this.looksLikeSecret(key) && typeof value === 'string') {
          if (!this.isEncryptedValue(value) && !this.isSecretReference(value)) {
            errors.push(`Potential plaintext secret at ${currentPath}`);
          }
        }
        
        // Recursively check nested objects
        if (typeof value === 'object' && value !== null && !this.isEncryptedValue(value)) {
          checkSecrets(value, currentPath);
        }
      }
    };
    
    checkSecrets(config);
    
    return {
      valid: errors.length === 0,
      errors: errors.map(message => ({
        path: '',
        message,
        keyword: 'security',
        params: {}
      }))
    };
  }
  
  private looksLikeSecret(key: string): boolean {
    const secretPatterns = [
      'password', 'secret', 'key', 'token', 'credential',
      'api_key', 'apikey', 'private_key', 'auth'
    ];
    
    const lowerKey = key.toLowerCase();
    return secretPatterns.some(pattern => lowerKey.includes(pattern));
  }
}

8. Configuration Documentation

Generate configuration documentation:

Config Documentation Generator

# config_docs_generator.py
from typing import Dict, List, Any
import json
import yaml

class ConfigDocGenerator:
    def generate_docs(self, schema: Dict, examples: Dict) -> str:
        """Generate comprehensive configuration documentation"""
        docs = ["# Configuration Reference\n"]
        
        # Add overview
        docs.append("## Overview\n")
        docs.append("This document describes all available configuration options.\n")
        
        # Add table of contents
        docs.append("## Table of Contents\n")
        toc = self._generate_toc(schema.get('properties', {}))
        docs.extend(toc)
        
        # Add configuration sections
        docs.append("\n## Configuration Options\n")
        sections = self._generate_sections(schema.get('properties', {}), examples)
        docs.extend(sections)
        
        # Add examples
        docs.append("\n## Complete Examples\n")
        docs.extend(self._generate_examples(examples))
        
        # Add validation rules
        docs.append("\n## Validation Rules\n")
        docs.extend(self._generate_validation_rules(schema))
        
        return '\n'.join(docs)
    
    def _generate_sections(self, properties: Dict, examples: Dict, level: int = 3) -> List[str]:
        """Generate documentation for each configuration section"""
        sections = []
        
        for prop_name, prop_schema in properties.items():
            # Section header
            sections.append(f"{'#' * level} {prop_name}\n")
            
            # Description
            if 'description' in prop_schema:
                sections.append(f"{prop_schema['description']}\n")
            
            # Type information
            sections.append(f"**Type:** `{prop_schema.get('type', 'any')}`\n")
            
            # Required
            if prop_name in prop_schema.get('required', []):
                sections.append("**Required:** Yes\n")
            
            # Default value
            if 'default' in prop_schema:
                sections.append(f"**Default:** `{json.dumps(prop_schema['default'])}`\n")
            
            # Validation constraints
            constraints = self._extract_constraints(prop_schema)
            if constraints:
                sections.append("**Constraints:**")
                for constraint in constraints:
                    sections.append(f"- {constraint}")
                sections.append("")
            
            # Example
            if prop_name in examples:
                sections.append("**Example:**")
                sections.append("```yaml")
                sections.append(yaml.dump({prop_name: examples[prop_name]}, default_flow_style=False))
                sections.append("```\n")
            
            # Nested properties
            if prop_schema.get('type') == 'object' and 'properties' in prop_schema:
                nested = self._generate_sections(
                    prop_schema['properties'],
                    examples.get(prop_name, {}),
                    level + 1
                )
                sections.extend(nested)
        
        return sections
    
    def _extract_constraints(self, schema: Dict) -> List[str]:
        """Extract validation constraints from schema"""
        constraints = []
        
        if 'enum' in schema:
            constraints.append(f"Must be one of: {', '.join(map(str, schema['enum']))}")
        
        if 'minimum' in schema:
            constraints.append(f"Minimum value: {schema['minimum']}")
        
        if 'maximum' in schema:
            constraints.append(f"Maximum value: {schema['maximum']}")
        
        if 'minLength' in schema:
            constraints.append(f"Minimum length: {schema['minLength']}")
        
        if 'maxLength' in schema:
            constraints.append(f"Maximum length: {schema['maxLength']}")
        
        if 'pattern' in schema:
            constraints.append(f"Must match pattern: `{schema['pattern']}`")
        
        if 'format' in schema:
            constraints.append(f"Format: {schema['format']}")
        
        return constraints

# Generate interactive config builder
class InteractiveConfigBuilder:
    def generate_html_builder(self, schema: Dict) -> str:
        """Generate interactive HTML configuration builder"""
        html = """
<!DOCTYPE html>
<html>
<head>
    <title>Configuration Builder</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .config-section { margin: 20px 0; padding: 20px; border: 1px solid #ddd; }
        .config-field { margin: 10px 0; }
        label { display: inline-block; width: 200px; font-weight: bold; }
        input, select { width: 300px; padding: 5px; }
        .error { color: red; font-size: 12px; }
        .preview { background: #f5f5f5; padding: 20px; margin-top: 20px; }
        pre { background: white; padding: 10px; overflow-x: auto; }
    </style>
</head>
<body>
    <h1>Configuration Builder</h1>
    <div id="config-form"></div>
    <button onclick="validateConfig()">Validate</button>
    <button onclick="exportConfig()">Export</button>
    
    <div class="preview">
        <h2>Preview</h2>
        <pre id="config-preview"></pre>
    </div>
    
    <script>
        const schema = """ + json.dumps(schema) + """;
        
        function buildForm() {
            const container = document.getElementById('config-form');
            container.innerHTML = renderSchema(schema.properties);
        }
        
        function renderSchema(properties, prefix = '') {
            let html = '';
            
            for (const [key, prop] of Object.entries(properties)) {
                const fieldId = prefix ? `${prefix}.${key}` : key;
                
                html += '<div class="config-field">';
                html += `<label for="${fieldId}">${key}:</label>`;
                
                if (prop.enum) {
                    html += `<select id="${fieldId}" onchange="updatePreview()">`;
                    for (const option of prop.enum) {
                        html += `<option value="${option}">${option}</option>`;
                    }
                    html += '</select>';
                } else if (prop.type === 'boolean') {
                    html += `<input type="checkbox" id="${fieldId}" onchange="updatePreview()">`;
                } else if (prop.type === 'number' || prop.type === 'integer') {
                    html += `<input type="number" id="${fieldId}" onchange="updatePreview()">`;
                } else {
                    html += `<input type="text" id="${fieldId}" onchange="updatePreview()">`;
                }
                
                html += `<div class="error" id="${fieldId}-error"></div>`;
                html += '</div>';
                
                if (prop.type === 'object' && prop.properties) {
                    html += '<div class="config-section">';
                    html += renderSchema(prop.properties, fieldId);
                    html += '</div>';
                }
            }
            
            return html;
        }
        
        function updatePreview() {
            const config = buildConfig();
            document.getElementById('config-preview').textContent = 
                JSON.stringify(config, null, 2);
        }
        
        function buildConfig() {
            // Build configuration from form values
            const config = {};
            // Implementation here
            return config;
        }
        
        function validateConfig() {
            // Validate against schema
            const config = buildConfig();
            // Implementation here
        }
        
        function exportConfig() {
            const config = buildConfig();
            const blob = new Blob([JSON.stringify(config, null, 2)], 
                                 {type: 'application/json'});
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = 'config.json';
            a.click();
        }
        
        // Initialize
        buildForm();
        updatePreview();
    </script>
</body>
</html>
        """
        return html

Output Format

  1. Configuration Analysis: Current configuration assessment
  2. Validation Schemas: JSON Schema definitions for all configs
  3. Environment Rules: Environment-specific validation rules
  4. Test Suite: Comprehensive configuration tests
  5. Migration Scripts: Version migration implementations
  6. Security Report: Security issues and recommendations
  7. Documentation: Auto-generated configuration reference
  8. Validation Pipeline: CI/CD integration for config validation
  9. Interactive Tools: Configuration builders and validators

Focus on preventing configuration errors, ensuring consistency across environments, and maintaining security best practices.