# RedFlag Agent Command Handling System - Architecture Analysis ## Executive Summary The agent implements a modular but **primarily monolithic** scanning architecture. While scanner implementations are isolated into separate files, the orchestration of scanning (the `handleScanUpdates` function) is a large, tightly-coupled function that combines all subsystems in a single control flow. Storage and system info gathering are separate, but not formally separated as distinct subsystems that can be independently managed. --- ## 1. Command Processing Pipeline ### Entry Point: Main Agent Loop **File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/cmd/agent/main.go` **Lines**: 410-549 (main check-in loop) The agent continuously loops, checking in with the server and processing commands: ```go for { // Lines 412-414: Add jitter jitter := time.Duration(rand.Intn(30)) * time.Second time.Sleep(jitter) // Lines 417-425: System info update every hour if time.Since(lastSystemInfoUpdate) >= systemInfoUpdateInterval { // Call reportSystemInfo() } // Lines 465-490: GetCommands from server with optional metrics commands, err := apiClient.GetCommands(cfg.AgentID, metrics) // Lines 499-544: Switch on command type for _, cmd := range commands { switch cmd.Type { case "scan_updates": handleScanUpdates(...) case "collect_specs": case "dry_run_update": case "install_updates": case "confirm_dependencies": case "enable_heartbeat": case "disable_heartbeat": case "reboot": } } // Line 547: Wait for next check-in time.Sleep(...) } ``` ### Command Types Supported 1. **scan_updates** - Main focus (lines 503-506) 2. collect_specs (not implemented) 3. dry_run_update (lines 511-514) 4. install_updates (lines 516-519) 5. confirm_dependencies (lines 521-524) 6. enable_heartbeat (lines 526-529) 7. disable_heartbeat (lines 531-534) 8. reboot (lines 537-540) --- ## 2. MONOLITHIC scan_updates Implementation ### Location and Size **File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/cmd/agent/main.go` **Function**: `handleScanUpdates()` **Lines**: 551-709 (159 lines) ### The Monolith Problem The function is a **single, large, sequential orchestrator** that tightly couples all scanning subsystems: ``` handleScanUpdates() ├─ APT Scanner (lines 559-574) │ ├─ IsAvailable() check │ ├─ Scan() │ └─ Error handling + accumulation │ ├─ DNF Scanner (lines 576-592) │ ├─ IsAvailable() check │ ├─ Scan() │ └─ Error handling + accumulation │ ├─ Docker Scanner (lines 594-610) │ ├─ IsAvailable() check │ ├─ Scan() │ └─ Error handling + accumulation │ ├─ Windows Update Scanner (lines 612-628) │ ├─ IsAvailable() check │ ├─ Scan() │ └─ Error handling + accumulation │ ├─ Winget Scanner (lines 630-646) │ ├─ IsAvailable() check │ ├─ Scan() │ └─ Error handling + accumulation │ ├─ Report Building (lines 648-677) │ ├─ Combine all errors │ ├─ Build scan log report │ └─ Report to server │ └─ Update Reporting (lines 686-708) ├─ Report updates if found └─ Return errors ``` ### Key Issues with Current Architecture 1. **No Abstraction Layer**: Each scanner is called directly with repeated `if available -> scan -> handle error` blocks 2. **Sequential Execution**: All scanners run one-by-one (lines 559-646) - no parallelization 3. **Tight Coupling**: Error handling logic is mixed with business logic 4. **No Subsystem State Management**: Cannot track individual subsystem health or readiness 5. **Repeated Code**: Same pattern repeated 5 times for different scanners **Code Pattern Repetition** (Example - APT): ```go // Lines 559-574: APT pattern if aptScanner.IsAvailable() { log.Println(" - Scanning APT packages...") updates, err := aptScanner.Scan() if err != nil { errorMsg := fmt.Sprintf("APT scan failed: %v", err) log.Printf(" %s\n", errorMsg) scanErrors = append(scanErrors, errorMsg) } else { resultMsg := fmt.Sprintf("Found %d APT updates", len(updates)) log.Printf(" %s\n", resultMsg) scanResults = append(scanResults, resultMsg) allUpdates = append(allUpdates, updates...) } } else { scanResults = append(scanResults, "APT scanner not available") } ``` This exact pattern repeats for DNF, Docker, Windows, and Winget scanners. --- ## 3. Scanner Implementations (Modular) ### 3.1 APT Scanner **File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/scanner/apt.go` **Lines**: 1-91 **Interface Implementation**: - `IsAvailable()` - Checks if `apt` command exists (line 23-26) - `Scan()` - Returns `[]client.UpdateReportItem` (lines 29-42) - `parseAPTOutput()` - Helper function (lines 44-90) **Key Behavior**: - Runs `apt-get update` (optional, line 31) - Runs `apt list --upgradable` (line 35) - Parses output with regex (line 50) - Determines severity based on repository name (lines 69-71) **Severity Logic**: ```go severity := "moderate" if strings.Contains(repository, "security") { severity = "important" } ``` --- ### 3.2 DNF Scanner **File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/scanner/dnf.go` **Lines**: 1-157 **Interface Implementation**: - `IsAvailable()` - Checks if `dnf` command exists (lines 23-26) - `Scan()` - Returns `[]client.UpdateReportItem` (lines 29-43) - `parseDNFOutput()` - Parses output (lines 45-108) - `getInstalledVersion()` - Queries RPM (lines 111-118) - `determineSeverity()` - Complex logic (lines 121-157) **Severity Determination** (lines 121-157): - Security keywords: critical - Kernel updates: important - Core system packages (glibc, systemd, bash): important - Development tools: moderate - Default: low --- ### 3.3 Docker Scanner **File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/scanner/docker.go` **Lines**: 1-163 **Interface Implementation**: - `IsAvailable()` - Checks docker command + daemon ping (lines 34-47) - `Scan()` - Returns `[]client.UpdateReportItem` (lines 50-123) - `checkForUpdate()` - Compare local vs remote digests (lines 137-154) - `Close()` - Close Docker client (lines 157-162) **Key Behavior**: - Lists all containers (line 54) - Gets image inspect details (line 72) - Calls registry client for remote digest (line 86) - Compares digest hashes to detect updates (line 151) **RegistryClient Subsystem** (registry.go, lines 1-260): - Handles Docker Registry HTTP API v2 - Caches manifest responses (5 min TTL) - Parses image names into registry/repository - Gets authentication tokens for Docker Hub - Supports manifest digest extraction --- ### 3.4 Windows Update Scanner (WUA API) **File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/scanner/windows_wua.go` **Lines**: 1-553 **Interface Implementation**: - `IsAvailable()` - Returns true only on Windows (lines 27-30) - `Scan()` - Returns `[]client.UpdateReportItem` (lines 33-67) - Windows-specific COM integration (lines 38-43) - Conversion methods (lines 70-211) **Key Behavior**: - Initializes COM for Windows Update Agent API (lines 38-43) - Creates update session and searcher (lines 46-55) - Searches with criteria: `"IsInstalled=0 AND IsHidden=0"` (line 58) - Converts WUA results with rich metadata (lines 90-211) **Metadata Extraction** (lines 112-186): - KB articles - Update identity - Security bulletins (includes CVEs) - MSRC severity - Download size - Deployment dates - More info URLs - Release notes - Categories **Severity Mapping** (lines 463-479): - MSRC critical/important → critical - MSRC moderate → moderate - MSRC low → low --- ### 3.5 Winget Scanner **File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/scanner/winget.go` **Lines**: 1-662 **Interface Implementation**: - `IsAvailable()` - Windows-only, checks winget command (lines 34-43) - `Scan()` - Multi-method with fallbacks (lines 46-84) - Multiple scan methods for resilience (lines 87-178) - Package parsing (lines 279-508) **Key Behavior - Multiple Scan Methods**: 1. **Method 1**: `scanWithJSON()` - Primary, JSON output (lines 87-122) 2. **Method 2**: `scanWithBasicOutput()` - Fallback, text parsing (lines 125-134) 3. **Method 3**: `attemptWingetRecovery()` - Recovery procedures (lines 533-576) **Recovery Procedures** (lines 533-576): - Reset winget sources - Update winget itself - Repair Windows App Installer - Scan with admin privileges **Severity Determination** (lines 324-371): - Security tools: critical - Browsers/communication: high - Development tools: moderate - Microsoft Store apps: low - Default: moderate **Package Categorization** (lines 374-484): - Development, Security, Browser, Communication, Media, Productivity, Utility, Gaming, Application --- ## 4. System Info and Storage Integration ### 4.1 System Info Collection **File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/system/info.go` **Lines**: 1-100+ (first 100 shown) **SystemInfo Structure** (lines 13-28): ```go type SystemInfo struct { Hostname string OSType string OSVersion string OSArchitecture string AgentVersion string IPAddress string CPUInfo CPUInfo MemoryInfo MemoryInfo DiskInfo []DiskInfo // MODULAR: Multiple disks! RunningProcesses int Uptime string RebootRequired bool RebootReason string Metadata map[string]string } ``` **DiskInfo Structure** (lines 45-57): ```go type DiskInfo struct { Mountpoint string Total uint64 Available uint64 Used uint64 UsedPercent float64 Filesystem string IsRoot bool // Primary system disk IsLargest bool // Largest storage disk DiskType string // SSD, HDD, NVMe, etc. Device string // Block device name } ``` ### 4.2 System Info Reporting **File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/cmd/agent/main.go` **Function**: `reportSystemInfo()` **Lines**: 1357-1407 **Reporting Frequency**: - Lines 407-408: `const systemInfoUpdateInterval = 1 * time.Hour` - Lines 417-425: Updates hourly during main loop **What Gets Reported**: - CPU model, cores, threads - Memory total/used/percent - Disk total/used/percent (primary disk) - IP address - Process count - Uptime - OS type/version/architecture - All metadata from SystemInfo ### 4.3 Local Cache Subsystem **File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/cache/local.go` **Key Functions**: - `Load()` - Load cache from disk - `UpdateScanResults()` - Store latest scan results - `SetAgentInfo()` - Store agent metadata - `SetAgentStatus()` - Update status - `Save()` - Persist cache to disk --- ## 5. Lightweight Metrics vs Full System Info ### 5.1 Lightweight Metrics (Every Check-in) **File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/cmd/agent/main.go` **Lines**: 429-444 **What Gets Collected Every Check-in**: ```go sysMetrics, err := system.GetLightweightMetrics() if err == nil { metrics = &client.SystemMetrics{ CPUPercent: sysMetrics.CPUPercent, MemoryPercent: sysMetrics.MemoryPercent, MemoryUsedGB: sysMetrics.MemoryUsedGB, MemoryTotalGB: sysMetrics.MemoryTotalGB, DiskUsedGB: sysMetrics.DiskUsedGB, DiskTotalGB: sysMetrics.DiskTotalGB, DiskPercent: sysMetrics.DiskPercent, Uptime: sysMetrics.Uptime, Version: AgentVersion, } } ``` ### 5.2 Full System Info (Hourly) **Lines**: 417-425 + reportSystemInfo function **Difference**: Full info includes CPU model, detailed disk info, process count, IP address, and more detailed metadata --- ## 6. Current Modularity Assessment ### Modular (Good): 1. **Scanner Implementations**: Each scanner is a separate file with its own logic 2. **Registry Client**: Docker registry communication is separated 3. **System Info**: Platform-specific implementations split (windows.go, windows_stub.go, windows_wua.go, etc.) 4. **Installers**: Separate installer implementations per package type 5. **Local Cache**: Separate subsystem for caching ### Monolithic (Bad): 1. **handleScanUpdates()**: Tight coupling of all scanners in one function 2. **Command Processing**: All command types in a single switch statement 3. **Error Aggregation**: No formal error handling subsystem; just accumulates strings 4. **No Subsystem Health Tracking**: Can't individually monitor scanner status 5. **No Parallelization**: Scanners run sequentially, wasting time 6. **Logging Mixed with Logic**: Log statements interleaved with business logic --- ## 7. Key Data Flow Paths ### Path 1: scan_updates Command ``` GetCommands() ↓ switch cmd.Type == "scan_updates" ↓ handleScanUpdates() ├─ aptScanner.Scan() → UpdateReportItem[] ├─ dnfScanner.Scan() → UpdateReportItem[] ├─ dockerScanner.Scan() → UpdateReportItem[] (includes registryClient) ├─ windowsUpdateScanner.Scan() → UpdateReportItem[] ├─ wingetScanner.Scan() → UpdateReportItem[] (with recovery procedures) ├─ Combine all updates ├─ ReportLog() [scan summary] └─ ReportUpdates() [actual updates] ``` ### Path 2: Local Scan via CLI **Lines**: 712-805, `handleScanCommand()` - Same scanner initialization and execution - Save results to cache - Display via display.PrintScanResults() ### Path 3: System Metrics Reporting ``` Main Loop (every check-in) ├─ GetLightweightMetrics() [every 5-300 sec] └─ Every hour: ├─ GetSystemInfo() [detailed] ├─ ReportSystemInfo() [to server] ``` --- ## 8. File Structure Summary ### Core Agent ``` aggregator-agent/ ├── cmd/agent/ │ └── main.go [ENTRY POINT - 1510 lines] │ ├─ registerAgent() [266-348] │ ├─ runAgent() [387-549] [MAIN LOOP] │ ├─ handleScanUpdates() [551-709] [MONOLITHIC] │ ├─ handleScanCommand() [712-805] │ ├─ handleStatusCommand() [808-846] │ ├─ handleListUpdatesCommand() [849-871] │ ├─ handleInstallUpdates() [873-989] │ ├─ handleDryRunUpdate() [992-1105] │ ├─ handleConfirmDependencies() [1108-1216] │ ├─ handleEnableHeartbeat() [1219-1291] │ ├─ handleDisableHeartbeat() [1294-1355] │ ├─ reportSystemInfo() [1357-1407] │ └─ handleReboot() [1410-1495] ``` ### Scanners ``` internal/scanner/ ├── apt.go [91 lines] - APT package manager ├── dnf.go [157 lines] - DNF/RPM package manager ├── docker.go [163 lines] - Docker image scanning ├── registry.go [260 lines] - Docker Registry API client ├── windows.go [Stub for non-Windows] ├── windows_wua.go [553 lines] - Windows Update Agent API ├── winget.go [662 lines] - Windows package manager └── windows_override.go [Overrides for Windows builds] ``` ### System & Supporting ``` internal/ ├── system/ │ ├── info.go [100+ lines] - System information gathering │ └── windows.go [Windows-specific system info] ├── cache/ │ └── local.go [Local caching of scan results] ├── client/ │ └── client.go [API communication] ├── config/ │ └── config.go [Configuration management] ├── installer/ │ ├── installer.go [Factory pattern] │ ├── apt.go │ ├── dnf.go │ ├── docker.go │ ├── windows.go │ └── winget.go ├── service/ │ ├── service_stub.go │ └── windows.go [Windows service management] └── display/ └── terminal.go [Terminal display utilities] ``` --- ## 9. Summary of Architecture Findings ### Subsystems Included in scan_updates 1. **APT Scanner** - Linux Debian/Ubuntu package updates 2. **DNF Scanner** - Linux Fedora/RHEL package updates 3. **Docker Scanner** - Container image updates (with Registry subsystem) 4. **Windows Update Scanner** - Windows OS updates (WUA API) 5. **Winget Scanner** - Windows application updates ### Integration Model **Not a subsystem architecture**, but rather: - **Sequential execution** of isolated scanner modules - **Error accumulation** without formal subsystem health tracking - **Sequential reporting** - all errors reported together at end - **No dependency management** between subsystems - **No resource pooling** (each scanner creates its own connections) ### Monolithic Aspects The `handleScanUpdates()` function exhibits monolithic characteristics: - Single responsibility is violated (orchestrates 5+ distinct scanning systems) - Tight coupling between orchestrator and scanners - Repeated code patterns suggest missing abstraction - No separation of concerns between: - Scanner availability checking - Actual scanning - Error handling - Result aggregation - Reporting ### Modular Aspects The individual scanner implementations ARE modular: - Each scanner has own file - Each implements common interface (IsAvailable, Scan) - Each scanner logic is isolated - Registry client is separated from Docker scanner - Platform-specific code is separated (windows_wua.go vs windows.go stub) --- ## Recommendations for Refactoring If modularity/subsystem architecture is desired: 1. **Create ScannerRegistry/Factory** - Manage scanner lifecycle 2. **Extract orchestration logic** - Create ScanOrchestrator interface 3. **Implement health tracking** - Track subsystem readiness 4. **Enable parallelization** - Run scanners concurrently 5. **Formal error handling** - Per-subsystem error types 6. **Dependency injection** - Inject scanners into handlers 7. **Configuration per subsystem** - Enable/disable individual scanners 8. **Metrics/observability** - Track scan duration, success rate per subsystem