YAML Bulletproofing Proposal: Making packages.yaml 100% Reliable

Executive Summary

This proposal outlines a comprehensive plan to bulletproof the existing packages.yaml configuration system. Rather than replacing YAML (which is the right choice), we’ll add robust validation, safety checks, and error recovery to eliminate potential failure points.

Current State Analysis

Strengths âś…

  • Well-Structured: Clear hierarchical organization (core → system → desktop → development → media)
  • Consistent Format: All modules follow the same pattern (packages, configs, commands, depends, start, end, type)
  • Working Validation: Basic field validation exists in source/config/loader.go
  • Dependency System: Modules can depend on other modules

Vulnerabilities 🚨

  • No Cycle Detection: Circular dependencies could cause infinite loops
  • Unsafe Commands: Raw shell commands without safety validation
  • Missing Package Validation: No check if packages exist before installation
  • Limited Schema Validation: Only basic field presence checks
  • No Error Recovery: Single failure can break entire installation

Bulletproofing Strategy

Phase 1: Critical Safety (HIGH PRIORITY)

1.1 Dependency Cycle Detection

Problem: Module A depends on B, B depends on C, C depends on A = infinite loop Solution: Graph traversal algorithm to detect cycles before execution

// NEW: source/config/dependency_validator.go
func ValidateDependencies(cfg *Config) error {
    allModules := collectAllModules(cfg)

    // Check for missing dependencies
    for moduleID, module := range allModules {
        for _, dep := range module.Depends {
            if _, exists := allModules[dep]; !exists {
                return fmt.Errorf("module %s depends on non-existent module %s", moduleID, dep)
            }
        }
    }

    // Check for circular dependencies using DFS
    for moduleID := range allModules {
        if hasCyclicDependency(moduleID, allModules, make(map[string]bool), make(map[string]bool)) {
            return fmt.Errorf("circular dependency detected involving module %s", moduleID)
        }
    }

    return nil
}

Benefits:

  • Prevents installation deadlocks
  • Clear error messages for dependency issues
  • Validates entire dependency graph before starting

1.2 Command Safety Validation

Problem: Commands like rm -rf / or chmod 777 could be catastrophic Solution: Pattern-based command validation with safety requirements

// NEW: source/config/command_validator.go
var dangerousPatterns = []*regexp.Regexp{
    regexp.MustCompile(`rm\s+-rf\s+/[^h]`),      // Never rm -rf / (except /home/user subdirs)
    regexp.MustCompile(`>\s*/dev/sd[a-z]`),      // Don't write to block devices
    regexp.MustCompile(`dd\s+.*of=/dev/`),       // Dangerous dd commands
    regexp.MustCompile(`chmod\s+777`),           // Overly permissive permissions
    regexp.MustCompile(`\$\(.*curl.*\|.*sh\)`), // Pipe curl to shell
    regexp.MustCompile(`sudo\s+rm\s+-rf\s+/`),  // Sudo + dangerous rm
}

func ValidateCommands(module Module, moduleID string) error {
    for i, cmd := range module.Commands {
        // Check for dangerous patterns
        for _, pattern := range dangerousPatterns {
            if pattern.MatchString(cmd) {
                return fmt.Errorf("module %s command %d contains dangerous pattern: %s",
                    moduleID, i+1, cmd)
            }
        }

        // Require safety patterns for critical operations
        if strings.Contains(cmd, "sudo") && !strings.Contains(cmd, "|| true") && !isSafeCommand(cmd) {
            return fmt.Errorf("module %s command %d uses sudo but lacks safety handling: %s",
                moduleID, i+1, cmd)
        }
    }
    return nil
}

Benefits:

  • Prevents accidental system destruction
  • Enforces “   true” pattern for non-critical sudo commands
  • Blocks known dangerous command patterns

1.3 JSON Schema Validation

Problem: YAML structure errors only caught at runtime Solution: Comprehensive schema validation with clear error messages

// NEW: install/packages.schema.json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "ArchRiot Package Configuration",
  "type": "object",
  "properties": {
    "metadata": {
      "type": "object",
      "properties": {
        "schema_version": { "type": "string" },
        "archriot_version": { "type": "string" },
        "arch_required": { "enum": ["x86_64", "aarch64", "any"] }
      },
      "required": ["schema_version"]
    },
    "core": { "$ref": "#/$defs/moduleCategory" },
    "desktop": { "$ref": "#/$defs/moduleCategory" },
    "system": { "$ref": "#/$defs/moduleCategory" },
    "development": { "$ref": "#/$defs/moduleCategory" },
    "media": { "$ref": "#/$defs/moduleCategory" }
  },
  "$defs": {
    "moduleCategory": {
      "type": "object",
      "patternProperties": {
        "^[a-z][a-z0-9_]*$": { "$ref": "#/$defs/module" }
      },
      "additionalProperties": false
    },
    "module": {
      "type": "object",
      "required": ["start", "end", "type"],
      "properties": {
        "packages": {
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[a-zA-Z0-9._+-]+$",
            "minLength": 1
          }
        },
        "configs": {
          "type": "array",
          "items": {
            "type": "object",
            "required": ["pattern"],
            "properties": {
              "pattern": { "type": "string", "minLength": 1 },
              "target": { "type": "string" },
              "preserve_if_exists": {
                "oneOf": [
                  { "type": "boolean" },
                  { "type": "array", "items": { "type": "string" } }
                ]
              }
            }
          }
        },
        "commands": {
          "type": "array",
          "items": { "type": "string", "minLength": 1 }
        },
        "depends": {
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[a-z]+\\.[a-z][a-z0-9_]*$",
            "description": "Format: category.module (e.g., core.base)"
          }
        },
        "start": { "type": "string", "minLength": 1 },
        "end": { "type": "string", "minLength": 1 },
        "type": {
          "enum": ["Package", "Git", "System", "File", "Module"]
        },
        "critical": {
          "type": "boolean",
          "description": "If true, failure stops entire installation"
        },
        "retry_policy": {
          "type": "object",
          "properties": {
            "max_attempts": { "type": "integer", "minimum": 1, "maximum": 10 },
            "backoff_seconds": { "type": "integer", "minimum": 1, "maximum": 300 }
          }
        }
      },
      "additionalProperties": false
    }
  }
}

