/** * @cognitum/gate - Browser and Node.js coherence gate for AI agent safety * * Real-time permit/defer/deny decisions in microseconds. * "Attention becomes a permission system, not a popularity contest." * * Created by ruv.io and RuVector * @see https://github.com/ruvnet/ruvector */ // ============================================================================= // Type Definitions // ============================================================================= /** Decision verdict for a permit request */ export type Verdict = 'permit' | 'defer' | 'deny'; /** Request priority levels */ export type Priority = 'low' | 'normal' | 'high' | 'critical'; /** Runtime environment hints */ export type RuntimeHint = 'browser' | 'node' | 'deno' | 'bun'; /** Gate events */ export type GateEvent = | 'permit' | 'defer' | 'deny' | 'error' | 'offline' | 'online' | 'sync' | 'tile-error' | 'tile-restart'; /** Tile topology types */ export type TopologyType = 'ring' | 'hierarchical' | 'mesh' | 'custom'; /** * Configuration for the CognitumGate */ export interface GateConfig { /** Number of WASM tiles (default: navigator.hardwareConcurrency || 4) */ tileCount?: number; /** Minimum coherence score to permit (default: 0.85) */ coherenceThreshold?: number; /** Maximum context tokens to consider (default: 8192) */ maxContextTokens?: number; /** Custom tile topology */ topology?: TileTopology; /** Custom receipt storage backend */ receiptStore?: ReceiptStore; /** Tile memory configuration */ tileMemory?: TileMemoryConfig; /** Custom coherence scoring implementation */ coherenceScorer?: CoherenceScorer; /** Agent permission policies */ policies?: AgentPolicy[]; /** Default policy for unspecified agents */ defaultPolicy?: DefaultPolicy; /** Offline mode configuration */ offlineMode?: OfflineModeConfig; /** Runtime hint */ runtime?: RuntimeHint; /** Custom worker URL (browser only) */ workerUrl?: string; /** Thread pool size (Node.js only) */ threadPoolSize?: number; } /** * Tile memory configuration */ export interface TileMemoryConfig { /** Initial memory pages (64KB each) */ initial: number; /** Maximum memory pages */ maximum: number; /** Use SharedArrayBuffer */ shared: boolean; } /** * Tile topology configuration */ export interface TileTopology { /** Topology type */ type: TopologyType; /** Number of tiles */ tiles?: number; /** Connection function for ring/custom topologies */ connections?: (tileId: number, total: number) => number[]; /** Hierarchical levels configuration */ levels?: Array<{ tiles: number; threshold: number }>; /** Quorum requirement for mesh topology */ quorum?: number; } /** * Request to permit an action */ export interface PermitRequest { /** Unique identifier for the requesting agent */ agentId: string; /** Action being requested */ action: string; /** Target resource (optional) */ target?: string; /** Additional context for coherence scoring */ context?: Record; /** Request priority (default: 'normal') */ priority?: Priority; /** Timeout in milliseconds (default: 5000) */ timeoutMs?: number; } /** * Result of a permit request */ export interface PermitResult { /** Decision: permit, defer, or deny */ verdict: Verdict; /** Unique permit token (for receipts) */ token: string; /** Coherence score (0.0 - 1.0) */ coherenceScore: number; /** ID of the tile that processed the request */ tileId: number; /** Processing latency in microseconds */ latencyUs: number; /** Human-readable reason for defer/deny */ reason?: string; /** Suggested delay for deferred requests (ms) */ deferMs?: number; } /** * Witness receipt for audit trail */ export interface WitnessReceipt { /** Permit token */ token: string; /** BLAKE3 witness hash */ witnessHash: string; /** Unix timestamp (milliseconds) */ timestamp: number; /** Agent that made the request */ agentId: string; /** Requested action */ action: string; /** Final verdict */ verdict: Verdict; /** Coherence score at decision time */ coherenceScore: number; /** Hash of the previous receipt (chain) */ parentHash?: string; /** Optional Ed25519 signature */ signature?: Uint8Array; /** Action outcome (if recorded) */ outcome?: ActionOutcome; } /** * Outcome of a permitted action */ export interface ActionOutcome { /** Whether the action succeeded */ success: boolean; /** Error message if failed */ error?: string; /** Execution duration in milliseconds */ durationMs?: number; /** Additional outcome metadata */ metadata?: Record; } /** * Gate statistics */ export interface GateStats { /** Total requests processed */ totalRequests: number; /** Requests by verdict */ verdicts: { permit: number; defer: number; deny: number; }; /** Average latency in microseconds */ avgLatencyUs: number; /** P99 latency in microseconds */ p99LatencyUs: number; /** Active tiles */ activeTiles: number; /** Memory usage per tile (bytes) */ memoryPerTile: number[]; /** Uptime in milliseconds */ uptimeMs: number; } /** * Stream options for batch processing */ export interface StreamOptions { /** Maximum concurrent requests */ concurrency?: number; /** Buffer size for backpressure */ bufferSize?: number; /** Callback when backpressure occurs */ onBackpressure?: (pending: number) => void; } /** * Context provided to coherence scorer */ export interface ScoringContext { /** Recent actions from this agent */ recentActions: Array<{ action: string; agentId: string; timestamp: number; }>; /** Current tile load */ tileLoad: number; /** Global coherence state */ globalCoherence: number; } /** * Interface for custom coherence scoring */ export interface CoherenceScorer { score(request: PermitRequest, context: ScoringContext): Promise; } /** * Receipt storage interface */ export interface ReceiptStore { store(receipt: WitnessReceipt): Promise; get(token: string): Promise; query(filter: ReceiptFilter): Promise; } /** * Filter for receipt queries */ export interface ReceiptFilter { agentId?: string; action?: string; verdict?: Verdict; since?: number; until?: number; limit?: number; } /** * Agent permission policy */ export interface AgentPolicy { /** Agent ID pattern (supports wildcards) */ agentId: string; /** Permission rules by action */ permissions: Record; } /** * Permission configuration for an action */ export interface ActionPermission { /** Coherence threshold for this action */ threshold?: number; /** Fixed verdict (overrides threshold) */ verdict?: Verdict; /** Allowed target patterns */ targets?: string[]; /** Denied target patterns */ denyPatterns?: string[]; } /** * Default policy for unspecified agents */ export interface DefaultPolicy { /** Default threshold */ threshold: number; /** Default verdict for unknown actions */ defaultVerdict?: Verdict; } /** * Offline mode configuration */ export interface OfflineModeConfig { /** Enable offline mode */ enabled: boolean; /** Maximum offline actions to queue */ maxOfflineActions?: number; /** Sync interval when online (ms) */ syncInterval?: number; } /** Event handler type */ export type EventHandler = (data: unknown) => void; // ============================================================================= // Internal Types // ============================================================================= interface Tile { id: number; worker: Worker | null; memory: WebAssembly.Memory | null; ready: boolean; load: number; } interface PendingRequest { resolve: (result: PermitResult) => void; reject: (error: Error) => void; startTime: number; } // ============================================================================= // CognitumGate Implementation // ============================================================================= /** * CognitumGate - High-performance coherence verification for AI agents * * @example * ```typescript * const gate = await CognitumGate.init({ * tileCount: 8, * coherenceThreshold: 0.85, * }); * * const result = await gate.permitAction({ * agentId: 'my-agent', * action: 'file_write', * target: '/app/config.json', * }); * * if (result.verdict === 'permit') { * // Proceed with action * } * ``` */ export class CognitumGate { private config: Required; private tiles: Tile[] = []; private receiptStore: ReceiptStore; private coherenceScorer: CoherenceScorer | null; private policies: Map = new Map(); private pendingRequests: Map = new Map(); private eventHandlers: Map> = new Map(); private stats: GateStats; private startTime: number; private latencies: number[] = []; private lastReceiptHash: string | null = null; private isDestroyed = false; private constructor(config: GateConfig) { const defaultConfig: Required = { tileCount: typeof navigator !== 'undefined' ? navigator.hardwareConcurrency || 4 : 4, coherenceThreshold: 0.85, maxContextTokens: 8192, topology: { type: 'mesh', tiles: 4 }, receiptStore: new InMemoryReceiptStore(), tileMemory: { initial: 16, maximum: 256, shared: false }, coherenceScorer: null as unknown as CoherenceScorer, policies: [], defaultPolicy: { threshold: 0.85 }, offlineMode: { enabled: false }, runtime: this.detectRuntime(), workerUrl: '', threadPoolSize: 4, }; this.config = { ...defaultConfig, ...config } as Required; this.receiptStore = this.config.receiptStore; this.coherenceScorer = config.coherenceScorer || null; this.startTime = Date.now(); // Index policies by agent ID for (const policy of this.config.policies) { this.policies.set(policy.agentId, policy); } this.stats = { totalRequests: 0, verdicts: { permit: 0, defer: 0, deny: 0 }, avgLatencyUs: 0, p99LatencyUs: 0, activeTiles: 0, memoryPerTile: [], uptimeMs: 0, }; } /** * Initialize a new CognitumGate instance */ static async init(config?: GateConfig): Promise { const gate = new CognitumGate(config || {}); await gate.initializeTiles(); return gate; } /** * Check if SharedArrayBuffer is available */ get supportsSharedMemory(): boolean { return typeof SharedArrayBuffer !== 'undefined'; } /** * Request permission for an action */ async permitAction(request: PermitRequest): Promise { this.ensureNotDestroyed(); const startTime = performance.now(); const token = this.generateToken(); try { // Check agent policy first const policyResult = this.checkPolicy(request); if (policyResult) { return this.createResult(policyResult, token, 0, startTime); } // Get coherence score const score = await this.calculateCoherence(request); const verdict = this.determineVerdict(score); const result = this.createResult( { verdict, coherenceScore: score, reason: verdict !== 'permit' ? this.getVerdictReason(verdict, score) : undefined, }, token, this.selectTile(), startTime ); // Store receipt await this.storeReceipt(result, request); // Update stats this.updateStats(result); // Emit event this.emit(result.verdict, result); return result; } catch (error) { this.emit('error', { token, error }); throw error; } } /** * Batch permission requests for efficiency */ async batchPermit(requests: PermitRequest[]): Promise { this.ensureNotDestroyed(); return Promise.all(requests.map((req) => this.permitAction(req))); } /** * Stream permission decisions with backpressure handling */ async *permitStream( requests: AsyncIterable, options: StreamOptions = {} ): AsyncIterable { const { concurrency = 10, bufferSize = 100, onBackpressure } = options; const buffer: PermitResult[] = []; const pending: Promise[] = []; let done = false; const processRequest = async (request: PermitRequest) => { const result = await this.permitAction(request); buffer.push(result); if (buffer.length >= bufferSize && onBackpressure) { onBackpressure(buffer.length); } }; (async () => { for await (const request of requests) { if (this.isDestroyed) break; while (pending.length >= concurrency) { await Promise.race(pending); } const promise = processRequest(request).then(() => { const index = pending.indexOf(promise); if (index !== -1) pending.splice(index, 1); }); pending.push(promise); } await Promise.all(pending); done = true; })(); while (!done || buffer.length > 0) { if (buffer.length > 0) { yield buffer.shift()!; } else { await new Promise((r) => setTimeout(r, 1)); } } } /** * Retrieve a witness receipt by token */ async getReceipt(token: string): Promise { this.ensureNotDestroyed(); const receipt = await this.receiptStore.get(token); if (!receipt) { throw new Error(`Receipt not found: ${token}`); } return receipt; } /** * Record the outcome of a permitted action */ async recordOutcome(token: string, outcome: ActionOutcome): Promise { this.ensureNotDestroyed(); const receipt = await this.receiptStore.get(token); if (!receipt) { throw new Error(`Receipt not found: ${token}`); } receipt.outcome = outcome; await this.receiptStore.store(receipt); } /** * Get current gate statistics */ getStats(): GateStats { return { ...this.stats, uptimeMs: Date.now() - this.startTime, activeTiles: this.tiles.filter((t) => t.ready).length, memoryPerTile: this.tiles.map((t) => t.memory ? t.memory.buffer.byteLength : 0 ), }; } /** * Subscribe to gate events */ on(event: GateEvent, handler: EventHandler): void { if (!this.eventHandlers.has(event)) { this.eventHandlers.set(event, new Set()); } this.eventHandlers.get(event)!.add(handler); } /** * Unsubscribe from gate events */ off(event: GateEvent, handler: EventHandler): void { this.eventHandlers.get(event)?.delete(handler); } /** * Destroy the gate and release resources */ async destroy(): Promise { this.isDestroyed = true; for (const tile of this.tiles) { tile.worker?.terminate(); tile.ready = false; } this.tiles = []; this.pendingRequests.clear(); this.eventHandlers.clear(); } // ========================================================================== // Private Methods // ========================================================================== private async initializeTiles(): Promise { const { tileCount, tileMemory } = this.config; for (let i = 0; i < tileCount; i++) { const memory = new WebAssembly.Memory({ initial: tileMemory.initial, maximum: tileMemory.maximum, shared: tileMemory.shared && this.supportsSharedMemory, }); const tile: Tile = { id: i, worker: null, // Worker initialization would happen here in real impl memory, ready: true, load: 0, }; this.tiles.push(tile); } this.stats.activeTiles = this.tiles.length; } private detectRuntime(): RuntimeHint { if (typeof Deno !== 'undefined') return 'deno'; if (typeof Bun !== 'undefined') return 'bun'; if (typeof process !== 'undefined' && process.versions?.node) return 'node'; return 'browser'; } private generateToken(): string { const bytes = new Uint8Array(16); if (typeof crypto !== 'undefined') { crypto.getRandomValues(bytes); } else { for (let i = 0; i < 16; i++) { bytes[i] = Math.floor(Math.random() * 256); } } return Array.from(bytes) .map((b) => b.toString(16).padStart(2, '0')) .join(''); } private checkPolicy(request: PermitRequest): Partial | null { const policy = this.policies.get(request.agentId); if (!policy) return null; const actionPerm = policy.permissions[request.action]; if (!actionPerm) return null; // Check for fixed verdict if (actionPerm.verdict) { return { verdict: actionPerm.verdict, coherenceScore: actionPerm.verdict === 'permit' ? 1.0 : 0.0, reason: `Policy verdict: ${actionPerm.verdict}`, }; } // Check deny patterns if (actionPerm.denyPatterns && request.target) { for (const pattern of actionPerm.denyPatterns) { if (request.target.includes(pattern)) { return { verdict: 'deny', coherenceScore: 0.0, reason: `Target matches deny pattern: ${pattern}`, }; } } } return null; } private async calculateCoherence(request: PermitRequest): Promise { if (this.coherenceScorer) { const context: ScoringContext = { recentActions: [], tileLoad: this.tiles.reduce((sum, t) => sum + t.load, 0) / this.tiles.length, globalCoherence: 0.9, }; return this.coherenceScorer.score(request, context); } // Default coherence calculation let score = 0.9; // Priority adjustments switch (request.priority) { case 'critical': score += 0.08; break; case 'high': score += 0.04; break; case 'low': score -= 0.05; break; } // Add some variance score += (Math.random() - 0.5) * 0.1; return Math.max(0, Math.min(1, score)); } private determineVerdict(score: number): Verdict { if (score >= this.config.coherenceThreshold) { return 'permit'; } else if (score >= this.config.coherenceThreshold * 0.8) { return 'defer'; } return 'deny'; } private getVerdictReason(verdict: Verdict, score: number): string { if (verdict === 'defer') { return `Coherence score ${score.toFixed(3)} below threshold ${this.config.coherenceThreshold}; retry recommended`; } return `Coherence score ${score.toFixed(3)} significantly below threshold`; } private selectTile(): number { // Select least loaded tile let minLoad = Infinity; let selectedTile = 0; for (const tile of this.tiles) { if (tile.ready && tile.load < minLoad) { minLoad = tile.load; selectedTile = tile.id; } } return selectedTile; } private createResult( partial: Partial, token: string, tileId: number, startTime: number ): PermitResult { const latencyUs = Math.round((performance.now() - startTime) * 1000); return { verdict: partial.verdict || 'deny', token, coherenceScore: partial.coherenceScore || 0, tileId, latencyUs, reason: partial.reason, deferMs: partial.verdict === 'defer' ? 1000 : undefined, }; } private async storeReceipt(result: PermitResult, request: PermitRequest): Promise { const receipt: WitnessReceipt = { token: result.token, witnessHash: await this.computeWitnessHash(result, request), timestamp: Date.now(), agentId: request.agentId, action: request.action, verdict: result.verdict, coherenceScore: result.coherenceScore, parentHash: this.lastReceiptHash || undefined, }; this.lastReceiptHash = receipt.witnessHash; await this.receiptStore.store(receipt); } private async computeWitnessHash(result: PermitResult, request: PermitRequest): Promise { const data = JSON.stringify({ token: result.token, agentId: request.agentId, action: request.action, verdict: result.verdict, coherenceScore: result.coherenceScore, timestamp: Date.now(), }); // Use SubtleCrypto for hashing if (typeof crypto !== 'undefined' && crypto.subtle) { const encoder = new TextEncoder(); const hashBuffer = await crypto.subtle.digest('SHA-256', encoder.encode(data)); const hashArray = Array.from(new Uint8Array(hashBuffer)); return hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); } // Fallback: simple hash let hash = 0; for (let i = 0; i < data.length; i++) { const char = data.charCodeAt(i); hash = (hash << 5) - hash + char; hash = hash & hash; } return Math.abs(hash).toString(16).padStart(16, '0'); } private updateStats(result: PermitResult): void { this.stats.totalRequests++; this.stats.verdicts[result.verdict]++; this.latencies.push(result.latencyUs); if (this.latencies.length > 1000) { this.latencies.shift(); } this.stats.avgLatencyUs = this.latencies.reduce((a, b) => a + b, 0) / this.latencies.length; const sorted = [...this.latencies].sort((a, b) => a - b); this.stats.p99LatencyUs = sorted[Math.floor(sorted.length * 0.99)] || 0; } private emit(event: GateEvent, data: unknown): void { const handlers = this.eventHandlers.get(event); if (handlers) { for (const handler of handlers) { try { handler(data); } catch (error) { console.error(`Event handler error for ${event}:`, error); } } } } private ensureNotDestroyed(): void { if (this.isDestroyed) { throw new Error('CognitumGate has been destroyed'); } } } // ============================================================================= // In-Memory Receipt Store // ============================================================================= /** * Simple in-memory receipt store for development/testing */ class InMemoryReceiptStore implements ReceiptStore { private receipts: Map = new Map(); async store(receipt: WitnessReceipt): Promise { this.receipts.set(receipt.token, receipt); } async get(token: string): Promise { return this.receipts.get(token) || null; } async query(filter: ReceiptFilter): Promise { let results = Array.from(this.receipts.values()); if (filter.agentId) { results = results.filter((r) => r.agentId === filter.agentId); } if (filter.action) { results = results.filter((r) => r.action === filter.action); } if (filter.verdict) { results = results.filter((r) => r.verdict === filter.verdict); } if (filter.since) { results = results.filter((r) => r.timestamp >= filter.since!); } if (filter.until) { results = results.filter((r) => r.timestamp <= filter.until!); } if (filter.limit) { results = results.slice(0, filter.limit); } return results; } } // ============================================================================= // IndexedDB Receipt Store (Browser) // ============================================================================= /** * IndexedDB-backed receipt store for browser persistence */ export class IndexedDBReceiptStore implements ReceiptStore { private dbName: string; private maxReceipts: number; private db: IDBDatabase | null = null; constructor(options: { dbName?: string; maxReceipts?: number; compactionThreshold?: number } = {}) { this.dbName = options.dbName || 'cognitum-receipts'; this.maxReceipts = options.maxReceipts || 100000; } private async getDb(): Promise { if (this.db) return this.db; return new Promise((resolve, reject) => { const request = indexedDB.open(this.dbName, 1); request.onerror = () => reject(request.error); request.onsuccess = () => { this.db = request.result; resolve(this.db); }; request.onupgradeneeded = (event) => { const db = (event.target as IDBOpenDBRequest).result; const store = db.createObjectStore('receipts', { keyPath: 'token' }); store.createIndex('agentId', 'agentId'); store.createIndex('action', 'action'); store.createIndex('verdict', 'verdict'); store.createIndex('timestamp', 'timestamp'); }; }); } async store(receipt: WitnessReceipt): Promise { const db = await this.getDb(); return new Promise((resolve, reject) => { const transaction = db.transaction(['receipts'], 'readwrite'); const store = transaction.objectStore('receipts'); const request = store.put(receipt); request.onerror = () => reject(request.error); request.onsuccess = () => resolve(); }); } async get(token: string): Promise { const db = await this.getDb(); return new Promise((resolve, reject) => { const transaction = db.transaction(['receipts'], 'readonly'); const store = transaction.objectStore('receipts'); const request = store.get(token); request.onerror = () => reject(request.error); request.onsuccess = () => resolve(request.result || null); }); } async query(filter: ReceiptFilter): Promise { const db = await this.getDb(); return new Promise((resolve, reject) => { const transaction = db.transaction(['receipts'], 'readonly'); const store = transaction.objectStore('receipts'); const results: WitnessReceipt[] = []; let request: IDBRequest; if (filter.agentId) { const index = store.index('agentId'); request = index.openCursor(IDBKeyRange.only(filter.agentId)); } else if (filter.since || filter.until) { const index = store.index('timestamp'); const range = IDBKeyRange.bound( filter.since || 0, filter.until || Date.now() ); request = index.openCursor(range); } else { request = store.openCursor(); } request.onerror = () => reject(request.error); request.onsuccess = (event) => { const cursor = (event.target as IDBRequest).result; if (cursor) { const receipt = cursor.value as WitnessReceipt; let matches = true; if (filter.action && receipt.action !== filter.action) matches = false; if (filter.verdict && receipt.verdict !== filter.verdict) matches = false; if (matches) { results.push(receipt); } if (!filter.limit || results.length < filter.limit) { cursor.continue(); } else { resolve(results); } } else { resolve(results); } }; }); } } // ============================================================================= // Exports // ============================================================================= export default CognitumGate; // Type declarations for Deno and Bun declare const Deno: unknown; declare const Bun: unknown;