@vqp/core
Version:
Core VQP protocol implementation - adapter-agnostic
190 lines • 8.06 kB
JavaScript
/**
* 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