Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'

This commit is contained in:
ruv
2026-02-28 14:39:40 -05:00
7854 changed files with 3522914 additions and 0 deletions

View File

@@ -0,0 +1,753 @@
/**
* @ruvector/edge-net Model Integrity System
*
* Content-addressed integrity with:
* - Canonical JSON signing
* - Threshold signatures with trust roots
* - Merkle chunk verification for streaming
* - Transparency log integration
*
* Design principle: Manifest is truth, everything else is replaceable.
*
* @module @ruvector/edge-net/models/integrity
*/
import { createHash } from 'crypto';
// ============================================================================
// CANONICAL JSON
// ============================================================================
/**
* Canonical JSON encoding for deterministic signing.
* - Keys sorted lexicographically
* - No whitespace
* - Unicode escaped consistently
* - Numbers without trailing zeros
*/
export function canonicalize(obj) {
if (obj === null || obj === undefined) {
return 'null';
}
if (typeof obj === 'boolean') {
return obj ? 'true' : 'false';
}
if (typeof obj === 'number') {
if (!Number.isFinite(obj)) {
throw new Error('Cannot canonicalize Infinity or NaN');
}
// Use JSON for consistent number formatting
return JSON.stringify(obj);
}
if (typeof obj === 'string') {
// Escape unicode consistently
return JSON.stringify(obj).replace(/[\u007f-\uffff]/g, (c) => {
return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
});
}
if (Array.isArray(obj)) {
const elements = obj.map(canonicalize);
return '[' + elements.join(',') + ']';
}
if (typeof obj === 'object') {
const keys = Object.keys(obj).sort();
const pairs = keys
.filter(k => obj[k] !== undefined)
.map(k => canonicalize(k) + ':' + canonicalize(obj[k]));
return '{' + pairs.join(',') + '}';
}
throw new Error(`Cannot canonicalize type: ${typeof obj}`);
}
/**
* Hash canonical JSON bytes
*/
export function hashCanonical(obj, algorithm = 'sha256') {
const canonical = canonicalize(obj);
const hash = createHash(algorithm);
hash.update(canonical, 'utf8');
return hash.digest('hex');
}
// ============================================================================
// TRUST ROOT
// ============================================================================
/**
* Built-in root keys shipped with SDK.
* These are the only keys trusted by default.
*/
export const BUILTIN_ROOT_KEYS = Object.freeze({
'ruvector-root-2024': {
keyId: 'ruvector-root-2024',
algorithm: 'ed25519',
publicKey: 'MCowBQYDK2VwAyEAaGVsbG8td29ybGQta2V5LXBsYWNlaG9sZGVy', // Placeholder
validFrom: '2024-01-01T00:00:00Z',
validUntil: '2030-01-01T00:00:00Z',
capabilities: ['sign-manifest', 'sign-adapter', 'delegate'],
},
'ruvector-models-2024': {
keyId: 'ruvector-models-2024',
algorithm: 'ed25519',
publicKey: 'MCowBQYDK2VwAyEAbW9kZWxzLWtleS1wbGFjZWhvbGRlcg==', // Placeholder
validFrom: '2024-01-01T00:00:00Z',
validUntil: '2026-01-01T00:00:00Z',
capabilities: ['sign-manifest'],
delegatedBy: 'ruvector-root-2024',
},
});
/**
* Trust root configuration
*/
export class TrustRoot {
constructor(options = {}) {
// Start with built-in keys
this.trustedKeys = new Map();
for (const [id, key] of Object.entries(BUILTIN_ROOT_KEYS)) {
this.trustedKeys.set(id, key);
}
// Add enterprise keys if configured
if (options.enterpriseKeys) {
for (const key of options.enterpriseKeys) {
this.addEnterpriseKey(key);
}
}
// Revocation list
this.revokedKeys = new Set(options.revokedKeys || []);
// Minimum signatures required for official releases
this.minimumSignaturesRequired = options.minimumSignaturesRequired || 1;
// Threshold for high-security operations (e.g., new root key)
this.thresholdSignaturesRequired = options.thresholdSignaturesRequired || 2;
}
/**
* Add an enterprise root key (for private deployments)
*/
addEnterpriseKey(key) {
if (!key.keyId || !key.publicKey) {
throw new Error('Enterprise key must have keyId and publicKey');
}
// Verify delegation chain if not self-signed
if (key.delegatedBy && key.delegationSignature) {
const delegator = this.trustedKeys.get(key.delegatedBy);
if (!delegator) {
throw new Error(`Unknown delegator: ${key.delegatedBy}`);
}
if (!delegator.capabilities.includes('delegate')) {
throw new Error(`Key ${key.delegatedBy} cannot delegate`);
}
// In production, verify delegationSignature here
}
this.trustedKeys.set(key.keyId, {
...key,
isEnterprise: true,
});
}
/**
* Revoke a key
*/
revokeKey(keyId, reason) {
this.revokedKeys.add(keyId);
console.warn(`[TrustRoot] Key revoked: ${keyId} - ${reason}`);
}
/**
* Check if a key is trusted for a capability
*/
isKeyTrusted(keyId, capability = 'sign-manifest') {
if (this.revokedKeys.has(keyId)) {
return false;
}
const key = this.trustedKeys.get(keyId);
if (!key) {
return false;
}
// Check validity period
const now = new Date();
if (key.validFrom && new Date(key.validFrom) > now) {
return false;
}
if (key.validUntil && new Date(key.validUntil) < now) {
return false;
}
// Check capability
if (!key.capabilities.includes(capability)) {
return false;
}
return true;
}
/**
* Get public key for verification
*/
getPublicKey(keyId) {
const key = this.trustedKeys.get(keyId);
if (!key || this.revokedKeys.has(keyId)) {
return null;
}
return key.publicKey;
}
/**
* Verify signature set meets threshold
*/
verifySignatureThreshold(signatures, requiredCount = null) {
const required = requiredCount || this.minimumSignaturesRequired;
let validCount = 0;
const validSigners = [];
for (const sig of signatures) {
if (this.isKeyTrusted(sig.keyId, 'sign-manifest')) {
// In production, verify actual signature here
validCount++;
validSigners.push(sig.keyId);
}
}
return {
valid: validCount >= required,
validCount,
required,
validSigners,
};
}
/**
* Export current trust configuration
*/
export() {
return {
trustedKeys: Object.fromEntries(this.trustedKeys),
revokedKeys: Array.from(this.revokedKeys),
minimumSignaturesRequired: this.minimumSignaturesRequired,
thresholdSignaturesRequired: this.thresholdSignaturesRequired,
};
}
}
// ============================================================================
// MERKLE CHUNK VERIFICATION
// ============================================================================
/**
* Compute Merkle tree from chunk hashes
*/
export function computeMerkleRoot(chunkHashes) {
if (chunkHashes.length === 0) {
return hashCanonical({ empty: true });
}
if (chunkHashes.length === 1) {
return chunkHashes[0];
}
// Build tree bottom-up
let level = [...chunkHashes];
while (level.length > 1) {
const nextLevel = [];
for (let i = 0; i < level.length; i += 2) {
const left = level[i];
const right = level[i + 1] || left; // Duplicate last if odd
const combined = createHash('sha256')
.update(left, 'hex')
.update(right, 'hex')
.digest('hex');
nextLevel.push(combined);
}
level = nextLevel;
}
return level[0];
}
/**
* Generate Merkle proof for a chunk
*/
export function generateMerkleProof(chunkHashes, chunkIndex) {
const proof = [];
let level = [...chunkHashes];
let index = chunkIndex;
while (level.length > 1) {
const isRight = index % 2 === 1;
const siblingIndex = isRight ? index - 1 : index + 1;
if (siblingIndex < level.length) {
proof.push({
hash: level[siblingIndex],
position: isRight ? 'left' : 'right',
});
} else {
// Odd number, sibling is self
proof.push({
hash: level[index],
position: 'right',
});
}
// Move up
const nextLevel = [];
for (let i = 0; i < level.length; i += 2) {
const left = level[i];
const right = level[i + 1] || left;
nextLevel.push(
createHash('sha256')
.update(left, 'hex')
.update(right, 'hex')
.digest('hex')
);
}
level = nextLevel;
index = Math.floor(index / 2);
}
return proof;
}
/**
* Verify a chunk against Merkle root
*/
export function verifyMerkleProof(chunkHash, chunkIndex, proof, merkleRoot) {
let computed = chunkHash;
for (const step of proof) {
const left = step.position === 'left' ? step.hash : computed;
const right = step.position === 'right' ? step.hash : computed;
computed = createHash('sha256')
.update(left, 'hex')
.update(right, 'hex')
.digest('hex');
}
return computed === merkleRoot;
}
/**
* Chunk a buffer and compute hashes
*/
export function chunkAndHash(buffer, chunkSize = 256 * 1024) {
const chunks = [];
const hashes = [];
for (let offset = 0; offset < buffer.length; offset += chunkSize) {
const chunk = buffer.slice(offset, offset + chunkSize);
chunks.push(chunk);
hashes.push(
createHash('sha256').update(chunk).digest('hex')
);
}
return {
chunks,
chunkHashes: hashes,
chunkSize,
chunkCount: chunks.length,
totalSize: buffer.length,
merkleRoot: computeMerkleRoot(hashes),
};
}
// ============================================================================
// MANIFEST INTEGRITY
// ============================================================================
/**
* Integrity block for manifests
*/
export function createIntegrityBlock(manifest, chunkInfo) {
// Create the signed payload (everything except signatures)
const signedPayload = {
model: manifest.model,
version: manifest.version,
artifacts: manifest.artifacts,
provenance: manifest.provenance,
capabilities: manifest.capabilities,
timestamp: new Date().toISOString(),
};
const signedPayloadHash = hashCanonical(signedPayload);
return {
manifestHash: hashCanonical(manifest),
signedPayloadHash,
merkleRoot: chunkInfo.merkleRoot,
chunking: {
chunkSize: chunkInfo.chunkSize,
chunkCount: chunkInfo.chunkCount,
chunkHashes: chunkInfo.chunkHashes,
},
signatures: [], // To be filled by signing process
};
}
/**
* Provenance block for manifests
*/
export function createProvenanceBlock(options = {}) {
return {
builtBy: {
tool: options.tool || '@ruvector/model-optimizer',
version: options.toolVersion || '1.0.0',
commit: options.commit || 'unknown',
},
optimizationRecipeHash: options.recipeHash || null,
calibrationDatasetHash: options.calibrationHash || null,
parentLineage: options.parentLineage || null,
buildTimestamp: new Date().toISOString(),
environment: {
platform: process.platform,
arch: process.arch,
nodeVersion: process.version,
},
};
}
/**
* Full manifest with integrity
*/
export function createSecureManifest(model, artifacts, options = {}) {
const manifest = {
schemaVersion: '2.0.0',
model: {
id: model.id,
name: model.name,
version: model.version,
type: model.type, // 'embedding' | 'generation'
tier: model.tier, // 'micro' | 'small' | 'large'
capabilities: model.capabilities || [],
memoryRequirement: model.memoryRequirement,
},
artifacts: artifacts.map(a => ({
path: a.path,
size: a.size,
sha256: a.sha256,
format: a.format,
quantization: a.quantization,
})),
distribution: {
gcs: options.gcsUrl,
ipfs: options.ipfsCid,
fallbackUrls: options.fallbackUrls || [],
},
provenance: createProvenanceBlock(options.provenance || {}),
capabilities: model.capabilities || [],
};
// Add integrity block if chunk info provided
if (options.chunkInfo) {
manifest.integrity = createIntegrityBlock(manifest, options.chunkInfo);
}
// Add trust metadata
manifest.trust = {
trustedKeySetId: options.trustedKeySetId || 'ruvector-default-2024',
minimumSignaturesRequired: options.minimumSignaturesRequired || 1,
};
return manifest;
}
// ============================================================================
// MANIFEST VERIFICATION
// ============================================================================
/**
* Verify a manifest's integrity
*/
export class ManifestVerifier {
constructor(trustRoot = null) {
this.trustRoot = trustRoot || new TrustRoot();
}
/**
* Full verification of a manifest
*/
verify(manifest) {
const errors = [];
const warnings = [];
// 1. Schema version check
if (!manifest.schemaVersion || manifest.schemaVersion < '2.0.0') {
warnings.push('Manifest uses old schema version');
}
// 2. Verify integrity block
if (manifest.integrity) {
// Check manifest hash
const computed = hashCanonical(manifest);
// Note: manifestHash is computed before adding integrity, so we skip this
// Check signed payload hash
const signedPayload = {
model: manifest.model,
version: manifest.version,
artifacts: manifest.artifacts,
provenance: manifest.provenance,
capabilities: manifest.capabilities,
timestamp: manifest.integrity.timestamp,
};
const computedPayloadHash = hashCanonical(signedPayload);
// 3. Verify signatures meet threshold
if (manifest.integrity.signatures?.length > 0) {
const sigResult = this.trustRoot.verifySignatureThreshold(
manifest.integrity.signatures,
manifest.trust?.minimumSignaturesRequired
);
if (!sigResult.valid) {
errors.push(`Insufficient valid signatures: ${sigResult.validCount}/${sigResult.required}`);
}
} else {
warnings.push('No signatures present');
}
// 4. Verify Merkle root matches chunk hashes
if (manifest.integrity.chunking) {
const computedRoot = computeMerkleRoot(manifest.integrity.chunking.chunkHashes);
if (computedRoot !== manifest.integrity.merkleRoot) {
errors.push('Merkle root mismatch');
}
}
} else {
warnings.push('No integrity block present');
}
// 5. Check provenance
if (!manifest.provenance) {
warnings.push('No provenance information');
}
// 6. Check required fields
if (!manifest.model?.id) errors.push('Missing model.id');
if (!manifest.model?.version) errors.push('Missing model.version');
if (!manifest.artifacts?.length) errors.push('No artifacts defined');
return {
valid: errors.length === 0,
errors,
warnings,
trust: manifest.trust,
provenance: manifest.provenance,
};
}
/**
* Verify a single chunk during streaming download
*/
verifyChunk(chunkData, chunkIndex, manifest) {
if (!manifest.integrity?.chunking) {
return { valid: false, error: 'No chunking info in manifest' };
}
const expectedHash = manifest.integrity.chunking.chunkHashes[chunkIndex];
if (!expectedHash) {
return { valid: false, error: `No hash for chunk ${chunkIndex}` };
}
const actualHash = createHash('sha256').update(chunkData).digest('hex');
if (actualHash !== expectedHash) {
return {
valid: false,
error: `Chunk ${chunkIndex} hash mismatch`,
expected: expectedHash,
actual: actualHash,
};
}
return { valid: true, chunkIndex, hash: actualHash };
}
}
// ============================================================================
// TRANSPARENCY LOG
// ============================================================================
/**
* Entry in the transparency log
*/
export function createLogEntry(manifest, publisherKeyId) {
return {
manifestHash: hashCanonical(manifest),
modelId: manifest.model.id,
version: manifest.model.version,
publisherKeyId,
timestamp: new Date().toISOString(),
signedPayloadHash: manifest.integrity?.signedPayloadHash,
};
}
/**
* Simple append-only transparency log
* In production, this would be backed by a Merkle tree or blockchain
*/
export class TransparencyLog {
constructor(options = {}) {
this.entries = [];
this.indexByModel = new Map();
this.indexByHash = new Map();
this.logRoot = null;
}
/**
* Append an entry to the log
*/
append(entry) {
const index = this.entries.length;
// Compute log entry hash including previous
const logEntryHash = hashCanonical({
...entry,
index,
previousHash: this.logRoot,
});
const fullEntry = {
...entry,
index,
previousHash: this.logRoot,
logEntryHash,
};
this.entries.push(fullEntry);
this.logRoot = logEntryHash;
// Update indexes
if (!this.indexByModel.has(entry.modelId)) {
this.indexByModel.set(entry.modelId, []);
}
this.indexByModel.get(entry.modelId).push(index);
this.indexByHash.set(entry.manifestHash, index);
return fullEntry;
}
/**
* Generate inclusion proof
*/
getInclusionProof(manifestHash) {
const index = this.indexByHash.get(manifestHash);
if (index === undefined) {
return null;
}
const entry = this.entries[index];
const proof = [];
// Simple chain proof (in production, use Merkle tree)
for (let i = index; i < this.entries.length; i++) {
proof.push({
index: i,
logEntryHash: this.entries[i].logEntryHash,
});
}
return {
entry,
proof,
currentRoot: this.logRoot,
logLength: this.entries.length,
};
}
/**
* Verify inclusion proof
*/
verifyInclusionProof(proof) {
if (!proof || !proof.entry || !proof.proof.length) {
return false;
}
// Verify chain
let expectedHash = proof.entry.logEntryHash;
for (let i = 1; i < proof.proof.length; i++) {
const entry = proof.proof[i];
// Verify chain continuity
if (i < proof.proof.length - 1) {
// Each entry should reference the previous
}
}
return proof.proof[proof.proof.length - 1].logEntryHash === proof.currentRoot;
}
/**
* Get history for a model
*/
getModelHistory(modelId) {
const indices = this.indexByModel.get(modelId) || [];
return indices.map(i => this.entries[i]);
}
/**
* Export log for persistence
*/
export() {
return {
entries: this.entries,
logRoot: this.logRoot,
};
}
/**
* Import log
*/
import(data) {
this.entries = data.entries || [];
this.logRoot = data.logRoot;
// Rebuild indexes
this.indexByModel.clear();
this.indexByHash.clear();
for (let i = 0; i < this.entries.length; i++) {
const entry = this.entries[i];
if (!this.indexByModel.has(entry.modelId)) {
this.indexByModel.set(entry.modelId, []);
}
this.indexByModel.get(entry.modelId).push(i);
this.indexByHash.set(entry.manifestHash, i);
}
}
}
// ============================================================================
// EXPORTS
// ============================================================================
export default {
canonicalize,
hashCanonical,
TrustRoot,
BUILTIN_ROOT_KEYS,
computeMerkleRoot,
generateMerkleProof,
verifyMerkleProof,
chunkAndHash,
createIntegrityBlock,
createProvenanceBlock,
createSecureManifest,
ManifestVerifier,
createLogEntry,
TransparencyLog,
};