Validation¶
Comprehensive validation utilities for Verdikta manifests and requests using Joi schemas.
Overview¶
The validation module provides robust schema validation for: - Manifest.json files - Request objects - Configuration objects - Content sanitization
Validator Object¶
Import¶
Methods¶
validateManifest(manifest)
¶
Validate a manifest object against the Verdikta manifest schema.
const manifestData = {
version: "1.0",
primary: {
filename: "primary_query.json"
},
juryParameters: {
NUMBER_OF_OUTCOMES: 2,
AI_NODES: [
{
AI_MODEL: "gpt-4o",
AI_PROVIDER: "OpenAI",
NO_COUNTS: 1,
WEIGHT: 0.5
}
]
}
};
try {
await validator.validateManifest(manifestData);
console.log('✅ Manifest is valid');
} catch (error) {
console.error('❌ Validation failed:', error.details);
}
Parameters: - manifest
(Object): Manifest object to validate
Returns: Promise
Throws: ValidationError with detailed error information
validateRequest(request)
¶
Validate a request object for arbitration queries.
const requestData = {
query: "Should party A receive compensation?",
references: ["contract.pdf", "evidence.jpg"],
outcomes: ["Yes, full compensation", "No compensation"]
};
await validator.validateRequest(requestData);
Parameters: - request
(Object): Request object to validate
Returns: Promise
Throws: ValidationError
validateConfiguration(config)
¶
Validate a configuration object.
const config = {
ipfs: {
gateway: 'https://ipfs.io',
timeout: 30000
},
logging: {
level: 'info'
}
};
await validator.validateConfiguration(config);
Parameters: - config
(Object): Configuration object to validate
Returns: Promise
Throws: ValidationError
sanitizeContent(content)
¶
Sanitize content for safe processing (removes potentially dangerous characters).
Parameters: - content
(string): Content to sanitize
Returns: string - Sanitized content
Validation Schemas¶
Manifest Schema¶
The manifest schema validates the complete structure of a Verdikta manifest:
const manifestSchema = Joi.object({
version: Joi.string().required(),
name: Joi.string().optional(),
primary: Joi.object({
filename: Joi.string().when('hash', {
is: Joi.exist(),
then: Joi.forbidden(),
otherwise: Joi.required()
}),
hash: Joi.string().optional()
}).required(),
juryParameters: Joi.object({
NUMBER_OF_OUTCOMES: Joi.number().integer().min(2).default(2),
AI_NODES: Joi.array().items(
Joi.object({
AI_MODEL: Joi.string().required(),
AI_PROVIDER: Joi.string().required(),
NO_COUNTS: Joi.number().integer().min(1).required(),
WEIGHT: Joi.number().min(0).max(1).required()
})
).default([{
AI_MODEL: "gpt-4o",
AI_PROVIDER: "OpenAI",
NO_COUNTS: 1,
WEIGHT: 1.0
}]),
ITERATIONS: Joi.number().integer().min(1).default(1)
}).optional(),
additional: Joi.array().items(
Joi.object({
name: Joi.string().required(),
type: Joi.string().required(),
filename: Joi.string().when('hash', {
is: Joi.exist(),
then: Joi.forbidden(),
otherwise: Joi.required()
}),
hash: Joi.string().optional(),
description: Joi.string().optional()
})
).optional(),
support: Joi.array().items(
Joi.object({
hash: Joi.object({
cid: Joi.string().required(),
description: Joi.string().optional(),
id: Joi.number().optional()
}).required()
})
).optional(),
bCIDs: Joi.object().pattern(
Joi.string(),
Joi.string()
).optional(),
addendum: Joi.string().optional()
});
Request Schema¶
Validates arbitration request objects:
const requestSchema = Joi.object({
query: Joi.string().required().min(10).max(5000),
references: Joi.array().items(Joi.string()).default([]),
outcomes: Joi.array().items(Joi.string()).min(2).optional(),
metadata: Joi.object().optional()
});
Configuration Schema¶
Validates client configuration:
const configSchema = Joi.object({
ipfs: Joi.object({
gateway: Joi.string().uri().default('https://ipfs.io'),
pinningService: Joi.string().uri().default('https://api.pinata.cloud'),
pinningKey: Joi.string().default(''),
timeout: Joi.number().integer().min(1000).default(30000),
retryOptions: Joi.object({
retries: Joi.number().integer().min(0).default(5),
factor: Joi.number().min(1).default(2),
minTimeout: Joi.number().integer().min(100).default(1000),
maxTimeout: Joi.number().integer().min(1000).default(15000),
randomize: Joi.boolean().default(true)
}).default()
}).default(),
logging: Joi.object({
level: Joi.string().valid('debug', 'info', 'warn', 'error').default('info'),
console: Joi.boolean().default(true),
file: Joi.boolean().default(false),
filename: Joi.string().optional()
}).default(),
temp: Joi.object({
dir: Joi.string().default('./tmp')
}).default()
});
Validation Errors¶
ValidationError Class¶
const { ValidationError } = require('@verdikta/common');
class ValidationError extends Error {
constructor(message, details) {
super(message);
this.name = 'ValidationError';
this.details = details;
}
}
Error Details¶
Validation errors include detailed information about what failed:
try {
await validator.validateManifest(invalidManifest);
} catch (error) {
if (error instanceof ValidationError) {
console.log('Error message:', error.message);
console.log('Validation details:', error.details);
// Example error.details:
{
"version": "\"version\" is required",
"primary.filename": "\"filename\" is required when \"hash\" is not provided",
"juryParameters.AI_NODES[0].WEIGHT": "\"WEIGHT\" must be less than or equal to 1"
}
}
}
Common Validation Patterns¶
Conditional Validation¶
Many fields in manifests are conditionally required:
// Either filename OR hash must be provided (but not both)
const fileReference = {
filename: "local-file.txt" // Valid
// hash: "QmCID" // Also valid
// Both would be invalid
};
// Weights must sum to reasonable values
const aiNodes = [
{ AI_MODEL: "gpt-4o", AI_PROVIDER: "OpenAI", NO_COUNTS: 1, WEIGHT: 0.7 },
{ AI_MODEL: "claude-3", AI_PROVIDER: "Anthropic", NO_COUNTS: 1, WEIGHT: 0.3 }
];
Default Value Handling¶
The validator provides sensible defaults:
// Minimal manifest (defaults will be applied)
const minimalManifest = {
version: "1.0",
primary: { filename: "query.json" }
};
// After validation, becomes:
{
version: "1.0",
primary: { filename: "query.json" },
juryParameters: {
NUMBER_OF_OUTCOMES: 2,
AI_NODES: [{
AI_MODEL: "gpt-4o",
AI_PROVIDER: "OpenAI",
NO_COUNTS: 1,
WEIGHT: 1.0
}],
ITERATIONS: 1
}
}
Custom Validation Messages¶
const customValidator = {
async validateWithCustomMessages(manifest) {
try {
return await validator.validateManifest(manifest);
} catch (error) {
if (error instanceof ValidationError) {
// Customize error messages
const friendlyErrors = Object.entries(error.details).map(([field, msg]) => {
switch (field) {
case 'version':
return 'Manifest version is required and must be "1.0"';
case 'primary':
return 'A primary query file must be specified';
default:
return `${field}: ${msg}`;
}
});
throw new ValidationError('Validation failed', friendlyErrors);
}
throw error;
}
}
};
Content Sanitization¶
Purpose¶
Sanitization prevents code injection and ensures safe content processing:
// Potentially dangerous input
const userInput = 'Query with <script>alert("xss")</script> embedded code';
// Sanitized output
const safe = validator.sanitizeContent(userInput);
// Result: 'Query with alert("xss") embedded code'
Sanitization Rules¶
- Removes HTML/XML tags
- Strips potential code injection patterns
- Preserves basic formatting characters
- Maintains readability while ensuring safety
Usage in Processing¶
Sanitization is automatically applied during: - Manifest parsing - Addendum processing - User-provided content integration
// Automatic sanitization in manifest processing
const manifest = await parseManifest('./archive');
// All user content in manifest is automatically sanitized
For more information on validation patterns and error handling, see the Error Handling Guide and Examples.