Benefits:

  • Catches structural errors before execution
  • Validates package names, dependency format, command syntax
  • Provides clear error messages with line numbers
  • IDE integration for real-time validation

Phase 2: Enhanced Reliability (MEDIUM PRIORITY)

2.1 Package Availability Validation

Problem: Installation fails if packages don’t exist in repos Solution: Pre-validate all packages against pacman/AUR

// NEW: source/config/package_validator.go
func ValidatePackageAvailability(packages []string, moduleID string) error {
    var missing []string

    for _, pkg := range packages {
        if !isPackageAvailable(pkg) {
            missing = append(missing, pkg)
        }
    }

    if len(missing) > 0 {
        return fmt.Errorf("module %s references unavailable packages: %v", moduleID, missing)
    }
    return nil
}

func isPackageAvailable(pkg string) bool {
    // Check official repos first (faster)
    if err := exec.Command("pacman", "-Si", pkg).Run(); err == nil {
        return true
    }

    // Check AUR if yay is available
    if _, err := exec.LookPath("yay"); err == nil {
        if err := exec.Command("yay", "-Si", pkg).Run(); err == nil {
            return true
        }
    }

    return false
}

2.2 Enhanced YAML Structure with Metadata

Enhancement: Add metadata section for versioning and compatibility

# NEW: Enhanced packages.yaml structure
metadata:
  schema_version: "1.0.0"
  archriot_version: ">=2.9.0"
  arch_required: "x86_64"
  last_updated: "2024-12-19"
  validators:
    - dependency_cycles
    - command_safety
    - package_availability
    - schema_compliance

core:
  base:
    packages:
      - base-devel
      - git
    critical: true  # Installation fails if this fails
    retry_policy:
      max_attempts: 3
      backoff_seconds: 5
    # ... rest of existing structure

2.3 Rollback and Recovery Mechanisms

Enhancement: Add rollback commands for safer installations

desktop:
  hyprland:
    packages:
      - hyprland
      - waybar
    rollback_commands:
      - "sudo pacman -Rns --noconfirm hyprland waybar || true"
      - "rm -rf ~/.config/hypr ~/.config/waybar || true"
    # If installation fails, run rollback_commands automatically

Phase 3: Advanced Features (LOW PRIORITY)

3.1 Configuration Drift Detection

Feature: Detect when system configs have changed since installation

3.2 Update Compatibility Checking

Feature: Validate that config updates are compatible with installed versions

3.3 Performance Optimization

Feature: Parallel package installation where dependencies allow

Implementation Plan

Week 1: Core Safety

  • Implement dependency cycle detection
  • Add command safety validation
  • Create JSON schema file
  • Integrate schema validation into loader

Week 2: Enhanced Validation

  • Package availability checking
  • Metadata section implementation
  • Enhanced error messages
  • Comprehensive testing

Week 3: Error Recovery

  • Retry mechanisms
  • Rollback system
  • Critical vs non-critical module handling
  • Documentation updates

Expected Benefits

Reliability Improvements

  • 99%+ Success Rate: Catch issues before they cause failures
  • Zero Infinite Loops: Dependency cycle detection prevents deadlocks
  • Safe Commands: Dangerous operations blocked or require explicit override
  • Clear Error Messages: Know exactly what failed and why

Maintenance Benefits

  • Schema Validation: IDE support with autocomplete and error highlighting
  • Automated Testing: Validate config changes in CI/CD
  • Version Management: Track compatibility and breaking changes
  • Documentation: Self-documenting schema with field descriptions

User Experience

  • Faster Failures: Catch issues in validation phase (seconds) vs installation phase (minutes)
  • Recovery Options: Automatic rollback on failure
  • Progress Visibility: Know if failure is critical or can be skipped

Risk Assessment

Low Risk Changes

  • Dependency validation (read-only analysis)
  • Schema validation (opt-in initially)
  • Enhanced error messages

Medium Risk Changes

  • Command validation (could block legitimate commands)
  • Package availability checking (network dependent)

Mitigation Strategies

  • Gradual Rollout: Implement as warnings first, then errors
  • Override Mechanisms: Allow experienced users to bypass validations
  • Comprehensive Testing: Test on multiple systems before release

Recommendation

PROCEED WITH PHASE 1 - The critical safety features provide huge reliability improvements with minimal risk. Start with dependency cycle detection as it’s completely safe and catches a real failure mode.

This bulletproofing approach keeps the excellent YAML structure while making it production-grade reliable.