@rookdaemon/agora 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-D7Y66GFC.js → chunk-IR2AIJ3K.js} +86 -2
- package/dist/chunk-IR2AIJ3K.js.map +1 -0
- package/dist/{chunk-2U4PZINT.js → chunk-KITX5XAA.js} +2 -2
- package/dist/{chunk-IOHECZYT.js → chunk-NXPATMD4.js} +2 -2
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +41 -1
- package/dist/index.js +36 -3
- package/dist/index.js.map +1 -1
- package/dist/relay/relay-server.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-D7Y66GFC.js.map +0 -1
- /package/dist/{chunk-2U4PZINT.js.map → chunk-KITX5XAA.js.map} +0 -0
- /package/dist/{chunk-IOHECZYT.js.map → chunk-NXPATMD4.js.map} +0 -0
|
@@ -150,7 +150,7 @@ var MessageStore = class {
|
|
|
150
150
|
// src/relay/server.ts
|
|
151
151
|
import { EventEmitter } from "events";
|
|
152
152
|
import { WebSocketServer, WebSocket } from "ws";
|
|
153
|
-
var RelayServer = class extends EventEmitter {
|
|
153
|
+
var RelayServer = class _RelayServer extends EventEmitter {
|
|
154
154
|
wss = null;
|
|
155
155
|
/** publicKey -> sessionId -> ConnectedAgent (multiple sessions per key) */
|
|
156
156
|
sessions = /* @__PURE__ */ new Map();
|
|
@@ -158,6 +158,14 @@ var RelayServer = class extends EventEmitter {
|
|
|
158
158
|
storagePeers = [];
|
|
159
159
|
store = null;
|
|
160
160
|
maxPeers = 100;
|
|
161
|
+
senderWindows = /* @__PURE__ */ new Map();
|
|
162
|
+
static MAX_SENDER_ENTRIES = 500;
|
|
163
|
+
processedEnvelopeIds = /* @__PURE__ */ new Set();
|
|
164
|
+
rateLimitEnabled = true;
|
|
165
|
+
rateLimitMaxMessages = 10;
|
|
166
|
+
rateLimitWindowMs = 6e4;
|
|
167
|
+
envelopeDedupEnabled = true;
|
|
168
|
+
envelopeDedupMaxIds = 1e3;
|
|
161
169
|
constructor(options) {
|
|
162
170
|
super();
|
|
163
171
|
if (options) {
|
|
@@ -174,7 +182,71 @@ var RelayServer = class extends EventEmitter {
|
|
|
174
182
|
if (opts.maxPeers !== void 0) {
|
|
175
183
|
this.maxPeers = opts.maxPeers;
|
|
176
184
|
}
|
|
185
|
+
if (opts.rateLimit) {
|
|
186
|
+
if (opts.rateLimit.enabled !== void 0) {
|
|
187
|
+
this.rateLimitEnabled = opts.rateLimit.enabled;
|
|
188
|
+
}
|
|
189
|
+
if (opts.rateLimit.maxMessages !== void 0 && opts.rateLimit.maxMessages > 0) {
|
|
190
|
+
this.rateLimitMaxMessages = opts.rateLimit.maxMessages;
|
|
191
|
+
}
|
|
192
|
+
if (opts.rateLimit.windowMs !== void 0 && opts.rateLimit.windowMs > 0) {
|
|
193
|
+
this.rateLimitWindowMs = opts.rateLimit.windowMs;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (opts.envelopeDedup) {
|
|
197
|
+
if (opts.envelopeDedup.enabled !== void 0) {
|
|
198
|
+
this.envelopeDedupEnabled = opts.envelopeDedup.enabled;
|
|
199
|
+
}
|
|
200
|
+
if (opts.envelopeDedup.maxIds !== void 0 && opts.envelopeDedup.maxIds > 0) {
|
|
201
|
+
this.envelopeDedupMaxIds = opts.envelopeDedup.maxIds;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
isRateLimitedSender(senderPublicKey) {
|
|
207
|
+
if (!this.rateLimitEnabled) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
const now = Date.now();
|
|
211
|
+
const window = this.senderWindows.get(senderPublicKey);
|
|
212
|
+
if (this.senderWindows.size >= _RelayServer.MAX_SENDER_ENTRIES && !window) {
|
|
213
|
+
this.evictOldestSenderWindow();
|
|
214
|
+
}
|
|
215
|
+
if (!window || now - window.windowStart > this.rateLimitWindowMs) {
|
|
216
|
+
this.senderWindows.set(senderPublicKey, { count: 1, windowStart: now });
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
window.count++;
|
|
220
|
+
return window.count > this.rateLimitMaxMessages;
|
|
221
|
+
}
|
|
222
|
+
evictOldestSenderWindow() {
|
|
223
|
+
let oldestKey = null;
|
|
224
|
+
let oldestTime = Infinity;
|
|
225
|
+
for (const [key, window] of this.senderWindows.entries()) {
|
|
226
|
+
if (window.windowStart < oldestTime) {
|
|
227
|
+
oldestTime = window.windowStart;
|
|
228
|
+
oldestKey = key;
|
|
229
|
+
}
|
|
177
230
|
}
|
|
231
|
+
if (oldestKey !== null) {
|
|
232
|
+
this.senderWindows.delete(oldestKey);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
isDuplicateEnvelopeId(envelopeId) {
|
|
236
|
+
if (!this.envelopeDedupEnabled) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
if (this.processedEnvelopeIds.has(envelopeId)) {
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
this.processedEnvelopeIds.add(envelopeId);
|
|
243
|
+
if (this.processedEnvelopeIds.size > this.envelopeDedupMaxIds) {
|
|
244
|
+
const oldest = this.processedEnvelopeIds.values().next().value;
|
|
245
|
+
if (oldest !== void 0) {
|
|
246
|
+
this.processedEnvelopeIds.delete(oldest);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return false;
|
|
178
250
|
}
|
|
179
251
|
/**
|
|
180
252
|
* Start the relay server
|
|
@@ -337,6 +409,12 @@ var RelayServer = class extends EventEmitter {
|
|
|
337
409
|
this.sendError(socket, "Envelope sender does not match registered public key");
|
|
338
410
|
return;
|
|
339
411
|
}
|
|
412
|
+
if (this.isRateLimitedSender(agentPublicKey)) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
if (this.isDuplicateEnvelopeId(envelope.id)) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
340
418
|
const senderSessionMap = this.sessions.get(agentPublicKey);
|
|
341
419
|
if (senderSessionMap) {
|
|
342
420
|
for (const a of senderSessionMap.values()) {
|
|
@@ -399,6 +477,12 @@ var RelayServer = class extends EventEmitter {
|
|
|
399
477
|
this.sendError(socket, "Envelope sender does not match registered public key");
|
|
400
478
|
return;
|
|
401
479
|
}
|
|
480
|
+
if (this.isRateLimitedSender(agentPublicKey)) {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
if (this.isDuplicateEnvelopeId(envelope.id)) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
402
486
|
const senderSessionMap = this.sessions.get(agentPublicKey);
|
|
403
487
|
if (senderSessionMap) {
|
|
404
488
|
for (const a of senderSessionMap.values()) {
|
|
@@ -569,4 +653,4 @@ export {
|
|
|
569
653
|
MessageStore,
|
|
570
654
|
RelayServer
|
|
571
655
|
};
|
|
572
|
-
//# sourceMappingURL=chunk-
|
|
656
|
+
//# sourceMappingURL=chunk-IR2AIJ3K.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/identity/keypair.ts","../src/message/envelope.ts","../src/relay/store.ts","../src/relay/server.ts"],"sourcesContent":["import { sign, verify, generateKeyPairSync } from 'node:crypto';\n\n/**\n * Represents an ed25519 key pair for agent identity\n */\nexport interface KeyPair {\n publicKey: string; // hex-encoded\n privateKey: string; // hex-encoded\n}\n\n/**\n * Generates a new ed25519 key pair\n * @returns KeyPair with hex-encoded public and private keys\n */\nexport function generateKeyPair(): KeyPair {\n const { publicKey, privateKey } = generateKeyPairSync('ed25519');\n \n return {\n publicKey: publicKey.export({ type: 'spki', format: 'der' }).toString('hex'),\n privateKey: privateKey.export({ type: 'pkcs8', format: 'der' }).toString('hex'),\n };\n}\n\n/**\n * Signs a message with the private key\n * @param message - The message to sign (string or Buffer)\n * @param privateKeyHex - The private key in hex format\n * @returns Signature as hex string\n */\nexport function signMessage(message: string | Buffer, privateKeyHex: string): string {\n const messageBuffer = typeof message === 'string' ? Buffer.from(message) : message;\n const privateKey = Buffer.from(privateKeyHex, 'hex');\n \n const signature = sign(null, messageBuffer, {\n key: privateKey,\n format: 'der',\n type: 'pkcs8',\n });\n \n return signature.toString('hex');\n}\n\n/**\n * Verifies a signature with the public key\n * @param message - The original message (string or Buffer)\n * @param signatureHex - The signature in hex format\n * @param publicKeyHex - The public key in hex format\n * @returns true if signature is valid, false otherwise\n */\nexport function verifySignature(\n message: string | Buffer,\n signatureHex: string,\n publicKeyHex: string\n): boolean {\n const messageBuffer = typeof message === 'string' ? Buffer.from(message) : message;\n const signature = Buffer.from(signatureHex, 'hex');\n const publicKey = Buffer.from(publicKeyHex, 'hex');\n \n try {\n return verify(null, messageBuffer, {\n key: publicKey,\n format: 'der',\n type: 'spki',\n }, signature);\n } catch {\n return false;\n }\n}\n\n/**\n * Exports a key pair to a JSON-serializable format\n * @param keyPair - The key pair to export\n * @returns KeyPair object with hex-encoded keys\n */\nexport function exportKeyPair(keyPair: KeyPair): KeyPair {\n return {\n publicKey: keyPair.publicKey,\n privateKey: keyPair.privateKey,\n };\n}\n\n/**\n * Imports a key pair from hex strings\n * @param publicKeyHex - The public key in hex format\n * @param privateKeyHex - The private key in hex format\n * @returns KeyPair object\n * @throws Error if keys are not valid hex strings\n */\nexport function importKeyPair(publicKeyHex: string, privateKeyHex: string): KeyPair {\n // Validate that keys are valid hex strings\n const hexPattern = /^[0-9a-f]+$/i;\n if (!hexPattern.test(publicKeyHex)) {\n throw new Error('Invalid public key: must be a hex string');\n }\n if (!hexPattern.test(privateKeyHex)) {\n throw new Error('Invalid private key: must be a hex string');\n }\n \n return {\n publicKey: publicKeyHex,\n privateKey: privateKeyHex,\n };\n}\n","import { createHash } from 'node:crypto';\nimport { signMessage, verifySignature } from '../identity/keypair';\n\n/**\n * Message types on the Agora network.\n * Every piece of data flowing between agents is wrapped in an envelope.\n */\nexport type MessageType =\n | 'announce' // Agent publishes capabilities/state\n | 'discover' // Agent requests peer discovery\n | 'request' // Agent requests a service\n | 'response' // Agent responds to a request\n | 'publish' // Agent publishes knowledge/state\n | 'subscribe' // Agent subscribes to a topic/domain\n | 'verify' // Agent verifies another agent's claim\n | 'ack' // Acknowledgement\n | 'error' // Error response\n | 'paper_discovery' // Agent publishes a discovered academic paper\n | 'peer_list_request' // Request peer list from relay\n | 'peer_list_response' // Relay responds with connected peers\n | 'peer_referral' // Agent recommends another agent\n | 'capability_announce' // Agent publishes capabilities to network\n | 'capability_query' // Agent queries for capabilities\n | 'capability_response' // Response with matching peers\n | 'commit' // Agent commits to a prediction (commit-reveal pattern)\n | 'reveal' // Agent reveals prediction and outcome\n | 'verification' // Agent verifies another agent's output\n | 'revocation' // Agent revokes a prior verification\n | 'reputation_query' // Agent queries for reputation data\n | 'reputation_response'; // Response to reputation query\n\n/**\n * The signed envelope that wraps every message on the network.\n * Content-addressed: the ID is the hash of the canonical payload.\n * Signed: every envelope carries a signature from the sender's private key.\n */\nexport interface Envelope<T = unknown> {\n /** Content-addressed ID: SHA-256 hash of canonical payload */\n id: string;\n /** Message type */\n type: MessageType;\n /** Sender's public key (hex-encoded ed25519) */\n sender: string;\n /** Unix timestamp (ms) when the message was created */\n timestamp: number;\n /** Optional: ID of the message this is responding to */\n inReplyTo?: string;\n /** The actual payload */\n payload: T;\n /** ed25519 signature over the canonical form (hex-encoded) */\n signature: string;\n}\n\n/**\n * Deterministic JSON serialization with recursively sorted keys.\n */\nfunction stableStringify(value: unknown): string {\n if (value === null || value === undefined) return JSON.stringify(value);\n if (typeof value !== 'object') return JSON.stringify(value);\n if (Array.isArray(value)) {\n return '[' + value.map(stableStringify).join(',') + ']';\n }\n const keys = Object.keys(value as Record<string, unknown>).sort();\n const pairs = keys.map(k => JSON.stringify(k) + ':' + stableStringify((value as Record<string, unknown>)[k]));\n return '{' + pairs.join(',') + '}';\n}\n\n/**\n * Canonical form of an envelope for signing/hashing.\n * Deterministic JSON serialization: recursively sorted keys, no whitespace.\n */\nexport function canonicalize(type: MessageType, sender: string, timestamp: number, payload: unknown, inReplyTo?: string): string {\n const obj: Record<string, unknown> = { payload, sender, timestamp, type };\n if (inReplyTo !== undefined) {\n obj.inReplyTo = inReplyTo;\n }\n return stableStringify(obj);\n}\n\n/**\n * Compute the content-addressed ID for a message.\n */\nexport function computeId(canonical: string): string {\n return createHash('sha256').update(canonical).digest('hex');\n}\n\n/**\n * Create a signed envelope.\n * @param type - Message type\n * @param sender - Sender's public key (hex)\n * @param privateKey - Sender's private key (hex) for signing\n * @param payload - The message payload\n * @param timestamp - Timestamp for the envelope (ms), defaults to Date.now()\n * @param inReplyTo - Optional ID of the message being replied to\n * @returns A signed Envelope\n */\nexport function createEnvelope<T>(\n type: MessageType,\n sender: string,\n privateKey: string,\n payload: T,\n timestamp: number = Date.now(),\n inReplyTo?: string,\n): Envelope<T> {\n const canonical = canonicalize(type, sender, timestamp, payload, inReplyTo);\n const id = computeId(canonical);\n const signature = signMessage(canonical, privateKey);\n\n return {\n id,\n type,\n sender,\n timestamp,\n ...(inReplyTo !== undefined ? { inReplyTo } : {}),\n payload,\n signature,\n };\n}\n\n/**\n * Verify an envelope's integrity and authenticity.\n * Checks:\n * 1. Canonical form matches the ID (content-addressing)\n * 2. Signature is valid for the sender's public key\n * \n * @returns Object with `valid` boolean and optional `reason` for failure\n */\nexport function verifyEnvelope(envelope: Envelope): { valid: boolean; reason?: string } {\n const { id, type, sender, timestamp, payload, signature, inReplyTo } = envelope;\n\n // Reconstruct canonical form\n const canonical = canonicalize(type, sender, timestamp, payload, inReplyTo);\n\n // Check content-addressed ID\n const expectedId = computeId(canonical);\n if (id !== expectedId) {\n return { valid: false, reason: 'id_mismatch' };\n }\n\n // Check signature\n const sigValid = verifySignature(canonical, signature, sender);\n if (!sigValid) {\n return { valid: false, reason: 'signature_invalid' };\n }\n\n return { valid: true };\n}\n","/**\n * store.ts — File-based message store for offline peers.\n * When the relay has storage enabled for certain public keys, messages\n * for offline recipients are persisted and delivered when they connect.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface StoredMessage {\n from: string;\n name?: string;\n envelope: object;\n}\n\nexport class MessageStore {\n private storageDir: string;\n\n constructor(storageDir: string) {\n this.storageDir = storageDir;\n fs.mkdirSync(storageDir, { recursive: true });\n }\n\n private recipientDir(publicKey: string): string {\n const safe = publicKey.replace(/[^a-zA-Z0-9_-]/g, '_');\n return path.join(this.storageDir, safe);\n }\n\n save(recipientKey: string, message: StoredMessage): void {\n const dir = this.recipientDir(recipientKey);\n fs.mkdirSync(dir, { recursive: true });\n const filename = `${Date.now()}-${crypto.randomUUID()}.json`;\n fs.writeFileSync(path.join(dir, filename), JSON.stringify(message));\n }\n\n load(recipientKey: string): StoredMessage[] {\n const dir = this.recipientDir(recipientKey);\n if (!fs.existsSync(dir)) return [];\n const files = fs.readdirSync(dir).sort();\n const messages: StoredMessage[] = [];\n for (const file of files) {\n if (!file.endsWith('.json')) continue;\n try {\n const data = fs.readFileSync(path.join(dir, file), 'utf8');\n messages.push(JSON.parse(data) as StoredMessage);\n } catch {\n // Skip files that cannot be read or parsed\n }\n }\n return messages;\n }\n\n clear(recipientKey: string): void {\n const dir = this.recipientDir(recipientKey);\n if (!fs.existsSync(dir)) return;\n const files = fs.readdirSync(dir);\n for (const file of files) {\n if (file.endsWith('.json')) {\n fs.unlinkSync(path.join(dir, file));\n }\n }\n }\n}\n","import { EventEmitter } from 'node:events';\nimport { WebSocketServer, WebSocket } from 'ws';\nimport { verifyEnvelope, createEnvelope, type Envelope } from '../message/envelope';\nimport type { PeerListRequestPayload, PeerListResponsePayload } from '../message/types/peer-discovery';\nimport { MessageStore } from './store';\n\ninterface SenderWindow {\n count: number;\n windowStart: number;\n}\n\nexport interface RelayRateLimitOptions {\n enabled?: boolean;\n maxMessages?: number;\n windowMs?: number;\n}\n\nexport interface RelayEnvelopeDedupOptions {\n enabled?: boolean;\n maxIds?: number;\n}\n\n/**\n * Represents a connected agent in the relay\n */\ninterface ConnectedAgent {\n /** Agent's public key */\n publicKey: string;\n /** Optional agent name */\n name?: string;\n /** WebSocket connection */\n socket: WebSocket;\n /** Last seen timestamp (ms) */\n lastSeen: number;\n /** Optional metadata */\n metadata?: {\n version?: string;\n capabilities?: string[];\n };\n}\n\n/**\n * Events emitted by RelayServer\n */\nexport interface RelayServerEvents {\n 'agent-registered': (publicKey: string) => void;\n 'agent-disconnected': (publicKey: string) => void;\n /** Emitted when a session disconnects (same as agent-disconnected for compatibility) */\n 'disconnection': (publicKey: string) => void;\n 'message-relayed': (from: string, to: string, envelope: Envelope) => void;\n 'error': (error: Error) => void;\n}\n\n/**\n * WebSocket relay server for routing messages between agents.\n * \n * Agents connect to the relay and register with their public key.\n * Messages are routed to recipients based on the 'to' field.\n * All envelopes are verified before being forwarded.\n */\nexport interface RelayServerOptions {\n /** Optional relay identity for peer_list_request handling */\n identity?: { publicKey: string; privateKey: string };\n /** Public keys that should have messages stored when offline */\n storagePeers?: string[];\n /** Directory for persisting messages for storage peers */\n storageDir?: string;\n /** Maximum number of concurrent registered peers (default: 100) */\n maxPeers?: number;\n /** Per-sender sliding-window message rate limiting */\n rateLimit?: RelayRateLimitOptions;\n /** Envelope ID deduplication options */\n envelopeDedup?: RelayEnvelopeDedupOptions;\n}\n\nexport class RelayServer extends EventEmitter {\n private wss: WebSocketServer | null = null;\n /** publicKey -> sessionId -> ConnectedAgent (multiple sessions per key) */\n private sessions = new Map<string, Map<string, ConnectedAgent>>();\n private identity?: { publicKey: string; privateKey: string };\n private storagePeers: string[] = [];\n private store: MessageStore | null = null;\n private maxPeers: number = 100;\n private readonly senderWindows: Map<string, SenderWindow> = new Map();\n private static readonly MAX_SENDER_ENTRIES = 500;\n private readonly processedEnvelopeIds: Set<string> = new Set();\n private rateLimitEnabled = true;\n private rateLimitMaxMessages = 10;\n private rateLimitWindowMs = 60_000;\n private envelopeDedupEnabled = true;\n private envelopeDedupMaxIds = 1000;\n\n constructor(options?: { publicKey: string; privateKey: string } | RelayServerOptions) {\n super();\n if (options) {\n if ('identity' in options && options.identity) {\n this.identity = options.identity;\n } else if ('publicKey' in options && 'privateKey' in options) {\n this.identity = { publicKey: options.publicKey, privateKey: options.privateKey };\n }\n const opts = options as RelayServerOptions;\n if (opts.storagePeers?.length && opts.storageDir) {\n this.storagePeers = opts.storagePeers;\n this.store = new MessageStore(opts.storageDir);\n }\n if (opts.maxPeers !== undefined) {\n this.maxPeers = opts.maxPeers;\n }\n if (opts.rateLimit) {\n if (opts.rateLimit.enabled !== undefined) {\n this.rateLimitEnabled = opts.rateLimit.enabled;\n }\n if (opts.rateLimit.maxMessages !== undefined && opts.rateLimit.maxMessages > 0) {\n this.rateLimitMaxMessages = opts.rateLimit.maxMessages;\n }\n if (opts.rateLimit.windowMs !== undefined && opts.rateLimit.windowMs > 0) {\n this.rateLimitWindowMs = opts.rateLimit.windowMs;\n }\n }\n if (opts.envelopeDedup) {\n if (opts.envelopeDedup.enabled !== undefined) {\n this.envelopeDedupEnabled = opts.envelopeDedup.enabled;\n }\n if (opts.envelopeDedup.maxIds !== undefined && opts.envelopeDedup.maxIds > 0) {\n this.envelopeDedupMaxIds = opts.envelopeDedup.maxIds;\n }\n }\n }\n }\n\n private isRateLimitedSender(senderPublicKey: string): boolean {\n if (!this.rateLimitEnabled) {\n return false;\n }\n\n const now = Date.now();\n const window = this.senderWindows.get(senderPublicKey);\n\n if (this.senderWindows.size >= RelayServer.MAX_SENDER_ENTRIES && !window) {\n this.evictOldestSenderWindow();\n }\n\n if (!window || (now - window.windowStart) > this.rateLimitWindowMs) {\n this.senderWindows.set(senderPublicKey, { count: 1, windowStart: now });\n return false;\n }\n\n window.count++;\n return window.count > this.rateLimitMaxMessages;\n }\n\n private evictOldestSenderWindow(): void {\n let oldestKey: string | null = null;\n let oldestTime = Infinity;\n\n for (const [key, window] of this.senderWindows.entries()) {\n if (window.windowStart < oldestTime) {\n oldestTime = window.windowStart;\n oldestKey = key;\n }\n }\n\n if (oldestKey !== null) {\n this.senderWindows.delete(oldestKey);\n }\n }\n\n private isDuplicateEnvelopeId(envelopeId: string): boolean {\n if (!this.envelopeDedupEnabled) {\n return false;\n }\n\n if (this.processedEnvelopeIds.has(envelopeId)) {\n return true;\n }\n\n this.processedEnvelopeIds.add(envelopeId);\n if (this.processedEnvelopeIds.size > this.envelopeDedupMaxIds) {\n const oldest = this.processedEnvelopeIds.values().next().value;\n if (oldest !== undefined) {\n this.processedEnvelopeIds.delete(oldest);\n }\n }\n\n return false;\n }\n\n /**\n * Start the relay server\n * @param port - Port to listen on\n * @param host - Optional host (default: all interfaces)\n */\n start(port: number, host?: string): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.wss = new WebSocketServer({ port, host: host ?? '0.0.0.0' });\n let resolved = false;\n\n this.wss.on('error', (error) => {\n this.emit('error', error);\n if (!resolved) {\n resolved = true;\n reject(error);\n }\n });\n\n this.wss.on('listening', () => {\n if (!resolved) {\n resolved = true;\n resolve();\n }\n });\n\n this.wss.on('connection', (socket: WebSocket) => {\n this.handleConnection(socket);\n });\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Stop the relay server\n */\n async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.wss) {\n resolve();\n return;\n }\n\n // Close all agent connections (all sessions)\n for (const sessionMap of this.sessions.values()) {\n for (const agent of sessionMap.values()) {\n agent.socket.close();\n }\n }\n this.sessions.clear();\n\n this.wss.close((err) => {\n if (err) {\n reject(err);\n } else {\n this.wss = null;\n resolve();\n }\n });\n });\n }\n\n /**\n * Get one connected agent per public key (first session). For backward compatibility.\n */\n getAgents(): Map<string, ConnectedAgent> {\n const out = new Map<string, ConnectedAgent>();\n for (const [key, sessionMap] of this.sessions) {\n const first = sessionMap.values().next().value;\n if (first) out.set(key, first);\n }\n return out;\n }\n\n /**\n * Handle incoming connection\n */\n private handleConnection(socket: WebSocket): void {\n let agentPublicKey: string | null = null;\n let sessionId: string | null = null;\n\n socket.on('message', (data: Buffer) => {\n try {\n const msg = JSON.parse(data.toString());\n\n // Handle registration\n if (msg.type === 'register' && !agentPublicKey) {\n if (!msg.publicKey || typeof msg.publicKey !== 'string') {\n this.sendError(socket, 'Invalid registration: missing or invalid publicKey');\n socket.close();\n return;\n }\n\n const publicKey = msg.publicKey;\n const name = msg.name;\n agentPublicKey = publicKey;\n sessionId = crypto.randomUUID();\n\n // Allow multiple sessions per publicKey; only enforce max unique peers\n if (!this.sessions.has(publicKey) && this.sessions.size >= this.maxPeers) {\n this.sendError(socket, `Relay is at capacity (max ${this.maxPeers} peers)`);\n socket.close();\n return;\n }\n\n const agent: ConnectedAgent = {\n publicKey,\n name,\n socket,\n lastSeen: Date.now(),\n };\n\n if (!this.sessions.has(publicKey)) {\n this.sessions.set(publicKey, new Map());\n }\n this.sessions.get(publicKey)!.set(sessionId, agent);\n const isFirstSession = this.sessions.get(publicKey)!.size === 1;\n\n this.emit('agent-registered', publicKey);\n\n // Build peers list: one entry per connected publicKey + storage peers\n const peers: Array<{ publicKey: string; name?: string }> = [];\n for (const [key, sessionMap] of this.sessions) {\n if (key === publicKey) continue;\n const firstAgent = sessionMap.values().next().value;\n peers.push({ publicKey: key, name: firstAgent?.name });\n }\n for (const storagePeer of this.storagePeers) {\n if (storagePeer !== publicKey && !this.sessions.has(storagePeer)) {\n peers.push({ publicKey: storagePeer, name: undefined });\n }\n }\n\n socket.send(JSON.stringify({\n type: 'registered',\n publicKey,\n sessionId,\n peers,\n }));\n\n // Notify other agents only when this is the first session for this peer\n if (isFirstSession) {\n this.broadcastPeerEvent('peer_online', publicKey, name);\n }\n\n // Deliver any stored messages for this peer\n if (this.store && this.storagePeers.includes(publicKey)) {\n const queued = this.store.load(publicKey);\n for (const stored of queued) {\n socket.send(JSON.stringify({\n type: 'message',\n from: stored.from,\n name: stored.name,\n envelope: stored.envelope,\n }));\n }\n this.store.clear(publicKey);\n }\n return;\n }\n\n // Require registration before processing messages\n if (!agentPublicKey) {\n this.sendError(socket, 'Not registered: send registration message first');\n socket.close();\n return;\n }\n\n // Handle message relay\n if (msg.type === 'message') {\n if (!msg.to || typeof msg.to !== 'string') {\n this.sendError(socket, 'Invalid message: missing or invalid \"to\" field');\n return;\n }\n\n if (!msg.envelope || typeof msg.envelope !== 'object') {\n this.sendError(socket, 'Invalid message: missing or invalid \"envelope\" field');\n return;\n }\n\n const envelope = msg.envelope as Envelope;\n\n // Verify envelope signature\n const verification = verifyEnvelope(envelope);\n if (!verification.valid) {\n this.sendError(socket, `Invalid envelope: ${verification.reason || 'verification failed'}`);\n return;\n }\n\n // Verify sender matches registered agent\n if (envelope.sender !== agentPublicKey) {\n this.sendError(socket, 'Envelope sender does not match registered public key');\n return;\n }\n\n if (this.isRateLimitedSender(agentPublicKey)) {\n return;\n }\n\n if (this.isDuplicateEnvelopeId(envelope.id)) {\n return;\n }\n\n // Update lastSeen for any session of sender\n const senderSessionMap = this.sessions.get(agentPublicKey);\n if (senderSessionMap) {\n for (const a of senderSessionMap.values()) {\n a.lastSeen = Date.now();\n }\n }\n\n // Handle peer_list_request directed at relay\n if (envelope.type === 'peer_list_request' && this.identity && msg.to === this.identity.publicKey) {\n this.handlePeerListRequest(envelope as Envelope<PeerListRequestPayload>, socket, agentPublicKey);\n return;\n }\n\n // Find all recipient sessions\n const recipientSessionMap = this.sessions.get(msg.to);\n const openRecipients = recipientSessionMap\n ? Array.from(recipientSessionMap.values()).filter(a => a.socket.readyState === WebSocket.OPEN)\n : [];\n if (openRecipients.length === 0) {\n // If recipient is a storage peer, queue the message\n if (this.store && this.storagePeers.includes(msg.to)) {\n const senderSessionMap = this.sessions.get(agentPublicKey);\n const senderAgent = senderSessionMap?.values().next().value;\n this.store.save(msg.to, {\n from: agentPublicKey,\n name: senderAgent?.name,\n envelope,\n });\n this.emit('message-relayed', agentPublicKey, msg.to, envelope);\n } else {\n this.sendError(socket, 'Recipient not connected', 'unknown_recipient');\n }\n return;\n }\n\n // Forward envelope to all sessions of the recipient\n try {\n const senderSessionMap = this.sessions.get(agentPublicKey);\n const senderAgent = senderSessionMap?.values().next().value;\n const relayMessage = {\n type: 'message',\n from: agentPublicKey,\n name: senderAgent?.name,\n envelope,\n };\n const messageStr = JSON.stringify(relayMessage);\n for (const recipient of openRecipients) {\n recipient.socket.send(messageStr);\n }\n this.emit('message-relayed', agentPublicKey, msg.to, envelope);\n } catch (err) {\n this.sendError(socket, 'Failed to relay message');\n this.emit('error', err as Error);\n }\n return;\n }\n\n // Handle broadcast: same validation as message, then forward to all other agents\n if (msg.type === 'broadcast') {\n if (!msg.envelope || typeof msg.envelope !== 'object') {\n this.sendError(socket, 'Invalid broadcast: missing or invalid \"envelope\" field');\n return;\n }\n\n const envelope = msg.envelope as Envelope;\n\n const verification = verifyEnvelope(envelope);\n if (!verification.valid) {\n this.sendError(socket, `Invalid envelope: ${verification.reason || 'verification failed'}`);\n return;\n }\n\n if (envelope.sender !== agentPublicKey) {\n this.sendError(socket, 'Envelope sender does not match registered public key');\n return;\n }\n\n if (this.isRateLimitedSender(agentPublicKey)) {\n return;\n }\n\n if (this.isDuplicateEnvelopeId(envelope.id)) {\n return;\n }\n\n // Update lastSeen for any session of sender\n const senderSessionMap = this.sessions.get(agentPublicKey);\n if (senderSessionMap) {\n for (const a of senderSessionMap.values()) {\n a.lastSeen = Date.now();\n }\n }\n\n const senderSessionMapForName = this.sessions.get(agentPublicKey);\n const senderAgent = senderSessionMapForName?.values().next().value;\n const relayMessage = {\n type: 'message' as const,\n from: agentPublicKey,\n name: senderAgent?.name,\n envelope,\n };\n const messageStr = JSON.stringify(relayMessage);\n\n for (const [key, sessionMap] of this.sessions) {\n if (key === agentPublicKey) continue;\n for (const agent of sessionMap.values()) {\n if (agent.socket.readyState === WebSocket.OPEN) {\n try {\n agent.socket.send(messageStr);\n } catch (err) {\n this.emit('error', err as Error);\n }\n }\n }\n }\n return;\n }\n\n // Handle ping\n if (msg.type === 'ping') {\n socket.send(JSON.stringify({ type: 'pong' }));\n return;\n }\n\n // Unknown message type\n this.sendError(socket, `Unknown message type: ${msg.type}`);\n } catch (err) {\n // Invalid JSON or other parsing errors\n this.emit('error', new Error(`Message parsing failed: ${err instanceof Error ? err.message : String(err)}`));\n this.sendError(socket, 'Invalid message format');\n }\n });\n\n socket.on('close', () => {\n if (agentPublicKey && sessionId) {\n const sessionMap = this.sessions.get(agentPublicKey);\n if (sessionMap) {\n const agent = sessionMap.get(sessionId);\n const agentName = agent?.name;\n sessionMap.delete(sessionId);\n if (sessionMap.size === 0) {\n this.sessions.delete(agentPublicKey);\n this.emit('agent-disconnected', agentPublicKey);\n this.emit('disconnection', agentPublicKey);\n // Storage-enabled peers are always considered connected; skip peer_offline for them\n if (!this.storagePeers.includes(agentPublicKey)) {\n this.broadcastPeerEvent('peer_offline', agentPublicKey, agentName);\n }\n }\n }\n }\n });\n\n socket.on('error', (error) => {\n this.emit('error', error);\n });\n }\n\n /**\n * Send an error message to a client\n */\n private sendError(socket: WebSocket, message: string, code?: string): void {\n try {\n if (socket.readyState === WebSocket.OPEN) {\n const payload: { type: 'error'; message: string; code?: string } = { type: 'error', message };\n if (code) payload.code = code;\n socket.send(JSON.stringify(payload));\n }\n } catch (err) {\n // Log errors when sending error messages, but don't propagate to avoid cascading failures\n this.emit('error', new Error(`Failed to send error message: ${err instanceof Error ? err.message : String(err)}`));\n }\n }\n\n /**\n * Broadcast a peer event to all connected agents (all sessions except the one for publicKey)\n */\n private broadcastPeerEvent(eventType: 'peer_online' | 'peer_offline', publicKey: string, name?: string): void {\n const message = {\n type: eventType,\n publicKey,\n name,\n };\n const messageStr = JSON.stringify(message);\n\n for (const [key, sessionMap] of this.sessions) {\n if (key === publicKey) continue;\n for (const agent of sessionMap.values()) {\n if (agent.socket.readyState === WebSocket.OPEN) {\n try {\n agent.socket.send(messageStr);\n } catch (err) {\n this.emit('error', new Error(`Failed to send ${eventType} event: ${err instanceof Error ? err.message : String(err)}`));\n }\n }\n }\n }\n }\n\n /**\n * Handle peer list request from an agent\n */\n private handlePeerListRequest(envelope: Envelope<PeerListRequestPayload>, socket: WebSocket, requesterPublicKey: string): void {\n if (!this.identity) {\n this.sendError(socket, 'Relay does not support peer discovery (no identity configured)');\n return;\n }\n\n const { filters } = envelope.payload;\n const now = Date.now();\n\n // One entry per publicKey (first session for lastSeen/metadata)\n const peersList: ConnectedAgent[] = [];\n for (const [key, sessionMap] of this.sessions) {\n if (key === requesterPublicKey) continue;\n const first = sessionMap.values().next().value;\n if (first) peersList.push(first);\n }\n\n let peers = peersList;\n\n // Apply filters\n if (filters?.activeWithin) {\n peers = peers.filter(p => (now - p.lastSeen) < filters.activeWithin!);\n }\n\n if (filters?.limit && filters.limit > 0) {\n peers = peers.slice(0, filters.limit);\n }\n\n // Build response payload\n const response: PeerListResponsePayload = {\n peers: peers.map(p => ({\n publicKey: p.publicKey,\n metadata: p.name || p.metadata ? {\n name: p.name,\n version: p.metadata?.version,\n capabilities: p.metadata?.capabilities,\n } : undefined,\n lastSeen: p.lastSeen,\n })),\n totalPeers: this.sessions.size - (this.sessions.has(requesterPublicKey) ? 1 : 0),\n relayPublicKey: this.identity.publicKey,\n };\n\n // Create signed envelope\n const responseEnvelope = createEnvelope(\n 'peer_list_response',\n this.identity.publicKey,\n this.identity.privateKey,\n response,\n Date.now(),\n envelope.id // Reply to the request\n );\n\n // Send response\n const relayMessage = {\n type: 'message',\n from: this.identity.publicKey,\n name: 'relay',\n envelope: responseEnvelope,\n };\n\n try {\n socket.send(JSON.stringify(relayMessage));\n } catch (err) {\n this.emit('error', new Error(`Failed to send peer list response: ${err instanceof Error ? err.message : String(err)}`));\n }\n }\n}\n"],"mappings":"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","names":["senderSessionMap"]}
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
RelayServer,
|
|
3
3
|
createEnvelope,
|
|
4
4
|
verifyEnvelope
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-IR2AIJ3K.js";
|
|
6
6
|
|
|
7
7
|
// src/relay/message-buffer.ts
|
|
8
8
|
var MAX_MESSAGES_PER_AGENT = 100;
|
|
@@ -450,4 +450,4 @@ export {
|
|
|
450
450
|
createRestRouter,
|
|
451
451
|
runRelay
|
|
452
452
|
};
|
|
453
|
-
//# sourceMappingURL=chunk-
|
|
453
|
+
//# sourceMappingURL=chunk-KITX5XAA.js.map
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
createEnvelope,
|
|
3
3
|
generateKeyPair,
|
|
4
4
|
verifyEnvelope
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-IR2AIJ3K.js";
|
|
6
6
|
|
|
7
7
|
// src/transport/peer-config.ts
|
|
8
8
|
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
@@ -1162,4 +1162,4 @@ export {
|
|
|
1162
1162
|
computeTrustScores,
|
|
1163
1163
|
computeAllTrustScores
|
|
1164
1164
|
};
|
|
1165
|
-
//# sourceMappingURL=chunk-
|
|
1165
|
+
//# sourceMappingURL=chunk-NXPATMD4.js.map
|
package/dist/cli.js
CHANGED
|
@@ -16,12 +16,12 @@ import {
|
|
|
16
16
|
sendToPeer,
|
|
17
17
|
sendViaRelay,
|
|
18
18
|
verifyReveal
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-NXPATMD4.js";
|
|
20
20
|
import {
|
|
21
21
|
RelayServer,
|
|
22
22
|
createEnvelope,
|
|
23
23
|
verifyEnvelope
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-IR2AIJ3K.js";
|
|
25
25
|
|
|
26
26
|
// src/cli.ts
|
|
27
27
|
import { parseArgs } from "util";
|
package/dist/index.d.ts
CHANGED
|
@@ -630,6 +630,15 @@ declare function loadAgoraConfig(path?: string): AgoraConfig;
|
|
|
630
630
|
*/
|
|
631
631
|
declare function loadAgoraConfigAsync(path?: string): Promise<AgoraConfig>;
|
|
632
632
|
|
|
633
|
+
interface RelayRateLimitOptions {
|
|
634
|
+
enabled?: boolean;
|
|
635
|
+
maxMessages?: number;
|
|
636
|
+
windowMs?: number;
|
|
637
|
+
}
|
|
638
|
+
interface RelayEnvelopeDedupOptions {
|
|
639
|
+
enabled?: boolean;
|
|
640
|
+
maxIds?: number;
|
|
641
|
+
}
|
|
633
642
|
/**
|
|
634
643
|
* Represents a connected agent in the relay
|
|
635
644
|
*/
|
|
@@ -678,6 +687,10 @@ interface RelayServerOptions {
|
|
|
678
687
|
storageDir?: string;
|
|
679
688
|
/** Maximum number of concurrent registered peers (default: 100) */
|
|
680
689
|
maxPeers?: number;
|
|
690
|
+
/** Per-sender sliding-window message rate limiting */
|
|
691
|
+
rateLimit?: RelayRateLimitOptions;
|
|
692
|
+
/** Envelope ID deduplication options */
|
|
693
|
+
envelopeDedup?: RelayEnvelopeDedupOptions;
|
|
681
694
|
}
|
|
682
695
|
declare class RelayServer extends EventEmitter {
|
|
683
696
|
private wss;
|
|
@@ -687,10 +700,21 @@ declare class RelayServer extends EventEmitter {
|
|
|
687
700
|
private storagePeers;
|
|
688
701
|
private store;
|
|
689
702
|
private maxPeers;
|
|
703
|
+
private readonly senderWindows;
|
|
704
|
+
private static readonly MAX_SENDER_ENTRIES;
|
|
705
|
+
private readonly processedEnvelopeIds;
|
|
706
|
+
private rateLimitEnabled;
|
|
707
|
+
private rateLimitMaxMessages;
|
|
708
|
+
private rateLimitWindowMs;
|
|
709
|
+
private envelopeDedupEnabled;
|
|
710
|
+
private envelopeDedupMaxIds;
|
|
690
711
|
constructor(options?: {
|
|
691
712
|
publicKey: string;
|
|
692
713
|
privateKey: string;
|
|
693
714
|
} | RelayServerOptions);
|
|
715
|
+
private isRateLimitedSender;
|
|
716
|
+
private evictOldestSenderWindow;
|
|
717
|
+
private isDuplicateEnvelopeId;
|
|
694
718
|
/**
|
|
695
719
|
* Start the relay server
|
|
696
720
|
* @param port - Port to listen on
|
|
@@ -1139,6 +1163,16 @@ interface SendMessageResult {
|
|
|
1139
1163
|
status: number;
|
|
1140
1164
|
error?: string;
|
|
1141
1165
|
}
|
|
1166
|
+
interface ReplyToEnvelopeOptions {
|
|
1167
|
+
/** The public key of the target (from envelope.sender) */
|
|
1168
|
+
targetPubkey: string;
|
|
1169
|
+
/** Message type for the reply */
|
|
1170
|
+
type: MessageType;
|
|
1171
|
+
/** Reply payload */
|
|
1172
|
+
payload: unknown;
|
|
1173
|
+
/** The envelope ID being replied to (required — this IS a reply) */
|
|
1174
|
+
inReplyTo: string;
|
|
1175
|
+
}
|
|
1142
1176
|
interface DecodeInboundResult {
|
|
1143
1177
|
ok: boolean;
|
|
1144
1178
|
envelope?: Envelope;
|
|
@@ -1193,6 +1227,12 @@ declare class AgoraService {
|
|
|
1193
1227
|
* Tries HTTP webhook first; falls back to relay if HTTP is unavailable.
|
|
1194
1228
|
*/
|
|
1195
1229
|
sendMessage(options: SendMessageOptions): Promise<SendMessageResult>;
|
|
1230
|
+
/**
|
|
1231
|
+
* Reply to an envelope from any sender via relay.
|
|
1232
|
+
* Unlike sendMessage(), this does NOT require the target to be a configured peer.
|
|
1233
|
+
* Uses the target's public key directly — relay-only (no HTTP, since unknown peers have no URL).
|
|
1234
|
+
*/
|
|
1235
|
+
replyToEnvelope(options: ReplyToEnvelopeOptions): Promise<SendMessageResult>;
|
|
1196
1236
|
/**
|
|
1197
1237
|
* Decode and verify an inbound envelope from a webhook message.
|
|
1198
1238
|
*/
|
|
@@ -1702,4 +1742,4 @@ declare function syncReputationFromPeer(agentPublicKey: string, domain: string,
|
|
|
1702
1742
|
skipped: number;
|
|
1703
1743
|
}>;
|
|
1704
1744
|
|
|
1705
|
-
export { type AgoraConfig, type AgoraIdentity, type AgoraPeerConfig, AgoraService, type AgoraServiceConfig, type AnnouncePayload, type AuthenticatedRequest, type BootstrapConfig, type BufferedMessage, type Capability, type CapabilityAnnouncePayload, type CapabilityQueryPayload, type CapabilityResponsePayload, type CommitRecord, type CreateEnvelopeFn, DEFAULT_BOOTSTRAP_RELAYS, type DecodeInboundResult, type DiscoverPayload, type DiscoverResponsePayload, DiscoveryService, type Envelope, type JwtPayload, type KeyPair, type Logger, MessageBuffer, MessageStore, type MessageType, type PaperDiscoveryPayload, type Peer, type PeerConfig, type PeerConfigFile, type PeerDiscoveryConfig, type PeerDiscoveryEvents, PeerDiscoveryService, type PeerListRequestPayload, type PeerListResponsePayload, type PeerReferralPayload, PeerStore, RelayClient, type RelayClientConfig, type RelayClientEvents, type RelayClientFactory, type RelayClientLike, type RelayClientMessage, type RelayConfig, type RelayInterface, type RelayMessageHandler, type RelayMessageHandlerWithName, type RelayPeer, RelayServer, type RelayServerEvents, type RelayServerMessage, type RelayServerOptions, type ReputationQuery, type ReputationResponse, ReputationStore, type RestSession, type RevealRecord, type RevocationRecord, type RunRelayOptions, type SendMessageOptions, type SendMessageResult, type StoredMessage, type TransportConfig, type TrustScore, type TrustScoreOptions, type ValidationResult, type VerificationRecord, type VerifyEnvelopeFn, canonicalize, computeAllTrustScores, computeId, computeTrustScore, computeTrustScores, createCapability, createCommit, createEnvelope, createRestRouter, createReveal, createToken, createVerification, decay, decodeInboundEnvelope, exportKeyPair, formatDisplayName, generateKeyPair, getDefaultBootstrapRelay, getDefaultConfigPath, handleReputationQuery, hashPrediction, importKeyPair, initPeerConfig, loadAgoraConfig, loadAgoraConfigAsync, loadPeerConfig, parseBootstrapRelay, requireAuth, resolveBroadcastName, revokeToken, runRelay, savePeerConfig, sendToPeer, shortKey, signMessage, syncReputationFromPeer, validateCapability, validateCommitRecord, validatePeerListRequest, validatePeerListResponse, validatePeerReferral, validateRevealRecord, validateVerificationRecord, verifyEnvelope, verifyReveal, verifySignature, verifyVerificationSignature };
|
|
1745
|
+
export { type AgoraConfig, type AgoraIdentity, type AgoraPeerConfig, AgoraService, type AgoraServiceConfig, type AnnouncePayload, type AuthenticatedRequest, type BootstrapConfig, type BufferedMessage, type Capability, type CapabilityAnnouncePayload, type CapabilityQueryPayload, type CapabilityResponsePayload, type CommitRecord, type CreateEnvelopeFn, DEFAULT_BOOTSTRAP_RELAYS, type DecodeInboundResult, type DiscoverPayload, type DiscoverResponsePayload, DiscoveryService, type Envelope, type JwtPayload, type KeyPair, type Logger, MessageBuffer, MessageStore, type MessageType, type PaperDiscoveryPayload, type Peer, type PeerConfig, type PeerConfigFile, type PeerDiscoveryConfig, type PeerDiscoveryEvents, PeerDiscoveryService, type PeerListRequestPayload, type PeerListResponsePayload, type PeerReferralPayload, PeerStore, RelayClient, type RelayClientConfig, type RelayClientEvents, type RelayClientFactory, type RelayClientLike, type RelayClientMessage, type RelayConfig, type RelayEnvelopeDedupOptions, type RelayInterface, type RelayMessageHandler, type RelayMessageHandlerWithName, type RelayPeer, type RelayRateLimitOptions, RelayServer, type RelayServerEvents, type RelayServerMessage, type RelayServerOptions, type ReplyToEnvelopeOptions, type ReputationQuery, type ReputationResponse, ReputationStore, type RestSession, type RevealRecord, type RevocationRecord, type RunRelayOptions, type SendMessageOptions, type SendMessageResult, type StoredMessage, type TransportConfig, type TrustScore, type TrustScoreOptions, type ValidationResult, type VerificationRecord, type VerifyEnvelopeFn, canonicalize, computeAllTrustScores, computeId, computeTrustScore, computeTrustScores, createCapability, createCommit, createEnvelope, createRestRouter, createReveal, createToken, createVerification, decay, decodeInboundEnvelope, exportKeyPair, formatDisplayName, generateKeyPair, getDefaultBootstrapRelay, getDefaultConfigPath, handleReputationQuery, hashPrediction, importKeyPair, initPeerConfig, loadAgoraConfig, loadAgoraConfigAsync, loadPeerConfig, parseBootstrapRelay, requireAuth, resolveBroadcastName, revokeToken, runRelay, savePeerConfig, sendToPeer, shortKey, signMessage, syncReputationFromPeer, validateCapability, validateCommitRecord, validatePeerListRequest, validatePeerListResponse, validatePeerReferral, validateRevealRecord, validateVerificationRecord, verifyEnvelope, verifyReveal, verifySignature, verifyVerificationSignature };
|
package/dist/index.js
CHANGED
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
validateVerificationRecord,
|
|
28
28
|
verifyReveal,
|
|
29
29
|
verifyVerificationSignature
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-NXPATMD4.js";
|
|
31
31
|
import {
|
|
32
32
|
MessageBuffer,
|
|
33
33
|
createRestRouter,
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
requireAuth,
|
|
36
36
|
revokeToken,
|
|
37
37
|
runRelay
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-KITX5XAA.js";
|
|
39
39
|
import {
|
|
40
40
|
MessageStore,
|
|
41
41
|
RelayServer,
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
signMessage,
|
|
49
49
|
verifyEnvelope,
|
|
50
50
|
verifySignature
|
|
51
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-IR2AIJ3K.js";
|
|
52
52
|
|
|
53
53
|
// src/registry/capability.ts
|
|
54
54
|
import { createHash } from "crypto";
|
|
@@ -594,6 +594,39 @@ var AgoraService = class {
|
|
|
594
594
|
error: peer.url ? `HTTP send failed and relay not available for peer: ${options.peerName}` : `No webhook URL and relay not available for peer: ${options.peerName}`
|
|
595
595
|
};
|
|
596
596
|
}
|
|
597
|
+
/**
|
|
598
|
+
* Reply to an envelope from any sender via relay.
|
|
599
|
+
* Unlike sendMessage(), this does NOT require the target to be a configured peer.
|
|
600
|
+
* Uses the target's public key directly — relay-only (no HTTP, since unknown peers have no URL).
|
|
601
|
+
*/
|
|
602
|
+
async replyToEnvelope(options) {
|
|
603
|
+
if (!this.relayClient?.connected() || !this.config.relay) {
|
|
604
|
+
return {
|
|
605
|
+
ok: false,
|
|
606
|
+
status: 0,
|
|
607
|
+
error: "Relay not connected \u2014 cannot reply to envelope without relay"
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
this.logger?.debug(
|
|
611
|
+
`Replying to envelope via relay: target=${shortKey(options.targetPubkey)} type=${options.type} inReplyTo=${options.inReplyTo}`
|
|
612
|
+
);
|
|
613
|
+
const relayResult = await sendViaRelay(
|
|
614
|
+
{
|
|
615
|
+
identity: this.config.identity,
|
|
616
|
+
relayUrl: this.config.relay.url,
|
|
617
|
+
relayClient: this.relayClient
|
|
618
|
+
},
|
|
619
|
+
options.targetPubkey,
|
|
620
|
+
options.type,
|
|
621
|
+
options.payload,
|
|
622
|
+
options.inReplyTo
|
|
623
|
+
);
|
|
624
|
+
return {
|
|
625
|
+
ok: relayResult.ok,
|
|
626
|
+
status: 0,
|
|
627
|
+
error: relayResult.error
|
|
628
|
+
};
|
|
629
|
+
}
|
|
597
630
|
/**
|
|
598
631
|
* Decode and verify an inbound envelope from a webhook message.
|
|
599
632
|
*/
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/registry/capability.ts","../src/registry/peer-store.ts","../src/registry/discovery-service.ts","../src/message/types/peer-discovery.ts","../src/config.ts","../src/service.ts","../src/reputation/network.ts","../src/reputation/sync.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\n\n/**\n * A capability describes something an agent can do\n */\nexport interface Capability {\n /** Unique ID (content-addressed hash of name + version + schema) */\n id: string;\n /** Human-readable name: 'code-review', 'summarization', 'translation' */\n name: string;\n /** Semantic version */\n version: string;\n /** What the capability does */\n description: string;\n /** JSON Schema for expected input */\n inputSchema?: object;\n /** JSON Schema for expected output */\n outputSchema?: object;\n /** Discovery tags: ['code', 'typescript', 'review'] */\n tags: string[];\n}\n\n/**\n * Deterministic JSON serialization for capability hashing.\n * Recursively sorts object keys.\n */\nfunction stableStringify(value: unknown): string {\n if (value === null || value === undefined) return JSON.stringify(value);\n if (typeof value !== 'object') return JSON.stringify(value);\n if (Array.isArray(value)) {\n return '[' + value.map(stableStringify).join(',') + ']';\n }\n const keys = Object.keys(value as Record<string, unknown>).sort();\n const pairs = keys.map(k => JSON.stringify(k) + ':' + stableStringify((value as Record<string, unknown>)[k]));\n return '{' + pairs.join(',') + '}';\n}\n\n/**\n * Compute content-addressed ID for a capability based on name, version, and schemas.\n */\nfunction computeCapabilityId(name: string, version: string, inputSchema?: object, outputSchema?: object): string {\n const data = {\n name,\n version,\n ...(inputSchema !== undefined ? { inputSchema } : {}),\n ...(outputSchema !== undefined ? { outputSchema } : {}),\n };\n const canonical = stableStringify(data);\n return createHash('sha256').update(canonical).digest('hex');\n}\n\n/**\n * Creates a capability with a content-addressed ID.\n * \n * @param name - Human-readable capability name\n * @param version - Semantic version string\n * @param description - Description of what the capability does\n * @param options - Optional input/output schemas and tags\n * @returns A Capability object with computed ID\n */\nexport function createCapability(\n name: string,\n version: string,\n description: string,\n options: {\n inputSchema?: object;\n outputSchema?: object;\n tags?: string[];\n } = {}\n): Capability {\n const { inputSchema, outputSchema, tags = [] } = options;\n \n const id = computeCapabilityId(name, version, inputSchema, outputSchema);\n \n return {\n id,\n name,\n version,\n description,\n ...(inputSchema !== undefined ? { inputSchema } : {}),\n ...(outputSchema !== undefined ? { outputSchema } : {}),\n tags,\n };\n}\n\n/**\n * Validates that a capability has all required fields.\n * \n * @param capability - The capability to validate\n * @returns Object with `valid` boolean and optional `errors` array\n */\nexport function validateCapability(capability: unknown): { valid: boolean; errors?: string[] } {\n const errors: string[] = [];\n \n if (!capability || typeof capability !== 'object') {\n return { valid: false, errors: ['Capability must be an object'] };\n }\n \n const cap = capability as Record<string, unknown>;\n \n if (!cap.id || typeof cap.id !== 'string') {\n errors.push('Missing or invalid field: id (must be a string)');\n }\n \n if (!cap.name || typeof cap.name !== 'string') {\n errors.push('Missing or invalid field: name (must be a string)');\n }\n \n if (!cap.version || typeof cap.version !== 'string') {\n errors.push('Missing or invalid field: version (must be a string)');\n }\n \n if (!cap.description || typeof cap.description !== 'string') {\n errors.push('Missing or invalid field: description (must be a string)');\n }\n \n if (!Array.isArray(cap.tags)) {\n errors.push('Missing or invalid field: tags (must be an array)');\n } else if (!cap.tags.every(tag => typeof tag === 'string')) {\n errors.push('Invalid field: tags (all elements must be strings)');\n }\n \n if (cap.inputSchema !== undefined && (typeof cap.inputSchema !== 'object' || cap.inputSchema === null)) {\n errors.push('Invalid field: inputSchema (must be an object)');\n }\n \n if (cap.outputSchema !== undefined && (typeof cap.outputSchema !== 'object' || cap.outputSchema === null)) {\n errors.push('Invalid field: outputSchema (must be an object)');\n }\n \n if (errors.length > 0) {\n return { valid: false, errors };\n }\n \n return { valid: true };\n}\n","import type { Peer } from './peer';\n\n/**\n * In-memory store for known peers on the network\n */\nexport class PeerStore {\n private peers: Map<string, Peer> = new Map();\n\n /**\n * Add or update a peer in the store.\n * If a peer with the same publicKey exists, it will be updated.\n * \n * @param peer - The peer to add or update\n */\n addOrUpdatePeer(peer: Peer): void {\n this.peers.set(peer.publicKey, peer);\n }\n\n /**\n * Remove a peer from the store.\n * \n * @param publicKey - The public key of the peer to remove\n * @returns true if the peer was removed, false if it didn't exist\n */\n removePeer(publicKey: string): boolean {\n return this.peers.delete(publicKey);\n }\n\n /**\n * Get a peer by their public key.\n * \n * @param publicKey - The public key of the peer to retrieve\n * @returns The peer if found, undefined otherwise\n */\n getPeer(publicKey: string): Peer | undefined {\n return this.peers.get(publicKey);\n }\n\n /**\n * Find all peers that offer a specific capability by name.\n * \n * @param name - The capability name to search for\n * @returns Array of peers that have a capability with the given name\n */\n findByCapability(name: string): Peer[] {\n const result: Peer[] = [];\n \n for (const peer of this.peers.values()) {\n const hasCapability = peer.capabilities.some(cap => cap.name === name);\n if (hasCapability) {\n result.push(peer);\n }\n }\n \n return result;\n }\n\n /**\n * Find all peers that have capabilities with a specific tag.\n * \n * @param tag - The tag to search for\n * @returns Array of peers that have at least one capability with the given tag\n */\n findByTag(tag: string): Peer[] {\n const result: Peer[] = [];\n \n for (const peer of this.peers.values()) {\n const hasTag = peer.capabilities.some(cap => cap.tags.includes(tag));\n if (hasTag) {\n result.push(peer);\n }\n }\n \n return result;\n }\n\n /**\n * Get all peers in the store.\n * \n * @returns Array of all peers\n */\n allPeers(): Peer[] {\n return Array.from(this.peers.values());\n }\n\n /**\n * Remove peers that haven't been seen within the specified time window.\n * \n * @param maxAgeMs - Maximum age in milliseconds. Peers older than this will be removed.\n * @param currentTime - Current timestamp (ms), defaults to Date.now()\n * @returns Number of peers removed\n */\n prune(maxAgeMs: number, currentTime: number = Date.now()): number {\n const cutoff = currentTime - maxAgeMs;\n let removed = 0;\n \n for (const [publicKey, peer] of this.peers.entries()) {\n if (peer.lastSeen < cutoff) {\n this.peers.delete(publicKey);\n removed++;\n }\n }\n \n return removed;\n }\n}\n","import { createEnvelope, type Envelope } from '../message/envelope';\nimport { PeerStore } from './peer-store';\nimport type { Capability } from './capability';\nimport type { Peer } from './peer';\nimport type {\n CapabilityAnnouncePayload,\n CapabilityQueryPayload,\n CapabilityResponsePayload,\n} from './messages';\n\n/**\n * DiscoveryService manages capability-based peer discovery.\n * It maintains a local index of peer capabilities and handles\n * capability announce, query, and response messages.\n */\nexport class DiscoveryService {\n constructor(\n private peerStore: PeerStore,\n private identity: { publicKey: string; privateKey: string }\n ) {}\n\n /**\n * Announce own capabilities to the network.\n * Creates a capability_announce envelope that can be broadcast to peers.\n * \n * @param capabilities - List of capabilities this agent offers\n * @param metadata - Optional metadata about this agent\n * @returns A signed capability_announce envelope\n */\n announce(\n capabilities: Capability[],\n metadata?: { name?: string; version?: string }\n ): Envelope<CapabilityAnnouncePayload> {\n const payload: CapabilityAnnouncePayload = {\n publicKey: this.identity.publicKey,\n capabilities,\n metadata: metadata ? {\n ...metadata,\n lastSeen: Date.now(),\n } : {\n lastSeen: Date.now(),\n },\n };\n\n return createEnvelope(\n 'capability_announce',\n this.identity.publicKey,\n this.identity.privateKey,\n payload\n );\n }\n\n /**\n * Handle an incoming capability_announce message.\n * Updates the peer store with the announced capabilities.\n * \n * @param envelope - The capability_announce envelope to process\n */\n handleAnnounce(envelope: Envelope<CapabilityAnnouncePayload>): void {\n const { payload } = envelope;\n \n const peer: Peer = {\n publicKey: payload.publicKey,\n capabilities: payload.capabilities,\n lastSeen: payload.metadata?.lastSeen || envelope.timestamp,\n metadata: payload.metadata ? {\n name: payload.metadata.name,\n version: payload.metadata.version,\n } : undefined,\n };\n\n this.peerStore.addOrUpdatePeer(peer);\n }\n\n /**\n * Create a capability query payload.\n * \n * @param queryType - Type of query: 'name', 'tag', or 'schema'\n * @param query - The query value (capability name, tag, or schema)\n * @param filters - Optional filters (limit, minTrustScore)\n * @returns A capability_query payload\n */\n query(\n queryType: 'name' | 'tag' | 'schema',\n query: string | object,\n filters?: { limit?: number; minTrustScore?: number }\n ): CapabilityQueryPayload {\n return {\n queryType,\n query,\n filters,\n };\n }\n\n /**\n * Handle an incoming capability_query message.\n * Searches the local peer store and returns matching peers.\n * \n * @param envelope - The capability_query envelope to process\n * @returns A capability_response envelope with matching peers\n */\n handleQuery(\n envelope: Envelope<CapabilityQueryPayload>\n ): Envelope<CapabilityResponsePayload> {\n const { payload } = envelope;\n let peers: Peer[] = [];\n\n // Execute query based on type\n if (payload.queryType === 'name' && typeof payload.query === 'string') {\n peers = this.peerStore.findByCapability(payload.query);\n } else if (payload.queryType === 'tag' && typeof payload.query === 'string') {\n peers = this.peerStore.findByTag(payload.query);\n } else if (payload.queryType === 'schema') {\n // Schema-based matching is deferred to Phase 2b\n // For now, return empty results\n peers = [];\n }\n\n // Apply filters\n const limit = payload.filters?.limit;\n const totalMatches = peers.length;\n \n if (limit !== undefined && limit > 0) {\n peers = peers.slice(0, limit);\n }\n\n // Transform peers to response format\n const responsePeers = peers.map(peer => ({\n publicKey: peer.publicKey,\n capabilities: peer.capabilities,\n metadata: peer.metadata ? {\n name: peer.metadata.name,\n version: peer.metadata.version,\n lastSeen: peer.lastSeen,\n } : {\n lastSeen: peer.lastSeen,\n },\n // Trust score integration deferred to Phase 2b (RFC-001)\n trustScore: undefined,\n }));\n\n const responsePayload: CapabilityResponsePayload = {\n queryId: envelope.id,\n peers: responsePeers,\n totalMatches,\n };\n\n return createEnvelope(\n 'capability_response',\n this.identity.publicKey,\n this.identity.privateKey,\n responsePayload,\n Date.now(),\n envelope.id // inReplyTo\n );\n }\n\n /**\n * Remove peers that haven't been seen within the specified time window.\n * \n * @param maxAgeMs - Maximum age in milliseconds\n * @returns Number of peers removed\n */\n pruneStale(maxAgeMs: number, currentTime: number = Date.now()): number {\n return this.peerStore.prune(maxAgeMs, currentTime);\n }\n}\n","/**\n * Peer discovery message types for the Agora network.\n */\n\n/**\n * Request peer list from relay\n */\nexport interface PeerListRequestPayload {\n /** Optional filters */\n filters?: {\n /** Only peers seen in last N ms */\n activeWithin?: number;\n /** Maximum peers to return */\n limit?: number;\n };\n}\n\n/**\n * Relay responds with connected peers\n */\nexport interface PeerListResponsePayload {\n /** List of known peers */\n peers: Array<{\n /** Peer's Ed25519 public key */\n publicKey: string;\n /** Optional metadata (if peer announced) */\n metadata?: {\n name?: string;\n version?: string;\n capabilities?: string[];\n };\n /** Last seen timestamp (ms) */\n lastSeen: number;\n }>;\n /** Total peer count (may be > peers.length if limited) */\n totalPeers: number;\n /** Relay's public key (for trust verification) */\n relayPublicKey: string;\n}\n\n/**\n * Agent recommends another agent\n */\nexport interface PeerReferralPayload {\n /** Referred peer's public key */\n publicKey: string;\n /** Optional endpoint (if known) */\n endpoint?: string;\n /** Optional metadata */\n metadata?: {\n name?: string;\n version?: string;\n capabilities?: string[];\n };\n /** Referrer's comment */\n comment?: string;\n /** Trust hint (RFC-001 integration) */\n trustScore?: number;\n}\n\n/**\n * Validate PeerListRequestPayload\n */\nexport function validatePeerListRequest(payload: unknown): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n if (typeof payload !== 'object' || payload === null) {\n errors.push('Payload must be an object');\n return { valid: false, errors };\n }\n\n const p = payload as Record<string, unknown>;\n\n if (p.filters !== undefined) {\n if (typeof p.filters !== 'object' || p.filters === null) {\n errors.push('filters must be an object');\n } else {\n const filters = p.filters as Record<string, unknown>;\n if (filters.activeWithin !== undefined && typeof filters.activeWithin !== 'number') {\n errors.push('filters.activeWithin must be a number');\n }\n if (filters.limit !== undefined && typeof filters.limit !== 'number') {\n errors.push('filters.limit must be a number');\n }\n }\n }\n\n return { valid: errors.length === 0, errors };\n}\n\n/**\n * Validate PeerListResponsePayload\n */\nexport function validatePeerListResponse(payload: unknown): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n if (typeof payload !== 'object' || payload === null) {\n errors.push('Payload must be an object');\n return { valid: false, errors };\n }\n\n const p = payload as Record<string, unknown>;\n\n if (!Array.isArray(p.peers)) {\n errors.push('peers must be an array');\n } else {\n p.peers.forEach((peer, index) => {\n if (typeof peer !== 'object' || peer === null) {\n errors.push(`peers[${index}] must be an object`);\n return;\n }\n const peerObj = peer as Record<string, unknown>;\n if (typeof peerObj.publicKey !== 'string') {\n errors.push(`peers[${index}].publicKey must be a string`);\n }\n if (typeof peerObj.lastSeen !== 'number') {\n errors.push(`peers[${index}].lastSeen must be a number`);\n }\n });\n }\n\n if (typeof p.totalPeers !== 'number') {\n errors.push('totalPeers must be a number');\n }\n\n if (typeof p.relayPublicKey !== 'string') {\n errors.push('relayPublicKey must be a string');\n }\n\n return { valid: errors.length === 0, errors };\n}\n\n/**\n * Validate PeerReferralPayload\n */\nexport function validatePeerReferral(payload: unknown): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n if (typeof payload !== 'object' || payload === null) {\n errors.push('Payload must be an object');\n return { valid: false, errors };\n }\n\n const p = payload as Record<string, unknown>;\n\n if (typeof p.publicKey !== 'string') {\n errors.push('publicKey must be a string');\n }\n\n if (p.endpoint !== undefined && typeof p.endpoint !== 'string') {\n errors.push('endpoint must be a string');\n }\n\n if (p.comment !== undefined && typeof p.comment !== 'string') {\n errors.push('comment must be a string');\n }\n\n if (p.trustScore !== undefined && typeof p.trustScore !== 'number') {\n errors.push('trustScore must be a number');\n }\n\n return { valid: errors.length === 0, errors };\n}\n","import { readFileSync, existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { homedir } from 'node:os';\n\n/**\n * Normalized relay configuration (supports both string and object in config file).\n */\nexport interface RelayConfig {\n url: string;\n autoConnect: boolean;\n name?: string;\n reconnectMaxMs?: number;\n}\n\n/**\n * Peer entry in config (webhook URL, token, public key).\n */\nexport interface AgoraPeerConfig {\n publicKey: string;\n /** Webhook URL (undefined for relay-only peers) */\n url?: string;\n /** Webhook auth token (undefined for relay-only peers) */\n token?: string;\n name?: string;\n}\n\n/**\n * Identity with optional display name (e.g. for relay registration).\n */\nexport interface AgoraIdentity {\n publicKey: string;\n privateKey: string;\n name?: string;\n}\n\n/**\n * Canonical Agora configuration shape.\n * Use loadAgoraConfig() to load from file with normalized relay.\n */\nexport interface AgoraConfig {\n identity: AgoraIdentity;\n peers: Record<string, AgoraPeerConfig>;\n relay?: RelayConfig;\n}\n\n/**\n * Default config file path: AGORA_CONFIG env or ~/.config/agora/config.json\n */\nexport function getDefaultConfigPath(): string {\n if (process.env.AGORA_CONFIG) {\n return resolve(process.env.AGORA_CONFIG);\n }\n return resolve(homedir(), '.config', 'agora', 'config.json');\n}\n\n/**\n * Parse and normalize config from a JSON object (shared by sync and async loaders).\n */\nfunction parseConfig(config: Record<string, unknown>): AgoraConfig {\n const rawIdentity = config.identity as Record<string, unknown> | undefined;\n if (!rawIdentity?.publicKey || !rawIdentity?.privateKey) {\n throw new Error('Invalid config: missing identity.publicKey or identity.privateKey');\n }\n const identity: AgoraIdentity = {\n publicKey: rawIdentity.publicKey as string,\n privateKey: rawIdentity.privateKey as string,\n name: typeof rawIdentity.name === 'string' ? rawIdentity.name : undefined,\n };\n\n const peers: Record<string, AgoraPeerConfig> = {};\n if (config.peers && typeof config.peers === 'object') {\n for (const [name, entry] of Object.entries(config.peers)) {\n const peer = entry as Record<string, unknown>;\n if (peer && typeof peer.publicKey === 'string') {\n peers[name] = {\n publicKey: peer.publicKey as string,\n url: typeof peer.url === 'string' ? peer.url : undefined,\n token: typeof peer.token === 'string' ? peer.token : undefined,\n name: typeof peer.name === 'string' ? peer.name : undefined,\n };\n }\n }\n }\n\n let relay: RelayConfig | undefined;\n const rawRelay = config.relay;\n if (typeof rawRelay === 'string') {\n relay = { url: rawRelay, autoConnect: true };\n } else if (rawRelay && typeof rawRelay === 'object') {\n const r = rawRelay as Record<string, unknown>;\n if (typeof r.url === 'string') {\n relay = {\n url: r.url,\n autoConnect: typeof r.autoConnect === 'boolean' ? r.autoConnect : true,\n name: typeof r.name === 'string' ? r.name : undefined,\n reconnectMaxMs: typeof r.reconnectMaxMs === 'number' ? r.reconnectMaxMs : undefined,\n };\n }\n }\n\n return {\n identity,\n peers,\n ...(relay ? { relay } : {}),\n };\n}\n\n/**\n * Load and normalize Agora configuration from a JSON file (sync).\n * Supports relay as string (backward compat) or object { url?, autoConnect?, name?, reconnectMaxMs? }.\n *\n * @param path - Config file path; defaults to getDefaultConfigPath()\n * @returns Normalized AgoraConfig\n * @throws Error if file doesn't exist or config is invalid\n */\nexport function loadAgoraConfig(path?: string): AgoraConfig {\n const configPath = path ?? getDefaultConfigPath();\n\n if (!existsSync(configPath)) {\n throw new Error(`Config file not found at ${configPath}. Run 'npx @rookdaemon/agora init' first.`);\n }\n\n const content = readFileSync(configPath, 'utf-8');\n let config: Record<string, unknown>;\n try {\n config = JSON.parse(content) as Record<string, unknown>;\n } catch {\n throw new Error(`Invalid JSON in config file: ${configPath}`);\n }\n\n return parseConfig(config);\n}\n\n/**\n * Load and normalize Agora configuration from a JSON file (async).\n *\n * @param path - Config file path; defaults to getDefaultConfigPath()\n * @returns Normalized AgoraConfig\n * @throws Error if file doesn't exist or config is invalid\n */\nexport async function loadAgoraConfigAsync(path?: string): Promise<AgoraConfig> {\n const configPath = path ?? getDefaultConfigPath();\n\n let content: string;\n try {\n content = await readFile(configPath, 'utf-8');\n } catch (err) {\n const code = err && typeof err === 'object' && 'code' in err ? (err as NodeJS.ErrnoException).code : undefined;\n if (code === 'ENOENT') {\n throw new Error(`Config file not found at ${configPath}. Run 'npx @rookdaemon/agora init' first.`);\n }\n throw err;\n }\n\n let config: Record<string, unknown>;\n try {\n config = JSON.parse(content) as Record<string, unknown>;\n } catch {\n throw new Error(`Invalid JSON in config file: ${configPath}`);\n }\n\n return parseConfig(config);\n}\n","import type { AgoraIdentity, RelayConfig } from './config';\nimport { getDefaultConfigPath, loadAgoraConfigAsync } from './config';\nimport type { Envelope } from './message/envelope';\nimport type { MessageType } from './message/envelope';\nimport { RelayClient } from './relay/client';\nimport type { PeerConfig } from './transport/http';\nimport { decodeInboundEnvelope, sendToPeer } from './transport/http';\nimport { sendViaRelay } from './transport/relay';\nimport { shortKey } from './utils';\n\n/**\n * Service config: identity, peers keyed by name, optional relay.\n */\nexport interface AgoraServiceConfig {\n identity: AgoraIdentity;\n peers: Map<string, PeerConfig>;\n relay?: RelayConfig;\n}\n\nexport interface SendMessageOptions {\n peerName: string;\n type: MessageType;\n payload: unknown;\n inReplyTo?: string;\n /** Skip relay, send directly via HTTP only. Fails if peer has no URL or is unreachable. */\n direct?: boolean;\n /** Skip direct HTTP, always use relay even if peer has a URL. */\n relayOnly?: boolean;\n}\n\nexport interface SendMessageResult {\n ok: boolean;\n status: number;\n error?: string;\n}\n\nexport interface DecodeInboundResult {\n ok: boolean;\n envelope?: Envelope;\n reason?: string;\n}\n\n/** Handler for relay messages. (envelope, fromPublicKey, fromName?) */\nexport type RelayMessageHandlerWithName = (envelope: Envelope, from: string, fromName?: string) => void;\n\n/** @deprecated Use RelayMessageHandlerWithName. Kept for backward compatibility. */\nexport type RelayMessageHandler = (envelope: Envelope) => void;\n\nexport interface Logger {\n debug(message: string): void;\n}\n\nexport interface RelayClientLike {\n connect(): Promise<void>;\n disconnect(): void;\n connected(): boolean;\n send(to: string, envelope: Envelope): Promise<{ ok: boolean; error?: string }>;\n on(event: 'message', handler: (envelope: Envelope, from: string, fromName?: string) => void): void;\n on(event: 'error', handler: (error: Error) => void): void;\n}\n\nexport interface RelayClientFactory {\n (opts: {\n relayUrl: string;\n publicKey: string;\n privateKey: string;\n name?: string;\n pingInterval: number;\n maxReconnectDelay: number;\n }): RelayClientLike;\n}\n\n/**\n * High-level Agora service: send by peer name, decode inbound, relay lifecycle.\n */\nexport class AgoraService {\n private config: AgoraServiceConfig;\n private relayClient: RelayClientLike | null = null;\n private readonly onRelayMessage: RelayMessageHandlerWithName;\n private logger: Logger | null;\n private relayClientFactory: RelayClientFactory | null;\n\n /**\n * @param config - Service config (identity, peers, optional relay)\n * @param onRelayMessage - Required callback for relay messages. Ensures no messages are lost between init and connect.\n * @param logger - Optional debug logger\n * @param relayClientFactory - Optional factory for relay client (for testing)\n */\n constructor(\n config: AgoraServiceConfig,\n onRelayMessage: RelayMessageHandlerWithName,\n logger?: Logger,\n relayClientFactory?: RelayClientFactory\n ) {\n this.config = config;\n this.onRelayMessage = onRelayMessage;\n this.logger = logger ?? null;\n this.relayClientFactory = relayClientFactory ?? null;\n }\n\n /**\n * Send a signed message to a named peer.\n * Tries HTTP webhook first; falls back to relay if HTTP is unavailable.\n */\n async sendMessage(options: SendMessageOptions): Promise<SendMessageResult> {\n const peer = this.config.peers.get(options.peerName);\n if (!peer) {\n return {\n ok: false,\n status: 0,\n error: `Unknown peer: ${options.peerName}`,\n };\n }\n\n // Try HTTP first (only if peer has a webhook URL and --relay-only not set)\n if (peer.url && !options.relayOnly) {\n const transportConfig = {\n identity: {\n publicKey: this.config.identity.publicKey,\n privateKey: this.config.identity.privateKey,\n },\n peers: new Map<string, PeerConfig>([[peer.publicKey, peer]]),\n };\n\n const httpResult = await sendToPeer(\n transportConfig,\n peer.publicKey,\n options.type,\n options.payload,\n options.inReplyTo\n );\n\n if (httpResult.ok) {\n return httpResult;\n }\n\n this.logger?.debug(`HTTP send to ${options.peerName} failed: ${httpResult.error}`);\n\n // --direct flag: do not fall back to relay\n if (options.direct) {\n return {\n ok: false,\n status: httpResult.status,\n error: `Direct send to ${options.peerName} failed: ${httpResult.error}`,\n };\n }\n } else if (options.direct && !peer.url) {\n // --direct requested but peer has no URL configured\n return {\n ok: false,\n status: 0,\n error: `Direct send failed: peer '${options.peerName}' has no URL configured`,\n };\n }\n\n // Fall back to relay\n if (this.relayClient?.connected() && this.config.relay) {\n const relayResult = await sendViaRelay(\n {\n identity: this.config.identity,\n relayUrl: this.config.relay.url,\n relayClient: this.relayClient,\n },\n peer.publicKey,\n options.type,\n options.payload,\n options.inReplyTo\n );\n\n return {\n ok: relayResult.ok,\n status: 0,\n error: relayResult.error,\n };\n }\n\n // Both failed\n return {\n ok: false,\n status: 0,\n error: peer.url\n ? `HTTP send failed and relay not available for peer: ${options.peerName}`\n : `No webhook URL and relay not available for peer: ${options.peerName}`,\n };\n }\n\n /**\n * Decode and verify an inbound envelope from a webhook message.\n */\n async decodeInbound(message: string): Promise<DecodeInboundResult> {\n const peersByPubKey = new Map<string, PeerConfig>();\n for (const peer of this.config.peers.values()) {\n peersByPubKey.set(peer.publicKey, peer);\n }\n const result = decodeInboundEnvelope(message, peersByPubKey);\n if (result.ok) {\n return { ok: true, envelope: result.envelope };\n }\n return { ok: false, reason: result.reason };\n }\n\n getPeers(): string[] {\n return Array.from(this.config.peers.keys());\n }\n\n getPeerConfig(name: string): PeerConfig | undefined {\n return this.config.peers.get(name);\n }\n\n /**\n * Connect to the relay server.\n */\n async connectRelay(url: string): Promise<void> {\n if (this.relayClient) {\n return;\n }\n\n const maxReconnectDelay = this.config.relay?.reconnectMaxMs ?? 300000;\n let name = this.config.identity.name ?? this.config.relay?.name;\n // Never use the short key (id) as the relay display name; treat it as no name\n if (name && name === shortKey(this.config.identity.publicKey)) {\n name = undefined;\n }\n const opts = {\n relayUrl: url,\n publicKey: this.config.identity.publicKey,\n privateKey: this.config.identity.privateKey,\n name,\n pingInterval: 30000,\n maxReconnectDelay,\n };\n\n if (this.relayClientFactory) {\n this.relayClient = this.relayClientFactory(opts);\n } else {\n this.relayClient = new RelayClient(opts);\n }\n\n this.relayClient.on('error', (error: Error) => {\n this.logger?.debug(`Agora relay error: ${error.message}`);\n });\n\n this.relayClient.on('message', (envelope: Envelope, from: string, fromName?: string) => {\n this.onRelayMessage(envelope, from, fromName);\n });\n\n try {\n await this.relayClient.connect();\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.logger?.debug(`Agora relay connect failed (${url}): ${message}`);\n this.relayClient = null;\n }\n }\n\n async disconnectRelay(): Promise<void> {\n if (this.relayClient) {\n this.relayClient.disconnect();\n this.relayClient = null;\n }\n }\n\n isRelayConnected(): boolean {\n return this.relayClient?.connected() ?? false;\n }\n\n /**\n * Load Agora configuration and return service config (peers as Map).\n */\n static async loadConfig(path?: string): Promise<AgoraServiceConfig> {\n const configPath = path ?? getDefaultConfigPath();\n const loaded = await loadAgoraConfigAsync(configPath);\n\n const peers = new Map<string, PeerConfig>();\n for (const [name, p] of Object.entries(loaded.peers)) {\n peers.set(name, {\n publicKey: p.publicKey,\n url: p.url,\n token: p.token,\n } satisfies PeerConfig);\n }\n\n return {\n identity: loaded.identity,\n peers,\n relay: loaded.relay,\n };\n }\n}\n","/**\n * Network reputation query handler.\n * Handles incoming reputation_query messages and returns reputation_response.\n */\n\nimport type { ReputationQuery, ReputationResponse, TrustScore } from './types.js';\nimport { computeTrustScore, computeTrustScores } from './scoring.js';\nimport type { ReputationStore } from './store.js';\n\n/** Maximum number of verification records to include in a response */\nconst MAX_VERIFICATIONS_IN_RESPONSE = 50;\n\n/**\n * Handle an incoming reputation query by reading from the local store,\n * computing trust scores, and returning a response.\n *\n * @param query - The reputation query (agent, optional domain, optional after timestamp)\n * @param store - Local reputation store to read from\n * @param currentTime - Current timestamp (ms) for score computation\n * @returns Reputation response with computed scores and verification records\n */\nexport async function handleReputationQuery(\n query: ReputationQuery,\n store: ReputationStore,\n currentTime: number\n): Promise<ReputationResponse> {\n // Get all verifications from the store for score computation\n const allVerifications = await store.getVerifications();\n\n // Filter to verifications targeting the queried agent\n let relevantVerifications = allVerifications.filter(v => v.target === query.agent);\n\n // Apply domain filter if specified\n if (query.domain !== undefined) {\n relevantVerifications = relevantVerifications.filter(v => v.domain === query.domain);\n }\n\n // Apply after-timestamp filter if specified\n if (query.after !== undefined) {\n const after = query.after;\n relevantVerifications = relevantVerifications.filter(v => v.timestamp > after);\n }\n\n // Compute trust scores\n let scores: Record<string, TrustScore>;\n\n if (query.domain !== undefined) {\n const score = computeTrustScore(query.agent, query.domain, allVerifications, currentTime);\n scores = { [query.domain]: score };\n } else {\n const scoreMap = computeTrustScores(query.agent, allVerifications, currentTime);\n scores = {};\n for (const [domain, score] of scoreMap.entries()) {\n scores[domain] = score;\n }\n }\n\n // Size-limit verifications to most recent MAX_VERIFICATIONS_IN_RESPONSE\n const limitedVerifications = relevantVerifications\n .slice()\n .sort((a, b) => b.timestamp - a.timestamp)\n .slice(0, MAX_VERIFICATIONS_IN_RESPONSE);\n\n const response: ReputationResponse = {\n agent: query.agent,\n verifications: limitedVerifications,\n scores,\n };\n\n if (query.domain !== undefined) {\n response.domain = query.domain;\n }\n\n return response;\n}\n","/**\n * Cross-peer reputation synchronization.\n * Pull and merge verification records from trusted peers.\n */\n\nimport type { ReputationQuery, ReputationResponse } from './types.js';\nimport { verifyVerificationSignature } from './verification.js';\nimport type { ReputationStore } from './store.js';\n\n/**\n * Pull reputation data from a peer and merge it into the local store.\n *\n * Flow:\n * 1. Send `reputation_query` to peer via sendMessage\n * 2. Receive `reputation_response` with verification records\n * 3. For each record: verify signature, check domain matches, check not duplicate\n * 4. Append new records to local store\n * 5. Return count of added/skipped\n *\n * @param agentPublicKey - Public key of the agent whose reputation to sync\n * @param domain - Domain to sync reputation for\n * @param store - Local reputation store to merge records into\n * @param sendMessage - Function that sends a reputation_query and returns the response\n * @returns Counts of records added and skipped\n */\nexport async function syncReputationFromPeer(\n agentPublicKey: string,\n domain: string,\n store: ReputationStore,\n sendMessage: (type: string, payload: ReputationQuery) => Promise<ReputationResponse>\n): Promise<{ added: number; skipped: number }> {\n // Build and send the query\n const query: ReputationQuery = {\n agent: agentPublicKey,\n domain,\n };\n\n const response = await sendMessage('reputation_query', query);\n\n // Build set of existing record IDs for fast deduplication\n const existing = await store.getVerifications();\n const existingIds = new Set(existing.map(v => v.id));\n\n let added = 0;\n let skipped = 0;\n\n for (const record of response.verifications) {\n // Skip duplicate records (content-addressed by ID)\n if (existingIds.has(record.id)) {\n skipped++;\n continue;\n }\n\n // Verify cryptographic signature before accepting\n const sigResult = verifyVerificationSignature(record);\n if (!sigResult.valid) {\n skipped++;\n continue;\n }\n\n // Ensure the record's domain matches what we requested\n if (record.domain !== domain) {\n skipped++;\n continue;\n }\n\n // Add to local store and track for deduplication within this batch\n await store.addVerification(record);\n existingIds.add(record.id);\n added++;\n }\n\n return { added, skipped };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/registry/capability.ts","../src/registry/peer-store.ts","../src/registry/discovery-service.ts","../src/message/types/peer-discovery.ts","../src/config.ts","../src/service.ts","../src/reputation/network.ts","../src/reputation/sync.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\n\n/**\n * A capability describes something an agent can do\n */\nexport interface Capability {\n /** Unique ID (content-addressed hash of name + version + schema) */\n id: string;\n /** Human-readable name: 'code-review', 'summarization', 'translation' */\n name: string;\n /** Semantic version */\n version: string;\n /** What the capability does */\n description: string;\n /** JSON Schema for expected input */\n inputSchema?: object;\n /** JSON Schema for expected output */\n outputSchema?: object;\n /** Discovery tags: ['code', 'typescript', 'review'] */\n tags: string[];\n}\n\n/**\n * Deterministic JSON serialization for capability hashing.\n * Recursively sorts object keys.\n */\nfunction stableStringify(value: unknown): string {\n if (value === null || value === undefined) return JSON.stringify(value);\n if (typeof value !== 'object') return JSON.stringify(value);\n if (Array.isArray(value)) {\n return '[' + value.map(stableStringify).join(',') + ']';\n }\n const keys = Object.keys(value as Record<string, unknown>).sort();\n const pairs = keys.map(k => JSON.stringify(k) + ':' + stableStringify((value as Record<string, unknown>)[k]));\n return '{' + pairs.join(',') + '}';\n}\n\n/**\n * Compute content-addressed ID for a capability based on name, version, and schemas.\n */\nfunction computeCapabilityId(name: string, version: string, inputSchema?: object, outputSchema?: object): string {\n const data = {\n name,\n version,\n ...(inputSchema !== undefined ? { inputSchema } : {}),\n ...(outputSchema !== undefined ? { outputSchema } : {}),\n };\n const canonical = stableStringify(data);\n return createHash('sha256').update(canonical).digest('hex');\n}\n\n/**\n * Creates a capability with a content-addressed ID.\n * \n * @param name - Human-readable capability name\n * @param version - Semantic version string\n * @param description - Description of what the capability does\n * @param options - Optional input/output schemas and tags\n * @returns A Capability object with computed ID\n */\nexport function createCapability(\n name: string,\n version: string,\n description: string,\n options: {\n inputSchema?: object;\n outputSchema?: object;\n tags?: string[];\n } = {}\n): Capability {\n const { inputSchema, outputSchema, tags = [] } = options;\n \n const id = computeCapabilityId(name, version, inputSchema, outputSchema);\n \n return {\n id,\n name,\n version,\n description,\n ...(inputSchema !== undefined ? { inputSchema } : {}),\n ...(outputSchema !== undefined ? { outputSchema } : {}),\n tags,\n };\n}\n\n/**\n * Validates that a capability has all required fields.\n * \n * @param capability - The capability to validate\n * @returns Object with `valid` boolean and optional `errors` array\n */\nexport function validateCapability(capability: unknown): { valid: boolean; errors?: string[] } {\n const errors: string[] = [];\n \n if (!capability || typeof capability !== 'object') {\n return { valid: false, errors: ['Capability must be an object'] };\n }\n \n const cap = capability as Record<string, unknown>;\n \n if (!cap.id || typeof cap.id !== 'string') {\n errors.push('Missing or invalid field: id (must be a string)');\n }\n \n if (!cap.name || typeof cap.name !== 'string') {\n errors.push('Missing or invalid field: name (must be a string)');\n }\n \n if (!cap.version || typeof cap.version !== 'string') {\n errors.push('Missing or invalid field: version (must be a string)');\n }\n \n if (!cap.description || typeof cap.description !== 'string') {\n errors.push('Missing or invalid field: description (must be a string)');\n }\n \n if (!Array.isArray(cap.tags)) {\n errors.push('Missing or invalid field: tags (must be an array)');\n } else if (!cap.tags.every(tag => typeof tag === 'string')) {\n errors.push('Invalid field: tags (all elements must be strings)');\n }\n \n if (cap.inputSchema !== undefined && (typeof cap.inputSchema !== 'object' || cap.inputSchema === null)) {\n errors.push('Invalid field: inputSchema (must be an object)');\n }\n \n if (cap.outputSchema !== undefined && (typeof cap.outputSchema !== 'object' || cap.outputSchema === null)) {\n errors.push('Invalid field: outputSchema (must be an object)');\n }\n \n if (errors.length > 0) {\n return { valid: false, errors };\n }\n \n return { valid: true };\n}\n","import type { Peer } from './peer';\n\n/**\n * In-memory store for known peers on the network\n */\nexport class PeerStore {\n private peers: Map<string, Peer> = new Map();\n\n /**\n * Add or update a peer in the store.\n * If a peer with the same publicKey exists, it will be updated.\n * \n * @param peer - The peer to add or update\n */\n addOrUpdatePeer(peer: Peer): void {\n this.peers.set(peer.publicKey, peer);\n }\n\n /**\n * Remove a peer from the store.\n * \n * @param publicKey - The public key of the peer to remove\n * @returns true if the peer was removed, false if it didn't exist\n */\n removePeer(publicKey: string): boolean {\n return this.peers.delete(publicKey);\n }\n\n /**\n * Get a peer by their public key.\n * \n * @param publicKey - The public key of the peer to retrieve\n * @returns The peer if found, undefined otherwise\n */\n getPeer(publicKey: string): Peer | undefined {\n return this.peers.get(publicKey);\n }\n\n /**\n * Find all peers that offer a specific capability by name.\n * \n * @param name - The capability name to search for\n * @returns Array of peers that have a capability with the given name\n */\n findByCapability(name: string): Peer[] {\n const result: Peer[] = [];\n \n for (const peer of this.peers.values()) {\n const hasCapability = peer.capabilities.some(cap => cap.name === name);\n if (hasCapability) {\n result.push(peer);\n }\n }\n \n return result;\n }\n\n /**\n * Find all peers that have capabilities with a specific tag.\n * \n * @param tag - The tag to search for\n * @returns Array of peers that have at least one capability with the given tag\n */\n findByTag(tag: string): Peer[] {\n const result: Peer[] = [];\n \n for (const peer of this.peers.values()) {\n const hasTag = peer.capabilities.some(cap => cap.tags.includes(tag));\n if (hasTag) {\n result.push(peer);\n }\n }\n \n return result;\n }\n\n /**\n * Get all peers in the store.\n * \n * @returns Array of all peers\n */\n allPeers(): Peer[] {\n return Array.from(this.peers.values());\n }\n\n /**\n * Remove peers that haven't been seen within the specified time window.\n * \n * @param maxAgeMs - Maximum age in milliseconds. Peers older than this will be removed.\n * @param currentTime - Current timestamp (ms), defaults to Date.now()\n * @returns Number of peers removed\n */\n prune(maxAgeMs: number, currentTime: number = Date.now()): number {\n const cutoff = currentTime - maxAgeMs;\n let removed = 0;\n \n for (const [publicKey, peer] of this.peers.entries()) {\n if (peer.lastSeen < cutoff) {\n this.peers.delete(publicKey);\n removed++;\n }\n }\n \n return removed;\n }\n}\n","import { createEnvelope, type Envelope } from '../message/envelope';\nimport { PeerStore } from './peer-store';\nimport type { Capability } from './capability';\nimport type { Peer } from './peer';\nimport type {\n CapabilityAnnouncePayload,\n CapabilityQueryPayload,\n CapabilityResponsePayload,\n} from './messages';\n\n/**\n * DiscoveryService manages capability-based peer discovery.\n * It maintains a local index of peer capabilities and handles\n * capability announce, query, and response messages.\n */\nexport class DiscoveryService {\n constructor(\n private peerStore: PeerStore,\n private identity: { publicKey: string; privateKey: string }\n ) {}\n\n /**\n * Announce own capabilities to the network.\n * Creates a capability_announce envelope that can be broadcast to peers.\n * \n * @param capabilities - List of capabilities this agent offers\n * @param metadata - Optional metadata about this agent\n * @returns A signed capability_announce envelope\n */\n announce(\n capabilities: Capability[],\n metadata?: { name?: string; version?: string }\n ): Envelope<CapabilityAnnouncePayload> {\n const payload: CapabilityAnnouncePayload = {\n publicKey: this.identity.publicKey,\n capabilities,\n metadata: metadata ? {\n ...metadata,\n lastSeen: Date.now(),\n } : {\n lastSeen: Date.now(),\n },\n };\n\n return createEnvelope(\n 'capability_announce',\n this.identity.publicKey,\n this.identity.privateKey,\n payload\n );\n }\n\n /**\n * Handle an incoming capability_announce message.\n * Updates the peer store with the announced capabilities.\n * \n * @param envelope - The capability_announce envelope to process\n */\n handleAnnounce(envelope: Envelope<CapabilityAnnouncePayload>): void {\n const { payload } = envelope;\n \n const peer: Peer = {\n publicKey: payload.publicKey,\n capabilities: payload.capabilities,\n lastSeen: payload.metadata?.lastSeen || envelope.timestamp,\n metadata: payload.metadata ? {\n name: payload.metadata.name,\n version: payload.metadata.version,\n } : undefined,\n };\n\n this.peerStore.addOrUpdatePeer(peer);\n }\n\n /**\n * Create a capability query payload.\n * \n * @param queryType - Type of query: 'name', 'tag', or 'schema'\n * @param query - The query value (capability name, tag, or schema)\n * @param filters - Optional filters (limit, minTrustScore)\n * @returns A capability_query payload\n */\n query(\n queryType: 'name' | 'tag' | 'schema',\n query: string | object,\n filters?: { limit?: number; minTrustScore?: number }\n ): CapabilityQueryPayload {\n return {\n queryType,\n query,\n filters,\n };\n }\n\n /**\n * Handle an incoming capability_query message.\n * Searches the local peer store and returns matching peers.\n * \n * @param envelope - The capability_query envelope to process\n * @returns A capability_response envelope with matching peers\n */\n handleQuery(\n envelope: Envelope<CapabilityQueryPayload>\n ): Envelope<CapabilityResponsePayload> {\n const { payload } = envelope;\n let peers: Peer[] = [];\n\n // Execute query based on type\n if (payload.queryType === 'name' && typeof payload.query === 'string') {\n peers = this.peerStore.findByCapability(payload.query);\n } else if (payload.queryType === 'tag' && typeof payload.query === 'string') {\n peers = this.peerStore.findByTag(payload.query);\n } else if (payload.queryType === 'schema') {\n // Schema-based matching is deferred to Phase 2b\n // For now, return empty results\n peers = [];\n }\n\n // Apply filters\n const limit = payload.filters?.limit;\n const totalMatches = peers.length;\n \n if (limit !== undefined && limit > 0) {\n peers = peers.slice(0, limit);\n }\n\n // Transform peers to response format\n const responsePeers = peers.map(peer => ({\n publicKey: peer.publicKey,\n capabilities: peer.capabilities,\n metadata: peer.metadata ? {\n name: peer.metadata.name,\n version: peer.metadata.version,\n lastSeen: peer.lastSeen,\n } : {\n lastSeen: peer.lastSeen,\n },\n // Trust score integration deferred to Phase 2b (RFC-001)\n trustScore: undefined,\n }));\n\n const responsePayload: CapabilityResponsePayload = {\n queryId: envelope.id,\n peers: responsePeers,\n totalMatches,\n };\n\n return createEnvelope(\n 'capability_response',\n this.identity.publicKey,\n this.identity.privateKey,\n responsePayload,\n Date.now(),\n envelope.id // inReplyTo\n );\n }\n\n /**\n * Remove peers that haven't been seen within the specified time window.\n * \n * @param maxAgeMs - Maximum age in milliseconds\n * @returns Number of peers removed\n */\n pruneStale(maxAgeMs: number, currentTime: number = Date.now()): number {\n return this.peerStore.prune(maxAgeMs, currentTime);\n }\n}\n","/**\n * Peer discovery message types for the Agora network.\n */\n\n/**\n * Request peer list from relay\n */\nexport interface PeerListRequestPayload {\n /** Optional filters */\n filters?: {\n /** Only peers seen in last N ms */\n activeWithin?: number;\n /** Maximum peers to return */\n limit?: number;\n };\n}\n\n/**\n * Relay responds with connected peers\n */\nexport interface PeerListResponsePayload {\n /** List of known peers */\n peers: Array<{\n /** Peer's Ed25519 public key */\n publicKey: string;\n /** Optional metadata (if peer announced) */\n metadata?: {\n name?: string;\n version?: string;\n capabilities?: string[];\n };\n /** Last seen timestamp (ms) */\n lastSeen: number;\n }>;\n /** Total peer count (may be > peers.length if limited) */\n totalPeers: number;\n /** Relay's public key (for trust verification) */\n relayPublicKey: string;\n}\n\n/**\n * Agent recommends another agent\n */\nexport interface PeerReferralPayload {\n /** Referred peer's public key */\n publicKey: string;\n /** Optional endpoint (if known) */\n endpoint?: string;\n /** Optional metadata */\n metadata?: {\n name?: string;\n version?: string;\n capabilities?: string[];\n };\n /** Referrer's comment */\n comment?: string;\n /** Trust hint (RFC-001 integration) */\n trustScore?: number;\n}\n\n/**\n * Validate PeerListRequestPayload\n */\nexport function validatePeerListRequest(payload: unknown): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n if (typeof payload !== 'object' || payload === null) {\n errors.push('Payload must be an object');\n return { valid: false, errors };\n }\n\n const p = payload as Record<string, unknown>;\n\n if (p.filters !== undefined) {\n if (typeof p.filters !== 'object' || p.filters === null) {\n errors.push('filters must be an object');\n } else {\n const filters = p.filters as Record<string, unknown>;\n if (filters.activeWithin !== undefined && typeof filters.activeWithin !== 'number') {\n errors.push('filters.activeWithin must be a number');\n }\n if (filters.limit !== undefined && typeof filters.limit !== 'number') {\n errors.push('filters.limit must be a number');\n }\n }\n }\n\n return { valid: errors.length === 0, errors };\n}\n\n/**\n * Validate PeerListResponsePayload\n */\nexport function validatePeerListResponse(payload: unknown): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n if (typeof payload !== 'object' || payload === null) {\n errors.push('Payload must be an object');\n return { valid: false, errors };\n }\n\n const p = payload as Record<string, unknown>;\n\n if (!Array.isArray(p.peers)) {\n errors.push('peers must be an array');\n } else {\n p.peers.forEach((peer, index) => {\n if (typeof peer !== 'object' || peer === null) {\n errors.push(`peers[${index}] must be an object`);\n return;\n }\n const peerObj = peer as Record<string, unknown>;\n if (typeof peerObj.publicKey !== 'string') {\n errors.push(`peers[${index}].publicKey must be a string`);\n }\n if (typeof peerObj.lastSeen !== 'number') {\n errors.push(`peers[${index}].lastSeen must be a number`);\n }\n });\n }\n\n if (typeof p.totalPeers !== 'number') {\n errors.push('totalPeers must be a number');\n }\n\n if (typeof p.relayPublicKey !== 'string') {\n errors.push('relayPublicKey must be a string');\n }\n\n return { valid: errors.length === 0, errors };\n}\n\n/**\n * Validate PeerReferralPayload\n */\nexport function validatePeerReferral(payload: unknown): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n if (typeof payload !== 'object' || payload === null) {\n errors.push('Payload must be an object');\n return { valid: false, errors };\n }\n\n const p = payload as Record<string, unknown>;\n\n if (typeof p.publicKey !== 'string') {\n errors.push('publicKey must be a string');\n }\n\n if (p.endpoint !== undefined && typeof p.endpoint !== 'string') {\n errors.push('endpoint must be a string');\n }\n\n if (p.comment !== undefined && typeof p.comment !== 'string') {\n errors.push('comment must be a string');\n }\n\n if (p.trustScore !== undefined && typeof p.trustScore !== 'number') {\n errors.push('trustScore must be a number');\n }\n\n return { valid: errors.length === 0, errors };\n}\n","import { readFileSync, existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { homedir } from 'node:os';\n\n/**\n * Normalized relay configuration (supports both string and object in config file).\n */\nexport interface RelayConfig {\n url: string;\n autoConnect: boolean;\n name?: string;\n reconnectMaxMs?: number;\n}\n\n/**\n * Peer entry in config (webhook URL, token, public key).\n */\nexport interface AgoraPeerConfig {\n publicKey: string;\n /** Webhook URL (undefined for relay-only peers) */\n url?: string;\n /** Webhook auth token (undefined for relay-only peers) */\n token?: string;\n name?: string;\n}\n\n/**\n * Identity with optional display name (e.g. for relay registration).\n */\nexport interface AgoraIdentity {\n publicKey: string;\n privateKey: string;\n name?: string;\n}\n\n/**\n * Canonical Agora configuration shape.\n * Use loadAgoraConfig() to load from file with normalized relay.\n */\nexport interface AgoraConfig {\n identity: AgoraIdentity;\n peers: Record<string, AgoraPeerConfig>;\n relay?: RelayConfig;\n}\n\n/**\n * Default config file path: AGORA_CONFIG env or ~/.config/agora/config.json\n */\nexport function getDefaultConfigPath(): string {\n if (process.env.AGORA_CONFIG) {\n return resolve(process.env.AGORA_CONFIG);\n }\n return resolve(homedir(), '.config', 'agora', 'config.json');\n}\n\n/**\n * Parse and normalize config from a JSON object (shared by sync and async loaders).\n */\nfunction parseConfig(config: Record<string, unknown>): AgoraConfig {\n const rawIdentity = config.identity as Record<string, unknown> | undefined;\n if (!rawIdentity?.publicKey || !rawIdentity?.privateKey) {\n throw new Error('Invalid config: missing identity.publicKey or identity.privateKey');\n }\n const identity: AgoraIdentity = {\n publicKey: rawIdentity.publicKey as string,\n privateKey: rawIdentity.privateKey as string,\n name: typeof rawIdentity.name === 'string' ? rawIdentity.name : undefined,\n };\n\n const peers: Record<string, AgoraPeerConfig> = {};\n if (config.peers && typeof config.peers === 'object') {\n for (const [name, entry] of Object.entries(config.peers)) {\n const peer = entry as Record<string, unknown>;\n if (peer && typeof peer.publicKey === 'string') {\n peers[name] = {\n publicKey: peer.publicKey as string,\n url: typeof peer.url === 'string' ? peer.url : undefined,\n token: typeof peer.token === 'string' ? peer.token : undefined,\n name: typeof peer.name === 'string' ? peer.name : undefined,\n };\n }\n }\n }\n\n let relay: RelayConfig | undefined;\n const rawRelay = config.relay;\n if (typeof rawRelay === 'string') {\n relay = { url: rawRelay, autoConnect: true };\n } else if (rawRelay && typeof rawRelay === 'object') {\n const r = rawRelay as Record<string, unknown>;\n if (typeof r.url === 'string') {\n relay = {\n url: r.url,\n autoConnect: typeof r.autoConnect === 'boolean' ? r.autoConnect : true,\n name: typeof r.name === 'string' ? r.name : undefined,\n reconnectMaxMs: typeof r.reconnectMaxMs === 'number' ? r.reconnectMaxMs : undefined,\n };\n }\n }\n\n return {\n identity,\n peers,\n ...(relay ? { relay } : {}),\n };\n}\n\n/**\n * Load and normalize Agora configuration from a JSON file (sync).\n * Supports relay as string (backward compat) or object { url?, autoConnect?, name?, reconnectMaxMs? }.\n *\n * @param path - Config file path; defaults to getDefaultConfigPath()\n * @returns Normalized AgoraConfig\n * @throws Error if file doesn't exist or config is invalid\n */\nexport function loadAgoraConfig(path?: string): AgoraConfig {\n const configPath = path ?? getDefaultConfigPath();\n\n if (!existsSync(configPath)) {\n throw new Error(`Config file not found at ${configPath}. Run 'npx @rookdaemon/agora init' first.`);\n }\n\n const content = readFileSync(configPath, 'utf-8');\n let config: Record<string, unknown>;\n try {\n config = JSON.parse(content) as Record<string, unknown>;\n } catch {\n throw new Error(`Invalid JSON in config file: ${configPath}`);\n }\n\n return parseConfig(config);\n}\n\n/**\n * Load and normalize Agora configuration from a JSON file (async).\n *\n * @param path - Config file path; defaults to getDefaultConfigPath()\n * @returns Normalized AgoraConfig\n * @throws Error if file doesn't exist or config is invalid\n */\nexport async function loadAgoraConfigAsync(path?: string): Promise<AgoraConfig> {\n const configPath = path ?? getDefaultConfigPath();\n\n let content: string;\n try {\n content = await readFile(configPath, 'utf-8');\n } catch (err) {\n const code = err && typeof err === 'object' && 'code' in err ? (err as NodeJS.ErrnoException).code : undefined;\n if (code === 'ENOENT') {\n throw new Error(`Config file not found at ${configPath}. Run 'npx @rookdaemon/agora init' first.`);\n }\n throw err;\n }\n\n let config: Record<string, unknown>;\n try {\n config = JSON.parse(content) as Record<string, unknown>;\n } catch {\n throw new Error(`Invalid JSON in config file: ${configPath}`);\n }\n\n return parseConfig(config);\n}\n","import type { AgoraIdentity, RelayConfig } from './config';\nimport { getDefaultConfigPath, loadAgoraConfigAsync } from './config';\nimport type { Envelope } from './message/envelope';\nimport type { MessageType } from './message/envelope';\nimport { RelayClient } from './relay/client';\nimport type { PeerConfig } from './transport/http';\nimport { decodeInboundEnvelope, sendToPeer } from './transport/http';\nimport { sendViaRelay } from './transport/relay';\nimport { shortKey } from './utils';\n\n/**\n * Service config: identity, peers keyed by name, optional relay.\n */\nexport interface AgoraServiceConfig {\n identity: AgoraIdentity;\n peers: Map<string, PeerConfig>;\n relay?: RelayConfig;\n}\n\nexport interface SendMessageOptions {\n peerName: string;\n type: MessageType;\n payload: unknown;\n inReplyTo?: string;\n /** Skip relay, send directly via HTTP only. Fails if peer has no URL or is unreachable. */\n direct?: boolean;\n /** Skip direct HTTP, always use relay even if peer has a URL. */\n relayOnly?: boolean;\n}\n\nexport interface SendMessageResult {\n ok: boolean;\n status: number;\n error?: string;\n}\n\nexport interface ReplyToEnvelopeOptions {\n /** The public key of the target (from envelope.sender) */\n targetPubkey: string;\n /** Message type for the reply */\n type: MessageType;\n /** Reply payload */\n payload: unknown;\n /** The envelope ID being replied to (required — this IS a reply) */\n inReplyTo: string;\n}\n\nexport interface DecodeInboundResult {\n ok: boolean;\n envelope?: Envelope;\n reason?: string;\n}\n\n/** Handler for relay messages. (envelope, fromPublicKey, fromName?) */\nexport type RelayMessageHandlerWithName = (envelope: Envelope, from: string, fromName?: string) => void;\n\n/** @deprecated Use RelayMessageHandlerWithName. Kept for backward compatibility. */\nexport type RelayMessageHandler = (envelope: Envelope) => void;\n\nexport interface Logger {\n debug(message: string): void;\n}\n\nexport interface RelayClientLike {\n connect(): Promise<void>;\n disconnect(): void;\n connected(): boolean;\n send(to: string, envelope: Envelope): Promise<{ ok: boolean; error?: string }>;\n on(event: 'message', handler: (envelope: Envelope, from: string, fromName?: string) => void): void;\n on(event: 'error', handler: (error: Error) => void): void;\n}\n\nexport interface RelayClientFactory {\n (opts: {\n relayUrl: string;\n publicKey: string;\n privateKey: string;\n name?: string;\n pingInterval: number;\n maxReconnectDelay: number;\n }): RelayClientLike;\n}\n\n/**\n * High-level Agora service: send by peer name, decode inbound, relay lifecycle.\n */\nexport class AgoraService {\n private config: AgoraServiceConfig;\n private relayClient: RelayClientLike | null = null;\n private readonly onRelayMessage: RelayMessageHandlerWithName;\n private logger: Logger | null;\n private relayClientFactory: RelayClientFactory | null;\n\n /**\n * @param config - Service config (identity, peers, optional relay)\n * @param onRelayMessage - Required callback for relay messages. Ensures no messages are lost between init and connect.\n * @param logger - Optional debug logger\n * @param relayClientFactory - Optional factory for relay client (for testing)\n */\n constructor(\n config: AgoraServiceConfig,\n onRelayMessage: RelayMessageHandlerWithName,\n logger?: Logger,\n relayClientFactory?: RelayClientFactory\n ) {\n this.config = config;\n this.onRelayMessage = onRelayMessage;\n this.logger = logger ?? null;\n this.relayClientFactory = relayClientFactory ?? null;\n }\n\n /**\n * Send a signed message to a named peer.\n * Tries HTTP webhook first; falls back to relay if HTTP is unavailable.\n */\n async sendMessage(options: SendMessageOptions): Promise<SendMessageResult> {\n const peer = this.config.peers.get(options.peerName);\n if (!peer) {\n return {\n ok: false,\n status: 0,\n error: `Unknown peer: ${options.peerName}`,\n };\n }\n\n // Try HTTP first (only if peer has a webhook URL and --relay-only not set)\n if (peer.url && !options.relayOnly) {\n const transportConfig = {\n identity: {\n publicKey: this.config.identity.publicKey,\n privateKey: this.config.identity.privateKey,\n },\n peers: new Map<string, PeerConfig>([[peer.publicKey, peer]]),\n };\n\n const httpResult = await sendToPeer(\n transportConfig,\n peer.publicKey,\n options.type,\n options.payload,\n options.inReplyTo\n );\n\n if (httpResult.ok) {\n return httpResult;\n }\n\n this.logger?.debug(`HTTP send to ${options.peerName} failed: ${httpResult.error}`);\n\n // --direct flag: do not fall back to relay\n if (options.direct) {\n return {\n ok: false,\n status: httpResult.status,\n error: `Direct send to ${options.peerName} failed: ${httpResult.error}`,\n };\n }\n } else if (options.direct && !peer.url) {\n // --direct requested but peer has no URL configured\n return {\n ok: false,\n status: 0,\n error: `Direct send failed: peer '${options.peerName}' has no URL configured`,\n };\n }\n\n // Fall back to relay\n if (this.relayClient?.connected() && this.config.relay) {\n const relayResult = await sendViaRelay(\n {\n identity: this.config.identity,\n relayUrl: this.config.relay.url,\n relayClient: this.relayClient,\n },\n peer.publicKey,\n options.type,\n options.payload,\n options.inReplyTo\n );\n\n return {\n ok: relayResult.ok,\n status: 0,\n error: relayResult.error,\n };\n }\n\n // Both failed\n return {\n ok: false,\n status: 0,\n error: peer.url\n ? `HTTP send failed and relay not available for peer: ${options.peerName}`\n : `No webhook URL and relay not available for peer: ${options.peerName}`,\n };\n }\n\n /**\n * Reply to an envelope from any sender via relay.\n * Unlike sendMessage(), this does NOT require the target to be a configured peer.\n * Uses the target's public key directly — relay-only (no HTTP, since unknown peers have no URL).\n */\n async replyToEnvelope(options: ReplyToEnvelopeOptions): Promise<SendMessageResult> {\n if (!this.relayClient?.connected() || !this.config.relay) {\n return {\n ok: false,\n status: 0,\n error: 'Relay not connected — cannot reply to envelope without relay',\n };\n }\n\n this.logger?.debug(\n `Replying to envelope via relay: target=${shortKey(options.targetPubkey)} type=${options.type} inReplyTo=${options.inReplyTo}`\n );\n\n const relayResult = await sendViaRelay(\n {\n identity: this.config.identity,\n relayUrl: this.config.relay.url,\n relayClient: this.relayClient,\n },\n options.targetPubkey,\n options.type,\n options.payload,\n options.inReplyTo\n );\n\n return {\n ok: relayResult.ok,\n status: 0,\n error: relayResult.error,\n };\n }\n\n /**\n * Decode and verify an inbound envelope from a webhook message.\n */\n async decodeInbound(message: string): Promise<DecodeInboundResult> {\n const peersByPubKey = new Map<string, PeerConfig>();\n for (const peer of this.config.peers.values()) {\n peersByPubKey.set(peer.publicKey, peer);\n }\n const result = decodeInboundEnvelope(message, peersByPubKey);\n if (result.ok) {\n return { ok: true, envelope: result.envelope };\n }\n return { ok: false, reason: result.reason };\n }\n\n getPeers(): string[] {\n return Array.from(this.config.peers.keys());\n }\n\n getPeerConfig(name: string): PeerConfig | undefined {\n return this.config.peers.get(name);\n }\n\n /**\n * Connect to the relay server.\n */\n async connectRelay(url: string): Promise<void> {\n if (this.relayClient) {\n return;\n }\n\n const maxReconnectDelay = this.config.relay?.reconnectMaxMs ?? 300000;\n let name = this.config.identity.name ?? this.config.relay?.name;\n // Never use the short key (id) as the relay display name; treat it as no name\n if (name && name === shortKey(this.config.identity.publicKey)) {\n name = undefined;\n }\n const opts = {\n relayUrl: url,\n publicKey: this.config.identity.publicKey,\n privateKey: this.config.identity.privateKey,\n name,\n pingInterval: 30000,\n maxReconnectDelay,\n };\n\n if (this.relayClientFactory) {\n this.relayClient = this.relayClientFactory(opts);\n } else {\n this.relayClient = new RelayClient(opts);\n }\n\n this.relayClient.on('error', (error: Error) => {\n this.logger?.debug(`Agora relay error: ${error.message}`);\n });\n\n this.relayClient.on('message', (envelope: Envelope, from: string, fromName?: string) => {\n this.onRelayMessage(envelope, from, fromName);\n });\n\n try {\n await this.relayClient.connect();\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.logger?.debug(`Agora relay connect failed (${url}): ${message}`);\n this.relayClient = null;\n }\n }\n\n async disconnectRelay(): Promise<void> {\n if (this.relayClient) {\n this.relayClient.disconnect();\n this.relayClient = null;\n }\n }\n\n isRelayConnected(): boolean {\n return this.relayClient?.connected() ?? false;\n }\n\n /**\n * Load Agora configuration and return service config (peers as Map).\n */\n static async loadConfig(path?: string): Promise<AgoraServiceConfig> {\n const configPath = path ?? getDefaultConfigPath();\n const loaded = await loadAgoraConfigAsync(configPath);\n\n const peers = new Map<string, PeerConfig>();\n for (const [name, p] of Object.entries(loaded.peers)) {\n peers.set(name, {\n publicKey: p.publicKey,\n url: p.url,\n token: p.token,\n } satisfies PeerConfig);\n }\n\n return {\n identity: loaded.identity,\n peers,\n relay: loaded.relay,\n };\n }\n}\n","/**\n * Network reputation query handler.\n * Handles incoming reputation_query messages and returns reputation_response.\n */\n\nimport type { ReputationQuery, ReputationResponse, TrustScore } from './types.js';\nimport { computeTrustScore, computeTrustScores } from './scoring.js';\nimport type { ReputationStore } from './store.js';\n\n/** Maximum number of verification records to include in a response */\nconst MAX_VERIFICATIONS_IN_RESPONSE = 50;\n\n/**\n * Handle an incoming reputation query by reading from the local store,\n * computing trust scores, and returning a response.\n *\n * @param query - The reputation query (agent, optional domain, optional after timestamp)\n * @param store - Local reputation store to read from\n * @param currentTime - Current timestamp (ms) for score computation\n * @returns Reputation response with computed scores and verification records\n */\nexport async function handleReputationQuery(\n query: ReputationQuery,\n store: ReputationStore,\n currentTime: number\n): Promise<ReputationResponse> {\n // Get all verifications from the store for score computation\n const allVerifications = await store.getVerifications();\n\n // Filter to verifications targeting the queried agent\n let relevantVerifications = allVerifications.filter(v => v.target === query.agent);\n\n // Apply domain filter if specified\n if (query.domain !== undefined) {\n relevantVerifications = relevantVerifications.filter(v => v.domain === query.domain);\n }\n\n // Apply after-timestamp filter if specified\n if (query.after !== undefined) {\n const after = query.after;\n relevantVerifications = relevantVerifications.filter(v => v.timestamp > after);\n }\n\n // Compute trust scores\n let scores: Record<string, TrustScore>;\n\n if (query.domain !== undefined) {\n const score = computeTrustScore(query.agent, query.domain, allVerifications, currentTime);\n scores = { [query.domain]: score };\n } else {\n const scoreMap = computeTrustScores(query.agent, allVerifications, currentTime);\n scores = {};\n for (const [domain, score] of scoreMap.entries()) {\n scores[domain] = score;\n }\n }\n\n // Size-limit verifications to most recent MAX_VERIFICATIONS_IN_RESPONSE\n const limitedVerifications = relevantVerifications\n .slice()\n .sort((a, b) => b.timestamp - a.timestamp)\n .slice(0, MAX_VERIFICATIONS_IN_RESPONSE);\n\n const response: ReputationResponse = {\n agent: query.agent,\n verifications: limitedVerifications,\n scores,\n };\n\n if (query.domain !== undefined) {\n response.domain = query.domain;\n }\n\n return response;\n}\n","/**\n * Cross-peer reputation synchronization.\n * Pull and merge verification records from trusted peers.\n */\n\nimport type { ReputationQuery, ReputationResponse } from './types.js';\nimport { verifyVerificationSignature } from './verification.js';\nimport type { ReputationStore } from './store.js';\n\n/**\n * Pull reputation data from a peer and merge it into the local store.\n *\n * Flow:\n * 1. Send `reputation_query` to peer via sendMessage\n * 2. Receive `reputation_response` with verification records\n * 3. For each record: verify signature, check domain matches, check not duplicate\n * 4. Append new records to local store\n * 5. Return count of added/skipped\n *\n * @param agentPublicKey - Public key of the agent whose reputation to sync\n * @param domain - Domain to sync reputation for\n * @param store - Local reputation store to merge records into\n * @param sendMessage - Function that sends a reputation_query and returns the response\n * @returns Counts of records added and skipped\n */\nexport async function syncReputationFromPeer(\n agentPublicKey: string,\n domain: string,\n store: ReputationStore,\n sendMessage: (type: string, payload: ReputationQuery) => Promise<ReputationResponse>\n): Promise<{ added: number; skipped: number }> {\n // Build and send the query\n const query: ReputationQuery = {\n agent: agentPublicKey,\n domain,\n };\n\n const response = await sendMessage('reputation_query', query);\n\n // Build set of existing record IDs for fast deduplication\n const existing = await store.getVerifications();\n const existingIds = new Set(existing.map(v => v.id));\n\n let added = 0;\n let skipped = 0;\n\n for (const record of response.verifications) {\n // Skip duplicate records (content-addressed by ID)\n if (existingIds.has(record.id)) {\n skipped++;\n continue;\n }\n\n // Verify cryptographic signature before accepting\n const sigResult = verifyVerificationSignature(record);\n if (!sigResult.valid) {\n skipped++;\n continue;\n }\n\n // Ensure the record's domain matches what we requested\n if (record.domain !== domain) {\n skipped++;\n continue;\n }\n\n // Add to local store and track for deduplication within this batch\n await store.addVerification(record);\n existingIds.add(record.id);\n added++;\n }\n\n return { added, skipped };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/identity/keypair.ts","../src/message/envelope.ts","../src/relay/store.ts","../src/relay/server.ts"],"sourcesContent":["import { sign, verify, generateKeyPairSync } from 'node:crypto';\n\n/**\n * Represents an ed25519 key pair for agent identity\n */\nexport interface KeyPair {\n publicKey: string; // hex-encoded\n privateKey: string; // hex-encoded\n}\n\n/**\n * Generates a new ed25519 key pair\n * @returns KeyPair with hex-encoded public and private keys\n */\nexport function generateKeyPair(): KeyPair {\n const { publicKey, privateKey } = generateKeyPairSync('ed25519');\n \n return {\n publicKey: publicKey.export({ type: 'spki', format: 'der' }).toString('hex'),\n privateKey: privateKey.export({ type: 'pkcs8', format: 'der' }).toString('hex'),\n };\n}\n\n/**\n * Signs a message with the private key\n * @param message - The message to sign (string or Buffer)\n * @param privateKeyHex - The private key in hex format\n * @returns Signature as hex string\n */\nexport function signMessage(message: string | Buffer, privateKeyHex: string): string {\n const messageBuffer = typeof message === 'string' ? Buffer.from(message) : message;\n const privateKey = Buffer.from(privateKeyHex, 'hex');\n \n const signature = sign(null, messageBuffer, {\n key: privateKey,\n format: 'der',\n type: 'pkcs8',\n });\n \n return signature.toString('hex');\n}\n\n/**\n * Verifies a signature with the public key\n * @param message - The original message (string or Buffer)\n * @param signatureHex - The signature in hex format\n * @param publicKeyHex - The public key in hex format\n * @returns true if signature is valid, false otherwise\n */\nexport function verifySignature(\n message: string | Buffer,\n signatureHex: string,\n publicKeyHex: string\n): boolean {\n const messageBuffer = typeof message === 'string' ? Buffer.from(message) : message;\n const signature = Buffer.from(signatureHex, 'hex');\n const publicKey = Buffer.from(publicKeyHex, 'hex');\n \n try {\n return verify(null, messageBuffer, {\n key: publicKey,\n format: 'der',\n type: 'spki',\n }, signature);\n } catch {\n return false;\n }\n}\n\n/**\n * Exports a key pair to a JSON-serializable format\n * @param keyPair - The key pair to export\n * @returns KeyPair object with hex-encoded keys\n */\nexport function exportKeyPair(keyPair: KeyPair): KeyPair {\n return {\n publicKey: keyPair.publicKey,\n privateKey: keyPair.privateKey,\n };\n}\n\n/**\n * Imports a key pair from hex strings\n * @param publicKeyHex - The public key in hex format\n * @param privateKeyHex - The private key in hex format\n * @returns KeyPair object\n * @throws Error if keys are not valid hex strings\n */\nexport function importKeyPair(publicKeyHex: string, privateKeyHex: string): KeyPair {\n // Validate that keys are valid hex strings\n const hexPattern = /^[0-9a-f]+$/i;\n if (!hexPattern.test(publicKeyHex)) {\n throw new Error('Invalid public key: must be a hex string');\n }\n if (!hexPattern.test(privateKeyHex)) {\n throw new Error('Invalid private key: must be a hex string');\n }\n \n return {\n publicKey: publicKeyHex,\n privateKey: privateKeyHex,\n };\n}\n","import { createHash } from 'node:crypto';\nimport { signMessage, verifySignature } from '../identity/keypair';\n\n/**\n * Message types on the Agora network.\n * Every piece of data flowing between agents is wrapped in an envelope.\n */\nexport type MessageType =\n | 'announce' // Agent publishes capabilities/state\n | 'discover' // Agent requests peer discovery\n | 'request' // Agent requests a service\n | 'response' // Agent responds to a request\n | 'publish' // Agent publishes knowledge/state\n | 'subscribe' // Agent subscribes to a topic/domain\n | 'verify' // Agent verifies another agent's claim\n | 'ack' // Acknowledgement\n | 'error' // Error response\n | 'paper_discovery' // Agent publishes a discovered academic paper\n | 'peer_list_request' // Request peer list from relay\n | 'peer_list_response' // Relay responds with connected peers\n | 'peer_referral' // Agent recommends another agent\n | 'capability_announce' // Agent publishes capabilities to network\n | 'capability_query' // Agent queries for capabilities\n | 'capability_response' // Response with matching peers\n | 'commit' // Agent commits to a prediction (commit-reveal pattern)\n | 'reveal' // Agent reveals prediction and outcome\n | 'verification' // Agent verifies another agent's output\n | 'revocation' // Agent revokes a prior verification\n | 'reputation_query' // Agent queries for reputation data\n | 'reputation_response'; // Response to reputation query\n\n/**\n * The signed envelope that wraps every message on the network.\n * Content-addressed: the ID is the hash of the canonical payload.\n * Signed: every envelope carries a signature from the sender's private key.\n */\nexport interface Envelope<T = unknown> {\n /** Content-addressed ID: SHA-256 hash of canonical payload */\n id: string;\n /** Message type */\n type: MessageType;\n /** Sender's public key (hex-encoded ed25519) */\n sender: string;\n /** Unix timestamp (ms) when the message was created */\n timestamp: number;\n /** Optional: ID of the message this is responding to */\n inReplyTo?: string;\n /** The actual payload */\n payload: T;\n /** ed25519 signature over the canonical form (hex-encoded) */\n signature: string;\n}\n\n/**\n * Deterministic JSON serialization with recursively sorted keys.\n */\nfunction stableStringify(value: unknown): string {\n if (value === null || value === undefined) return JSON.stringify(value);\n if (typeof value !== 'object') return JSON.stringify(value);\n if (Array.isArray(value)) {\n return '[' + value.map(stableStringify).join(',') + ']';\n }\n const keys = Object.keys(value as Record<string, unknown>).sort();\n const pairs = keys.map(k => JSON.stringify(k) + ':' + stableStringify((value as Record<string, unknown>)[k]));\n return '{' + pairs.join(',') + '}';\n}\n\n/**\n * Canonical form of an envelope for signing/hashing.\n * Deterministic JSON serialization: recursively sorted keys, no whitespace.\n */\nexport function canonicalize(type: MessageType, sender: string, timestamp: number, payload: unknown, inReplyTo?: string): string {\n const obj: Record<string, unknown> = { payload, sender, timestamp, type };\n if (inReplyTo !== undefined) {\n obj.inReplyTo = inReplyTo;\n }\n return stableStringify(obj);\n}\n\n/**\n * Compute the content-addressed ID for a message.\n */\nexport function computeId(canonical: string): string {\n return createHash('sha256').update(canonical).digest('hex');\n}\n\n/**\n * Create a signed envelope.\n * @param type - Message type\n * @param sender - Sender's public key (hex)\n * @param privateKey - Sender's private key (hex) for signing\n * @param payload - The message payload\n * @param timestamp - Timestamp for the envelope (ms), defaults to Date.now()\n * @param inReplyTo - Optional ID of the message being replied to\n * @returns A signed Envelope\n */\nexport function createEnvelope<T>(\n type: MessageType,\n sender: string,\n privateKey: string,\n payload: T,\n timestamp: number = Date.now(),\n inReplyTo?: string,\n): Envelope<T> {\n const canonical = canonicalize(type, sender, timestamp, payload, inReplyTo);\n const id = computeId(canonical);\n const signature = signMessage(canonical, privateKey);\n\n return {\n id,\n type,\n sender,\n timestamp,\n ...(inReplyTo !== undefined ? { inReplyTo } : {}),\n payload,\n signature,\n };\n}\n\n/**\n * Verify an envelope's integrity and authenticity.\n * Checks:\n * 1. Canonical form matches the ID (content-addressing)\n * 2. Signature is valid for the sender's public key\n * \n * @returns Object with `valid` boolean and optional `reason` for failure\n */\nexport function verifyEnvelope(envelope: Envelope): { valid: boolean; reason?: string } {\n const { id, type, sender, timestamp, payload, signature, inReplyTo } = envelope;\n\n // Reconstruct canonical form\n const canonical = canonicalize(type, sender, timestamp, payload, inReplyTo);\n\n // Check content-addressed ID\n const expectedId = computeId(canonical);\n if (id !== expectedId) {\n return { valid: false, reason: 'id_mismatch' };\n }\n\n // Check signature\n const sigValid = verifySignature(canonical, signature, sender);\n if (!sigValid) {\n return { valid: false, reason: 'signature_invalid' };\n }\n\n return { valid: true };\n}\n","/**\n * store.ts — File-based message store for offline peers.\n * When the relay has storage enabled for certain public keys, messages\n * for offline recipients are persisted and delivered when they connect.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface StoredMessage {\n from: string;\n name?: string;\n envelope: object;\n}\n\nexport class MessageStore {\n private storageDir: string;\n\n constructor(storageDir: string) {\n this.storageDir = storageDir;\n fs.mkdirSync(storageDir, { recursive: true });\n }\n\n private recipientDir(publicKey: string): string {\n const safe = publicKey.replace(/[^a-zA-Z0-9_-]/g, '_');\n return path.join(this.storageDir, safe);\n }\n\n save(recipientKey: string, message: StoredMessage): void {\n const dir = this.recipientDir(recipientKey);\n fs.mkdirSync(dir, { recursive: true });\n const filename = `${Date.now()}-${crypto.randomUUID()}.json`;\n fs.writeFileSync(path.join(dir, filename), JSON.stringify(message));\n }\n\n load(recipientKey: string): StoredMessage[] {\n const dir = this.recipientDir(recipientKey);\n if (!fs.existsSync(dir)) return [];\n const files = fs.readdirSync(dir).sort();\n const messages: StoredMessage[] = [];\n for (const file of files) {\n if (!file.endsWith('.json')) continue;\n try {\n const data = fs.readFileSync(path.join(dir, file), 'utf8');\n messages.push(JSON.parse(data) as StoredMessage);\n } catch {\n // Skip files that cannot be read or parsed\n }\n }\n return messages;\n }\n\n clear(recipientKey: string): void {\n const dir = this.recipientDir(recipientKey);\n if (!fs.existsSync(dir)) return;\n const files = fs.readdirSync(dir);\n for (const file of files) {\n if (file.endsWith('.json')) {\n fs.unlinkSync(path.join(dir, file));\n }\n }\n }\n}\n","import { EventEmitter } from 'node:events';\nimport { WebSocketServer, WebSocket } from 'ws';\nimport { verifyEnvelope, createEnvelope, type Envelope } from '../message/envelope';\nimport type { PeerListRequestPayload, PeerListResponsePayload } from '../message/types/peer-discovery';\nimport { MessageStore } from './store';\n\n/**\n * Represents a connected agent in the relay\n */\ninterface ConnectedAgent {\n /** Agent's public key */\n publicKey: string;\n /** Optional agent name */\n name?: string;\n /** WebSocket connection */\n socket: WebSocket;\n /** Last seen timestamp (ms) */\n lastSeen: number;\n /** Optional metadata */\n metadata?: {\n version?: string;\n capabilities?: string[];\n };\n}\n\n/**\n * Events emitted by RelayServer\n */\nexport interface RelayServerEvents {\n 'agent-registered': (publicKey: string) => void;\n 'agent-disconnected': (publicKey: string) => void;\n /** Emitted when a session disconnects (same as agent-disconnected for compatibility) */\n 'disconnection': (publicKey: string) => void;\n 'message-relayed': (from: string, to: string, envelope: Envelope) => void;\n 'error': (error: Error) => void;\n}\n\n/**\n * WebSocket relay server for routing messages between agents.\n * \n * Agents connect to the relay and register with their public key.\n * Messages are routed to recipients based on the 'to' field.\n * All envelopes are verified before being forwarded.\n */\nexport interface RelayServerOptions {\n /** Optional relay identity for peer_list_request handling */\n identity?: { publicKey: string; privateKey: string };\n /** Public keys that should have messages stored when offline */\n storagePeers?: string[];\n /** Directory for persisting messages for storage peers */\n storageDir?: string;\n /** Maximum number of concurrent registered peers (default: 100) */\n maxPeers?: number;\n}\n\nexport class RelayServer extends EventEmitter {\n private wss: WebSocketServer | null = null;\n /** publicKey -> sessionId -> ConnectedAgent (multiple sessions per key) */\n private sessions = new Map<string, Map<string, ConnectedAgent>>();\n private identity?: { publicKey: string; privateKey: string };\n private storagePeers: string[] = [];\n private store: MessageStore | null = null;\n private maxPeers: number = 100;\n\n constructor(options?: { publicKey: string; privateKey: string } | RelayServerOptions) {\n super();\n if (options) {\n if ('identity' in options && options.identity) {\n this.identity = options.identity;\n } else if ('publicKey' in options && 'privateKey' in options) {\n this.identity = { publicKey: options.publicKey, privateKey: options.privateKey };\n }\n const opts = options as RelayServerOptions;\n if (opts.storagePeers?.length && opts.storageDir) {\n this.storagePeers = opts.storagePeers;\n this.store = new MessageStore(opts.storageDir);\n }\n if (opts.maxPeers !== undefined) {\n this.maxPeers = opts.maxPeers;\n }\n }\n }\n\n /**\n * Start the relay server\n * @param port - Port to listen on\n * @param host - Optional host (default: all interfaces)\n */\n start(port: number, host?: string): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.wss = new WebSocketServer({ port, host: host ?? '0.0.0.0' });\n let resolved = false;\n\n this.wss.on('error', (error) => {\n this.emit('error', error);\n if (!resolved) {\n resolved = true;\n reject(error);\n }\n });\n\n this.wss.on('listening', () => {\n if (!resolved) {\n resolved = true;\n resolve();\n }\n });\n\n this.wss.on('connection', (socket: WebSocket) => {\n this.handleConnection(socket);\n });\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Stop the relay server\n */\n async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.wss) {\n resolve();\n return;\n }\n\n // Close all agent connections (all sessions)\n for (const sessionMap of this.sessions.values()) {\n for (const agent of sessionMap.values()) {\n agent.socket.close();\n }\n }\n this.sessions.clear();\n\n this.wss.close((err) => {\n if (err) {\n reject(err);\n } else {\n this.wss = null;\n resolve();\n }\n });\n });\n }\n\n /**\n * Get one connected agent per public key (first session). For backward compatibility.\n */\n getAgents(): Map<string, ConnectedAgent> {\n const out = new Map<string, ConnectedAgent>();\n for (const [key, sessionMap] of this.sessions) {\n const first = sessionMap.values().next().value;\n if (first) out.set(key, first);\n }\n return out;\n }\n\n /**\n * Handle incoming connection\n */\n private handleConnection(socket: WebSocket): void {\n let agentPublicKey: string | null = null;\n let sessionId: string | null = null;\n\n socket.on('message', (data: Buffer) => {\n try {\n const msg = JSON.parse(data.toString());\n\n // Handle registration\n if (msg.type === 'register' && !agentPublicKey) {\n if (!msg.publicKey || typeof msg.publicKey !== 'string') {\n this.sendError(socket, 'Invalid registration: missing or invalid publicKey');\n socket.close();\n return;\n }\n\n const publicKey = msg.publicKey;\n const name = msg.name;\n agentPublicKey = publicKey;\n sessionId = crypto.randomUUID();\n\n // Allow multiple sessions per publicKey; only enforce max unique peers\n if (!this.sessions.has(publicKey) && this.sessions.size >= this.maxPeers) {\n this.sendError(socket, `Relay is at capacity (max ${this.maxPeers} peers)`);\n socket.close();\n return;\n }\n\n const agent: ConnectedAgent = {\n publicKey,\n name,\n socket,\n lastSeen: Date.now(),\n };\n\n if (!this.sessions.has(publicKey)) {\n this.sessions.set(publicKey, new Map());\n }\n this.sessions.get(publicKey)!.set(sessionId, agent);\n const isFirstSession = this.sessions.get(publicKey)!.size === 1;\n\n this.emit('agent-registered', publicKey);\n\n // Build peers list: one entry per connected publicKey + storage peers\n const peers: Array<{ publicKey: string; name?: string }> = [];\n for (const [key, sessionMap] of this.sessions) {\n if (key === publicKey) continue;\n const firstAgent = sessionMap.values().next().value;\n peers.push({ publicKey: key, name: firstAgent?.name });\n }\n for (const storagePeer of this.storagePeers) {\n if (storagePeer !== publicKey && !this.sessions.has(storagePeer)) {\n peers.push({ publicKey: storagePeer, name: undefined });\n }\n }\n\n socket.send(JSON.stringify({\n type: 'registered',\n publicKey,\n sessionId,\n peers,\n }));\n\n // Notify other agents only when this is the first session for this peer\n if (isFirstSession) {\n this.broadcastPeerEvent('peer_online', publicKey, name);\n }\n\n // Deliver any stored messages for this peer\n if (this.store && this.storagePeers.includes(publicKey)) {\n const queued = this.store.load(publicKey);\n for (const stored of queued) {\n socket.send(JSON.stringify({\n type: 'message',\n from: stored.from,\n name: stored.name,\n envelope: stored.envelope,\n }));\n }\n this.store.clear(publicKey);\n }\n return;\n }\n\n // Require registration before processing messages\n if (!agentPublicKey) {\n this.sendError(socket, 'Not registered: send registration message first');\n socket.close();\n return;\n }\n\n // Handle message relay\n if (msg.type === 'message') {\n if (!msg.to || typeof msg.to !== 'string') {\n this.sendError(socket, 'Invalid message: missing or invalid \"to\" field');\n return;\n }\n\n if (!msg.envelope || typeof msg.envelope !== 'object') {\n this.sendError(socket, 'Invalid message: missing or invalid \"envelope\" field');\n return;\n }\n\n const envelope = msg.envelope as Envelope;\n\n // Verify envelope signature\n const verification = verifyEnvelope(envelope);\n if (!verification.valid) {\n this.sendError(socket, `Invalid envelope: ${verification.reason || 'verification failed'}`);\n return;\n }\n\n // Verify sender matches registered agent\n if (envelope.sender !== agentPublicKey) {\n this.sendError(socket, 'Envelope sender does not match registered public key');\n return;\n }\n\n // Update lastSeen for any session of sender\n const senderSessionMap = this.sessions.get(agentPublicKey);\n if (senderSessionMap) {\n for (const a of senderSessionMap.values()) {\n a.lastSeen = Date.now();\n }\n }\n\n // Handle peer_list_request directed at relay\n if (envelope.type === 'peer_list_request' && this.identity && msg.to === this.identity.publicKey) {\n this.handlePeerListRequest(envelope as Envelope<PeerListRequestPayload>, socket, agentPublicKey);\n return;\n }\n\n // Find all recipient sessions\n const recipientSessionMap = this.sessions.get(msg.to);\n const openRecipients = recipientSessionMap\n ? Array.from(recipientSessionMap.values()).filter(a => a.socket.readyState === WebSocket.OPEN)\n : [];\n if (openRecipients.length === 0) {\n // If recipient is a storage peer, queue the message\n if (this.store && this.storagePeers.includes(msg.to)) {\n const senderSessionMap = this.sessions.get(agentPublicKey);\n const senderAgent = senderSessionMap?.values().next().value;\n this.store.save(msg.to, {\n from: agentPublicKey,\n name: senderAgent?.name,\n envelope,\n });\n this.emit('message-relayed', agentPublicKey, msg.to, envelope);\n } else {\n this.sendError(socket, 'Recipient not connected', 'unknown_recipient');\n }\n return;\n }\n\n // Forward envelope to all sessions of the recipient\n try {\n const senderSessionMap = this.sessions.get(agentPublicKey);\n const senderAgent = senderSessionMap?.values().next().value;\n const relayMessage = {\n type: 'message',\n from: agentPublicKey,\n name: senderAgent?.name,\n envelope,\n };\n const messageStr = JSON.stringify(relayMessage);\n for (const recipient of openRecipients) {\n recipient.socket.send(messageStr);\n }\n this.emit('message-relayed', agentPublicKey, msg.to, envelope);\n } catch (err) {\n this.sendError(socket, 'Failed to relay message');\n this.emit('error', err as Error);\n }\n return;\n }\n\n // Handle broadcast: same validation as message, then forward to all other agents\n if (msg.type === 'broadcast') {\n if (!msg.envelope || typeof msg.envelope !== 'object') {\n this.sendError(socket, 'Invalid broadcast: missing or invalid \"envelope\" field');\n return;\n }\n\n const envelope = msg.envelope as Envelope;\n\n const verification = verifyEnvelope(envelope);\n if (!verification.valid) {\n this.sendError(socket, `Invalid envelope: ${verification.reason || 'verification failed'}`);\n return;\n }\n\n if (envelope.sender !== agentPublicKey) {\n this.sendError(socket, 'Envelope sender does not match registered public key');\n return;\n }\n\n // Update lastSeen for any session of sender\n const senderSessionMap = this.sessions.get(agentPublicKey);\n if (senderSessionMap) {\n for (const a of senderSessionMap.values()) {\n a.lastSeen = Date.now();\n }\n }\n\n const senderSessionMapForName = this.sessions.get(agentPublicKey);\n const senderAgent = senderSessionMapForName?.values().next().value;\n const relayMessage = {\n type: 'message' as const,\n from: agentPublicKey,\n name: senderAgent?.name,\n envelope,\n };\n const messageStr = JSON.stringify(relayMessage);\n\n for (const [key, sessionMap] of this.sessions) {\n if (key === agentPublicKey) continue;\n for (const agent of sessionMap.values()) {\n if (agent.socket.readyState === WebSocket.OPEN) {\n try {\n agent.socket.send(messageStr);\n } catch (err) {\n this.emit('error', err as Error);\n }\n }\n }\n }\n return;\n }\n\n // Handle ping\n if (msg.type === 'ping') {\n socket.send(JSON.stringify({ type: 'pong' }));\n return;\n }\n\n // Unknown message type\n this.sendError(socket, `Unknown message type: ${msg.type}`);\n } catch (err) {\n // Invalid JSON or other parsing errors\n this.emit('error', new Error(`Message parsing failed: ${err instanceof Error ? err.message : String(err)}`));\n this.sendError(socket, 'Invalid message format');\n }\n });\n\n socket.on('close', () => {\n if (agentPublicKey && sessionId) {\n const sessionMap = this.sessions.get(agentPublicKey);\n if (sessionMap) {\n const agent = sessionMap.get(sessionId);\n const agentName = agent?.name;\n sessionMap.delete(sessionId);\n if (sessionMap.size === 0) {\n this.sessions.delete(agentPublicKey);\n this.emit('agent-disconnected', agentPublicKey);\n this.emit('disconnection', agentPublicKey);\n // Storage-enabled peers are always considered connected; skip peer_offline for them\n if (!this.storagePeers.includes(agentPublicKey)) {\n this.broadcastPeerEvent('peer_offline', agentPublicKey, agentName);\n }\n }\n }\n }\n });\n\n socket.on('error', (error) => {\n this.emit('error', error);\n });\n }\n\n /**\n * Send an error message to a client\n */\n private sendError(socket: WebSocket, message: string, code?: string): void {\n try {\n if (socket.readyState === WebSocket.OPEN) {\n const payload: { type: 'error'; message: string; code?: string } = { type: 'error', message };\n if (code) payload.code = code;\n socket.send(JSON.stringify(payload));\n }\n } catch (err) {\n // Log errors when sending error messages, but don't propagate to avoid cascading failures\n this.emit('error', new Error(`Failed to send error message: ${err instanceof Error ? err.message : String(err)}`));\n }\n }\n\n /**\n * Broadcast a peer event to all connected agents (all sessions except the one for publicKey)\n */\n private broadcastPeerEvent(eventType: 'peer_online' | 'peer_offline', publicKey: string, name?: string): void {\n const message = {\n type: eventType,\n publicKey,\n name,\n };\n const messageStr = JSON.stringify(message);\n\n for (const [key, sessionMap] of this.sessions) {\n if (key === publicKey) continue;\n for (const agent of sessionMap.values()) {\n if (agent.socket.readyState === WebSocket.OPEN) {\n try {\n agent.socket.send(messageStr);\n } catch (err) {\n this.emit('error', new Error(`Failed to send ${eventType} event: ${err instanceof Error ? err.message : String(err)}`));\n }\n }\n }\n }\n }\n\n /**\n * Handle peer list request from an agent\n */\n private handlePeerListRequest(envelope: Envelope<PeerListRequestPayload>, socket: WebSocket, requesterPublicKey: string): void {\n if (!this.identity) {\n this.sendError(socket, 'Relay does not support peer discovery (no identity configured)');\n return;\n }\n\n const { filters } = envelope.payload;\n const now = Date.now();\n\n // One entry per publicKey (first session for lastSeen/metadata)\n const peersList: ConnectedAgent[] = [];\n for (const [key, sessionMap] of this.sessions) {\n if (key === requesterPublicKey) continue;\n const first = sessionMap.values().next().value;\n if (first) peersList.push(first);\n }\n\n let peers = peersList;\n\n // Apply filters\n if (filters?.activeWithin) {\n peers = peers.filter(p => (now - p.lastSeen) < filters.activeWithin!);\n }\n\n if (filters?.limit && filters.limit > 0) {\n peers = peers.slice(0, filters.limit);\n }\n\n // Build response payload\n const response: PeerListResponsePayload = {\n peers: peers.map(p => ({\n publicKey: p.publicKey,\n metadata: p.name || p.metadata ? {\n name: p.name,\n version: p.metadata?.version,\n capabilities: p.metadata?.capabilities,\n } : undefined,\n lastSeen: p.lastSeen,\n })),\n totalPeers: this.sessions.size - (this.sessions.has(requesterPublicKey) ? 1 : 0),\n relayPublicKey: this.identity.publicKey,\n };\n\n // Create signed envelope\n const responseEnvelope = createEnvelope(\n 'peer_list_response',\n this.identity.publicKey,\n this.identity.privateKey,\n response,\n Date.now(),\n envelope.id // Reply to the request\n );\n\n // Send response\n const relayMessage = {\n type: 'message',\n from: this.identity.publicKey,\n name: 'relay',\n envelope: responseEnvelope,\n };\n\n try {\n socket.send(JSON.stringify(relayMessage));\n } catch (err) {\n this.emit('error', new Error(`Failed to send peer list response: ${err instanceof Error ? err.message : String(err)}`));\n }\n }\n}\n"],"mappings":"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","names":["senderSessionMap"]}
|
|
File without changes
|
|
File without changes
|