# 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. ## nimpak/profile_manager.nim ## Profile Manager for Nippels ## ## Manages security profiles and applies appropriate settings for different system roles. ## Supports profile loading, application, and customization. ## ## Requirements: 6.1-6.8 import std/[os, strutils, json, tables, options, times] import utils/resultutils import nippel_types # ============================================================================= # Profile Overrides (Requirement 6.8) # ============================================================================= type ProfileOverrides* = object ## Per-Nippel profile customizations isolationLevel*: Option[IsolationLevel] desktopIntegration*: Option[bool] networkAccess*: Option[NetworkAccessLevel] resourceLimits*: Option[ResourceLimits] auditingEnabled*: Option[bool] ProfileError* = object of CatchableError ## Profile-specific errors profileName*: string context*: JsonNode ProfileManager* = object ## Manages security profiles for Nippels profilesDir*: string customProfilesDir*: string loadedProfiles*: Table[string, ProfileSettings] # ============================================================================= # Profile Settings Definitions (Requirement 6.1-6.5) # ============================================================================= proc getWorkstationProfile*(): ProfileSettings = ## Workstation profile: Standard isolation + desktop integration (Requirement 6.1) ## Suitable for desktop workstations with full GUI support ProfileSettings( isolationLevel: Standard, desktopIntegration: true, networkAccess: Full, resourceLimits: ResourceLimits( maxMemory: 8 * 1024 * 1024 * 1024, # 8GB maxCpu: 0.9, # 90% CPU maxDisk: 10 * 1024 * 1024 * 1024, # 10GB maxProcesses: 200, maxOpenFiles: 2048 ), auditingEnabled: false ) proc getHomestationProfile*(): ProfileSettings = ## Homestation profile: Standard isolation + relaxed network (Requirement 6.2) ## Default profile for home users with balanced security and convenience ProfileSettings( isolationLevel: Standard, desktopIntegration: true, networkAccess: Relaxed, resourceLimits: ResourceLimits( maxMemory: 4 * 1024 * 1024 * 1024, # 4GB maxCpu: 0.8, # 80% CPU maxDisk: 5 * 1024 * 1024 * 1024, # 5GB maxProcesses: 150, maxOpenFiles: 1024 ), auditingEnabled: false ) proc getSatelliteProfile*(): ProfileSettings = ## Satellite profile: Strict isolation + limited network (Requirement 6.3) ## For remote/mobile systems with enhanced security ProfileSettings( isolationLevel: Strict, desktopIntegration: true, networkAccess: Limited, resourceLimits: ResourceLimits( maxMemory: 2 * 1024 * 1024 * 1024, # 2GB maxCpu: 0.7, # 70% CPU maxDisk: 3 * 1024 * 1024 * 1024, # 3GB maxProcesses: 100, maxOpenFiles: 512 ), auditingEnabled: true ) proc getNetworkIOTProfile*(): ProfileSettings = ## Network/IOT profile: Strict isolation + minimal resources (Requirement 6.4) ## For embedded devices and IoT systems ProfileSettings( isolationLevel: Strict, desktopIntegration: false, networkAccess: Limited, resourceLimits: ResourceLimits( maxMemory: 512 * 1024 * 1024, # 512MB maxCpu: 0.5, # 50% CPU maxDisk: 1 * 1024 * 1024 * 1024, # 1GB maxProcesses: 50, maxOpenFiles: 256 ), auditingEnabled: true ) proc getServerProfile*(): ProfileSettings = ## Server profile: Strict isolation + no desktop + enhanced auditing (Requirement 6.5) ## For server environments with maximum security ProfileSettings( isolationLevel: Strict, desktopIntegration: false, networkAccess: Full, resourceLimits: ResourceLimits( maxMemory: 16 * 1024 * 1024 * 1024, # 16GB maxCpu: 1.0, # 100% CPU maxDisk: 50 * 1024 * 1024 * 1024, # 50GB maxProcesses: 500, maxOpenFiles: 4096 ), auditingEnabled: true ) # ============================================================================= # Profile Manager Initialization # ============================================================================= proc newProfileManager*(profilesDir: string = "", customProfilesDir: string = ""): ProfileManager = ## Create a new ProfileManager let defaultProfilesDir = if profilesDir.len > 0: profilesDir else: "/etc/nip/profiles/security" let defaultCustomDir = if customProfilesDir.len > 0: customProfilesDir else: getHomeDir() / ".config" / "nip" / "profiles" / "security" ProfileManager( profilesDir: defaultProfilesDir, customProfilesDir: defaultCustomDir, loadedProfiles: initTable[string, ProfileSettings]() ) # ============================================================================= # Profile Loading (Requirement 6.7) # ============================================================================= proc loadProfile*(profile: SecurityProfile): ProfileSettings = ## Load profile settings for a security profile (Requirement 6.7) case profile: of Workstation: getWorkstationProfile() of Homestation: getHomestationProfile() of Satellite: getSatelliteProfile() of NetworkIOT: getNetworkIOTProfile() of Server: getServerProfile() proc loadProfile*(manager: var ProfileManager, profile: SecurityProfile): ProfileSettings = ## Load profile settings through ProfileManager let profileName = $profile if profileName in manager.loadedProfiles: return manager.loadedProfiles[profileName] let settings = loadProfile(profile) manager.loadedProfiles[profileName] = settings return settings proc loadProfileFromFile*(path: string): Result[ProfileSettings, string] = ## Load profile settings from a custom file try: if not fileExists(path): return err[ProfileSettings]("Profile file not found: " & path) let config = parseJson(readFile(path)) # Parse isolation level let isolationStr = config["isolation"].getStr("Standard") let isolation = parseEnum[IsolationLevel](isolationStr) # Parse network access let networkStr = config["networkAccess"].getStr("Relaxed") let networkAccess = parseEnum[NetworkAccessLevel](networkStr) # Parse resource limits let limits = config["resourceLimits"] let resourceLimits = ResourceLimits( maxMemory: limits["maxMemory"].getInt(4 * 1024 * 1024 * 1024), maxCpu: limits["maxCpu"].getFloat(0.8), maxDisk: limits["maxDisk"].getInt(5 * 1024 * 1024 * 1024), maxProcesses: limits["maxProcesses"].getInt(150), maxOpenFiles: limits["maxOpenFiles"].getInt(1024) ) let settings = ProfileSettings( isolationLevel: isolation, desktopIntegration: config["desktopIntegration"].getBool(true), networkAccess: networkAccess, resourceLimits: resourceLimits, auditingEnabled: config["auditingEnabled"].getBool(false) ) return ok[ProfileSettings](settings) except Exception as e: return err[ProfileSettings]("Failed to load profile: " & e.msg) proc loadProfileFromFile*(manager: var ProfileManager, path: string): Result[ProfileSettings, string] = ## Load profile settings from file through ProfileManager loadProfileFromFile(path) # ============================================================================= # Profile Application (Requirement 6.7) # ============================================================================= proc applyProfile*(nippel: var Nippel, settings: ProfileSettings): Result[bool, string] = ## Apply profile settings to a Nippel (Requirement 6.7) ## Returns true on success try: # Apply isolation level nippel.isolationLevel = settings.isolationLevel nippel.profileSettings.isolationLevel = settings.isolationLevel # Apply desktop integration nippel.profileSettings.desktopIntegration = settings.desktopIntegration # Apply network access settings nippel.profileSettings.networkAccess = settings.networkAccess # Apply resource limits nippel.profileSettings.resourceLimits = settings.resourceLimits # Apply auditing settings nippel.profileSettings.auditingEnabled = settings.auditingEnabled # Update last used timestamp nippel.lastUsed = now() # Save updated configuration let cellConfig = %*{ "nippel": { "name": nippel.name, "id": nippel.id, "version": nippel.version, "created": $nippel.created, "lastUsed": $nippel.lastUsed }, "profile": { "type": $nippel.profile, "isolation": $nippel.isolationLevel, "desktopIntegration": nippel.profileSettings.desktopIntegration, "networkAccess": $nippel.profileSettings.networkAccess, "auditingEnabled": nippel.profileSettings.auditingEnabled }, "resourceLimits": { "maxMemory": nippel.profileSettings.resourceLimits.maxMemory, "maxCpu": nippel.profileSettings.resourceLimits.maxCpu, "maxDisk": nippel.profileSettings.resourceLimits.maxDisk, "maxProcesses": nippel.profileSettings.resourceLimits.maxProcesses, "maxOpenFiles": nippel.profileSettings.resourceLimits.maxOpenFiles }, "paths": { "root": nippel.cellRoot, "data": nippel.xdgDirs.dataHome, "config": nippel.xdgDirs.configHome, "cache": nippel.xdgDirs.cacheHome, "state": nippel.xdgDirs.stateHome, "runtime": nippel.xdgDirs.runtimeDir }, "storage": { "merkle_root": nippel.merkleRoot, "cas_entries": nippel.casEntries.len, "total_size": 0 }, "network": { "utcp_address": formatUTCPAddress(nippel.utcpAddress) }, "packages": newJArray() } writeFile(nippel.cellRoot / "cell.json", cellConfig.pretty()) echo "✅ Applied profile settings to Nippel: ", nippel.name echo " Isolation: ", nippel.isolationLevel echo " Desktop Integration: ", nippel.profileSettings.desktopIntegration echo " Network Access: ", nippel.profileSettings.networkAccess echo " Auditing: ", nippel.profileSettings.auditingEnabled return ok(true) except Exception as e: return err[bool]("Failed to apply profile: " & e.msg) # ============================================================================= # Profile Customization (Requirement 6.8) # ============================================================================= proc customizeProfile*(nippel: var Nippel, overrides: ProfileOverrides): Result[bool, string] = ## Apply per-Nippel profile overrides (Requirement 6.8) ## Returns true on success try: var modified = false # Apply isolation level override if overrides.isolationLevel.isSome: nippel.isolationLevel = overrides.isolationLevel.get() nippel.profileSettings.isolationLevel = overrides.isolationLevel.get() modified = true echo " Override: Isolation level -> ", nippel.isolationLevel # Apply desktop integration override if overrides.desktopIntegration.isSome: nippel.profileSettings.desktopIntegration = overrides.desktopIntegration.get() modified = true echo " Override: Desktop integration -> ", nippel.profileSettings.desktopIntegration # Apply network access override if overrides.networkAccess.isSome: nippel.profileSettings.networkAccess = overrides.networkAccess.get() modified = true echo " Override: Network access -> ", nippel.profileSettings.networkAccess # Apply resource limits override if overrides.resourceLimits.isSome: nippel.profileSettings.resourceLimits = overrides.resourceLimits.get() modified = true echo " Override: Resource limits updated" # Apply auditing override if overrides.auditingEnabled.isSome: nippel.profileSettings.auditingEnabled = overrides.auditingEnabled.get() modified = true echo " Override: Auditing -> ", nippel.profileSettings.auditingEnabled if not modified: echo " No overrides applied" return ok(true) # Update last used timestamp nippel.lastUsed = now() # Save updated configuration let cellConfig = %*{ "nippel": { "name": nippel.name, "id": nippel.id, "version": nippel.version, "created": $nippel.created, "lastUsed": $nippel.lastUsed }, "profile": { "type": $nippel.profile, "isolation": $nippel.isolationLevel, "desktopIntegration": nippel.profileSettings.desktopIntegration, "networkAccess": $nippel.profileSettings.networkAccess, "auditingEnabled": nippel.profileSettings.auditingEnabled }, "resourceLimits": { "maxMemory": nippel.profileSettings.resourceLimits.maxMemory, "maxCpu": nippel.profileSettings.resourceLimits.maxCpu, "maxDisk": nippel.profileSettings.resourceLimits.maxDisk, "maxProcesses": nippel.profileSettings.resourceLimits.maxProcesses, "maxOpenFiles": nippel.profileSettings.resourceLimits.maxOpenFiles }, "paths": { "root": nippel.cellRoot, "data": nippel.xdgDirs.dataHome, "config": nippel.xdgDirs.configHome, "cache": nippel.xdgDirs.cacheHome, "state": nippel.xdgDirs.stateHome, "runtime": nippel.xdgDirs.runtimeDir }, "storage": { "merkle_root": nippel.merkleRoot, "cas_entries": nippel.casEntries.len, "total_size": 0 }, "network": { "utcp_address": formatUTCPAddress(nippel.utcpAddress) }, "packages": newJArray() } writeFile(nippel.cellRoot / "cell.json", cellConfig.pretty()) echo "✅ Applied profile customizations to Nippel: ", nippel.name return ok(true) except Exception as e: return err[bool]("Failed to customize profile: " & e.msg) # ============================================================================= # Profile Information # ============================================================================= proc getProfileInfo*(profile: SecurityProfile): string = ## Get human-readable information about a profile let settings = loadProfile(profile) result = "Profile: " & $profile & "\n" result.add(" Isolation: " & $settings.isolationLevel & "\n") result.add(" Desktop Integration: " & $settings.desktopIntegration & "\n") result.add(" Network Access: " & $settings.networkAccess & "\n") result.add(" Auditing: " & $settings.auditingEnabled & "\n") result.add(" Resource Limits:\n") result.add(" Max Memory: " & $(settings.resourceLimits.maxMemory div (1024 * 1024)) & " MB\n") result.add(" Max CPU: " & $(settings.resourceLimits.maxCpu * 100) & "%\n") result.add(" Max Disk: " & $(settings.resourceLimits.maxDisk div (1024 * 1024)) & " MB\n") result.add(" Max Processes: " & $settings.resourceLimits.maxProcesses & "\n") result.add(" Max Open Files: " & $settings.resourceLimits.maxOpenFiles) proc listAvailableProfiles*(): seq[string] = ## List all available security profiles result = @[ "Workstation - Standard isolation + desktop integration", "Homestation - Standard isolation + relaxed network (default)", "Satellite - Strict isolation + limited network (remote/mobile)", "NetworkIOT - Strict isolation + minimal resources (embedded)", "Server - Strict isolation + no desktop + enhanced auditing" ] # ============================================================================= # Exports # ============================================================================= export ProfileOverrides, ProfileError, ProfileManager export getWorkstationProfile, getHomestationProfile, getSatelliteProfile export getNetworkIOTProfile, getServerProfile export newProfileManager, loadProfile, loadProfileFromFile export applyProfile, customizeProfile export getProfileInfo, listAvailableProfiles