package version import ( "fmt" "time" ) // Version coordination for Server Authority model // The server is the single source of truth for all version information // // Version Sources: // - Agent versions: Compiled into agent via ldflags during build (see agent/internal/version) // - Server versions: Compiled into server via ldflags during build (injected below) // - Database: agents table stores agent_version at registration // Build-time injected version information (SERVER AUTHORITY) // Injected by build script during server compilation var ( AgentVersion = "dev" // Server's agent version (format: 0.1.27) ConfigVersion = "dev" // Config schema version (format: 3) MinAgentVersion = "dev" // Minimum supported agent version ) // CurrentVersions holds the authoritative version information for API responses type CurrentVersions struct { AgentVersion string `json:"agent_version"` // e.g., "0.1.27" ConfigVersion string `json:"config_version"` // e.g., "3" MinAgentVersion string `json:"min_agent_version"` // e.g., "0.1.22" BuildTime time.Time `json:"build_time"` } // GetCurrentVersions returns the current version information // Version is compiled into the server binary at build time via ldflags func GetCurrentVersions() CurrentVersions { // Build-time injection allows version updates without code changes // See Dockerfile for injection via: -ldflags "-X .../version.AgentVersion=0.1.27" return CurrentVersions{ AgentVersion: AgentVersion, ConfigVersion: ConfigVersion, MinAgentVersion: MinAgentVersion, BuildTime: time.Now(), } } // ExtractConfigVersionFromAgent extracts config version from agent version // Agent version format: v0.1.23.6 where fourth octet maps to config version func ExtractConfigVersionFromAgent(agentVersion string) string { // Strip 'v' prefix if present cleanVersion := agentVersion if len(cleanVersion) > 0 && cleanVersion[0] == 'v' { cleanVersion = cleanVersion[1:] } // Split version parts parts := fmt.Sprintf("%s", cleanVersion) if len(parts) >= 1 { // For now, use the last octet as config version // v0.1.23 -> "3" (last digit) lastChar := parts[len(parts)-1:] return lastChar } // Default fallback return "3" } // ValidateAgentVersion checks if an agent version is compatible func ValidateAgentVersion(agentVersion string) error { current := GetCurrentVersions() // Check minimum version if agentVersion < current.MinAgentVersion { return fmt.Errorf("agent version %s is below minimum %s", agentVersion, current.MinAgentVersion) } return nil } // GetBuildFlags returns the ldflags to inject versions into agent builds func GetBuildFlags() []string { versions := GetCurrentVersions() return []string{ fmt.Sprintf("-X github.com/Fimeg/RedFlag/aggregator-agent/internal/version.Version=%s", versions.AgentVersion), fmt.Sprintf("-X github.com/Fimeg/RedFlag/aggregator-agent/internal/version.ConfigVersion=%s", versions.ConfigVersion), fmt.Sprintf("-X github.com/Fimeg/RedFlag/aggregator-agent/internal/version.BuildTime=%s", versions.BuildTime.Format(time.RFC3339)), } }