# 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. ## Package Format CAS Integration ## ## This module integrates all package formats with the Content-Addressable Storage ## system, providing unified storage, retrieval, deduplication, and garbage collection ## across all five package formats. It also implements format conversion pipelines ## between compatible formats. import std/[os, json, times, strutils, sequtils, tables, options, strformat, algorithm] import ./types_fixed import ./formats import ./cas import ./packages import ./recipes import ./chunks import ./snapshots import ./overlays type FormatCasError* = object of NimPakError formatType*: PackageFormat objectHash*: string StorageResult* = object ## Result of storing a package format in CAS format*: PackageFormat hash*: string size*: int64 compressed*: bool chunks*: seq[ChunkRef] RetrievalResult* = object ## Result of retrieving a package format from CAS format*: PackageFormat hash*: string data*: seq[byte] metadata*: JsonNode FormatCasManager* = object ## Manager for package format CAS operations cas*: CasManager formatRegistry*: Table[string, PackageFormat] conversionCache*: Table[string, string] ## Source hash -> converted hash # ============================================================================= # Format CAS Manager Initialization # ============================================================================= proc initFormatCasManager*(casManager: CasManager): FormatCasManager = ## Initialize format CAS manager with existing CAS manager var registry = initTable[string, PackageFormat]() # Register format extensions registry[".npr"] = NprRecipe registry[".npk.zst"] = NpkBinary registry[".npk.tar"] = NpkBinary registry[".nca"] = NcaChunk registry[".nss.zst"] = NssSnapshot registry[".nss.tar"] = NssSnapshot registry[".nof"] = NofOverlay FormatCasManager( cas: casManager, formatRegistry: registry, conversionCache: initTable[string, string]() ) # ============================================================================= # Universal Format Storage # ============================================================================= proc storeNprRecipe*(manager: var FormatCasManager, recipe: NprRecipe): Result[StorageResult, FormatCasError] = ## Store NPR recipe in CAS try: let kdlContent = serializeNprToKdl(recipe) let data = kdlContent.toOpenArrayByte(0, kdlContent.len - 1).toSeq() let storeResult = manager.cas.storeObject(data) if storeResult.isErr: return err[StorageResult, FormatCasError](FormatCasError( code: CasError, msg: "Failed to store NPR recipe: " & storeResult.getError().msg, formatType: NprRecipe, objectHash: "unknown" )) let casObject = storeResult.get() let result = StorageResult( format: NprRecipe, hash: casObject.hash, size: casObject.size, compressed: casObject.compressed, chunks: casObject.chunks ) return ok[StorageResult, FormatCasError](result) except Exception as e: return err[StorageResult, FormatCasError](FormatCasError( code: UnknownError, msg: "Failed to store NPR recipe: " & e.msg, formatType: NprRecipe, objectHash: "unknown" )) proc storeNpkPackage*(manager: var FormatCasManager, package: NpkPackage): Result[StorageResult, FormatCasError] = ## Store NPK package in CAS with file-level deduplication try: # Store package metadata let kdlContent = serializeToKdl(package) let metadataData = kdlContent.toOpenArrayByte(0, kdlContent.len - 1).toSeq() let metadataResult = manager.cas.storeObject(metadataData) if metadataResult.isErr: return err[StorageResult, FormatCasError](FormatCasError( code: CasError, msg: "Failed to store NPK metadata: " & metadataResult.getError().msg, formatType: NpkBinary, objectHash: "unknown" )) # Files are already stored in CAS through the package creation process # Just return the metadata storage result let casObject = metadataResult.get() let result = StorageResult( format: NpkBinary, hash: casObject.hash, size: casObject.size, compressed: casObject.compressed, chunks: casObject.chunks ) return ok[StorageResult, FormatCasError](result) except Exception as e: return err[StorageResult, FormatCasError](FormatCasError( code: UnknownError, msg: "Failed to store NPK package: " & e.msg, formatType: NpkBinary, objectHash: "unknown" )) proc storeNcaChunk*(manager: var FormatCasManager, chunk: NcaChunk): Result[StorageResult, FormatCasError] = ## Store NCA chunk in CAS try: let binaryData = serializeNcaChunk(chunk) let storeResult = manager.cas.storeObject(binaryData) if storeResult.isErr: return err[StorageResult, FormatCasError](FormatCasError( code: CasError, msg: "Failed to store NCA chunk: " & storeResult.getError().msg, formatType: NcaChunk, objectHash: chunk.hash )) let casObject = storeResult.get() let result = StorageResult( format: NcaChunk, hash: casObject.hash, size: casObject.size, compressed: casObject.compressed, chunks: casObject.chunks ) return ok[StorageResult, FormatCasError](result) except Exception as e: return err[StorageResult, FormatCasError](FormatCasError( code: UnknownError, msg: "Failed to store NCA chunk: " & e.msg, formatType: NcaChunk, objectHash: chunk.hash )) proc storeNssSnapshot*(manager: var FormatCasManager, snapshot: NssSnapshot): Result[StorageResult, FormatCasError] = ## Store NSS snapshot in CAS with package-level deduplication try: # Store snapshot metadata let kdlContent = serializeNssToKdl(snapshot) let metadataData = kdlContent.toOpenArrayByte(0, kdlContent.len - 1).toSeq() let metadataResult = manager.cas.storeObject(metadataData) if metadataResult.isErr: return err[StorageResult, FormatCasError](FormatCasError( code: CasError, msg: "Failed to store NSS metadata: " & metadataResult.getError().msg, formatType: NssSnapshot, objectHash: "unknown" )) # Store individual packages (they may already be in CAS) for package in snapshot.packages: let packageResult = manager.storeNpkPackage(package) if packageResult.isErr: # Log warning but continue - package might already be stored discard let casObject = metadataResult.get() let result = StorageResult( format: NssSnapshot, hash: casObject.hash, size: casObject.size, compressed: casObject.compressed, chunks: casObject.chunks ) return ok[StorageResult, FormatCasError](result) except Exception as e: return err[StorageResult, FormatCasError](FormatCasError( code: UnknownError, msg: "Failed to store NSS snapshot: " & e.msg, formatType: NssSnapshot, objectHash: "unknown" )) proc storeNofOverlay*(manager: var FormatCasManager, overlay: NofOverlay): Result[StorageResult, FormatCasError] = ## Store NOF overlay in CAS try: let kdlContent = serializeNofToKdl(overlay) let data = kdlContent.toOpenArrayByte(0, kdlContent.len - 1).toSeq() let storeResult = manager.cas.storeObject(data) if storeResult.isErr: return err[StorageResult, FormatCasError](FormatCasError( code: CasError, msg: "Failed to store NOF overlay: " & storeResult.getError().msg, formatType: NofOverlay, objectHash: "unknown" )) let casObject = storeResult.get() let result = StorageResult( format: NofOverlay, hash: casObject.hash, size: casObject.size, compressed: casObject.compressed, chunks: casObject.chunks ) return ok[StorageResult, FormatCasError](result) except Exception as e: return err[StorageResult, FormatCasError](FormatCasError( code: UnknownError, msg: "Failed to store NOF overlay: " & e.msg, formatType: NofOverlay, objectHash: "unknown" )) # ============================================================================= # Universal Format Retrieval # ============================================================================= proc retrieveNprRecipe*(manager: FormatCasManager, hash: string): Result[NprRecipe, FormatCasError] = ## Retrieve NPR recipe from CAS let dataResult = manager.cas.retrieveObject(hash) if dataResult.isErr: return err[NprRecipe, FormatCasError](FormatCasError( code: ObjectNotFound, msg: "Failed to retrieve NPR recipe: " & dataResult.getError().msg, formatType: NprRecipe, objectHash: hash )) let data = dataResult.get() let kdlContent = cast[string](data) let deserializeResult = deserializeNprFromKdl(kdlContent) if deserializeResult.isErr: return err[NprRecipe, FormatCasError](FormatCasError( code: InvalidMetadata, msg: "Failed to deserialize NPR recipe: " & deserializeResult.getError().msg, formatType: NprRecipe, objectHash: hash )) return ok[NprRecipe, FormatCasError](deserializeResult.get()) proc retrieveNpkPackage*(manager: FormatCasManager, hash: string): Result[NpkPackage, FormatCasError] = ## Retrieve NPK package from CAS let dataResult = manager.cas.retrieveObject(hash) if dataResult.isErr: return err[NpkPackage, FormatCasError](FormatCasError( code: ObjectNotFound, msg: "Failed to retrieve NPK package: " & dataResult.getError().msg, formatType: NpkBinary, objectHash: hash )) let data = dataResult.get() let kdlContent = cast[string](data) let deserializeResult = deserializeFromKdl(kdlContent) if deserializeResult.isErr: return err[NpkPackage, FormatCasError](FormatCasError( code: InvalidMetadata, msg: "Failed to deserialize NPK package: " & deserializeResult.getError().msg, formatType: NpkBinary, objectHash: hash )) return ok[NpkPackage, FormatCasError](deserializeResult.get()) proc retrieveNcaChunk*(manager: FormatCasManager, hash: string): Result[NcaChunk, FormatCasError] = ## Retrieve NCA chunk from CAS let dataResult = manager.cas.retrieveObject(hash) if dataResult.isErr: return err[NcaChunk, FormatCasError](FormatCasError( code: ObjectNotFound, msg: "Failed to retrieve NCA chunk: " & dataResult.getError().msg, formatType: NcaChunk, objectHash: hash )) let data = dataResult.get() let deserializeResult = deserializeNcaChunk(data) if deserializeResult.isErr: return err[NcaChunk, FormatCasError](FormatCasError( code: InvalidMetadata, msg: "Failed to deserialize NCA chunk: " & deserializeResult.getError().msg, formatType: NcaChunk, objectHash: hash )) return ok[NcaChunk, FormatCasError](deserializeResult.get()) proc retrieveNssSnapshot*(manager: FormatCasManager, hash: string): Result[NssSnapshot, FormatCasError] = ## Retrieve NSS snapshot from CAS let dataResult = manager.cas.retrieveObject(hash) if dataResult.isErr: return err[NssSnapshot, FormatCasError](FormatCasError( code: ObjectNotFound, msg: "Failed to retrieve NSS snapshot: " & dataResult.getError().msg, formatType: NssSnapshot, objectHash: hash )) let data = dataResult.get() let kdlContent = cast[string](data) let deserializeResult = deserializeNssFromKdl(kdlContent) if deserializeResult.isErr: return err[NssSnapshot, FormatCasError](FormatCasError( code: InvalidMetadata, msg: "Failed to deserialize NSS snapshot: " & deserializeResult.getError().msg, formatType: NssSnapshot, objectHash: hash )) return ok[NssSnapshot, FormatCasError](deserializeResult.get()) proc retrieveNofOverlay*(manager: FormatCasManager, hash: string): Result[NofOverlay, FormatCasError] = ## Retrieve NOF overlay from CAS let dataResult = manager.cas.retrieveObject(hash) if dataResult.isErr: return err[NofOverlay, FormatCasError](FormatCasError( code: ObjectNotFound, msg: "Failed to retrieve NOF overlay: " & dataResult.getError().msg, formatType: NofOverlay, objectHash: hash )) let data = dataResult.get() let kdlContent = cast[string](data) let deserializeResult = deserializeNofFromKdl(kdlContent) if deserializeResult.isErr: return err[NofOverlay, FormatCasError](FormatCasError( code: InvalidMetadata, msg: "Failed to deserialize NOF overlay: " & deserializeResult.getError().msg, formatType: NofOverlay, objectHash: hash )) return ok[NofOverlay, FormatCasError](deserializeResult.get()) # ============================================================================= # Format Detection and Universal Retrieval # ============================================================================= proc detectAndRetrieve*(manager: FormatCasManager, hash: string): Result[RetrievalResult, FormatCasError] = ## Detect format and retrieve object from CAS let dataResult = manager.cas.retrieveObject(hash) if dataResult.isErr: return err[RetrievalResult, FormatCasError](FormatCasError( code: ObjectNotFound, msg: "Failed to retrieve object: " & dataResult.getError().msg, formatType: NpkBinary, # Default objectHash: hash )) let data = dataResult.get() # Try to detect format from content var detectedFormat = NpkBinary # Default var metadata = newJObject() # Check for KDL format markers let content = cast[string](data) if content.contains("recipe \""): detectedFormat = NprRecipe metadata["type"] = newJString("recipe") elif content.contains("overlay \""): detectedFormat = NofOverlay metadata["type"] = newJString("overlay") elif content.contains("snapshot \""): detectedFormat = NssSnapshot metadata["type"] = newJString("snapshot") elif content.contains("package \""): detectedFormat = NpkBinary metadata["type"] = newJString("package") elif data.len >= 4 and cast[string](data[0..3]) == "NCA1": detectedFormat = NcaChunk metadata["type"] = newJString("chunk") let result = RetrievalResult( format: detectedFormat, hash: hash, data: data, metadata: metadata ) return ok[RetrievalResult, FormatCasError](result) # ============================================================================= # Cross-Format Deduplication # ============================================================================= proc deduplicateAcrossFormats*(manager: var FormatCasManager): Result[int, FormatCasError] = ## Perform deduplication across all package formats try: var removedCount = 0 let allObjects = manager.cas.listObjects() var contentHashes = initTable[string, seq[string]]() # Group objects by content hash (not storage hash) for objectHash in allObjects: let retrieveResult = manager.cas.retrieveObject(objectHash) if retrieveResult.isOk: let data = retrieveResult.get() let contentHash = calculateBlake3(data) if not contentHashes.hasKey(contentHash): contentHashes[contentHash] = @[] contentHashes[contentHash].add(objectHash) # Remove duplicates (keep first occurrence) for contentHash, objects in contentHashes: if objects.len > 1: # Keep the first object, remove the rest for i in 1..