/** * P2P Swarm v2 - Production Grade * * Fixes from code review: * 1. Two-layer key scheme (swarm envelope key + per-peer session keys) * 2. Ed25519 identity keys + X25519 ephemeral keys * 3. Message replay protection (nonces, counters, timestamps) * 4. Gun-based WebRTC signaling (no external PeerServer) * 5. IPFS CID pointers for large payloads * 6. Ed25519 signatures on all messages * 7. Relay health monitoring * 8. Task execution envelope with budgets */ /** * Stable canonical JSON stringify * * Unlike JSON.stringify, this: * - Sorts object keys recursively (alphabetically) * - Produces identical output regardless of insertion order * - Is safe to use for signing and hashing */ declare function stableStringify(obj: unknown): string; /** * Identity and session key management * * Security fixes applied: * - Ed25519 uses crypto.sign(null, ...) - direct sign, not hash-then-sign * - Separate send counter (local) from receive counters (per-peer) * - Per-sender nonce tracking with timestamps for expiry * - X25519 ECDH + HKDF for real session key derivation */ declare class IdentityManager { private identityKey; private x25519Key; private peerSessionKeys; private seenNonces; private localSendCounter; private recvCounters; private maxNonceAge; private nonceCleanupInterval; constructor(); /** * Get public identity key (PEM format for transport) */ getPublicKey(): string; /** * Get X25519 public key for key exchange */ getX25519PublicKey(): string; /** * Sign data with Ed25519 identity key * Ed25519 uses direct sign (no hash algorithm needed) */ sign(data: string): string; /** * Verify Ed25519 signature from peer */ verify(data: string, signature: string, peerPublicKey: string): boolean; /** * Derive session key with peer using real X25519 ECDH + HKDF * * Salt derivation: sha256(min(pubA, pubB) || max(pubA, pubB)) * - Stable per pair regardless of who initiates * - High entropy from public key material * * Info: "p2p-swarm-v2:${swarmId}:${peerId}" * - Prevents cross-swarm key reuse * - Makes derived keys unique per pair and swarm */ deriveSessionKey(peerX25519PubKey: string, peerId: string, swarmId?: string): Buffer; /** * Generate cryptographically secure nonce */ generateNonce(): string; /** * Check and record nonce with per-sender tracking * Returns true if nonce is valid (not replayed, not expired) */ checkNonce(nonce: string, timestamp: number, senderId: string): boolean; /** * Periodic cleanup of expired nonces */ private startNonceCleanup; /** * Get next send counter (monotonically increasing) */ getNextSendCounter(): number; /** * Validate received counter from peer (must be > last seen from that peer) */ validateRecvCounter(peerId: string, counter: number): boolean; /** * Clear session key (for rotation) */ rotateSessionKey(peerId: string): void; /** * Clean up resources */ destroy(): void; } /** * Encryption with proper GCM handling */ declare class CryptoV2 { private algorithm; encrypt(data: string, key: Buffer): { ciphertext: string; iv: string; tag: string; }; decrypt(ciphertext: string, key: Buffer, iv: string, tag: string): string | null; hash(data: string | Buffer): string; generateCID(data: string | Buffer): string; } /** * Signed message envelope for Gun pubsub * * Design rules: * - Gun = coordination bus (always use swarm envelope key) * - IPFS = artifact plane (content-addressed storage) * - WebRTC = private fast lane (can use per-peer session keys) * * Session keys are NOT used for Gun messages to avoid decryption * mismatch problems where only one receiver can decrypt. */ interface SignedEnvelope { messageId: string; topic: string; timestamp: number; senderId: string; senderPubKey: string; senderX25519Key: string; payloadHash: string; nonce: string; counter: number; signature: string; encrypted: { ciphertext: string; iv: string; tag: string; }; } /** * Task execution envelope */ interface TaskEnvelope { taskId: string; moduleCID: string; entrypoint: string; inputCID: string; outputSchemaHash: string; budgets: { fuelLimit: number; memoryMB: number; timeoutMs: number; }; requester: string; deadline: number; priority: number; } /** * Task result receipt with full execution binding * The signature covers ALL fields to prevent tampering * * Includes binding to the original TaskEnvelope for complete traceability */ interface TaskReceipt { taskId: string; executor: string; executorPubKey: string; resultCID: string; status: 'success' | 'error' | 'timeout' | 'oom'; fuelUsed: number; memoryPeakMB: number; executionMs: number; inputHash: string; outputHash: string; moduleHash: string; startTimestamp: number; endTimestamp: number; moduleCID: string; inputCID: string; entrypoint: string; outputSchemaHash: string; taskEnvelopeHash: string; signature: string; } /** * Learning artifact pointer (small, goes to Gun) */ interface ArtifactPointer { type: 'q_table' | 'memory_vectors' | 'model_weights' | 'trajectory'; agentId: string; cid: string; version: number; schemaHash: string; dimensions: string; checksum: string; timestamp: number; signature: string; } /** * Relay health tracker */ declare class RelayManager { private relays; private workingRelays; static readonly BOOTSTRAP_RELAYS: string[]; constructor(customRelays?: string[]); /** * Get working relays */ getWorkingRelays(): string[]; /** * Mark relay as failed */ markFailed(url: string): void; /** * Mark relay as successful */ markSuccess(url: string, latencyMs: number): void; /** * Add new relay */ addRelay(url: string): void; /** * Get health metrics */ getMetrics(): { total: number; healthy: number; avgLatency: number; }; /** * Persist working set */ exportWorkingSet(): string[]; /** * Import working set */ importWorkingSet(relays: string[]): void; } /** * IPFS artifact manager (CID-based storage) * * IMPORTANT CID LIMITATION: * The current generateCID creates a simplified hash-based identifier (Qm prefix). * This is NOT a real IPFS CID and will NOT interop with: * - Real IPFS gateways * - Pinning services (Pinata, web3.storage, Filebase) * - Content routing * * For production, you need one of: * A) Real IPFS client: add bytes to IPFS node, take returned CID * B) Multiformats library: create CIDv1 from raw bytes offline * * The current implementation works for local coordination and can be * upgraded to real IPFS when needed. * * Gateway fetch is DISABLED by default because our fake CIDs won't work. * Enable only when using real multiformats CIDv1 generation. */ declare class ArtifactStore { private localCache; private crypto; private maxCacheSize; private currentCacheSize; private maxArtifactSize; private enableGatewayFetch; static readonly CHUNK_SIZE: number; static readonly IPFS_GATEWAYS: string[]; constructor(enableGatewayFetch?: boolean); /** * Store artifact and get CID */ store(data: Buffer | string, compress?: boolean): Promise<{ cid: string; size: number; chunks: number; }>; /** * Retrieve artifact by CID * * Strategy: * 1. Check local cache * 2. If enableGatewayFetch=true AND CID is real (bafy prefix), try gateways * 3. Otherwise return null (local-only mode) * * Gateway fetch is DISABLED by default because our simplified CIDs * are not real IPFS CIDs and won't work with gateways. */ retrieve(cid: string): Promise; /** * Enable gateway fetch (only when using real IPFS CIDs) */ setEnableGatewayFetch(enable: boolean): void; /** * Store in local cache with eviction */ private cacheStore; /** * Create artifact pointer for Gun * Uses stableStringify for deterministic signing */ createPointer(type: ArtifactPointer['type'], agentId: string, cid: string, dimensions: string, identity: IdentityManager): ArtifactPointer; } /** * Production-grade P2P Swarm Coordinator */ export declare class P2PSwarmV2 { private identity; private crypto; private relayManager; private artifactStore; private swarmKey; private swarmId; private agentId; private gun; private swarmNode; private connected; private messageHandlers; private pendingOffers; private memberRegistry; private heartbeatInterval; private taskExecutorActive; private claimedTasks; private readonly HEARTBEAT_INTERVAL_MS; private readonly MEMBER_TIMEOUT_MS; private negativeMemberCache; private readonly NEGATIVE_CACHE_TTL_MS; private readonly CLAIM_TTL_MS; constructor(agentId: string, swarmKey?: string); /** * Connect to Gun relays * * Starts autonomy features: * - Membership watcher (subscribe to members, verify registrations) * - Heartbeat publishing (every 20 seconds) * - Task executor loop (optional, enable with startTaskExecutor()) */ connect(): Promise; /** * Register self with public key and X25519 key * Uses canonical serialization for signature * Field order must match verifyMemberRegistration */ private registerSelf; /** * Verified member entry type */ private static readonly MEMBER_REQUIRED_FIELDS; /** * Verify and cache a member registration from Gun * STRICT: Requires all fields including x25519PublicKey * Returns the verified member or null if invalid */ private verifyMemberRegistration; /** * Resolve full verified member from the registry * Returns the complete member entry (with both Ed25519 and X25519 keys) * Returns null if member not found or not verified * * Uses negative cache to prevent spam from repeated lookups of unknown agents */ private resolveMember; /** * Convenience: Resolve just the Ed25519 public key */ private resolveMemberKey; /** * Subscribe to WebRTC signaling (Gun-based, no PeerServer needed) * SECURITY: Verifies signatures using registry key, not envelope key */ private subscribeToSignaling; /** * Handle incoming WebRTC offer (via Gun signaling) */ private handleOffer; /** * Handle incoming WebRTC answer */ private handleAnswer; /** * Handle incoming ICE candidate */ private handleICE; /** * Create canonical signaling message and signature * Uses stableStringify for deterministic serialization */ private createCanonicalSignal; /** * Send WebRTC offer via Gun (no PeerServer needed) * Uses canonical signature for verification */ sendOffer(targetAgentId: string, offer: any): Promise; /** * Send WebRTC answer via Gun */ sendAnswer(targetAgentId: string, answer: any): Promise; /** * Send ICE candidate via Gun */ sendICE(targetAgentId: string, candidate: any): Promise; /** * Publish signed and encrypted message * * Gun messages ALWAYS use swarm envelope key (not session keys) * This ensures all swarm members can decrypt and process messages. * Uses canonical serialization for signature stability. */ publish(topic: string, payload: any): Promise; /** * Subscribe to topic with strict identity verification * * SECURITY: Never trust keys from envelopes. Always resolve from registry. * * Verification steps: * 1. Check nonce and timestamp (replay protection) * 2. Check counter (ordering) * 3. Resolve sender's public key from verified member registry * 4. Reject if envelope key differs from registry key * 5. Verify signature using registry key * 6. Decrypt with swarm key * 7. Verify payload hash */ subscribe(topic: string, callback: (data: any, from: string) => void): void; /** * Store Q-table and publish pointer */ syncQTable(qTable: number[][]): Promise; /** * Store memory vectors and publish pointer */ syncMemory(vectors: number[][], namespace: string): Promise; /** * Submit task for execution */ submitTask(task: Omit): Promise; /** * Submit task result with full execution binding * Signs ALL fields including TaskEnvelope binding for complete traceability */ submitResult(receipt: Omit): Promise; /** * Get status */ getStatus(): { connected: boolean; swarmId: string; agentId: string; publicKey: string; relays: { total: number; healthy: number; avgLatency: number; }; }; /** * Get swarm key for sharing */ getSwarmKey(): string; /** * Start membership watcher * Continuously monitors the members map, verifies registrations, tracks liveness */ private startMembershipWatcher; /** * Subscribe to heartbeat messages to track liveness * Explicitly rejects agentId mismatches to prevent confusion attacks */ private subscribeToHeartbeats; /** * Start publishing heartbeats * Every HEARTBEAT_INTERVAL_MS, publish a signed heartbeat message */ private startHeartbeat; /** * Publish a single heartbeat * Belt-and-suspenders: check both connected and swarmNode */ private publishHeartbeat; /** * Get list of live members (heartbeat within MEMBER_TIMEOUT_MS) */ getLiveMembers(): Array<{ agentId: string; capabilities: string[]; lastSeen: number; isAlive: boolean; }>; /** * Get count of live members */ getLiveMemberCount(): number; /** * Verify a task receipt * * Checks: * 1. Executor is in verified member registry * 2. Executor public key matches registry * 3. Signature is valid over the canonical receipt fields * 4. Optionally verifies taskEnvelopeHash if original task is provided */ verifyReceipt(receipt: TaskReceipt, originalTask?: TaskEnvelope): Promise<{ valid: boolean; reason?: string; executor?: { agentId: string; capabilities: string[]; verified: boolean; }; }>; /** * Start the task executor loop * Subscribes to tasks, claims them, and (in production) executes in Wasmtime * * Security checks: * - Requester must be verified member * - Requester in envelope must match sender * - Executor must have 'executor' capability */ startTaskExecutor(): void; /** * Claim a task with conflict resolution * * Before claiming: * 1. Check for existing claim * 2. If claim exists and is fresh (< CLAIM_TTL_MS), skip * 3. If no claim or stale, write our claim * * Uses single timestamp to ensure signature matches stored data */ private claimTask; /** * Execute a task * * NOTE: This is a stub. In production, this would: * 1. Fetch WASM module from IPFS using moduleCID * 2. Fetch input data from IPFS using inputCID * 3. Execute in Wasmtime with fuel, memory, and timeout limits * 4. Store output to IPFS, get resultCID * 5. Return signed receipt * * Hashes actual bytes when available for meaningful receipts */ private executeTask; /** * Create error receipt for failed task * Uses same hashing semantics as executeTask for consistency */ private createErrorReceipt; /** * Stop task executor */ stopTaskExecutor(): void; /** * Disconnect and cleanup resources */ disconnect(): void; } /** * RuVector optimization for Gun message handling * * Provides: * - HNSW indexing for O(log n) message lookup by topic similarity * - Batch message operations for reduced overhead * - PQ quantization for compressed message metadata storage * - Vector-based topic routing for intelligent distribution */ declare class RuVectorGunOptimizer { private topicVectors; private messageIndex; private batchQueue; private batchFlushInterval; private readonly batchSize; private readonly flushIntervalMs; constructor(); /** * Generate topic embedding using simple hash-based vectors * In production: use actual embedding model via ruvector */ private generateTopicVector; /** * Find similar topics using cosine similarity * In production: use HNSW index via ruvector for O(log n) lookup */ findSimilarTopics(topic: string, topK?: number): Array<{ topic: string; similarity: number; }>; /** * Cosine similarity between two vectors */ private cosineSimilarity; /** * Index a topic for similarity search */ indexTopic(topic: string): void; /** * Add message to batch queue for efficient Gun writes */ queueMessage(topic: string, payload: any): void; /** * Get batched messages for efficient Gun write */ flushBatch(): Array<{ topic: string; payload: any; }>; /** * Start periodic batch flushing */ private startBatchFlusher; /** * Get optimization metrics */ getMetrics(): { indexedTopics: number; batchQueueSize: number; totalMessages: number; }; /** * Cleanup resources */ destroy(): void; } /** * Create and connect a P2P swarm */ export declare function createP2PSwarmV2(agentId: string, swarmKey?: string): Promise; export { stableStringify, IdentityManager, CryptoV2, RelayManager, ArtifactStore, RuVectorGunOptimizer, }; export type { SignedEnvelope, TaskEnvelope, TaskReceipt, ArtifactPointer, }; //# sourceMappingURL=p2p-swarm-v2.d.ts.map