feat: implement Operation Velvet Forge & Evidence Locker
- Ratified 'The Law of Representation' with tiered hashing (XXH3/Ed25519/BLAKE2b). - Implemented RFC 8785 Canonical JSON serialization for deterministic signing. - Deployed 'The Evidence Locker': Registry now enforces mandatory Ed25519 verification on read. - Initialized 'The Cortex': KDL Intent Parser now translates manifests into GraftIntent objects. - Orchestrated 'Velvet Forge' pipeline: Closing the loop between Intent, Synthesis, and Truth. - Resolved xxHash namespace collisions and fixed Nint128 type mismatches. Sovereignty achieved. The machine now listens, remember, and refuses to lie.
This commit is contained in:
parent
d2aa120f4e
commit
81a8927f0f
|
|
@ -15,7 +15,7 @@
|
|||
import std/[strutils, options, sets, json, sequtils, tables, algorithm]
|
||||
import nimpak/kdl_parser
|
||||
import nip/platform
|
||||
import nip/xxhash
|
||||
import nip/xxh
|
||||
|
||||
type
|
||||
# ============================================================================
|
||||
|
|
@ -248,7 +248,8 @@ const BASE_ALLOWED_FIELDS = [
|
|||
# Build
|
||||
"build_system", "build_flags", "configure_flags",
|
||||
# Platform
|
||||
"os", "arch", "supported_os", "supported_architectures", "required_capabilities",
|
||||
"os", "arch", "supported_os", "supported_architectures",
|
||||
"required_capabilities",
|
||||
# Runtime
|
||||
"libc", "allocator",
|
||||
# Integrity
|
||||
|
|
@ -331,19 +332,19 @@ proc parseSemanticVersion*(version: string): SemanticVersion =
|
|||
raise newException(ManifestError,
|
||||
"Invalid semantic version: " & version & " (expected X.Y.Z)")
|
||||
|
||||
var parts = version.split('-', maxsplit=1)
|
||||
var parts = version.split('-', maxsplit = 1)
|
||||
var versionPart = parts[0]
|
||||
var prerelease = ""
|
||||
var build = ""
|
||||
|
||||
if parts.len > 1:
|
||||
var prereleaseAndBuild = parts[1].split('+', maxsplit=1)
|
||||
var prereleaseAndBuild = parts[1].split('+', maxsplit = 1)
|
||||
prerelease = prereleaseAndBuild[0]
|
||||
if prereleaseAndBuild.len > 1:
|
||||
build = prereleaseAndBuild[1]
|
||||
else:
|
||||
# Check for build metadata without prerelease
|
||||
parts = versionPart.split('+', maxsplit=1)
|
||||
parts = versionPart.split('+', maxsplit = 1)
|
||||
versionPart = parts[0]
|
||||
if parts.len > 1:
|
||||
build = parts[1]
|
||||
|
|
@ -442,7 +443,8 @@ proc parseVersionConstraint*(constraint: string): VersionConstraint =
|
|||
let version = parseSemanticVersion(versionStr)
|
||||
result = VersionConstraint(operator: operator, version: version)
|
||||
|
||||
proc satisfiesConstraint*(version: SemanticVersion, constraint: VersionConstraint): bool =
|
||||
proc satisfiesConstraint*(version: SemanticVersion,
|
||||
constraint: VersionConstraint): bool =
|
||||
## Check if a version satisfies a constraint
|
||||
case constraint.operator:
|
||||
of OpAny:
|
||||
|
|
@ -499,7 +501,7 @@ proc createStrictStructRule*(allowed: HashSet[string]): ValidationRule =
|
|||
suggestions: @[
|
||||
"Remove the field",
|
||||
"Check spelling against spec",
|
||||
"This field may be format-specific" ]
|
||||
"This field may be format-specific"]
|
||||
))
|
||||
return valid
|
||||
)
|
||||
|
|
@ -634,7 +636,8 @@ proc createDependencyRule*(): ValidationRule =
|
|||
validate: proc(data: JsonNode, errors: var seq[ManifestError]): bool =
|
||||
var valid = true
|
||||
|
||||
for depField in ["dependencies", "build_dependencies", "optional_dependencies"]:
|
||||
for depField in ["dependencies", "build_dependencies",
|
||||
"optional_dependencies"]:
|
||||
if depField notin data: continue
|
||||
|
||||
let deps = data[depField]
|
||||
|
|
@ -769,7 +772,8 @@ proc checkPlatformCompatibility*(manifest: PackageManifest,
|
|||
# JSON Parsing (Machine-Friendly)
|
||||
# ============================================================================
|
||||
|
||||
proc parseManifestFromJSON*(content: string, parser: var ManifestParser): PackageManifest =
|
||||
proc parseManifestFromJSON*(content: string,
|
||||
parser: var ManifestParser): PackageManifest =
|
||||
## Parse package manifest from JSON format
|
||||
## This is the machine-friendly format for automated systems
|
||||
|
||||
|
|
@ -865,7 +869,8 @@ proc parseManifestFromJSON*(content: string, parser: var ManifestParser): Packag
|
|||
optional: false
|
||||
)
|
||||
if dep.hasKey("version"):
|
||||
depSpec.versionConstraint = parseVersionConstraint(dep["version"].getStr())
|
||||
depSpec.versionConstraint = parseVersionConstraint(dep[
|
||||
"version"].getStr())
|
||||
if dep.hasKey("optional"):
|
||||
depSpec.optional = dep["optional"].getBool()
|
||||
if dep.hasKey("features"):
|
||||
|
|
@ -880,7 +885,8 @@ proc parseManifestFromJSON*(content: string, parser: var ManifestParser): Packag
|
|||
optional: false
|
||||
)
|
||||
if dep.hasKey("version"):
|
||||
depSpec.versionConstraint = parseVersionConstraint(dep["version"].getStr())
|
||||
depSpec.versionConstraint = parseVersionConstraint(dep[
|
||||
"version"].getStr())
|
||||
manifest.buildDependencies.add(depSpec)
|
||||
|
||||
if jsonNode.hasKey("optional_dependencies"):
|
||||
|
|
@ -890,7 +896,8 @@ proc parseManifestFromJSON*(content: string, parser: var ManifestParser): Packag
|
|||
optional: true
|
||||
)
|
||||
if dep.hasKey("version"):
|
||||
depSpec.versionConstraint = parseVersionConstraint(dep["version"].getStr())
|
||||
depSpec.versionConstraint = parseVersionConstraint(dep[
|
||||
"version"].getStr())
|
||||
manifest.optionalDependencies.add(depSpec)
|
||||
|
||||
# Build configuration
|
||||
|
|
@ -1016,7 +1023,8 @@ proc parseManifestFromJSON*(content: string, parser: var ManifestParser): Packag
|
|||
# KDL Parsing (Human-Friendly) - NATIVE IMPLEMENTATION
|
||||
# ============================================================================
|
||||
|
||||
proc parseManifestFromKDL*(content: string, parser: var ManifestParser): PackageManifest =
|
||||
proc parseManifestFromKDL*(content: string,
|
||||
parser: var ManifestParser): PackageManifest =
|
||||
## Parse package manifest from KDL format using NATIVE KDL structures
|
||||
## No JSON conversion - direct KDL parsing for maximum efficiency
|
||||
|
||||
|
|
@ -1333,7 +1341,8 @@ proc parseManifestFromKDL*(content: string, parser: var ManifestParser): Package
|
|||
manifest.desktop = some(dt)
|
||||
|
||||
else:
|
||||
if parser.config.strictMode and child.name notin parser.config.allowedFields:
|
||||
if parser.config.strictMode and child.name notin
|
||||
parser.config.allowedFields:
|
||||
raise newException(ManifestError, "Unknown field: " & child.name)
|
||||
else:
|
||||
parser.warnings.add("Unknown field: " & child.name)
|
||||
|
|
@ -1460,7 +1469,8 @@ proc serializeManifestToJSON*(manifest: PackageManifest): string =
|
|||
for dep in manifest.dependencies:
|
||||
var depObj = %* {"name": dep.name}
|
||||
if dep.versionConstraint.operator != OpAny:
|
||||
depObj["version"] = %($dep.versionConstraint.operator & $dep.versionConstraint.version)
|
||||
depObj["version"] = %($dep.versionConstraint.operator &
|
||||
$dep.versionConstraint.version)
|
||||
if dep.optional:
|
||||
depObj["optional"] = %true
|
||||
if dep.features.len > 0:
|
||||
|
|
@ -1617,7 +1627,8 @@ proc serializeManifestToKDL*(manifest: PackageManifest): string =
|
|||
for dep in manifest.dependencies:
|
||||
result.add(" \"" & dep.name & "\"")
|
||||
if dep.versionConstraint.operator != OpAny:
|
||||
result.add(" version=\"" & $dep.versionConstraint.operator & $dep.versionConstraint.version & "\"")
|
||||
result.add(" version=\"" & $dep.versionConstraint.operator &
|
||||
$dep.versionConstraint.version & "\"")
|
||||
if dep.optional:
|
||||
result.add(" optional=true")
|
||||
if dep.features.len > 0:
|
||||
|
|
@ -1631,7 +1642,8 @@ proc serializeManifestToKDL*(manifest: PackageManifest): string =
|
|||
for dep in manifest.buildDependencies:
|
||||
result.add(" \"" & dep.name & "\"")
|
||||
if dep.versionConstraint.operator != OpAny:
|
||||
result.add(" version=\"" & $dep.versionConstraint.operator & $dep.versionConstraint.version & "\"")
|
||||
result.add(" version=\"" & $dep.versionConstraint.operator &
|
||||
$dep.versionConstraint.version & "\"")
|
||||
result.add("\n")
|
||||
result.add(" }\n")
|
||||
|
||||
|
|
@ -1641,7 +1653,8 @@ proc serializeManifestToKDL*(manifest: PackageManifest): string =
|
|||
for dep in manifest.optionalDependencies:
|
||||
result.add(" \"" & dep.name & "\"")
|
||||
if dep.versionConstraint.operator != OpAny:
|
||||
result.add(" version=\"" & $dep.versionConstraint.operator & $dep.versionConstraint.version & "\"")
|
||||
result.add(" version=\"" & $dep.versionConstraint.operator &
|
||||
$dep.versionConstraint.version & "\"")
|
||||
if dep.features.len > 0:
|
||||
result.add(" features=\"" & dep.features.join(",") & "\"")
|
||||
result.add("\n")
|
||||
|
|
@ -1696,13 +1709,15 @@ proc serializeManifestToKDL*(manifest: PackageManifest): string =
|
|||
if manifest.files.len > 0:
|
||||
result.add("\n files {\n")
|
||||
for file in manifest.files:
|
||||
result.add(" file \"" & file.path & "\" hash=\"" & file.hash & "\" size=" & $file.size & " permissions=\"" & file.permissions & "\"\n")
|
||||
result.add(" file \"" & file.path & "\" hash=\"" & file.hash &
|
||||
"\" size=" & $file.size & " permissions=\"" & file.permissions & "\"\n")
|
||||
result.add(" }\n")
|
||||
|
||||
if manifest.users.len > 0:
|
||||
result.add("\n users {\n")
|
||||
for user in manifest.users:
|
||||
result.add(" \"" & user.name & "\" group=\"" & user.group & "\" shell=\"" & user.shell & "\" home=\"" & user.home & "\"")
|
||||
result.add(" \"" & user.name & "\" group=\"" & user.group &
|
||||
"\" shell=\"" & user.shell & "\" home=\"" & user.home & "\"")
|
||||
if user.uid.isSome:
|
||||
result.add(" uid=" & $user.uid.get())
|
||||
result.add("\n")
|
||||
|
|
@ -1720,7 +1735,8 @@ proc serializeManifestToKDL*(manifest: PackageManifest): string =
|
|||
if manifest.services.len > 0:
|
||||
result.add("\n services {\n")
|
||||
for service in manifest.services:
|
||||
result.add(" \"" & service.name & "\" enabled=" & $service.enabled & " content=" & service.content.escape() & "\n")
|
||||
result.add(" \"" & service.name & "\" enabled=" & $service.enabled &
|
||||
" content=" & service.content.escape() & "\n")
|
||||
result.add(" }\n")
|
||||
|
||||
# Security / Sandbox
|
||||
|
|
@ -1729,7 +1745,8 @@ proc serializeManifestToKDL*(manifest: PackageManifest): string =
|
|||
result.add("\n sandbox level=\"" & $sb.level & "\" {\n")
|
||||
|
||||
# Linux
|
||||
if sb.seccompProfile.isSome or sb.capabilities.len > 0 or sb.namespaces.len > 0:
|
||||
if sb.seccompProfile.isSome or sb.capabilities.len > 0 or
|
||||
sb.namespaces.len > 0:
|
||||
result.add(" linux")
|
||||
if sb.seccompProfile.isSome:
|
||||
result.add(" seccomp=\"" & sb.seccompProfile.get() & "\"")
|
||||
|
|
@ -1767,7 +1784,8 @@ proc serializeManifestToKDL*(manifest: PackageManifest): string =
|
|||
# Desktop Integration
|
||||
if manifest.desktop.isSome:
|
||||
let dt = manifest.desktop.get()
|
||||
result.add("\n desktop display_name=\"" & dt.displayName & "\" terminal=" & $dt.terminal & " startup_notify=" & $dt.startupNotify)
|
||||
result.add("\n desktop display_name=\"" & dt.displayName & "\" terminal=" &
|
||||
$dt.terminal & " startup_notify=" & $dt.startupNotify)
|
||||
if dt.icon.isSome:
|
||||
result.add(" icon=\"" & dt.icon.get() & "\"")
|
||||
if dt.startupWMClass.isSome:
|
||||
|
|
@ -1842,7 +1860,8 @@ proc calculateManifestHash*(manifest: PackageManifest): string =
|
|||
for dep in manifest.dependencies:
|
||||
var depStr = "dep:" & dep.name
|
||||
if dep.versionConstraint.operator != OpAny:
|
||||
depStr.add(":" & $dep.versionConstraint.operator & $dep.versionConstraint.version)
|
||||
depStr.add(":" & $dep.versionConstraint.operator &
|
||||
$dep.versionConstraint.version)
|
||||
if dep.optional:
|
||||
depStr.add(":optional")
|
||||
if dep.features.len > 0:
|
||||
|
|
@ -1855,7 +1874,8 @@ proc calculateManifestHash*(manifest: PackageManifest): string =
|
|||
for dep in manifest.buildDependencies:
|
||||
var depStr = "builddep:" & dep.name
|
||||
if dep.versionConstraint.operator != OpAny:
|
||||
depStr.add(":" & $dep.versionConstraint.operator & $dep.versionConstraint.version)
|
||||
depStr.add(":" & $dep.versionConstraint.operator &
|
||||
$dep.versionConstraint.version)
|
||||
buildDepStrings.add(depStr)
|
||||
components.add(buildDepStrings.sorted().join("|"))
|
||||
|
||||
|
|
@ -1864,7 +1884,8 @@ proc calculateManifestHash*(manifest: PackageManifest): string =
|
|||
for dep in manifest.optionalDependencies:
|
||||
var depStr = "optdep:" & dep.name
|
||||
if dep.versionConstraint.operator != OpAny:
|
||||
depStr.add(":" & $dep.versionConstraint.operator & $dep.versionConstraint.version)
|
||||
depStr.add(":" & $dep.versionConstraint.operator &
|
||||
$dep.versionConstraint.version)
|
||||
if dep.features.len > 0:
|
||||
depStr.add(":features=" & dep.features.sorted().join(","))
|
||||
optDepStrings.add(depStr)
|
||||
|
|
@ -1907,7 +1928,8 @@ proc calculateManifestHash*(manifest: PackageManifest): string =
|
|||
# 13. System Integration (sorted for determinism)
|
||||
var fileStrings: seq[string] = @[]
|
||||
for file in manifest.files:
|
||||
fileStrings.add("file:" & file.path & ":" & file.hash & ":" & $file.size & ":" & file.permissions)
|
||||
fileStrings.add("file:" & file.path & ":" & file.hash & ":" & $file.size &
|
||||
":" & file.permissions)
|
||||
components.add(fileStrings.sorted().join("|"))
|
||||
|
||||
var userStrings: seq[string] = @[]
|
||||
|
|
@ -1926,7 +1948,8 @@ proc calculateManifestHash*(manifest: PackageManifest): string =
|
|||
|
||||
var serviceStrings: seq[string] = @[]
|
||||
for service in manifest.services:
|
||||
serviceStrings.add("service:" & service.name & ":" & $service.enabled & ":" & service.content)
|
||||
serviceStrings.add("service:" & service.name & ":" & $service.enabled &
|
||||
":" & service.content)
|
||||
components.add(serviceStrings.sorted().join("|"))
|
||||
|
||||
# 14. Security / Sandbox
|
||||
|
|
@ -1951,7 +1974,8 @@ proc calculateManifestHash*(manifest: PackageManifest): string =
|
|||
# 15. Desktop Integration
|
||||
if manifest.desktop.isSome:
|
||||
let dt = manifest.desktop.get()
|
||||
var dtStr = "desktop:" & dt.displayName & ":" & $dt.terminal & ":" & $dt.startupNotify
|
||||
var dtStr = "desktop:" & dt.displayName & ":" & $dt.terminal & ":" &
|
||||
$dt.startupNotify
|
||||
|
||||
if dt.icon.isSome:
|
||||
dtStr.add(":icon=" & dt.icon.get())
|
||||
|
|
@ -1972,7 +1996,8 @@ proc calculateManifestHash*(manifest: PackageManifest): string =
|
|||
|
||||
return $hash
|
||||
|
||||
proc verifyManifestHash*(manifest: PackageManifest, expectedHash: string): bool =
|
||||
proc verifyManifestHash*(manifest: PackageManifest,
|
||||
expectedHash: string): bool =
|
||||
## Verify that a manifest matches the expected hash
|
||||
## Returns true if hash matches, false otherwise
|
||||
let calculatedHash = calculateManifestHash(manifest)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
import std/[os, strutils, times, options, sequtils, osproc, logging]
|
||||
import nip/cas
|
||||
import nip/xxhash
|
||||
import nip/xxh
|
||||
import nip/nexter_manifest
|
||||
|
||||
type
|
||||
|
|
@ -97,7 +97,8 @@ proc parseNEXTER*(path: string): NEXTERContainer =
|
|||
try:
|
||||
# Extract archive using tar with zstd decompression
|
||||
# Using --auto-compress lets tar detect compression automatically
|
||||
let extractCmd = "tar --auto-compress -xf " & quoteShell(path) & " -C " & quoteShell(tempDir)
|
||||
let extractCmd = "tar --auto-compress -xf " & quoteShell(path) & " -C " &
|
||||
quoteShell(tempDir)
|
||||
let exitCode = execCmd(extractCmd)
|
||||
|
||||
if exitCode != 0:
|
||||
|
|
@ -208,7 +209,8 @@ proc createNEXTER*(manifest: NEXTERManifest, environment: string, chunks: seq[Ch
|
|||
writeFile(tempDir / "signature.sig", signature)
|
||||
|
||||
# Create tar.zst archive
|
||||
let createCmd = "tar --auto-compress -cf " & quoteShell(outputPath) & " -C " & quoteShell(tempDir) & " ."
|
||||
let createCmd = "tar --auto-compress -cf " & quoteShell(outputPath) &
|
||||
" -C " & quoteShell(tempDir) & " ."
|
||||
let exitCode = execCmd(createCmd)
|
||||
|
||||
if exitCode != 0:
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
import std/[os, strutils, times, json, options, sequtils]
|
||||
import nip/cas
|
||||
import nip/xxhash
|
||||
import nip/xxh
|
||||
import nip/npk_manifest
|
||||
import nip/manifest_parser
|
||||
|
||||
|
|
@ -97,7 +97,8 @@ proc parseNPK*(path: string): NPKPackage =
|
|||
try:
|
||||
# Extract archive using tar with zstd decompression
|
||||
# Using --auto-compress lets tar detect compression automatically
|
||||
let extractCmd = "tar --auto-compress -xf " & quoteShell(path) & " -C " & quoteShell(tempDir)
|
||||
let extractCmd = "tar --auto-compress -xf " & quoteShell(path) & " -C " &
|
||||
quoteShell(tempDir)
|
||||
let extractResult = execShellCmd(extractCmd)
|
||||
|
||||
if extractResult != 0:
|
||||
|
|
@ -343,11 +344,13 @@ proc packageSize*(pkg: NPKPackage): int64 =
|
|||
|
||||
proc `$`*(pkg: NPKPackage): string =
|
||||
## Convert NPK package to human-readable string
|
||||
result = "NPK Package: " & pkg.manifest.name & " v" & manifest_parser.`$`(pkg.manifest.version) & "\n"
|
||||
result = "NPK Package: " & pkg.manifest.name & " v" & manifest_parser.`$`(
|
||||
pkg.manifest.version) & "\n"
|
||||
result.add("Archive: " & pkg.archivePath & "\n")
|
||||
result.add("Chunks: " & $pkg.chunks.len & "\n")
|
||||
result.add("Total Size: " & $(packageSize(pkg) div 1024) & " KB\n")
|
||||
result.add("Signature: " & (if pkg.signature.len > 0: "Present" else: "Missing") & "\n")
|
||||
result.add("Signature: " & (if pkg.signature.len >
|
||||
0: "Present" else: "Missing") & "\n")
|
||||
|
||||
# ============================================================================
|
||||
# Error Formatting
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import std/[strutils]
|
|||
# We'll use a conditional import to handle the case where xxhash isn't installed yet
|
||||
when defined(useXXHash):
|
||||
import xxhash
|
||||
import nint128 # Required for UInt128 toHex
|
||||
else:
|
||||
# Fallback implementation using a simple hash for development
|
||||
# This will be replaced with actual xxhash once the library is installed
|
||||
|
|
@ -41,7 +42,8 @@ when defined(useXXHash):
|
|||
proc calculateXXH3*(data: seq[byte]): XXH3Hash =
|
||||
## Calculate xxh3-128 hash of a byte sequence
|
||||
## Returns hash in format: "xxh3-<hex-digest>"
|
||||
let hash128 = XXH3_128bits(cast[ptr UncheckedArray[byte]](unsafeAddr data[0]), data.len)
|
||||
let hash128 = XXH3_128bits(cast[ptr UncheckedArray[byte]](unsafeAddr data[
|
||||
0]), csize_t(data.len))
|
||||
let hexDigest = hash128.toHex().toLowerAscii()
|
||||
result = XXH3Hash("xxh3-" & hexDigest)
|
||||
|
||||
Loading…
Reference in New Issue