UNPKG

@vqp/core

Version:

Core VQP protocol implementation - adapter-agnostic

190 lines 8.06 kB
/** * Default Response Mode Adapter - Handles all VQP response modes */ import { randomUUID } from 'node:crypto'; import { createVQPError, } from '../domain/types.js'; export class ResponseModeAdapter { config; constructor(config = {}) { this.config = config; } async processResponseMode(query, evaluationResult, actualValue) { const mode = query.responseMode?.type || this.config.defaultMode || 'strict'; switch (mode) { case 'strict': return this.processStrict(evaluationResult); case 'consensual': return this.processConsensual(query, evaluationResult, actualValue); case 'reciprocal': return this.processReciprocal(query, evaluationResult, actualValue); case 'obfuscated': return this.processObfuscated(query, evaluationResult, actualValue); default: throw createVQPError('UNSUPPORTED_RESPONSE_MODE', `Unsupported response mode: ${mode}`); } } async processStrict(result) { // Strict mode: only return boolean result, no actual values return { mode: 'strict', result: typeof result === 'boolean' ? result : Boolean(result), }; } async processConsensual(query, result, actualValue) { // Request consent (simplified for demo) const consentGranted = await this.requestConsent(query, actualValue); if (!consentGranted.granted) { throw createVQPError('CONSENT_DENIED', 'User denied consent to share actual value'); } return { mode: 'consensual', result: typeof result === 'boolean' ? result : Boolean(result), value: actualValue, additionalData: { consentProof: consentGranted, }, }; } async processReciprocal(query, result, actualValue) { // Verify requester has provided mutual verification const mutualVerification = query.responseMode?.config?.mutualVerification; if (!mutualVerification) { throw createVQPError('RECIPROCAL_VERIFICATION_FAILED', 'No mutual verification data provided'); } // In a real implementation, this would verify the requester's proof const verificationResult = await this.verifyRequesterClaims(mutualVerification.requesterProof, mutualVerification.requiredClaims); if (!verificationResult.requesterVerified) { throw createVQPError('RECIPROCAL_VERIFICATION_FAILED', 'Requester verification failed'); } return { mode: 'reciprocal', result: typeof result === 'boolean' ? result : Boolean(result), value: actualValue, additionalData: { mutualProof: verificationResult, }, }; } async processObfuscated(query, result, actualValue) { const obfuscationConfig = query.responseMode?.config?.obfuscation; if (!obfuscationConfig) { throw createVQPError('OBFUSCATION_ERROR', 'No obfuscation configuration provided'); } const obfuscatedResult = await this.applyObfuscation(actualValue, obfuscationConfig); return { mode: 'obfuscated', result: typeof result === 'boolean' ? result : Boolean(result), value: obfuscatedResult.obfuscatedValue, additionalData: { obfuscationApplied: obfuscatedResult.details, }, }; } // Helper methods for consent, verification, and obfuscation async requestConsent(query, actualValue) { // Simplified consent mechanism for demo purposes if (this.config.autoConsent) { return { granted: true, timestamp: new Date().toISOString(), signature: `auto-consent-${randomUUID()}`, userAgent: 'VQP-Demo-Agent', }; } // In a real implementation, this would show UI to user and wait for response // For now, we'll simulate based on justification const justification = query.responseMode?.config?.justification; const shouldGrant = justification && justification.includes('research'); // Simple heuristic return { granted: shouldGrant || false, timestamp: new Date().toISOString(), ...(shouldGrant && { signature: `consent-${randomUUID()}` }), userAgent: 'VQP-Demo-Agent', }; } async verifyRequesterClaims(requesterProof, requiredClaims) { // Simplified verification for demo if (this.config.requireActualMutualVerification) { // In production, this would verify the cryptographic proof // For now, just check if proof exists const verified = requesterProof && requiredClaims.length > 0; return { requesterVerified: verified, exchangeTimestamp: new Date().toISOString(), verifiedClaims: verified ? requiredClaims : [], reciprocalProof: verified ? requesterProof : undefined, }; } // Demo mode - always accept return { requesterVerified: true, exchangeTimestamp: new Date().toISOString(), verifiedClaims: requiredClaims, reciprocalProof: requesterProof, }; } async applyObfuscation(value, config) { const method = config.method || 'rounding'; if (typeof value !== 'number') { throw createVQPError('OBFUSCATION_ERROR', 'Obfuscation only supported for numeric values'); } switch (method) { case 'range': return this.createRange(value, config.precision || this.config.defaultPrecision || 10); case 'noise': return this.addNoise(value, config.noiseLevel || this.config.defaultNoiseLevel || 0.1); case 'rounding': return this.roundToPrecision(value, config.precision || this.config.defaultPrecision || 10); default: throw createVQPError('OBFUSCATION_ERROR', `Unsupported obfuscation method: ${method}`); } } async createRange(value, precision) { const lower = Math.floor(value / precision) * precision; const upper = lower + precision; return { obfuscatedValue: `${lower}-${upper}`, details: { method: 'range', originalPrecision: 1, appliedPrecision: precision, privacyBudgetUsed: 0.1, // Simplified privacy budget calculation }, }; } async addNoise(value, noiseLevel) { // Add Laplacian noise for differential privacy const noise = this.laplacianNoise(0, noiseLevel); const noisyValue = Math.max(0, value + noise); // Ensure non-negative return { obfuscatedValue: Math.round(noisyValue * 100) / 100, // Round to 2 decimals details: { method: 'noise', noiseLevel, privacyBudgetUsed: noiseLevel, }, }; } async roundToPrecision(value, precision) { const rounded = Math.round(value / precision) * precision; return { obfuscatedValue: rounded, details: { method: 'rounding', originalPrecision: 1, appliedPrecision: precision, privacyBudgetUsed: 0.05, // Lower privacy cost for rounding }, }; } // Simple Laplacian noise generator laplacianNoise(location, scale) { const u = Math.random() - 0.5; return location - scale * Math.sign(u) * Math.log(1 - 2 * Math.abs(u)); } } // Factory function for easy instantiation export function createResponseModeAdapter(config) { return new ResponseModeAdapter(config); } //# sourceMappingURL=response-mode-adapter.js.map