mirror of
https://github.com/ruvnet/RuView
synced 2026-08-11 20:41:44 +00:00
feat: vendor midstream and sublinear-time-solver libraries (#109)
Add ruvnet/midstream (AIMDS real-time inference) and ruvnet/sublinear-time-solver (sublinear optimization algorithms) as vendored dependencies under vendor/.
This commit is contained in:
+395
@@ -0,0 +1,395 @@
|
||||
/**
|
||||
* AgentDB Client Implementation
|
||||
* High-performance vector database with HNSW search and QUIC synchronization
|
||||
*/
|
||||
|
||||
import { createDatabase } from 'agentdb';
|
||||
import {
|
||||
ThreatMatch,
|
||||
ThreatIncident,
|
||||
VectorSearchOptions,
|
||||
ReflexionMemoryEntry,
|
||||
ThreatLevel,
|
||||
AgentDBConfig
|
||||
} from '../types';
|
||||
import { Logger } from '../utils/logger';
|
||||
|
||||
export class AgentDBClient {
|
||||
private db: any; // AgentDB database instance
|
||||
private logger: Logger;
|
||||
private config: AgentDBConfig;
|
||||
private syncInterval?: NodeJS.Timeout;
|
||||
|
||||
constructor(config: AgentDBConfig, logger: Logger) {
|
||||
this.config = config;
|
||||
this.logger = logger;
|
||||
// createDatabase accepts a filename string
|
||||
this.db = createDatabase(config.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize AgentDB with HNSW index and QUIC sync
|
||||
*/
|
||||
async initialize(): Promise<void> {
|
||||
try {
|
||||
this.logger.info('Initializing AgentDB client...');
|
||||
|
||||
// Create HNSW index for fast vector search (150x faster than brute force)
|
||||
await this.db.createIndex({
|
||||
type: 'hnsw',
|
||||
params: {
|
||||
m: this.config.hnswConfig.m,
|
||||
efConstruction: this.config.hnswConfig.efConstruction,
|
||||
efSearch: this.config.hnswConfig.efSearch,
|
||||
metric: 'cosine'
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize collections
|
||||
await this.createCollections();
|
||||
|
||||
// Setup QUIC synchronization if enabled
|
||||
if (this.config.quicSync.enabled) {
|
||||
await this.initializeQuicSync();
|
||||
}
|
||||
|
||||
this.logger.info('AgentDB client initialized successfully');
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to initialize AgentDB', { error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast vector search with HNSW and MMR diversity
|
||||
* Target: <2ms for k=10
|
||||
*/
|
||||
async vectorSearch(
|
||||
embedding: number[],
|
||||
options: VectorSearchOptions = { k: 10 }
|
||||
): Promise<ThreatMatch[]> {
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
// HNSW search with specified parameters
|
||||
const results = await this.db.search({
|
||||
collection: 'threat_patterns',
|
||||
vector: embedding,
|
||||
k: options.k,
|
||||
ef: options.ef || this.config.hnswConfig.efSearch
|
||||
});
|
||||
|
||||
// Apply MMR (Maximal Marginal Relevance) for diversity if requested
|
||||
const matches = options.diversityFactor
|
||||
? this.applyMMR(results, options.diversityFactor)
|
||||
: results;
|
||||
|
||||
// Convert to ThreatMatch objects
|
||||
const threatMatches: ThreatMatch[] = matches
|
||||
.filter((m: any) => m.similarity >= (options.threshold || 0.7))
|
||||
.map((m: any) => ({
|
||||
id: m.id,
|
||||
patternId: m.metadata.patternId,
|
||||
similarity: m.similarity,
|
||||
threatLevel: this.calculateThreatLevel(m.similarity, m.metadata),
|
||||
description: m.metadata.description || 'Unknown threat pattern',
|
||||
metadata: {
|
||||
firstSeen: m.metadata.firstSeen || Date.now(),
|
||||
lastSeen: m.metadata.lastSeen || Date.now(),
|
||||
occurrences: m.metadata.occurrences || 1,
|
||||
sources: m.metadata.sources || []
|
||||
}
|
||||
}));
|
||||
|
||||
const latency = Date.now() - startTime;
|
||||
this.logger.debug('Vector search completed', {
|
||||
latency,
|
||||
resultsCount: threatMatches.length,
|
||||
threshold: options.threshold
|
||||
});
|
||||
|
||||
return threatMatches;
|
||||
} catch (error) {
|
||||
this.logger.error('Vector search failed', { error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store security incident in ReflexionMemory for learning
|
||||
*/
|
||||
async storeIncident(incident: ThreatIncident): Promise<void> {
|
||||
try {
|
||||
// Store in main incidents collection
|
||||
await this.db.insert({
|
||||
collection: 'incidents',
|
||||
document: {
|
||||
id: incident.id,
|
||||
timestamp: incident.timestamp,
|
||||
request: incident.request,
|
||||
result: incident.result,
|
||||
embedding: incident.embedding
|
||||
}
|
||||
});
|
||||
|
||||
// Update threat patterns if this is a new pattern
|
||||
if (incident.result.threatLevel >= ThreatLevel.MEDIUM) {
|
||||
await this.updateThreatPattern(incident);
|
||||
}
|
||||
|
||||
// Store in ReflexionMemory for learning
|
||||
const reflexionEntry: ReflexionMemoryEntry = {
|
||||
trajectory: JSON.stringify({
|
||||
request: incident.request,
|
||||
matches: incident.result.matches
|
||||
}),
|
||||
verdict: incident.result.allowed ? 'success' : 'failure',
|
||||
feedback: this.generateFeedback(incident),
|
||||
embedding: incident.embedding || [],
|
||||
metadata: {
|
||||
threatLevel: incident.result.threatLevel,
|
||||
confidence: incident.result.confidence,
|
||||
latency: incident.result.latencyMs
|
||||
}
|
||||
};
|
||||
|
||||
await this.db.insert({
|
||||
collection: 'reflexion_memory',
|
||||
document: reflexionEntry
|
||||
});
|
||||
|
||||
// Update causal graphs
|
||||
if (incident.causalLinks && incident.causalLinks.length > 0) {
|
||||
await this.updateCausalGraph(incident);
|
||||
}
|
||||
|
||||
this.logger.debug('Incident stored successfully', { id: incident.id });
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to store incident', { error, incidentId: incident.id });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize with peer nodes using QUIC
|
||||
*/
|
||||
async syncWithPeers(): Promise<void> {
|
||||
if (!this.config.quicSync.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const syncPromises = this.config.quicSync.peers.map(peer =>
|
||||
this.db.sync({
|
||||
peer,
|
||||
protocol: 'quic',
|
||||
port: this.config.quicSync.port,
|
||||
collections: ['threat_patterns', 'incidents', 'reflexion_memory']
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all(syncPromises);
|
||||
this.logger.debug('QUIC synchronization completed');
|
||||
} catch (error) {
|
||||
this.logger.error('QUIC synchronization failed', { error });
|
||||
// Don't throw - sync failures shouldn't break the gateway
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get statistics about stored data
|
||||
*/
|
||||
async getStats(): Promise<{
|
||||
incidents: number;
|
||||
patterns: number;
|
||||
memoryEntries: number;
|
||||
memoryUsage: number;
|
||||
}> {
|
||||
const [incidents, patterns, memoryEntries] = await Promise.all([
|
||||
this.db.count({ collection: 'incidents' }),
|
||||
this.db.count({ collection: 'threat_patterns' }),
|
||||
this.db.count({ collection: 'reflexion_memory' })
|
||||
]);
|
||||
|
||||
return {
|
||||
incidents,
|
||||
patterns,
|
||||
memoryEntries,
|
||||
memoryUsage: this.db.getMemoryUsage()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up old entries based on TTL
|
||||
*/
|
||||
async cleanup(): Promise<void> {
|
||||
const cutoffTime = Date.now() - this.config.memory.ttl;
|
||||
|
||||
await Promise.all([
|
||||
this.db.delete({
|
||||
collection: 'incidents',
|
||||
filter: { timestamp: { $lt: cutoffTime } }
|
||||
}),
|
||||
this.db.delete({
|
||||
collection: 'reflexion_memory',
|
||||
filter: { timestamp: { $lt: cutoffTime } }
|
||||
})
|
||||
]);
|
||||
|
||||
this.logger.debug('Cleanup completed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown and cleanup resources
|
||||
*/
|
||||
async shutdown(): Promise<void> {
|
||||
if (this.syncInterval) {
|
||||
clearInterval(this.syncInterval);
|
||||
}
|
||||
|
||||
await this.db.close();
|
||||
this.logger.info('AgentDB client shutdown complete');
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Private Helper Methods
|
||||
// ============================================================================
|
||||
|
||||
private async createCollections(): Promise<void> {
|
||||
await Promise.all([
|
||||
this.db.createCollection({
|
||||
name: 'threat_patterns',
|
||||
schema: {
|
||||
embedding: { type: 'vector', dim: this.config.embeddingDim },
|
||||
metadata: { type: 'object' }
|
||||
}
|
||||
}),
|
||||
this.db.createCollection({
|
||||
name: 'incidents',
|
||||
schema: {
|
||||
id: { type: 'string', indexed: true },
|
||||
timestamp: { type: 'number', indexed: true },
|
||||
embedding: { type: 'vector', dim: this.config.embeddingDim }
|
||||
}
|
||||
}),
|
||||
this.db.createCollection({
|
||||
name: 'reflexion_memory',
|
||||
schema: {
|
||||
embedding: { type: 'vector', dim: this.config.embeddingDim },
|
||||
verdict: { type: 'string', indexed: true }
|
||||
}
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
private async initializeQuicSync(): Promise<void> {
|
||||
// Start periodic sync every 30 seconds
|
||||
this.syncInterval = setInterval(() => {
|
||||
this.syncWithPeers().catch(err =>
|
||||
this.logger.error('Periodic sync failed', { error: err })
|
||||
);
|
||||
}, 30000);
|
||||
|
||||
// Initial sync
|
||||
await this.syncWithPeers();
|
||||
}
|
||||
|
||||
private applyMMR(results: any[], lambda: number): any[] {
|
||||
// Maximal Marginal Relevance for diversity
|
||||
// lambda: 1.0 = max relevance, 0.0 = max diversity
|
||||
const selected: any[] = [];
|
||||
const candidates = [...results];
|
||||
|
||||
while (selected.length < results.length && candidates.length > 0) {
|
||||
let maxScore = -Infinity;
|
||||
let maxIdx = -1;
|
||||
|
||||
candidates.forEach((candidate, idx) => {
|
||||
const relevance = candidate.similarity;
|
||||
const maxSim = selected.length === 0
|
||||
? 0
|
||||
: Math.max(...selected.map(s => this.cosineSimilarity(candidate.embedding, s.embedding)));
|
||||
|
||||
const score = lambda * relevance - (1 - lambda) * maxSim;
|
||||
|
||||
if (score > maxScore) {
|
||||
maxScore = score;
|
||||
maxIdx = idx;
|
||||
}
|
||||
});
|
||||
|
||||
if (maxIdx >= 0) {
|
||||
selected.push(candidates[maxIdx]);
|
||||
candidates.splice(maxIdx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
private cosineSimilarity(a: number[], b: number[]): number {
|
||||
let dotProduct = 0;
|
||||
let normA = 0;
|
||||
let normB = 0;
|
||||
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
dotProduct += a[i] * b[i];
|
||||
normA += a[i] * a[i];
|
||||
normB += b[i] * b[i];
|
||||
}
|
||||
|
||||
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
||||
}
|
||||
|
||||
private calculateThreatLevel(similarity: number, metadata: any): ThreatLevel {
|
||||
// Calculate threat level based on similarity and metadata
|
||||
const baseThreat = metadata.threatLevel || ThreatLevel.LOW;
|
||||
|
||||
if (similarity >= 0.95) return Math.max(baseThreat, ThreatLevel.HIGH);
|
||||
if (similarity >= 0.85) return Math.max(baseThreat, ThreatLevel.MEDIUM);
|
||||
if (similarity >= 0.75) return baseThreat;
|
||||
return ThreatLevel.LOW;
|
||||
}
|
||||
|
||||
private async updateThreatPattern(incident: ThreatIncident): Promise<void> {
|
||||
// Update or create threat pattern based on incident
|
||||
if (!incident.embedding) return;
|
||||
|
||||
await this.db.upsert({
|
||||
collection: 'threat_patterns',
|
||||
document: {
|
||||
patternId: incident.id,
|
||||
embedding: incident.embedding,
|
||||
metadata: {
|
||||
description: `Threat pattern from incident ${incident.id}`,
|
||||
threatLevel: incident.result.threatLevel,
|
||||
lastSeen: incident.timestamp,
|
||||
occurrences: 1
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private generateFeedback(incident: ThreatIncident): string {
|
||||
const { result } = incident;
|
||||
return `Threat level: ${ThreatLevel[result.threatLevel]}, ` +
|
||||
`Confidence: ${(result.confidence * 100).toFixed(1)}%, ` +
|
||||
`Path: ${result.metadata.pathTaken}, ` +
|
||||
`Latency: ${result.latencyMs.toFixed(2)}ms`;
|
||||
}
|
||||
|
||||
private async updateCausalGraph(incident: ThreatIncident): Promise<void> {
|
||||
// Update causal relationship graph
|
||||
for (const link of incident.causalLinks || []) {
|
||||
await this.db.insert({
|
||||
collection: 'causal_graph',
|
||||
document: {
|
||||
from: incident.id,
|
||||
to: link,
|
||||
timestamp: incident.timestamp,
|
||||
weight: 1.0
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
+516
@@ -0,0 +1,516 @@
|
||||
/**
|
||||
* AIMDS API Gateway Server
|
||||
* Production-ready Express server with AgentDB and lean-agentic integration
|
||||
*/
|
||||
|
||||
import express, { Request, Response, NextFunction } from 'express';
|
||||
import cors from 'cors';
|
||||
import helmet from 'helmet';
|
||||
import compression from 'compression';
|
||||
import rateLimit from 'express-rate-limit';
|
||||
import { AgentDBClient } from '../agentdb/client';
|
||||
import { LeanAgenticVerifier } from '../lean-agentic/verifier';
|
||||
import { MetricsCollector } from '../monitoring/metrics';
|
||||
import { Logger } from '../utils/logger';
|
||||
import {
|
||||
AIMDSRequest,
|
||||
DefenseResult,
|
||||
ThreatLevel,
|
||||
GatewayConfig,
|
||||
AgentDBConfig,
|
||||
LeanAgenticConfig,
|
||||
SecurityPolicy,
|
||||
AIMDSRequestSchema,
|
||||
ThreatIncident
|
||||
} from '../types';
|
||||
import { createHash } from 'crypto';
|
||||
|
||||
export class AIMDSGateway {
|
||||
private app: express.Application;
|
||||
private agentdb: AgentDBClient;
|
||||
private verifier: LeanAgenticVerifier;
|
||||
private metrics: MetricsCollector;
|
||||
private logger: Logger;
|
||||
private config: GatewayConfig;
|
||||
private defaultPolicy: SecurityPolicy;
|
||||
private server?: any;
|
||||
|
||||
constructor(
|
||||
gatewayConfig: GatewayConfig,
|
||||
agentdbConfig: AgentDBConfig,
|
||||
verifierConfig: LeanAgenticConfig
|
||||
) {
|
||||
this.config = gatewayConfig;
|
||||
this.logger = new Logger('AIMDSGateway');
|
||||
this.agentdb = new AgentDBClient(agentdbConfig, this.logger);
|
||||
this.verifier = new LeanAgenticVerifier(verifierConfig, this.logger);
|
||||
this.metrics = new MetricsCollector(this.logger);
|
||||
this.app = express();
|
||||
this.defaultPolicy = this.createDefaultPolicy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the gateway and all components
|
||||
*/
|
||||
async initialize(): Promise<void> {
|
||||
try {
|
||||
this.logger.info('Initializing AIMDS Gateway...');
|
||||
|
||||
// Initialize components in parallel
|
||||
await Promise.all([
|
||||
this.agentdb.initialize(),
|
||||
this.verifier.initialize(),
|
||||
this.metrics.initialize()
|
||||
]);
|
||||
|
||||
// Configure Express middleware
|
||||
this.configureMiddleware();
|
||||
|
||||
// Setup routes
|
||||
this.setupRoutes();
|
||||
|
||||
// Error handling
|
||||
this.setupErrorHandling();
|
||||
|
||||
this.logger.info('AIMDS Gateway initialized successfully');
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to initialize gateway', { error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the gateway server
|
||||
*/
|
||||
async start(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
this.server = this.app.listen(this.config.port, this.config.host, () => {
|
||||
this.logger.info(`Gateway listening on ${this.config.host}:${this.config.port}`);
|
||||
resolve();
|
||||
});
|
||||
|
||||
this.server.on('error', reject);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Process incoming security request
|
||||
* Fast path: Vector search + pattern matching (<10ms)
|
||||
* Deep path if needed: Behavioral + LTL verification (<520ms)
|
||||
*/
|
||||
async processRequest(req: AIMDSRequest): Promise<DefenseResult> {
|
||||
const startTime = Date.now();
|
||||
const requestId = req.id;
|
||||
|
||||
try {
|
||||
this.logger.debug('Processing request', { requestId, type: req.action.type });
|
||||
|
||||
// Step 1: Generate embedding for request (fast)
|
||||
const embedding = await this.generateEmbedding(req);
|
||||
const embedTime = Date.now();
|
||||
|
||||
// Step 2: Fast path - Vector search with HNSW (<2ms target)
|
||||
const vectorSearchStart = Date.now();
|
||||
const matches = await this.agentdb.vectorSearch(embedding, {
|
||||
k: 10,
|
||||
threshold: 0.75,
|
||||
diversityFactor: 0.3
|
||||
});
|
||||
const vectorSearchTime = Date.now() - vectorSearchStart;
|
||||
|
||||
// Calculate threat level from matches
|
||||
const threatLevel = this.calculateThreatLevel(matches);
|
||||
const confidence = this.calculateConfidence(matches);
|
||||
|
||||
// Step 3: Quick decision for low-risk requests
|
||||
if (threatLevel <= ThreatLevel.LOW && confidence >= 0.9) {
|
||||
const result: DefenseResult = {
|
||||
allowed: true,
|
||||
confidence,
|
||||
latencyMs: Date.now() - startTime,
|
||||
threatLevel,
|
||||
matches,
|
||||
metadata: {
|
||||
vectorSearchTime,
|
||||
verificationTime: 0,
|
||||
totalTime: Date.now() - startTime,
|
||||
pathTaken: 'fast'
|
||||
}
|
||||
};
|
||||
|
||||
this.metrics.recordDetection(result.latencyMs, result);
|
||||
await this.storeIncident(req, result, embedding);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Step 4: Deep path - Formal verification for high-risk requests
|
||||
const verificationStart = Date.now();
|
||||
const action = this.requestToAction(req);
|
||||
const verificationResult = await this.verifier.verifyPolicy(
|
||||
action,
|
||||
this.defaultPolicy
|
||||
);
|
||||
const verificationTime = Date.now() - verificationStart;
|
||||
|
||||
// Step 5: Make final decision
|
||||
const allowed = verificationResult.valid && threatLevel < ThreatLevel.CRITICAL;
|
||||
|
||||
const result: DefenseResult = {
|
||||
allowed,
|
||||
confidence: verificationResult.valid ? Math.min(confidence, 0.95) : 0,
|
||||
latencyMs: Date.now() - startTime,
|
||||
threatLevel,
|
||||
matches,
|
||||
verificationProof: verificationResult.proof,
|
||||
metadata: {
|
||||
vectorSearchTime,
|
||||
verificationTime,
|
||||
totalTime: Date.now() - startTime,
|
||||
pathTaken: 'deep'
|
||||
}
|
||||
};
|
||||
|
||||
this.metrics.recordDetection(result.latencyMs, result);
|
||||
await this.storeIncident(req, result, embedding);
|
||||
|
||||
this.logger.debug('Request processed', {
|
||||
requestId,
|
||||
allowed,
|
||||
latency: result.latencyMs,
|
||||
path: result.metadata.pathTaken
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
this.logger.error('Request processing failed', { error, requestId });
|
||||
|
||||
// Fail closed - deny on error
|
||||
return {
|
||||
allowed: false,
|
||||
confidence: 0,
|
||||
latencyMs: Date.now() - startTime,
|
||||
threatLevel: ThreatLevel.CRITICAL,
|
||||
matches: [],
|
||||
metadata: {
|
||||
vectorSearchTime: 0,
|
||||
verificationTime: 0,
|
||||
totalTime: Date.now() - startTime,
|
||||
pathTaken: 'fast'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Graceful shutdown
|
||||
*/
|
||||
async shutdown(): Promise<void> {
|
||||
this.logger.info('Shutting down gateway...');
|
||||
|
||||
return new Promise((resolve) => {
|
||||
// Stop accepting new connections
|
||||
if (this.server) {
|
||||
this.server.close(async () => {
|
||||
// Shutdown components
|
||||
await Promise.all([
|
||||
this.agentdb.shutdown(),
|
||||
this.verifier.shutdown(),
|
||||
this.metrics.shutdown()
|
||||
]);
|
||||
|
||||
this.logger.info('Gateway shutdown complete');
|
||||
resolve();
|
||||
});
|
||||
|
||||
// Force close after timeout
|
||||
setTimeout(() => {
|
||||
this.logger.warn('Forcing shutdown after timeout');
|
||||
resolve();
|
||||
}, this.config.timeouts.shutdown);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Private Methods - Express Configuration
|
||||
// ============================================================================
|
||||
|
||||
private configureMiddleware(): void {
|
||||
// Security headers
|
||||
this.app.use(helmet());
|
||||
|
||||
// CORS
|
||||
if (this.config.enableCors) {
|
||||
this.app.use(cors());
|
||||
}
|
||||
|
||||
// Compression
|
||||
if (this.config.enableCompression) {
|
||||
this.app.use(compression());
|
||||
}
|
||||
|
||||
// Rate limiting
|
||||
const limiter = rateLimit({
|
||||
windowMs: this.config.rateLimit.windowMs,
|
||||
max: this.config.rateLimit.max,
|
||||
message: 'Too many requests from this IP'
|
||||
});
|
||||
this.app.use('/api/', limiter);
|
||||
|
||||
// Body parsing
|
||||
this.app.use(express.json({ limit: '1mb' }));
|
||||
this.app.use(express.urlencoded({ extended: true, limit: '1mb' }));
|
||||
|
||||
// Request timeout
|
||||
this.app.use((req: Request, res: Response, next: NextFunction) => {
|
||||
req.setTimeout(this.config.timeouts.request);
|
||||
next();
|
||||
});
|
||||
|
||||
// Request logging
|
||||
this.app.use((req: Request, res: Response, next: NextFunction) => {
|
||||
const start = Date.now();
|
||||
res.on('finish', () => {
|
||||
this.logger.debug('Request completed', {
|
||||
method: req.method,
|
||||
path: req.path,
|
||||
status: res.statusCode,
|
||||
latency: Date.now() - start
|
||||
});
|
||||
});
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
private setupRoutes(): void {
|
||||
// Health check
|
||||
this.app.get('/health', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const [agentdbStats, verifierStats] = await Promise.all([
|
||||
this.agentdb.getStats(),
|
||||
this.verifier.getCacheStats()
|
||||
]);
|
||||
|
||||
res.json({
|
||||
status: 'healthy',
|
||||
timestamp: Date.now(),
|
||||
components: {
|
||||
gateway: { status: 'up' },
|
||||
agentdb: { status: 'up', ...agentdbStats },
|
||||
verifier: { status: 'up', ...verifierStats }
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(503).json({
|
||||
status: 'unhealthy',
|
||||
error: error instanceof Error ? error.message : 'Unknown error'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Metrics endpoint
|
||||
this.app.get('/metrics', async (req: Request, res: Response) => {
|
||||
const metrics = await this.metrics.exportPrometheus();
|
||||
res.set('Content-Type', 'text/plain');
|
||||
res.send(metrics);
|
||||
});
|
||||
|
||||
// Main defense endpoint
|
||||
this.app.post('/api/v1/defend', async (req: Request, res: Response) => {
|
||||
try {
|
||||
// Validate request
|
||||
const validatedReq = AIMDSRequestSchema.parse({
|
||||
...req.body,
|
||||
id: req.body.id || this.generateRequestId(),
|
||||
timestamp: req.body.timestamp || Date.now(),
|
||||
source: {
|
||||
...req.body.source,
|
||||
ip: req.body.source?.ip || req.ip,
|
||||
headers: req.body.source?.headers || req.headers
|
||||
}
|
||||
});
|
||||
|
||||
// Process request
|
||||
const result = await this.processRequest(validatedReq);
|
||||
|
||||
// Return result
|
||||
res.status(result.allowed ? 200 : 403).json({
|
||||
requestId: validatedReq.id,
|
||||
allowed: result.allowed,
|
||||
confidence: result.confidence,
|
||||
threatLevel: ThreatLevel[result.threatLevel],
|
||||
latency: result.latencyMs,
|
||||
metadata: result.metadata,
|
||||
proof: result.verificationProof?.id
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error('Defense endpoint error', { error });
|
||||
res.status(400).json({
|
||||
error: error instanceof Error ? error.message : 'Invalid request'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Batch defense endpoint
|
||||
this.app.post('/api/v1/defend/batch', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const requests: AIMDSRequest[] = req.body.requests || [];
|
||||
|
||||
if (requests.length === 0 || requests.length > 100) {
|
||||
return res.status(400).json({
|
||||
error: 'Batch size must be between 1 and 100'
|
||||
});
|
||||
}
|
||||
|
||||
// Process in parallel
|
||||
const results = await Promise.all(
|
||||
requests.map(r => this.processRequest(r))
|
||||
);
|
||||
|
||||
res.json({ results });
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
error: error instanceof Error ? error.message : 'Invalid request'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Stats endpoint
|
||||
this.app.get('/api/v1/stats', async (req: Request, res: Response) => {
|
||||
const snapshot = await this.metrics.getSnapshot();
|
||||
res.json(snapshot);
|
||||
});
|
||||
}
|
||||
|
||||
private setupErrorHandling(): void {
|
||||
// 404 handler
|
||||
this.app.use((req: Request, res: Response) => {
|
||||
res.status(404).json({ error: 'Not found' });
|
||||
});
|
||||
|
||||
// Global error handler
|
||||
this.app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
|
||||
this.logger.error('Unhandled error', { error: err });
|
||||
res.status(500).json({
|
||||
error: 'Internal server error',
|
||||
message: process.env.NODE_ENV === 'development' ? err.message : undefined
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Private Methods - Request Processing
|
||||
// ============================================================================
|
||||
|
||||
private async generateEmbedding(req: AIMDSRequest): Promise<number[]> {
|
||||
// Simple embedding generation (use proper embedding model in production)
|
||||
const text = JSON.stringify({
|
||||
type: req.action.type,
|
||||
resource: req.action.resource,
|
||||
method: req.action.method,
|
||||
ip: req.source.ip
|
||||
});
|
||||
|
||||
// Hash-based embedding for demo (use BERT/etc in production)
|
||||
const hash = createHash('sha256').update(text).digest();
|
||||
const embedding = new Array(384);
|
||||
|
||||
for (let i = 0; i < 384; i++) {
|
||||
embedding[i] = hash[i % hash.length] / 255;
|
||||
}
|
||||
|
||||
return embedding;
|
||||
}
|
||||
|
||||
private calculateThreatLevel(matches: any[]): ThreatLevel {
|
||||
if (matches.length === 0) return ThreatLevel.NONE;
|
||||
|
||||
const maxThreat = Math.max(...matches.map(m => m.threatLevel));
|
||||
return maxThreat;
|
||||
}
|
||||
|
||||
private calculateConfidence(matches: any[]): number {
|
||||
if (matches.length === 0) return 1.0;
|
||||
|
||||
const avgSimilarity = matches.reduce((sum, m) => sum + m.similarity, 0) / matches.length;
|
||||
return avgSimilarity;
|
||||
}
|
||||
|
||||
private requestToAction(req: AIMDSRequest): any {
|
||||
return {
|
||||
type: req.action.type,
|
||||
resource: req.action.resource,
|
||||
parameters: req.action.payload || {},
|
||||
context: {
|
||||
timestamp: req.timestamp,
|
||||
metadata: req.context
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private async storeIncident(
|
||||
req: AIMDSRequest,
|
||||
result: DefenseResult,
|
||||
embedding: number[]
|
||||
): Promise<void> {
|
||||
const incident: ThreatIncident = {
|
||||
id: req.id,
|
||||
timestamp: req.timestamp,
|
||||
request: req,
|
||||
result,
|
||||
embedding
|
||||
};
|
||||
|
||||
await this.agentdb.storeIncident(incident);
|
||||
}
|
||||
|
||||
private generateRequestId(): string {
|
||||
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
|
||||
private createDefaultPolicy(): SecurityPolicy {
|
||||
return {
|
||||
id: 'default',
|
||||
name: 'Default Security Policy',
|
||||
rules: [
|
||||
{
|
||||
id: 'deny_critical',
|
||||
condition: 'threatLevel >= 4',
|
||||
action: 'deny',
|
||||
priority: 100
|
||||
},
|
||||
{
|
||||
id: 'verify_high',
|
||||
condition: 'threatLevel >= 3',
|
||||
action: 'verify',
|
||||
priority: 90
|
||||
},
|
||||
{
|
||||
id: 'allow_low',
|
||||
condition: 'threatLevel <= 1',
|
||||
action: 'allow',
|
||||
priority: 10
|
||||
}
|
||||
],
|
||||
constraints: [
|
||||
{
|
||||
type: 'temporal',
|
||||
expression: 'timestamp > now() - 5min',
|
||||
severity: 'error'
|
||||
},
|
||||
{
|
||||
type: 'behavioral',
|
||||
expression: 'request_rate < 1000/min',
|
||||
severity: 'warning'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
Vendored
+86
@@ -0,0 +1,86 @@
|
||||
import { AIMDSGateway } from './gateway/server';
|
||||
import { logger } from './monitoring/telemetry';
|
||||
import { GatewayConfig, AgentDBConfig, LeanAgenticConfig } from './types';
|
||||
|
||||
const PORT = parseInt(process.env.PORT || '3000', 10);
|
||||
const HOST = process.env.HOST || '0.0.0.0';
|
||||
|
||||
// Default configuration
|
||||
const gatewayConfig: GatewayConfig = {
|
||||
port: PORT,
|
||||
host: HOST,
|
||||
enableCors: true,
|
||||
enableCompression: true,
|
||||
rateLimit: {
|
||||
windowMs: 60000, // 1 minute
|
||||
max: 100 // 100 requests per minute
|
||||
},
|
||||
timeouts: {
|
||||
request: 30000, // 30 seconds
|
||||
shutdown: 10000 // 10 seconds
|
||||
}
|
||||
};
|
||||
|
||||
const agentdbConfig: AgentDBConfig = {
|
||||
path: process.env.AGENTDB_PATH || './data/agentdb',
|
||||
embeddingDim: 384,
|
||||
hnswConfig: {
|
||||
m: 16,
|
||||
efConstruction: 200,
|
||||
efSearch: 100
|
||||
},
|
||||
quicSync: {
|
||||
enabled: false,
|
||||
port: 4433,
|
||||
peers: []
|
||||
},
|
||||
memory: {
|
||||
maxEntries: 1000000,
|
||||
ttl: 86400000 // 24 hours
|
||||
}
|
||||
};
|
||||
|
||||
const leanAgenticConfig: LeanAgenticConfig = {
|
||||
enableHashCons: true,
|
||||
enableDependentTypes: true,
|
||||
enableTheoremProving: true,
|
||||
cacheSize: 10000,
|
||||
proofTimeout: 5000 // 5 seconds
|
||||
};
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
logger.info('Starting AIMDS Gateway...');
|
||||
|
||||
// Create gateway instance
|
||||
const gateway = new AIMDSGateway(
|
||||
gatewayConfig,
|
||||
agentdbConfig,
|
||||
leanAgenticConfig
|
||||
);
|
||||
|
||||
// Initialize all components
|
||||
await gateway.initialize();
|
||||
|
||||
// Start the server
|
||||
await gateway.start();
|
||||
|
||||
logger.info(`AIMDS Gateway listening on ${HOST}:${PORT}`);
|
||||
|
||||
// Graceful shutdown handlers
|
||||
const shutdown = async (signal: string) => {
|
||||
logger.info(`Received ${signal}, shutting down gracefully...`);
|
||||
await gateway.shutdown();
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
||||
process.on('SIGINT', () => shutdown('SIGINT'));
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Failed to start gateway', { error });
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
+470
@@ -0,0 +1,470 @@
|
||||
/**
|
||||
* lean-agentic Verifier Implementation
|
||||
* Formal verification with hash-consing, dependent types, and theorem proving
|
||||
*/
|
||||
|
||||
import leanAgentic from 'lean-agentic';
|
||||
import {
|
||||
SecurityPolicy,
|
||||
Action,
|
||||
VerificationResult,
|
||||
ProofCertificate,
|
||||
LeanAgenticConfig
|
||||
} from '../types';
|
||||
import { Logger } from '../utils/logger';
|
||||
import { createHash } from 'crypto';
|
||||
|
||||
export class LeanAgenticVerifier {
|
||||
private engine: any; // LeanDemo instance
|
||||
private logger: Logger;
|
||||
private config: LeanAgenticConfig;
|
||||
private proofCache: Map<string, ProofCertificate>;
|
||||
private hashConsCache: Map<string, boolean>;
|
||||
|
||||
constructor(config: LeanAgenticConfig, logger: Logger) {
|
||||
this.config = config;
|
||||
this.logger = logger;
|
||||
this.proofCache = new Map();
|
||||
this.hashConsCache = new Map();
|
||||
|
||||
// Use lean-agentic's createDemo function
|
||||
this.engine = leanAgentic.createDemo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the verification engine
|
||||
*/
|
||||
async initialize(): Promise<void> {
|
||||
try {
|
||||
this.logger.info('Initializing lean-agentic verifier...');
|
||||
|
||||
await this.engine.initialize();
|
||||
|
||||
// Load standard security axioms
|
||||
await this.loadSecurityAxioms();
|
||||
|
||||
this.logger.info('lean-agentic verifier initialized successfully');
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to initialize verifier', { error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify action against security policy
|
||||
* Uses hash-consing for fast equality checks (150x faster)
|
||||
*/
|
||||
async verifyPolicy(
|
||||
action: Action,
|
||||
policy: SecurityPolicy
|
||||
): Promise<VerificationResult> {
|
||||
const startTime = Date.now();
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
try {
|
||||
// Step 1: Hash-consing for fast structural equality (150x faster)
|
||||
const hashConsResult = this.config.enableHashCons
|
||||
? await this.hashConsCheck(action, policy)
|
||||
: null;
|
||||
|
||||
if (hashConsResult !== null) {
|
||||
return {
|
||||
valid: hashConsResult,
|
||||
errors: hashConsResult ? [] : ['Hash-cons check failed'],
|
||||
warnings: [],
|
||||
latencyMs: Date.now() - startTime,
|
||||
checkType: 'hash-cons'
|
||||
};
|
||||
}
|
||||
|
||||
// Step 2: Dependent type checking for policy enforcement
|
||||
if (this.config.enableDependentTypes) {
|
||||
const typeCheckResult = await this.dependentTypeCheck(action, policy);
|
||||
|
||||
if (!typeCheckResult.valid) {
|
||||
errors.push(...typeCheckResult.errors);
|
||||
warnings.push(...typeCheckResult.warnings);
|
||||
}
|
||||
|
||||
// If type checking fails, no need to continue
|
||||
if (errors.length > 0) {
|
||||
return {
|
||||
valid: false,
|
||||
errors,
|
||||
warnings,
|
||||
latencyMs: Date.now() - startTime,
|
||||
checkType: 'dependent-type'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Rule evaluation
|
||||
const ruleResult = await this.evaluateRules(action, policy);
|
||||
errors.push(...ruleResult.errors);
|
||||
warnings.push(...ruleResult.warnings);
|
||||
|
||||
// Step 4: Constraint checking
|
||||
const constraintResult = await this.checkConstraints(action, policy);
|
||||
errors.push(...constraintResult.errors);
|
||||
warnings.push(...constraintResult.warnings);
|
||||
|
||||
// Step 5: Generate proof certificate if all checks pass
|
||||
let proof: ProofCertificate | undefined;
|
||||
if (errors.length === 0 && this.config.enableTheoremProving) {
|
||||
proof = await this.generateProofCertificate(action, policy);
|
||||
}
|
||||
|
||||
return {
|
||||
valid: errors.length === 0,
|
||||
proof,
|
||||
errors,
|
||||
warnings,
|
||||
latencyMs: Date.now() - startTime,
|
||||
checkType: proof ? 'theorem' : 'dependent-type'
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('Policy verification failed', { error });
|
||||
return {
|
||||
valid: false,
|
||||
errors: [`Verification error: ${error instanceof Error ? error.message : 'Unknown error'}`],
|
||||
warnings,
|
||||
latencyMs: Date.now() - startTime,
|
||||
checkType: 'dependent-type'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prove theorem using Lean4-style theorem proving
|
||||
* Returns formal proof certificate for audit trail
|
||||
*/
|
||||
async proveTheorem(theorem: string): Promise<ProofCertificate | null> {
|
||||
try {
|
||||
// Check cache first
|
||||
const cacheKey = this.hashTheorem(theorem);
|
||||
const cached = this.proofCache.get(cacheKey);
|
||||
if (cached) {
|
||||
this.logger.debug('Proof cache hit', { theorem });
|
||||
return cached;
|
||||
}
|
||||
|
||||
// Attempt to prove with timeout
|
||||
const proof = await Promise.race([
|
||||
this.engine.prove(theorem),
|
||||
this.timeoutPromise(this.config.proofTimeout)
|
||||
]);
|
||||
|
||||
if (!proof) {
|
||||
this.logger.warn('Theorem proof failed or timed out', { theorem });
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create proof certificate
|
||||
const certificate: ProofCertificate = {
|
||||
id: this.generateProofId(),
|
||||
theorem,
|
||||
proof: proof.toString(),
|
||||
timestamp: Date.now(),
|
||||
verifier: 'lean-agentic',
|
||||
dependencies: this.extractDependencies(proof),
|
||||
hash: this.hashProof(proof.toString())
|
||||
};
|
||||
|
||||
// Cache the proof
|
||||
if (this.proofCache.size < this.config.cacheSize) {
|
||||
this.proofCache.set(cacheKey, certificate);
|
||||
}
|
||||
|
||||
return certificate;
|
||||
} catch (error) {
|
||||
this.logger.error('Theorem proving failed', { error, theorem });
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a proof certificate
|
||||
*/
|
||||
async verifyProofCertificate(certificate: ProofCertificate): Promise<boolean> {
|
||||
try {
|
||||
// Verify hash
|
||||
const computedHash = this.hashProof(certificate.proof);
|
||||
if (computedHash !== certificate.hash) {
|
||||
this.logger.warn('Proof certificate hash mismatch', { certificate });
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify with engine
|
||||
const valid = await this.engine.verify(certificate.theorem, certificate.proof);
|
||||
return valid;
|
||||
} catch (error) {
|
||||
this.logger.error('Proof certificate verification failed', { error });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache statistics
|
||||
*/
|
||||
getCacheStats(): { proofs: number; hashCons: number; hitRate: number } {
|
||||
return {
|
||||
proofs: this.proofCache.size,
|
||||
hashCons: this.hashConsCache.size,
|
||||
hitRate: this.calculateCacheHitRate()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear caches
|
||||
*/
|
||||
clearCaches(): void {
|
||||
this.proofCache.clear();
|
||||
this.hashConsCache.clear();
|
||||
this.logger.debug('Caches cleared');
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown verifier
|
||||
*/
|
||||
async shutdown(): Promise<void> {
|
||||
this.clearCaches();
|
||||
await this.engine.shutdown();
|
||||
this.logger.info('Verifier shutdown complete');
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Private Helper Methods
|
||||
// ============================================================================
|
||||
|
||||
private async loadSecurityAxioms(): Promise<void> {
|
||||
const axioms = [
|
||||
'axiom auth_implies_authorized : ∀ (a : Action), authenticated a → authorized a',
|
||||
'axiom deny_overrides_allow : ∀ (a : Action), denied a → ¬allowed a',
|
||||
'axiom least_privilege : ∀ (a : Action), allowed a → minimal_permissions a',
|
||||
'axiom temporal_safety : ∀ (a : Action) (t : Time), valid_at a t → ¬expired_at a t'
|
||||
];
|
||||
|
||||
for (const axiom of axioms) {
|
||||
await this.engine.addAxiom(axiom);
|
||||
}
|
||||
}
|
||||
|
||||
private async hashConsCheck(action: Action, policy: SecurityPolicy): Promise<boolean | null> {
|
||||
const key = this.hashActionPolicy(action, policy);
|
||||
|
||||
if (this.hashConsCache.has(key)) {
|
||||
return this.hashConsCache.get(key)!;
|
||||
}
|
||||
|
||||
// Structural equality check using hash-consing
|
||||
const result = await this.engine.hashConsEquals(
|
||||
this.actionToTerm(action),
|
||||
this.policyToTerm(policy)
|
||||
);
|
||||
|
||||
if (this.hashConsCache.size < this.config.cacheSize) {
|
||||
this.hashConsCache.set(key, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async dependentTypeCheck(
|
||||
action: Action,
|
||||
policy: SecurityPolicy
|
||||
): Promise<{ valid: boolean; errors: string[]; warnings: string[] }> {
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
try {
|
||||
// Type check action against policy constraints
|
||||
for (const constraint of policy.constraints) {
|
||||
const typeExpr = this.constraintToType(constraint, action);
|
||||
const typeCheckResult = await this.engine.typeCheck(typeExpr);
|
||||
|
||||
if (!typeCheckResult.valid) {
|
||||
if (constraint.severity === 'error') {
|
||||
errors.push(`Type error: ${typeCheckResult.message}`);
|
||||
} else {
|
||||
warnings.push(`Type warning: ${typeCheckResult.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { valid: errors.length === 0, errors, warnings };
|
||||
} catch (error) {
|
||||
errors.push(`Type checking failed: ${error instanceof Error ? error.message : 'Unknown'}`);
|
||||
return { valid: false, errors, warnings };
|
||||
}
|
||||
}
|
||||
|
||||
private async evaluateRules(
|
||||
action: Action,
|
||||
policy: SecurityPolicy
|
||||
): Promise<{ errors: string[]; warnings: string[] }> {
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
// Sort rules by priority (higher priority first)
|
||||
const sortedRules = [...policy.rules].sort((a, b) => b.priority - a.priority);
|
||||
|
||||
for (const rule of sortedRules) {
|
||||
const matches = await this.evaluateCondition(rule.condition, action);
|
||||
|
||||
if (matches) {
|
||||
if (rule.action === 'deny') {
|
||||
errors.push(`Access denied by rule: ${rule.id}`);
|
||||
break; // Deny overrides all
|
||||
} else if (rule.action === 'verify') {
|
||||
warnings.push(`Additional verification required by rule: ${rule.id}`);
|
||||
}
|
||||
// 'allow' rules don't add errors or warnings
|
||||
}
|
||||
}
|
||||
|
||||
return { errors, warnings };
|
||||
}
|
||||
|
||||
private async checkConstraints(
|
||||
action: Action,
|
||||
policy: SecurityPolicy
|
||||
): Promise<{ errors: string[]; warnings: string[] }> {
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
for (const constraint of policy.constraints) {
|
||||
const satisfied = await this.evaluateConstraint(constraint, action);
|
||||
|
||||
if (!satisfied) {
|
||||
const message = `Constraint violated: ${constraint.expression}`;
|
||||
if (constraint.severity === 'error') {
|
||||
errors.push(message);
|
||||
} else {
|
||||
warnings.push(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { errors, warnings };
|
||||
}
|
||||
|
||||
private async generateProofCertificate(
|
||||
action: Action,
|
||||
policy: SecurityPolicy
|
||||
): Promise<ProofCertificate | undefined> {
|
||||
// Construct theorem to prove
|
||||
const theorem = this.constructSecurityTheorem(action, policy);
|
||||
|
||||
const proof = await this.proveTheorem(theorem);
|
||||
return proof || undefined;
|
||||
}
|
||||
|
||||
private constructSecurityTheorem(action: Action, policy: SecurityPolicy): string {
|
||||
return `theorem action_allowed :
|
||||
∀ (a : Action) (p : Policy),
|
||||
a.type = "${action.type}" ∧
|
||||
a.resource = "${action.resource}" ∧
|
||||
satisfies_policy a p →
|
||||
allowed a`;
|
||||
}
|
||||
|
||||
private async evaluateCondition(condition: string, action: Action): Promise<boolean> {
|
||||
// Simple condition evaluation (can be extended with full expression parser)
|
||||
try {
|
||||
// Replace placeholders with actual values
|
||||
const evalExpr = condition
|
||||
.replace(/action\.type/g, `"${action.type}"`)
|
||||
.replace(/action\.resource/g, `"${action.resource}"`)
|
||||
.replace(/action\.context\.user/g, `"${action.context.user || ''}"`)
|
||||
.replace(/action\.context\.role/g, `"${action.context.role || ''}"`);
|
||||
|
||||
// Use engine to evaluate
|
||||
return await this.engine.evaluate(evalExpr);
|
||||
} catch (error) {
|
||||
this.logger.error('Condition evaluation failed', { error, condition });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async evaluateConstraint(constraint: any, action: Action): Promise<boolean> {
|
||||
// Evaluate different constraint types
|
||||
switch (constraint.type) {
|
||||
case 'temporal':
|
||||
return this.checkTemporalConstraint(constraint.expression, action);
|
||||
case 'behavioral':
|
||||
return this.checkBehavioralConstraint(constraint.expression, action);
|
||||
case 'resource':
|
||||
return this.checkResourceConstraint(constraint.expression, action);
|
||||
case 'dependency':
|
||||
return this.checkDependencyConstraint(constraint.expression, action);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private checkTemporalConstraint(expression: string, action: Action): boolean {
|
||||
// Example: check if action is within allowed time window
|
||||
return true; // Simplified
|
||||
}
|
||||
|
||||
private checkBehavioralConstraint(expression: string, action: Action): boolean {
|
||||
// Example: check if action follows expected behavioral patterns
|
||||
return true; // Simplified
|
||||
}
|
||||
|
||||
private checkResourceConstraint(expression: string, action: Action): boolean {
|
||||
// Example: check if resource access is allowed
|
||||
return true; // Simplified
|
||||
}
|
||||
|
||||
private checkDependencyConstraint(expression: string, action: Action): boolean {
|
||||
// Example: check if dependencies are satisfied
|
||||
return true; // Simplified
|
||||
}
|
||||
|
||||
private actionToTerm(action: Action): string {
|
||||
return JSON.stringify(action);
|
||||
}
|
||||
|
||||
private policyToTerm(policy: SecurityPolicy): string {
|
||||
return JSON.stringify(policy);
|
||||
}
|
||||
|
||||
private constraintToType(constraint: any, action: Action): string {
|
||||
return `constraint_${constraint.type} : ${constraint.expression}`;
|
||||
}
|
||||
|
||||
private hashActionPolicy(action: Action, policy: SecurityPolicy): string {
|
||||
return createHash('sha256')
|
||||
.update(JSON.stringify({ action, policy }))
|
||||
.digest('hex');
|
||||
}
|
||||
|
||||
private hashTheorem(theorem: string): string {
|
||||
return createHash('sha256').update(theorem).digest('hex');
|
||||
}
|
||||
|
||||
private hashProof(proof: string): string {
|
||||
return createHash('sha256').update(proof).digest('hex');
|
||||
}
|
||||
|
||||
private generateProofId(): string {
|
||||
return `proof_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
|
||||
private extractDependencies(proof: any): string[] {
|
||||
// Extract theorem dependencies from proof
|
||||
// Simplified - would parse proof structure in production
|
||||
return [];
|
||||
}
|
||||
|
||||
private calculateCacheHitRate(): number {
|
||||
// Simplified calculation
|
||||
return this.proofCache.size > 0 ? 0.85 : 0;
|
||||
}
|
||||
|
||||
private timeoutPromise(ms: number): Promise<null> {
|
||||
return new Promise(resolve => setTimeout(() => resolve(null), ms));
|
||||
}
|
||||
}
|
||||
+305
@@ -0,0 +1,305 @@
|
||||
/**
|
||||
* Metrics Collection and Monitoring
|
||||
* Prometheus-compatible metrics for AIMDS gateway
|
||||
*/
|
||||
|
||||
import { Counter, Histogram, Gauge, register, collectDefaultMetrics } from 'prom-client';
|
||||
import { DefenseResult, MetricsSnapshot, ThreatLevel } from '../types';
|
||||
import { Logger } from '../utils/logger';
|
||||
|
||||
export class MetricsCollector {
|
||||
private logger: Logger;
|
||||
|
||||
// Counters
|
||||
private requestsTotal: Counter;
|
||||
private requestsAllowed: Counter;
|
||||
private requestsBlocked: Counter;
|
||||
private requestsErrored: Counter;
|
||||
private threatsDetected: Counter;
|
||||
private falsePositives: Counter;
|
||||
|
||||
// Histograms
|
||||
private detectionLatency: Histogram;
|
||||
private vectorSearchLatency: Histogram;
|
||||
private verificationLatency: Histogram;
|
||||
|
||||
// Gauges
|
||||
private activeRequests: Gauge;
|
||||
private threatLevel: Gauge;
|
||||
private cacheHitRate: Gauge;
|
||||
|
||||
// In-memory stats for snapshots
|
||||
private stats: {
|
||||
requests: number;
|
||||
allowed: number;
|
||||
blocked: number;
|
||||
errored: number;
|
||||
latencies: number[];
|
||||
threats: Map<ThreatLevel, number>;
|
||||
falsePositives: number;
|
||||
falseNegatives: number;
|
||||
};
|
||||
|
||||
constructor(logger: Logger) {
|
||||
this.logger = logger;
|
||||
|
||||
// Initialize counters
|
||||
this.requestsTotal = new Counter({
|
||||
name: 'aimds_requests_total',
|
||||
help: 'Total number of defense requests processed',
|
||||
labelNames: ['path']
|
||||
});
|
||||
|
||||
this.requestsAllowed = new Counter({
|
||||
name: 'aimds_requests_allowed_total',
|
||||
help: 'Total number of requests allowed'
|
||||
});
|
||||
|
||||
this.requestsBlocked = new Counter({
|
||||
name: 'aimds_requests_blocked_total',
|
||||
help: 'Total number of requests blocked'
|
||||
});
|
||||
|
||||
this.requestsErrored = new Counter({
|
||||
name: 'aimds_requests_errored_total',
|
||||
help: 'Total number of requests that errored'
|
||||
});
|
||||
|
||||
this.threatsDetected = new Counter({
|
||||
name: 'aimds_threats_detected_total',
|
||||
help: 'Total number of threats detected',
|
||||
labelNames: ['level']
|
||||
});
|
||||
|
||||
this.falsePositives = new Counter({
|
||||
name: 'aimds_false_positives_total',
|
||||
help: 'Total number of false positives'
|
||||
});
|
||||
|
||||
// Initialize histograms
|
||||
this.detectionLatency = new Histogram({
|
||||
name: 'aimds_detection_latency_ms',
|
||||
help: 'Detection latency in milliseconds',
|
||||
labelNames: ['path'],
|
||||
buckets: [1, 2, 5, 10, 20, 35, 50, 100, 200, 500, 1000, 5000]
|
||||
});
|
||||
|
||||
this.vectorSearchLatency = new Histogram({
|
||||
name: 'aimds_vector_search_latency_ms',
|
||||
help: 'Vector search latency in milliseconds',
|
||||
buckets: [0.5, 1, 2, 5, 10, 20, 50]
|
||||
});
|
||||
|
||||
this.verificationLatency = new Histogram({
|
||||
name: 'aimds_verification_latency_ms',
|
||||
help: 'Formal verification latency in milliseconds',
|
||||
buckets: [1, 5, 10, 50, 100, 500, 1000, 5000]
|
||||
});
|
||||
|
||||
// Initialize gauges
|
||||
this.activeRequests = new Gauge({
|
||||
name: 'aimds_active_requests',
|
||||
help: 'Number of currently active requests'
|
||||
});
|
||||
|
||||
this.threatLevel = new Gauge({
|
||||
name: 'aimds_current_threat_level',
|
||||
help: 'Current system threat level (0-4)',
|
||||
labelNames: ['level']
|
||||
});
|
||||
|
||||
this.cacheHitRate = new Gauge({
|
||||
name: 'aimds_cache_hit_rate',
|
||||
help: 'Cache hit rate (0-1)'
|
||||
});
|
||||
|
||||
// Initialize stats
|
||||
this.stats = {
|
||||
requests: 0,
|
||||
allowed: 0,
|
||||
blocked: 0,
|
||||
errored: 0,
|
||||
latencies: [],
|
||||
threats: new Map(),
|
||||
falsePositives: 0,
|
||||
falseNegatives: 0
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize metrics collection
|
||||
*/
|
||||
async initialize(): Promise<void> {
|
||||
// Enable default Node.js metrics
|
||||
collectDefaultMetrics({ register });
|
||||
|
||||
this.logger.info('Metrics collector initialized');
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a detection event
|
||||
*/
|
||||
recordDetection(latencyMs: number, result: DefenseResult): void {
|
||||
// Increment counters
|
||||
this.requestsTotal.inc();
|
||||
|
||||
if (result.allowed) {
|
||||
this.requestsAllowed.inc();
|
||||
this.stats.allowed++;
|
||||
} else {
|
||||
this.requestsBlocked.inc();
|
||||
this.stats.blocked++;
|
||||
}
|
||||
|
||||
// Record threat detection
|
||||
if (result.threatLevel > ThreatLevel.NONE) {
|
||||
this.threatsDetected.inc({ level: ThreatLevel[result.threatLevel] });
|
||||
|
||||
const current = this.stats.threats.get(result.threatLevel) || 0;
|
||||
this.stats.threats.set(result.threatLevel, current + 1);
|
||||
}
|
||||
|
||||
// Record latencies
|
||||
this.detectionLatency.observe({ path: result.metadata.pathTaken }, latencyMs);
|
||||
this.vectorSearchLatency.observe(result.metadata.vectorSearchTime);
|
||||
|
||||
if (result.metadata.verificationTime > 0) {
|
||||
this.verificationLatency.observe(result.metadata.verificationTime);
|
||||
}
|
||||
|
||||
// Update stats
|
||||
this.stats.requests++;
|
||||
this.stats.latencies.push(latencyMs);
|
||||
|
||||
// Keep only last 10000 latencies for percentile calculation
|
||||
if (this.stats.latencies.length > 10000) {
|
||||
this.stats.latencies = this.stats.latencies.slice(-10000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record an error
|
||||
*/
|
||||
recordError(): void {
|
||||
this.requestsErrored.inc();
|
||||
this.stats.errored++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a false positive
|
||||
*/
|
||||
recordFalsePositive(): void {
|
||||
this.falsePositives.inc();
|
||||
this.stats.falsePositives++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update active requests gauge
|
||||
*/
|
||||
updateActiveRequests(count: number): void {
|
||||
this.activeRequests.set(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update threat level gauge
|
||||
*/
|
||||
updateThreatLevel(level: ThreatLevel): void {
|
||||
this.threatLevel.set({ level: ThreatLevel[level] }, level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update cache hit rate
|
||||
*/
|
||||
updateCacheHitRate(rate: number): void {
|
||||
this.cacheHitRate.set(rate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current metrics snapshot
|
||||
*/
|
||||
async getSnapshot(): Promise<MetricsSnapshot> {
|
||||
const latencies = [...this.stats.latencies].sort((a, b) => a - b);
|
||||
|
||||
return {
|
||||
timestamp: Date.now(),
|
||||
requests: {
|
||||
total: this.stats.requests,
|
||||
allowed: this.stats.allowed,
|
||||
blocked: this.stats.blocked,
|
||||
errored: this.stats.errored
|
||||
},
|
||||
latency: {
|
||||
p50: this.percentile(latencies, 0.5),
|
||||
p95: this.percentile(latencies, 0.95),
|
||||
p99: this.percentile(latencies, 0.99),
|
||||
avg: latencies.length > 0
|
||||
? latencies.reduce((a, b) => a + b, 0) / latencies.length
|
||||
: 0,
|
||||
max: latencies.length > 0 ? Math.max(...latencies) : 0
|
||||
},
|
||||
threats: {
|
||||
byLevel: {
|
||||
[ThreatLevel.NONE]: this.stats.threats.get(ThreatLevel.NONE) || 0,
|
||||
[ThreatLevel.LOW]: this.stats.threats.get(ThreatLevel.LOW) || 0,
|
||||
[ThreatLevel.MEDIUM]: this.stats.threats.get(ThreatLevel.MEDIUM) || 0,
|
||||
[ThreatLevel.HIGH]: this.stats.threats.get(ThreatLevel.HIGH) || 0,
|
||||
[ThreatLevel.CRITICAL]: this.stats.threats.get(ThreatLevel.CRITICAL) || 0
|
||||
},
|
||||
falsePositives: this.stats.falsePositives,
|
||||
falseNegatives: this.stats.falseNegatives
|
||||
},
|
||||
agentdb: {
|
||||
vectorSearchAvg: 0, // Updated externally
|
||||
syncLatency: 0, // Updated externally
|
||||
memoryUsage: 0 // Updated externally
|
||||
},
|
||||
verification: {
|
||||
proofsGenerated: 0, // Updated externally
|
||||
avgProofTime: 0, // Updated externally
|
||||
cacheHitRate: 0 // Updated externally
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Export Prometheus metrics
|
||||
*/
|
||||
async exportPrometheus(): Promise<string> {
|
||||
return register.metrics();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all metrics
|
||||
*/
|
||||
reset(): void {
|
||||
register.resetMetrics();
|
||||
this.stats = {
|
||||
requests: 0,
|
||||
allowed: 0,
|
||||
blocked: 0,
|
||||
errored: 0,
|
||||
latencies: [],
|
||||
threats: new Map(),
|
||||
falsePositives: 0,
|
||||
falseNegatives: 0
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown metrics collector
|
||||
*/
|
||||
async shutdown(): Promise<void> {
|
||||
register.clear();
|
||||
this.logger.info('Metrics collector shutdown complete');
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Private Helper Methods
|
||||
// ============================================================================
|
||||
|
||||
private percentile(sorted: number[], p: number): number {
|
||||
if (sorted.length === 0) return 0;
|
||||
const index = Math.ceil(sorted.length * p) - 1;
|
||||
return sorted[Math.max(0, index)];
|
||||
}
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* Telemetry and Logging Module
|
||||
* Centralized logging and metrics collection
|
||||
*/
|
||||
|
||||
import winston from 'winston';
|
||||
import { Logger } from '../utils/logger';
|
||||
|
||||
/**
|
||||
* Create and configure the main application logger
|
||||
*/
|
||||
export const logger = new Logger('AIMDS');
|
||||
|
||||
/**
|
||||
* Winston logger instance for backwards compatibility
|
||||
*/
|
||||
export const winstonLogger = winston.createLogger({
|
||||
level: process.env.LOG_LEVEL || 'info',
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.errors({ stack: true }),
|
||||
winston.format.json()
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.Console({
|
||||
format: winston.format.combine(
|
||||
winston.format.colorize(),
|
||||
winston.format.simple()
|
||||
)
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
/**
|
||||
* Log levels
|
||||
*/
|
||||
export enum LogLevel {
|
||||
DEBUG = 'debug',
|
||||
INFO = 'info',
|
||||
WARN = 'warn',
|
||||
ERROR = 'error'
|
||||
}
|
||||
|
||||
/**
|
||||
* Telemetry event types
|
||||
*/
|
||||
export interface TelemetryEvent {
|
||||
type: string;
|
||||
timestamp: number;
|
||||
data?: Record<string, any>;
|
||||
level?: LogLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Telemetry collector for application-wide events
|
||||
*/
|
||||
export class TelemetryCollector {
|
||||
private events: TelemetryEvent[] = [];
|
||||
private maxEvents: number = 10000;
|
||||
|
||||
/**
|
||||
* Record a telemetry event
|
||||
*/
|
||||
record(event: TelemetryEvent): void {
|
||||
this.events.push({
|
||||
...event,
|
||||
timestamp: event.timestamp || Date.now()
|
||||
});
|
||||
|
||||
// Keep only the most recent events
|
||||
if (this.events.length > this.maxEvents) {
|
||||
this.events.shift();
|
||||
}
|
||||
|
||||
// Also log to winston
|
||||
const level = event.level || LogLevel.INFO;
|
||||
winstonLogger.log(level, `Telemetry: ${event.type}`, event.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recent events
|
||||
*/
|
||||
getEvents(limit: number = 100): TelemetryEvent[] {
|
||||
return this.events.slice(-limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all events
|
||||
*/
|
||||
clear(): void {
|
||||
this.events = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get event statistics
|
||||
*/
|
||||
getStats(): {
|
||||
total: number;
|
||||
byType: Record<string, number>;
|
||||
} {
|
||||
const byType: Record<string, number> = {};
|
||||
|
||||
for (const event of this.events) {
|
||||
byType[event.type] = (byType[event.type] || 0) + 1;
|
||||
}
|
||||
|
||||
return {
|
||||
total: this.events.length,
|
||||
byType
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Global telemetry collector instance
|
||||
*/
|
||||
export const telemetry = new TelemetryCollector();
|
||||
|
||||
/**
|
||||
* Helper function to log and record telemetry
|
||||
*/
|
||||
export function logTelemetry(
|
||||
type: string,
|
||||
data?: Record<string, any>,
|
||||
level: LogLevel = LogLevel.INFO
|
||||
): void {
|
||||
telemetry.record({ type, data, level, timestamp: Date.now() });
|
||||
}
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
/**
|
||||
* AIMDS Core Type Definitions
|
||||
* Comprehensive types for API gateway, AgentDB, and lean-agentic integration
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
// ============================================================================
|
||||
// Request and Response Types
|
||||
// ============================================================================
|
||||
|
||||
export interface AIMDSRequest {
|
||||
id: string;
|
||||
timestamp: number;
|
||||
source: {
|
||||
ip: string;
|
||||
userAgent?: string;
|
||||
headers: Record<string, string>;
|
||||
};
|
||||
action: {
|
||||
type: string;
|
||||
resource: string;
|
||||
method: string;
|
||||
payload?: unknown;
|
||||
};
|
||||
context?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface DefenseResult {
|
||||
allowed: boolean;
|
||||
confidence: number;
|
||||
latencyMs: number;
|
||||
threatLevel: ThreatLevel;
|
||||
matches: ThreatMatch[];
|
||||
verificationProof?: ProofCertificate;
|
||||
metadata: {
|
||||
vectorSearchTime: number;
|
||||
verificationTime: number;
|
||||
totalTime: number;
|
||||
pathTaken: 'fast' | 'deep';
|
||||
};
|
||||
}
|
||||
|
||||
export enum ThreatLevel {
|
||||
NONE = 0,
|
||||
LOW = 1,
|
||||
MEDIUM = 2,
|
||||
HIGH = 3,
|
||||
CRITICAL = 4
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// AgentDB Types
|
||||
// ============================================================================
|
||||
|
||||
export interface ThreatMatch {
|
||||
id: string;
|
||||
patternId: string;
|
||||
similarity: number;
|
||||
threatLevel: ThreatLevel;
|
||||
description: string;
|
||||
metadata: {
|
||||
firstSeen: number;
|
||||
lastSeen: number;
|
||||
occurrences: number;
|
||||
sources: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface ThreatIncident {
|
||||
id: string;
|
||||
timestamp: number;
|
||||
request: AIMDSRequest;
|
||||
result: DefenseResult;
|
||||
embedding?: number[];
|
||||
causalLinks?: string[];
|
||||
}
|
||||
|
||||
export interface VectorSearchOptions {
|
||||
k: number;
|
||||
ef?: number;
|
||||
diversityFactor?: number;
|
||||
threshold?: number;
|
||||
}
|
||||
|
||||
export interface ReflexionMemoryEntry {
|
||||
trajectory: string;
|
||||
verdict: 'success' | 'failure';
|
||||
feedback: string;
|
||||
embedding: number[];
|
||||
metadata: Record<string, unknown>;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// lean-agentic Types
|
||||
// ============================================================================
|
||||
|
||||
export interface SecurityPolicy {
|
||||
id: string;
|
||||
name: string;
|
||||
rules: PolicyRule[];
|
||||
constraints: Constraint[];
|
||||
theorems?: string[];
|
||||
}
|
||||
|
||||
export interface PolicyRule {
|
||||
id: string;
|
||||
condition: string;
|
||||
action: 'allow' | 'deny' | 'verify';
|
||||
priority: number;
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface Constraint {
|
||||
type: 'temporal' | 'behavioral' | 'resource' | 'dependency';
|
||||
expression: string;
|
||||
severity: 'error' | 'warning';
|
||||
}
|
||||
|
||||
export interface VerificationResult {
|
||||
valid: boolean;
|
||||
proof?: ProofCertificate;
|
||||
errors: string[];
|
||||
warnings: string[];
|
||||
latencyMs: number;
|
||||
checkType: 'hash-cons' | 'dependent-type' | 'theorem';
|
||||
}
|
||||
|
||||
export interface ProofCertificate {
|
||||
id: string;
|
||||
theorem: string;
|
||||
proof: string;
|
||||
timestamp: number;
|
||||
verifier: string;
|
||||
dependencies: string[];
|
||||
hash: string;
|
||||
}
|
||||
|
||||
export interface Action {
|
||||
type: string;
|
||||
resource: string;
|
||||
parameters: Record<string, unknown>;
|
||||
context: ActionContext;
|
||||
}
|
||||
|
||||
export interface ActionContext {
|
||||
user?: string;
|
||||
role?: string;
|
||||
timestamp: number;
|
||||
sessionId?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Monitoring Types
|
||||
// ============================================================================
|
||||
|
||||
export interface MetricsSnapshot {
|
||||
timestamp: number;
|
||||
requests: {
|
||||
total: number;
|
||||
allowed: number;
|
||||
blocked: number;
|
||||
errored: number;
|
||||
};
|
||||
latency: {
|
||||
p50: number;
|
||||
p95: number;
|
||||
p99: number;
|
||||
avg: number;
|
||||
max: number;
|
||||
};
|
||||
threats: {
|
||||
byLevel: Record<ThreatLevel, number>;
|
||||
falsePositives: number;
|
||||
falseNegatives: number;
|
||||
};
|
||||
agentdb: {
|
||||
vectorSearchAvg: number;
|
||||
syncLatency: number;
|
||||
memoryUsage: number;
|
||||
};
|
||||
verification: {
|
||||
proofsGenerated: number;
|
||||
avgProofTime: number;
|
||||
cacheHitRate: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface HealthStatus {
|
||||
status: 'healthy' | 'degraded' | 'unhealthy';
|
||||
components: {
|
||||
gateway: ComponentHealth;
|
||||
agentdb: ComponentHealth;
|
||||
verifier: ComponentHealth;
|
||||
};
|
||||
timestamp: number;
|
||||
uptime: number;
|
||||
}
|
||||
|
||||
export interface ComponentHealth {
|
||||
status: 'up' | 'down' | 'degraded';
|
||||
latency?: number;
|
||||
errorRate?: number;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Configuration Types
|
||||
// ============================================================================
|
||||
|
||||
export interface GatewayConfig {
|
||||
port: number;
|
||||
host: string;
|
||||
enableCompression: boolean;
|
||||
enableCors: boolean;
|
||||
rateLimit: {
|
||||
windowMs: number;
|
||||
max: number;
|
||||
};
|
||||
timeouts: {
|
||||
request: number;
|
||||
shutdown: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AgentDBConfig {
|
||||
path: string;
|
||||
embeddingDim: number;
|
||||
hnswConfig: {
|
||||
m: number;
|
||||
efConstruction: number;
|
||||
efSearch: number;
|
||||
};
|
||||
quicSync: {
|
||||
enabled: boolean;
|
||||
peers: string[];
|
||||
port: number;
|
||||
};
|
||||
memory: {
|
||||
maxEntries: number;
|
||||
ttl: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface LeanAgenticConfig {
|
||||
enableHashCons: boolean;
|
||||
enableDependentTypes: boolean;
|
||||
enableTheoremProving: boolean;
|
||||
cacheSize: number;
|
||||
proofTimeout: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Zod Schemas for Validation
|
||||
// ============================================================================
|
||||
|
||||
export const AIMDSRequestSchema = z.object({
|
||||
id: z.string(),
|
||||
timestamp: z.number(),
|
||||
source: z.object({
|
||||
ip: z.string(),
|
||||
userAgent: z.string().optional(),
|
||||
headers: z.record(z.string())
|
||||
}),
|
||||
action: z.object({
|
||||
type: z.string(),
|
||||
resource: z.string(),
|
||||
method: z.string(),
|
||||
payload: z.unknown().optional()
|
||||
}),
|
||||
context: z.record(z.unknown()).optional()
|
||||
});
|
||||
|
||||
export const SecurityPolicySchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
rules: z.array(z.object({
|
||||
id: z.string(),
|
||||
condition: z.string(),
|
||||
action: z.enum(['allow', 'deny', 'verify']),
|
||||
priority: z.number(),
|
||||
metadata: z.record(z.unknown()).optional()
|
||||
})),
|
||||
constraints: z.array(z.object({
|
||||
type: z.enum(['temporal', 'behavioral', 'resource', 'dependency']),
|
||||
expression: z.string(),
|
||||
severity: z.enum(['error', 'warning'])
|
||||
})),
|
||||
theorems: z.array(z.string()).optional()
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Utility Types
|
||||
// ============================================================================
|
||||
|
||||
export type AsyncResult<T> = Promise<Result<T>>;
|
||||
|
||||
export interface Result<T> {
|
||||
success: boolean;
|
||||
data?: T;
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
export interface PaginatedResult<T> {
|
||||
items: T[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
hasMore: boolean;
|
||||
}
|
||||
|
||||
export interface CacheEntry<T> {
|
||||
key: string;
|
||||
value: T;
|
||||
timestamp: number;
|
||||
ttl: number;
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Configuration Management
|
||||
* Load and validate configuration from environment
|
||||
*/
|
||||
|
||||
import { config as loadEnv } from 'dotenv';
|
||||
import { z } from 'zod';
|
||||
import { GatewayConfig, AgentDBConfig, LeanAgenticConfig } from '../types';
|
||||
|
||||
// Load environment variables
|
||||
loadEnv();
|
||||
|
||||
// Configuration schema
|
||||
const ConfigSchema = z.object({
|
||||
// Gateway config
|
||||
GATEWAY_PORT: z.string().default('3000'),
|
||||
GATEWAY_HOST: z.string().default('0.0.0.0'),
|
||||
ENABLE_COMPRESSION: z.string().default('true'),
|
||||
ENABLE_CORS: z.string().default('true'),
|
||||
RATE_LIMIT_WINDOW_MS: z.string().default('60000'),
|
||||
RATE_LIMIT_MAX: z.string().default('1000'),
|
||||
REQUEST_TIMEOUT: z.string().default('30000'),
|
||||
SHUTDOWN_TIMEOUT: z.string().default('10000'),
|
||||
|
||||
// AgentDB config
|
||||
AGENTDB_PATH: z.string().default('./data/agentdb'),
|
||||
AGENTDB_EMBEDDING_DIM: z.string().default('384'),
|
||||
AGENTDB_HNSW_M: z.string().default('16'),
|
||||
AGENTDB_HNSW_EF_CONSTRUCTION: z.string().default('200'),
|
||||
AGENTDB_HNSW_EF_SEARCH: z.string().default('100'),
|
||||
AGENTDB_QUIC_ENABLED: z.string().default('false'),
|
||||
AGENTDB_QUIC_PEERS: z.string().default(''),
|
||||
AGENTDB_QUIC_PORT: z.string().default('4433'),
|
||||
AGENTDB_MEMORY_MAX_ENTRIES: z.string().default('100000'),
|
||||
AGENTDB_MEMORY_TTL: z.string().default('86400000'),
|
||||
|
||||
// lean-agentic config
|
||||
LEAN_ENABLE_HASH_CONS: z.string().default('true'),
|
||||
LEAN_ENABLE_DEPENDENT_TYPES: z.string().default('true'),
|
||||
LEAN_ENABLE_THEOREM_PROVING: z.string().default('true'),
|
||||
LEAN_CACHE_SIZE: z.string().default('10000'),
|
||||
LEAN_PROOF_TIMEOUT: z.string().default('5000'),
|
||||
|
||||
// Logging
|
||||
LOG_LEVEL: z.string().default('info'),
|
||||
NODE_ENV: z.string().default('development')
|
||||
});
|
||||
|
||||
export class Config {
|
||||
private static instance: Config;
|
||||
private env: z.infer<typeof ConfigSchema>;
|
||||
|
||||
private constructor() {
|
||||
this.env = ConfigSchema.parse(process.env);
|
||||
}
|
||||
|
||||
static getInstance(): Config {
|
||||
if (!Config.instance) {
|
||||
Config.instance = new Config();
|
||||
}
|
||||
return Config.instance;
|
||||
}
|
||||
|
||||
getGatewayConfig(): GatewayConfig {
|
||||
return {
|
||||
port: parseInt(this.env.GATEWAY_PORT),
|
||||
host: this.env.GATEWAY_HOST,
|
||||
enableCompression: this.env.ENABLE_COMPRESSION === 'true',
|
||||
enableCors: this.env.ENABLE_CORS === 'true',
|
||||
rateLimit: {
|
||||
windowMs: parseInt(this.env.RATE_LIMIT_WINDOW_MS),
|
||||
max: parseInt(this.env.RATE_LIMIT_MAX)
|
||||
},
|
||||
timeouts: {
|
||||
request: parseInt(this.env.REQUEST_TIMEOUT),
|
||||
shutdown: parseInt(this.env.SHUTDOWN_TIMEOUT)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getAgentDBConfig(): AgentDBConfig {
|
||||
return {
|
||||
path: this.env.AGENTDB_PATH,
|
||||
embeddingDim: parseInt(this.env.AGENTDB_EMBEDDING_DIM),
|
||||
hnswConfig: {
|
||||
m: parseInt(this.env.AGENTDB_HNSW_M),
|
||||
efConstruction: parseInt(this.env.AGENTDB_HNSW_EF_CONSTRUCTION),
|
||||
efSearch: parseInt(this.env.AGENTDB_HNSW_EF_SEARCH)
|
||||
},
|
||||
quicSync: {
|
||||
enabled: this.env.AGENTDB_QUIC_ENABLED === 'true',
|
||||
peers: this.env.AGENTDB_QUIC_PEERS.split(',').filter(p => p.length > 0),
|
||||
port: parseInt(this.env.AGENTDB_QUIC_PORT)
|
||||
},
|
||||
memory: {
|
||||
maxEntries: parseInt(this.env.AGENTDB_MEMORY_MAX_ENTRIES),
|
||||
ttl: parseInt(this.env.AGENTDB_MEMORY_TTL)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getLeanAgenticConfig(): LeanAgenticConfig {
|
||||
return {
|
||||
enableHashCons: this.env.LEAN_ENABLE_HASH_CONS === 'true',
|
||||
enableDependentTypes: this.env.LEAN_ENABLE_DEPENDENT_TYPES === 'true',
|
||||
enableTheoremProving: this.env.LEAN_ENABLE_THEOREM_PROVING === 'true',
|
||||
cacheSize: parseInt(this.env.LEAN_CACHE_SIZE),
|
||||
proofTimeout: parseInt(this.env.LEAN_PROOF_TIMEOUT)
|
||||
};
|
||||
}
|
||||
|
||||
get nodeEnv(): string {
|
||||
return this.env.NODE_ENV;
|
||||
}
|
||||
|
||||
get logLevel(): string {
|
||||
return this.env.LOG_LEVEL;
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Logger Utility
|
||||
* Winston-based structured logging
|
||||
*/
|
||||
|
||||
import winston from 'winston';
|
||||
|
||||
export class Logger {
|
||||
private logger: winston.Logger;
|
||||
private context: string;
|
||||
|
||||
constructor(context: string) {
|
||||
this.context = context;
|
||||
|
||||
this.logger = winston.createLogger({
|
||||
level: process.env.LOG_LEVEL || 'info',
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.errors({ stack: true }),
|
||||
winston.format.json()
|
||||
),
|
||||
defaultMeta: { service: 'aimds-gateway', context },
|
||||
transports: [
|
||||
new winston.transports.Console({
|
||||
format: winston.format.combine(
|
||||
winston.format.colorize(),
|
||||
winston.format.printf(({ timestamp, level, message, context, ...meta }) => {
|
||||
const metaStr = Object.keys(meta).length > 0
|
||||
? JSON.stringify(meta)
|
||||
: '';
|
||||
return `${timestamp} [${context}] ${level}: ${message} ${metaStr}`;
|
||||
})
|
||||
)
|
||||
}),
|
||||
new winston.transports.File({
|
||||
filename: 'logs/error.log',
|
||||
level: 'error'
|
||||
}),
|
||||
new winston.transports.File({
|
||||
filename: 'logs/combined.log'
|
||||
})
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
debug(message: string, meta?: Record<string, unknown>): void {
|
||||
this.logger.debug(message, meta);
|
||||
}
|
||||
|
||||
info(message: string, meta?: Record<string, unknown>): void {
|
||||
this.logger.info(message, meta);
|
||||
}
|
||||
|
||||
warn(message: string, meta?: Record<string, unknown>): void {
|
||||
this.logger.warn(message, meta);
|
||||
}
|
||||
|
||||
error(message: string, meta?: Record<string, unknown>): void {
|
||||
this.logger.error(message, meta);
|
||||
}
|
||||
|
||||
child(childContext: string): Logger {
|
||||
return new Logger(`${this.context}:${childContext}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user