Files
wifi-densepose/vendor/ruvector/examples/edge-net/pkg/models/adapter-security.js

793 lines
23 KiB
JavaScript

/**
* @ruvector/edge-net Adapter Security
*
* Security for MicroLoRA adapters:
* - Quarantine before activation
* - Local evaluation gating
* - Base model matching
* - Signature verification
* - Merge lineage tracking
*
* Invariant: Adapters never applied without full verification.
*
* @module @ruvector/edge-net/models/adapter-security
*/
import { createHash } from 'crypto';
import { canonicalize, hashCanonical, TrustRoot, ManifestVerifier } from './integrity.js';
// ============================================================================
// ADAPTER VERIFICATION
// ============================================================================
/**
* Adapter verification rules
*/
export const ADAPTER_REQUIREMENTS = Object.freeze({
// Base model must match exactly
requireExactBaseMatch: true,
// Checksum must match manifest
requireChecksumMatch: true,
// Signature must be verified
requireSignature: true,
// Must pass local evaluation OR have trusted quality proof
requireQualityGate: true,
// Minimum evaluation score to pass gate (0-1)
minEvaluationScore: 0.7,
// Maximum adapter size relative to base model
maxAdapterSizeRatio: 0.1, // 10% of base model
// Trusted quality proof publishers
trustedQualityProvers: ['ruvector-eval-2024', 'community-eval-2024'],
});
/**
* Adapter manifest structure
*/
export function createAdapterManifest(adapter) {
return {
schemaVersion: '2.0.0',
adapter: {
id: adapter.id,
name: adapter.name,
version: adapter.version,
baseModelId: adapter.baseModelId,
baseModelVersion: adapter.baseModelVersion,
rank: adapter.rank,
alpha: adapter.alpha,
targetModules: adapter.targetModules || ['q_proj', 'v_proj'],
},
artifacts: [{
path: adapter.path,
size: adapter.size,
sha256: adapter.sha256,
format: 'safetensors',
}],
quality: {
evaluationScore: adapter.evaluationScore,
evaluationDataset: adapter.evaluationDataset,
evaluationProof: adapter.evaluationProof,
domain: adapter.domain,
capabilities: adapter.capabilities,
},
lineage: adapter.lineage || null,
provenance: {
creator: adapter.creator,
createdAt: adapter.createdAt || new Date().toISOString(),
trainedOn: adapter.trainedOn,
trainingConfig: adapter.trainingConfig,
},
integrity: {
manifestHash: null, // Computed
signatures: [],
},
};
}
// ============================================================================
// QUARANTINE SYSTEM
// ============================================================================
/**
* Quarantine states for adapters
*/
export const QuarantineState = Object.freeze({
PENDING: 'pending',
EVALUATING: 'evaluating',
PASSED: 'passed',
FAILED: 'failed',
TRUSTED: 'trusted', // Has trusted quality proof
});
/**
* Quarantine manager for adapter verification
*/
export class AdapterQuarantine {
constructor(options = {}) {
this.trustRoot = options.trustRoot || new TrustRoot();
this.requirements = { ...ADAPTER_REQUIREMENTS, ...options.requirements };
// Quarantined adapters awaiting evaluation
this.quarantine = new Map();
// Approved adapters
this.approved = new Map();
// Failed adapters (blocked)
this.blocked = new Map();
// Evaluation test sets by domain
this.testSets = new Map();
}
/**
* Register a test set for a domain
*/
registerTestSet(domain, testCases) {
this.testSets.set(domain, testCases);
}
/**
* Quarantine an adapter for evaluation
*/
async quarantineAdapter(manifest, adapterData) {
const adapterId = manifest.adapter.id;
// 1. Verify checksum
const actualHash = createHash('sha256')
.update(Buffer.from(adapterData))
.digest('hex');
if (actualHash !== manifest.artifacts[0].sha256) {
const failure = {
adapterId,
reason: 'checksum_mismatch',
expected: manifest.artifacts[0].sha256,
actual: actualHash,
timestamp: Date.now(),
};
this.blocked.set(adapterId, failure);
return { state: QuarantineState.FAILED, failure };
}
// 2. Verify signature if required
if (this.requirements.requireSignature) {
const sigResult = this._verifySignatures(manifest);
if (!sigResult.valid) {
const failure = {
adapterId,
reason: 'invalid_signature',
details: sigResult.errors,
timestamp: Date.now(),
};
this.blocked.set(adapterId, failure);
return { state: QuarantineState.FAILED, failure };
}
}
// 3. Check for trusted quality proof
if (manifest.quality?.evaluationProof) {
const proofValid = await this._verifyQualityProof(manifest);
if (proofValid) {
this.approved.set(adapterId, {
manifest,
state: QuarantineState.TRUSTED,
approvedAt: Date.now(),
});
return { state: QuarantineState.TRUSTED };
}
}
// 4. Add to quarantine for local evaluation
this.quarantine.set(adapterId, {
manifest,
adapterData,
state: QuarantineState.PENDING,
quarantinedAt: Date.now(),
});
return { state: QuarantineState.PENDING };
}
/**
* Evaluate a quarantined adapter locally
*/
async evaluateAdapter(adapterId, inferenceSession) {
const quarantined = this.quarantine.get(adapterId);
if (!quarantined) {
throw new Error(`Adapter ${adapterId} not in quarantine`);
}
quarantined.state = QuarantineState.EVALUATING;
const manifest = quarantined.manifest;
const domain = manifest.quality?.domain || 'general';
// Get test set for domain
const testSet = this.testSets.get(domain) || this._getDefaultTestSet();
if (testSet.length === 0) {
throw new Error(`No test set available for domain: ${domain}`);
}
// Run evaluation
const results = await this._runEvaluation(
quarantined.adapterData,
testSet,
inferenceSession,
manifest.adapter.baseModelId
);
// Check if passed
const passed = results.score >= this.requirements.minEvaluationScore;
if (passed) {
this.quarantine.delete(adapterId);
this.approved.set(adapterId, {
manifest,
state: QuarantineState.PASSED,
evaluationResults: results,
approvedAt: Date.now(),
});
return { state: QuarantineState.PASSED, results };
} else {
this.quarantine.delete(adapterId);
this.blocked.set(adapterId, {
adapterId,
reason: 'evaluation_failed',
score: results.score,
required: this.requirements.minEvaluationScore,
timestamp: Date.now(),
});
return { state: QuarantineState.FAILED, results };
}
}
/**
* Check if an adapter can be used
*/
canUseAdapter(adapterId, baseModelId) {
const approved = this.approved.get(adapterId);
if (!approved) {
return { allowed: false, reason: 'not_approved' };
}
// Verify base model match
if (this.requirements.requireExactBaseMatch) {
const expectedBase = approved.manifest.adapter.baseModelId;
if (expectedBase !== baseModelId) {
return {
allowed: false,
reason: 'base_model_mismatch',
expected: expectedBase,
actual: baseModelId,
};
}
}
return { allowed: true, state: approved.state };
}
/**
* Get approved adapter data
*/
getApprovedAdapter(adapterId) {
return this.approved.get(adapterId) || null;
}
/**
* Verify signatures on adapter manifest
*/
_verifySignatures(manifest) {
if (!manifest.integrity?.signatures?.length) {
return { valid: false, errors: ['No signatures present'] };
}
return this.trustRoot.verifySignatureThreshold(
manifest.integrity.signatures,
1 // At least one valid signature for adapters
);
}
/**
* Verify a trusted quality proof
*/
async _verifyQualityProof(manifest) {
const proof = manifest.quality.evaluationProof;
if (!proof) return false;
// Check if prover is trusted
if (!this.requirements.trustedQualityProvers.includes(proof.proverId)) {
return false;
}
// Verify proof signature
const proofPayload = {
adapterId: manifest.adapter.id,
evaluationScore: manifest.quality.evaluationScore,
evaluationDataset: manifest.quality.evaluationDataset,
timestamp: proof.timestamp,
};
// In production, verify actual signature here
return proof.signature && proof.proverId;
}
/**
* Run local evaluation on adapter
*/
async _runEvaluation(adapterData, testSet, inferenceSession, baseModelId) {
const results = {
total: testSet.length,
passed: 0,
failed: 0,
errors: 0,
details: [],
};
for (const testCase of testSet) {
try {
// Apply adapter temporarily
await inferenceSession.loadAdapter(adapterData, { temporary: true });
// Run inference
const output = await inferenceSession.generate(testCase.input, {
maxTokens: testCase.maxTokens || 64,
});
// Check against expected
const passed = this._checkOutput(output, testCase.expected, testCase.criteria);
results.details.push({
input: testCase.input.slice(0, 50),
passed,
});
if (passed) {
results.passed++;
} else {
results.failed++;
}
// Unload temporary adapter
await inferenceSession.unloadAdapter();
} catch (error) {
results.errors++;
results.details.push({
input: testCase.input.slice(0, 50),
error: error.message,
});
}
}
results.score = results.passed / results.total;
return results;
}
/**
* Check if output matches expected criteria
*/
_checkOutput(output, expected, criteria = 'contains') {
const outputLower = output.toLowerCase();
const expectedLower = expected.toLowerCase();
switch (criteria) {
case 'exact':
return output.trim() === expected.trim();
case 'contains':
return outputLower.includes(expectedLower);
case 'startsWith':
return outputLower.startsWith(expectedLower);
case 'regex':
return new RegExp(expected).test(output);
default:
return outputLower.includes(expectedLower);
}
}
/**
* Get default test set for unknown domains
*/
_getDefaultTestSet() {
return [
{
input: 'Hello, how are you?',
expected: 'hello',
criteria: 'contains',
},
{
input: 'What is 2 + 2?',
expected: '4',
criteria: 'contains',
},
{
input: 'Translate to French: hello',
expected: 'bonjour',
criteria: 'contains',
},
];
}
/**
* Export quarantine state
*/
export() {
return {
quarantine: Array.from(this.quarantine.entries()),
approved: Array.from(this.approved.entries()),
blocked: Array.from(this.blocked.entries()),
};
}
/**
* Import quarantine state
*/
import(data) {
if (data.quarantine) {
this.quarantine = new Map(data.quarantine);
}
if (data.approved) {
this.approved = new Map(data.approved);
}
if (data.blocked) {
this.blocked = new Map(data.blocked);
}
}
}
// ============================================================================
// MERGE LINEAGE TRACKING
// ============================================================================
/**
* Lineage entry for merged adapters
*/
export function createMergeLineage(options) {
return {
parentAdapterIds: options.parentIds,
mergeMethod: options.method, // 'ties', 'dare', 'task_arithmetic', 'linear'
mergeParameters: options.parameters, // Method-specific params
mergeSeed: options.seed || Math.floor(Math.random() * 2 ** 32),
evaluationMetrics: options.metrics || {},
mergerIdentity: options.mergerId,
mergeTimestamp: new Date().toISOString(),
signature: null, // To be filled after signing
};
}
/**
* Lineage tracker for adapter merges
*/
export class AdapterLineage {
constructor(options = {}) {
this.trustRoot = options.trustRoot || new TrustRoot();
// DAG of adapter lineage
this.lineageGraph = new Map();
// Root adapters (no parents)
this.roots = new Set();
}
/**
* Register a new adapter in lineage
*/
registerAdapter(adapterId, manifest) {
const lineage = manifest.lineage;
const node = {
adapterId,
version: manifest.adapter.version,
baseModelId: manifest.adapter.baseModelId,
parents: lineage?.parentAdapterIds || [],
children: [],
lineage,
registeredAt: Date.now(),
};
this.lineageGraph.set(adapterId, node);
// Update parent-child relationships
if (node.parents.length === 0) {
this.roots.add(adapterId);
} else {
for (const parentId of node.parents) {
const parent = this.lineageGraph.get(parentId);
if (parent) {
parent.children.push(adapterId);
}
}
}
return node;
}
/**
* Get full ancestry path for an adapter
*/
getAncestry(adapterId) {
const ancestry = [];
const visited = new Set();
const queue = [adapterId];
while (queue.length > 0) {
const current = queue.shift();
if (visited.has(current)) continue;
visited.add(current);
const node = this.lineageGraph.get(current);
if (node) {
ancestry.push({
adapterId: current,
version: node.version,
baseModelId: node.baseModelId,
mergeMethod: node.lineage?.mergeMethod,
});
for (const parentId of node.parents) {
queue.push(parentId);
}
}
}
return ancestry;
}
/**
* Verify lineage integrity
*/
verifyLineage(adapterId) {
const node = this.lineageGraph.get(adapterId);
if (!node) {
return { valid: false, error: 'Adapter not found' };
}
const errors = [];
// Check all parents exist
for (const parentId of node.parents) {
if (!this.lineageGraph.has(parentId)) {
errors.push(`Missing parent: ${parentId}`);
}
}
// Verify lineage signature if present
if (node.lineage?.signature) {
// In production, verify actual signature
const sigValid = true; // Placeholder
if (!sigValid) {
errors.push('Invalid lineage signature');
}
}
// Check for circular references
const hasCircle = this._detectCircle(adapterId, new Set());
if (hasCircle) {
errors.push('Circular lineage detected');
}
return {
valid: errors.length === 0,
errors,
ancestry: this.getAncestry(adapterId),
};
}
/**
* Detect circular references in lineage
*/
_detectCircle(adapterId, visited) {
if (visited.has(adapterId)) return true;
visited.add(adapterId);
const node = this.lineageGraph.get(adapterId);
if (!node) return false;
for (const parentId of node.parents) {
if (this._detectCircle(parentId, new Set(visited))) {
return true;
}
}
return false;
}
/**
* Get descendants of an adapter
*/
getDescendants(adapterId) {
const descendants = [];
const queue = [adapterId];
const visited = new Set();
while (queue.length > 0) {
const current = queue.shift();
if (visited.has(current)) continue;
visited.add(current);
const node = this.lineageGraph.get(current);
if (node) {
for (const childId of node.children) {
descendants.push(childId);
queue.push(childId);
}
}
}
return descendants;
}
/**
* Compute reproducibility hash for a merge
*/
computeReproducibilityHash(lineage) {
const payload = {
parents: lineage.parentAdapterIds.sort(),
method: lineage.mergeMethod,
parameters: lineage.mergeParameters,
seed: lineage.mergeSeed,
};
return hashCanonical(payload);
}
/**
* Export lineage graph
*/
export() {
return {
nodes: Array.from(this.lineageGraph.entries()),
roots: Array.from(this.roots),
};
}
/**
* Import lineage graph
*/
import(data) {
if (data.nodes) {
this.lineageGraph = new Map(data.nodes);
}
if (data.roots) {
this.roots = new Set(data.roots);
}
}
}
// ============================================================================
// ADAPTER POOL WITH SECURITY
// ============================================================================
/**
* Secure adapter pool with quarantine integration
*/
export class SecureAdapterPool {
constructor(options = {}) {
this.maxSlots = options.maxSlots || 16;
this.quarantine = new AdapterQuarantine(options);
this.lineage = new AdapterLineage(options);
// Active adapters (LRU)
this.activeAdapters = new Map();
this.accessOrder = [];
}
/**
* Add adapter with full security checks
*/
async addAdapter(manifest, adapterData, inferenceSession = null) {
const adapterId = manifest.adapter.id;
// 1. Quarantine and verify
const quarantineResult = await this.quarantine.quarantineAdapter(manifest, adapterData);
if (quarantineResult.state === QuarantineState.FAILED) {
throw new Error(`Adapter blocked: ${quarantineResult.failure.reason}`);
}
// 2. If not trusted, run local evaluation
if (quarantineResult.state === QuarantineState.PENDING) {
if (!inferenceSession) {
throw new Error('Inference session required for local evaluation');
}
const evalResult = await this.quarantine.evaluateAdapter(adapterId, inferenceSession);
if (evalResult.state === QuarantineState.FAILED) {
throw new Error(`Adapter failed evaluation: score ${evalResult.results.score}`);
}
}
// 3. Register in lineage
this.lineage.registerAdapter(adapterId, manifest);
// 4. Add to active pool
await this._addToPool(adapterId, adapterData, manifest);
return { adapterId, state: 'active' };
}
/**
* Get an adapter if allowed
*/
getAdapter(adapterId, baseModelId) {
// Check if can use
const check = this.quarantine.canUseAdapter(adapterId, baseModelId);
if (!check.allowed) {
return { allowed: false, reason: check.reason };
}
// Get from pool
const adapter = this.activeAdapters.get(adapterId);
if (!adapter) {
return { allowed: false, reason: 'not_in_pool' };
}
// Update access order
this._updateAccessOrder(adapterId);
return { allowed: true, adapter };
}
/**
* Add to pool with LRU eviction
*/
async _addToPool(adapterId, adapterData, manifest) {
// Evict if at capacity
while (this.activeAdapters.size >= this.maxSlots) {
const evictId = this.accessOrder.shift();
this.activeAdapters.delete(evictId);
}
this.activeAdapters.set(adapterId, {
data: adapterData,
manifest,
loadedAt: Date.now(),
});
this._updateAccessOrder(adapterId);
}
/**
* Update LRU access order
*/
_updateAccessOrder(adapterId) {
const index = this.accessOrder.indexOf(adapterId);
if (index > -1) {
this.accessOrder.splice(index, 1);
}
this.accessOrder.push(adapterId);
}
/**
* Get pool statistics
*/
getStats() {
return {
activeCount: this.activeAdapters.size,
maxSlots: this.maxSlots,
quarantinedCount: this.quarantine.quarantine.size,
approvedCount: this.quarantine.approved.size,
blockedCount: this.quarantine.blocked.size,
lineageNodes: this.lineage.lineageGraph.size,
};
}
}
// ============================================================================
// EXPORTS
// ============================================================================
export default {
ADAPTER_REQUIREMENTS,
QuarantineState,
createAdapterManifest,
AdapterQuarantine,
createMergeLineage,
AdapterLineage,
SecureAdapterPool,
};