# SPDX-License-Identifier: LSL-1.0 # Copyright (c) 2026 Markus Maiwald # Stewardship: Self Sovereign Society Foundation # # This file is part of the Nexus Sovereign Core. # See legal/LICENSE_SOVEREIGN.md for license terms. ## variants.nim ## Typed variant system for deterministic, content-addressed packages ## Evolution of USE flags into semantic domains with type safety import std/[tables, strutils, sequtils, algorithm, sets, os] import config type FlagType* = enum ftBool ## Simple on/off: +lto, -debug ftChoice ## Mutually exclusive: init=dinit ftSet ## Multiple allowed: security=pie,relro,hardened ftEnum ## Predefined options with validation VariantFlag* = object domain*: string ## "init", "graphics", "security" name*: string ## "dinit", "wayland", "pie" flagType*: FlagType enabled*: bool value*: string ## For choice/enum types affects*: seq[string] ## What this flag impacts conflicts*: seq[string] requires*: seq[string] FlagDomain* = object name*: string description*: string flagType*: FlagType exclusive*: bool ## Only one flag can be enabled options*: seq[string] default*: string ## Default value for choice types defaults*: seq[string] ## Default values for set types VariantFingerprint* = object hash*: string ## BLAKE3 hash of variant configuration packageName*: string version*: string domains*: Table[string, seq[string]] compilerFlags*: CompilerFlags toolchain*: string target*: string PackageVariant* = object fingerprint*: VariantFingerprint installPath*: string isDefault*: bool installedAt*: string # ============================================ # Domain Definitions (9 Semantic Domains) # ============================================ proc getSemanticDomains*(): Table[string, FlagDomain] = ## Get the 9 semantic domains for typed variants result = initTable[string, FlagDomain]() # 1. Init System (exclusive choice) result["init"] = FlagDomain( name: "init", description: "Init system selection", flagType: ftChoice, exclusive: true, options: @["systemd", "dinit", "openrc", "runit", "s6"], default: "dinit" ) # 2. Runtime Features (set) result["runtime"] = FlagDomain( name: "runtime", description: "Core runtime features", flagType: ftSet, exclusive: false, options: @[ "ssl", "http3", "zstd", "lz4", "ipv6", "dbus", "doc", "examples", "python", "ruby", "perl", "lua", "go", "rust", "cuda", "rocm", "onnx", "tensorrt", "steam", "wine", "proton" ], defaults: @["ssl", "ipv6"] ) # 3. Graphics (choice + sub-features) result["graphics"] = FlagDomain( name: "graphics", description: "Display server and GPU API", flagType: ftChoice, exclusive: false, # Can have display + GPU APIs options: @[ "none", "X", "wayland", "vulkan", "opengl", "mesa", "nvidia", "amd", "intel-gpu" ], default: "none" ) # 4. Audio System (exclusive choice) result["audio"] = FlagDomain( name: "audio", description: "Sound server selection", flagType: ftChoice, exclusive: true, options: @["none", "pipewire", "pulseaudio", "alsa", "jack", "oss"], default: "pipewire" ) # 5. Security Hardening (set) result["security"] = FlagDomain( name: "security", description: "Security hardening features", flagType: ftSet, exclusive: false, options: @["pie", "relro", "hardened", "fortify", "stack-protector"], defaults: @["pie", "relro"] ) # 6. Optimization (set with conflicts) result["optimization"] = FlagDomain( name: "optimization", description: "Build optimizations", flagType: ftSet, exclusive: false, options: @["lto", "pgo", "march-native", "debug", "strip"], defaults: @["lto"] ) # 7. Integration (set) result["integration"] = FlagDomain( name: "integration", description: "System interfaces and integration", flagType: ftSet, exclusive: false, options: @[ "docker", "podman", "nipcells", "containerd", "runc", "crun", "kvm", "qemu", "libvirt", "xen", "bhyve", "nexus-api", "nexus-db", "nexus-sync", "nexus-monitor", "nexus-security" ], defaults: @[] ) # 8. Network (set) result["network"] = FlagDomain( name: "network", description: "Networking stack and protocols", flagType: ftSet, exclusive: false, options: @[ "ipv6", "wireguard", "zerotier", "tailscale", "mesh", "p2p", "ipfs", "libp2p" ], defaults: @["ipv6"] ) # 9. Developer Tools (set) result["developer"] = FlagDomain( name: "developer", description: "Development tools and features", flagType: ftSet, exclusive: false, options: @["debugger", "profiler", "lsp", "repl", "hot-reload", "sanitizer", "coverage"], defaults: @[] ) # ============================================ # Variant Flag Parsing # ============================================ proc parseVariantFlag*(flagStr: string): VariantFlag = ## Parse domain-scoped variant flag ## Examples: ## "+init=dinit" ## "+runtime=ssl,http3,zstd" ## "+optimization=lto" ## "-debug" let trimmed = flagStr.strip() if trimmed.len == 0: raise newException(ValueError, "Empty variant flag") # Determine enabled/disabled var enabled = true var flagPart = trimmed if trimmed[0] == '+': enabled = true flagPart = trimmed[1..^1] elif trimmed[0] == '-': enabled = false flagPart = trimmed[1..^1] # Check for domain syntax: domain=value if '=' in flagPart: let parts = flagPart.split('=', 1) result = VariantFlag( domain: parts[0], name: parts[1], flagType: ftChoice, # Will be determined by domain enabled: enabled, value: parts[1] ) else: # Simple flag (backward compatible) result = VariantFlag( domain: "", # Will be inferred name: flagPart, flagType: ftBool, enabled: enabled, value: "" ) proc inferDomain*(flagName: string, domains: Table[string, FlagDomain]): string = ## Infer domain from flag name for backward compatibility for domainName, domain in domains: if flagName in domain.options: return domainName return "runtime" # Default domain # ============================================ # Variant Fingerprint Generation # ============================================ proc calculateVariantFingerprint*( packageName: string, version: string, domains: Table[string, seq[string]], compilerFlags: CompilerFlags, toolchain: string = "default", target: string = "native" ): string = ## Calculate deterministic BLAKE3 hash for variant ## Hash of: source + version + flags + toolchain + target var hashInput = "" # 1. Package identity hashInput.add(packageName & "\n") hashInput.add(version & "\n") # 2. Sorted domain flags (deterministic) var sortedDomainNames = domains.keys.toSeq sortedDomainNames.sort() for domainName in sortedDomainNames: hashInput.add(domainName & ":") var sortedFlags = domains[domainName] sortedFlags.sort() hashInput.add(sortedFlags.join(",") & "\n") # 3. Compiler flags hashInput.add("cflags:" & compilerFlags.cflags & "\n") hashInput.add("cxxflags:" & compilerFlags.cxxflags & "\n") hashInput.add("ldflags:" & compilerFlags.ldflags & "\n") # 4. Toolchain hashInput.add("toolchain:" & toolchain & "\n") # 5. Target hashInput.add("target:" & target & "\n") # Calculate hash (simplified for now - will use BLAKE3) # For now, use a simple hash var simpleHash = 0 for c in hashInput: simpleHash = (simpleHash * 31 + ord(c)) and 0x7FFFFFFF result = "blake3-" & simpleHash.toHex()[0..11].toLower() proc generateVariantPath*( packageName: string, version: string, fingerprint: string, baseDir: string = "/Programs" ): string = ## Generate variant installation path ## Format: /Programs//-/ result = baseDir / packageName / (version & "-" & fingerprint) # ============================================ # Variant Management # ============================================ proc listVariants*(packageName: string, baseDir: string = "/Programs"): seq[PackageVariant] = ## List all installed variants of a package result = @[] let packageDir = baseDir / packageName if not dirExists(packageDir): return for kind, path in walkDir(packageDir): if kind == pcDir: let dirName = path.splitPath().tail if dirName.contains("-blake3-"): let parts = dirName.split("-blake3-", 1) if parts.len == 2: result.add(PackageVariant( fingerprint: VariantFingerprint( hash: "blake3-" & parts[1], packageName: packageName, version: parts[0] ), installPath: path, isDefault: false, # TODO: Check symlinks installedAt: "" )) proc getDefaultVariant*(packageName: string): string = ## Get the default variant fingerprint for a package ## Checks which variant is currently symlinked # TODO: Implement by checking /System/Links/ symlinks result = "" proc setDefaultVariant*(packageName: string, fingerprint: string): bool = ## Set which variant is the default (symlinked) ## Updates symlinks in /System/Links/ # TODO: Implement symlink switching result = false # ============================================ # Display Functions # ============================================ proc displayVariant*(variant: PackageVariant) = ## Display variant information echo variant.fingerprint.packageName & " " & variant.fingerprint.version & "-" & variant.fingerprint.hash if variant.isDefault: echo " [DEFAULT]" echo " Path: " & variant.installPath proc displayVariants*(variants: seq[PackageVariant]) = ## Display list of variants if variants.len == 0: echo "No variants found" return echo "Installed Variants:" echo "" for variant in variants: displayVariant(variant) echo ""