@highway1/core 0.1.53 → 0.1.55

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.
Files changed (39) hide show
  1. package/package.json +5 -18
  2. package/src/discovery/agent-card-encoder.ts +0 -119
  3. package/src/discovery/agent-card-schema.ts +0 -87
  4. package/src/discovery/agent-card-types.ts +0 -99
  5. package/src/discovery/agent-card.ts +0 -190
  6. package/src/discovery/bootstrap.ts +0 -63
  7. package/src/discovery/capability-matcher.ts +0 -167
  8. package/src/discovery/dht.ts +0 -310
  9. package/src/discovery/index.ts +0 -3
  10. package/src/discovery/relay-index.ts +0 -98
  11. package/src/discovery/search-index.ts +0 -247
  12. package/src/discovery/semantic-search.ts +0 -218
  13. package/src/identity/did.ts +0 -48
  14. package/src/identity/document.ts +0 -77
  15. package/src/identity/index.ts +0 -4
  16. package/src/identity/keys.ts +0 -79
  17. package/src/identity/signer.ts +0 -55
  18. package/src/index.ts +0 -39
  19. package/src/messaging/codec.ts +0 -47
  20. package/src/messaging/defense.ts +0 -236
  21. package/src/messaging/envelope.ts +0 -107
  22. package/src/messaging/index.ts +0 -8
  23. package/src/messaging/queue.ts +0 -181
  24. package/src/messaging/rate-limiter.ts +0 -85
  25. package/src/messaging/router.ts +0 -209
  26. package/src/messaging/storage.ts +0 -281
  27. package/src/messaging/types.ts +0 -149
  28. package/src/transport/connection.ts +0 -77
  29. package/src/transport/index.ts +0 -2
  30. package/src/transport/node.ts +0 -154
  31. package/src/transport/relay-client.ts +0 -437
  32. package/src/transport/relay-types.ts +0 -196
  33. package/src/trust/endorsement.ts +0 -167
  34. package/src/trust/index.ts +0 -194
  35. package/src/trust/interaction-history.ts +0 -155
  36. package/src/trust/sybil-defense.ts +0 -232
  37. package/src/trust/trust-score.ts +0 -136
  38. package/src/utils/errors.ts +0 -38
  39. package/src/utils/logger.ts +0 -48
@@ -1,437 +0,0 @@
1
- /**
2
- * CVP-0011: WebSocket relay client with reconnection logic
3
- */
4
-
5
- import { WebSocket } from 'ws';
6
- import { encode as encodeCBOR, decode as decodeCBOR } from 'cbor-x';
7
- import { createLogger } from '../utils/logger.js';
8
- import { TransportError } from '../utils/errors.js';
9
- import { sign } from '../identity/keys.js';
10
- import type { KeyPair } from '../identity/keys.js';
11
- import type { AgentCard } from '../discovery/agent-card-types.js';
12
- import type {
13
- RelayMessage,
14
- HelloMessage,
15
- WelcomeMessage,
16
- DeliverMessage,
17
- DiscoveredAgent,
18
- DeliveryReportMessage,
19
- } from './relay-types.js';
20
-
21
- const logger = createLogger('relay-client');
22
-
23
- export interface RelayClientConfig {
24
- relayUrls: string[];
25
- did: string;
26
- keyPair: KeyPair;
27
- card: AgentCard;
28
- reconnect?: {
29
- baseMs?: number;
30
- jitterMs?: number;
31
- maxDelayMs?: number;
32
- stableAfterMs?: number;
33
- };
34
- }
35
-
36
- export type MessageDeliveryHandler = (msg: DeliverMessage) => Promise<void>;
37
- export type DeliveryReportHandler = (msg: DeliveryReportMessage) => void;
38
-
39
- export interface RelayClient {
40
- start(): Promise<void>;
41
- stop(): Promise<void>;
42
- sendEnvelope(toDid: string, envelopeBytes: Uint8Array): Promise<void>;
43
- discover(query: string, minTrust?: number, limit?: number): Promise<DiscoveredAgent[]>;
44
- fetchCard(did: string): Promise<AgentCard | null>;
45
- onDeliver(handler: MessageDeliveryHandler): void;
46
- onDeliveryReport(handler: DeliveryReportHandler): void;
47
- isConnected(): boolean;
48
- getConnectedRelays(): string[];
49
- getPeerCount(): number;
50
- }
51
-
52
- interface RelayConnection {
53
- url: string;
54
- ws: WebSocket | null;
55
- connected: boolean;
56
- reconnectAttempt: number;
57
- reconnectTimer: NodeJS.Timeout | null;
58
- stableTimer: NodeJS.Timeout | null;
59
- pingTimer: NodeJS.Timeout | null; // CVP-0011 §2.5: PING every 30s
60
- peerCount: number;
61
- }
62
-
63
- const DEFAULT_RECONNECT = {
64
- baseMs: 1000,
65
- jitterMs: 2000,
66
- maxDelayMs: 30000,
67
- stableAfterMs: 60000,
68
- };
69
-
70
- export function createRelayClient(config: RelayClientConfig): RelayClient {
71
- const { relayUrls, did, keyPair, card } = config;
72
- const reconnectConfig = { ...DEFAULT_RECONNECT, ...config.reconnect };
73
-
74
- const connections: RelayConnection[] = relayUrls.map((url) => ({
75
- url,
76
- ws: null,
77
- connected: false,
78
- reconnectAttempt: 0,
79
- reconnectTimer: null,
80
- stableTimer: null,
81
- pingTimer: null,
82
- peerCount: 0,
83
- }));
84
-
85
- let deliveryHandler: MessageDeliveryHandler | null = null;
86
- let deliveryReportHandler: DeliveryReportHandler | null = null;
87
- let stopped = false;
88
-
89
- async function connectToRelay(conn: RelayConnection): Promise<void> {
90
- if (stopped) return;
91
-
92
- try {
93
- logger.info('Connecting to relay', { url: conn.url });
94
-
95
- const ws = new WebSocket(conn.url);
96
- conn.ws = ws;
97
-
98
- await new Promise<void>((resolve, reject) => {
99
- const timeout = setTimeout(() => {
100
- reject(new Error('Connection timeout'));
101
- }, 10000);
102
-
103
- ws.on('open', async () => {
104
- clearTimeout(timeout);
105
- logger.info('WebSocket connected', { url: conn.url });
106
-
107
- try {
108
- // Send HELLO message (CBOR-encoded)
109
- const timestamp = Date.now();
110
- const helloData = encodeCBOR({ did, card, timestamp });
111
- const signature = await sign(helloData, keyPair.privateKey);
112
-
113
- const hello: HelloMessage = {
114
- type: 'HELLO',
115
- protocolVersion: 1,
116
- did,
117
- card,
118
- timestamp,
119
- signature,
120
- };
121
-
122
- ws.send(encodeCBOR(hello));
123
- resolve();
124
- } catch (err) {
125
- reject(err);
126
- }
127
- });
128
-
129
- ws.on('error', (err) => {
130
- clearTimeout(timeout);
131
- reject(err);
132
- });
133
- });
134
-
135
- // Set up message handler (CBOR decoding)
136
- ws.on('message', async (data: Buffer) => {
137
- try {
138
- const msg: RelayMessage = decodeCBOR(data);
139
- await handleRelayMessage(conn, msg);
140
- } catch (err) {
141
- logger.warn('Failed to parse relay message', { error: (err as Error).message });
142
- }
143
- });
144
-
145
- ws.on('close', () => {
146
- logger.info('WebSocket closed', { url: conn.url });
147
- conn.connected = false;
148
- conn.ws = null;
149
-
150
- // Clear timers
151
- if (conn.stableTimer) {
152
- clearTimeout(conn.stableTimer);
153
- conn.stableTimer = null;
154
- }
155
- if (conn.pingTimer) {
156
- clearInterval(conn.pingTimer);
157
- conn.pingTimer = null;
158
- }
159
-
160
- if (!stopped) {
161
- scheduleReconnect(conn);
162
- }
163
- });
164
-
165
- ws.on('error', (err) => {
166
- logger.warn('WebSocket error', { url: conn.url, error: err.message });
167
- });
168
- } catch (err) {
169
- logger.warn('Failed to connect to relay', { url: conn.url, error: (err as Error).message });
170
- conn.ws = null;
171
- conn.connected = false;
172
-
173
- if (!stopped) {
174
- scheduleReconnect(conn);
175
- }
176
- }
177
- }
178
-
179
- function scheduleReconnect(conn: RelayConnection): void {
180
- if (conn.reconnectTimer) return;
181
-
182
- const delay = Math.min(
183
- reconnectConfig.baseMs * Math.pow(2, conn.reconnectAttempt) + Math.random() * reconnectConfig.jitterMs,
184
- reconnectConfig.maxDelayMs
185
- );
186
-
187
- conn.reconnectAttempt++;
188
- logger.debug('Scheduling reconnect', { url: conn.url, attempt: conn.reconnectAttempt, delayMs: delay });
189
-
190
- conn.reconnectTimer = setTimeout(() => {
191
- conn.reconnectTimer = null;
192
- connectToRelay(conn);
193
- }, delay);
194
- }
195
-
196
- async function handleRelayMessage(conn: RelayConnection, msg: RelayMessage): Promise<void> {
197
- switch (msg.type) {
198
- case 'WELCOME': {
199
- const welcome = msg as WelcomeMessage;
200
- logger.info('Received WELCOME', { relayId: welcome.relayId, peers: welcome.peers });
201
- conn.connected = true;
202
- conn.peerCount = welcome.peers;
203
- conn.reconnectAttempt = 0;
204
-
205
- // Start stable timer
206
- if (conn.stableTimer) clearTimeout(conn.stableTimer);
207
- conn.stableTimer = setTimeout(() => {
208
- conn.reconnectAttempt = 0;
209
- logger.debug('Connection stable', { url: conn.url });
210
- }, reconnectConfig.stableAfterMs);
211
-
212
- // CVP-0011 §2.5: Start PING heartbeat (every 30s)
213
- if (conn.pingTimer) clearInterval(conn.pingTimer);
214
- conn.pingTimer = setInterval(() => {
215
- if (conn.ws && conn.connected) {
216
- conn.ws.send(encodeCBOR({ type: 'PING' }));
217
- logger.debug('Sent PING', { url: conn.url });
218
- }
219
- }, 30000); // 30 seconds
220
-
221
- break;
222
- }
223
-
224
- case 'DELIVER': {
225
- const deliver = msg as DeliverMessage;
226
- logger.info('Received DELIVER', { messageId: deliver.messageId, from: deliver.from });
227
- if (deliveryHandler) {
228
- await deliveryHandler(deliver);
229
- }
230
- // CVP-0011 §2.7: Send ACK after processing message
231
- if (conn.ws && conn.connected) {
232
- conn.ws.send(encodeCBOR({ type: 'ACK', messageId: deliver.messageId }));
233
- logger.debug('Sent ACK', { messageId: deliver.messageId });
234
- }
235
- break;
236
- }
237
-
238
- case 'DELIVERY_REPORT': {
239
- const report = msg as DeliveryReportMessage;
240
- logger.info('Received DELIVERY_REPORT', { messageId: report.messageId, status: report.status });
241
- if (deliveryReportHandler) {
242
- deliveryReportHandler(report);
243
- }
244
- break;
245
- }
246
-
247
- case 'PONG': {
248
- conn.peerCount = msg.peers;
249
- logger.debug('Received PONG', { peers: msg.peers });
250
- break;
251
- }
252
-
253
- case 'GOODBYE': {
254
- // CVP-0011 §2.6: Handle graceful shutdown with reconnectAfter
255
- const goodbye = msg as any;
256
- const reconnectAfter = goodbye.reconnectAfter || 5000;
257
- logger.info('Received GOODBYE', { url: conn.url, reconnectAfter });
258
-
259
- // Close connection and schedule reconnection
260
- if (conn.ws) {
261
- conn.ws.close();
262
- }
263
-
264
- // Override reconnect delay with server's suggestion
265
- setTimeout(() => {
266
- if (!stopped) {
267
- connectToRelay(conn);
268
- }
269
- }, reconnectAfter);
270
- break;
271
- }
272
-
273
- default:
274
- logger.debug('Received relay message', { type: msg.type });
275
- }
276
- }
277
-
278
- function getConnectedConnection(): RelayConnection | null {
279
- return connections.find((c) => c.connected && c.ws) || null;
280
- }
281
-
282
- async function sendToRelay(msg: RelayMessage): Promise<void> {
283
- const conn = getConnectedConnection();
284
- if (!conn || !conn.ws) {
285
- throw new TransportError('No connected relay');
286
- }
287
-
288
- conn.ws.send(encodeCBOR(msg));
289
- }
290
-
291
- return {
292
- async start(): Promise<void> {
293
- stopped = false;
294
- logger.info('Starting relay client', { relays: relayUrls.length });
295
-
296
- // Stagger initial connections
297
- for (let i = 0; i < connections.length; i++) {
298
- const conn = connections[i];
299
- setTimeout(() => {
300
- connectToRelay(conn);
301
- }, Math.random() * 2000);
302
- }
303
-
304
- // Wait for at least one connection
305
- const maxWait = 15000;
306
- const start = Date.now();
307
- while (Date.now() - start < maxWait) {
308
- if (getConnectedConnection()) {
309
- logger.info('Relay client started');
310
- return;
311
- }
312
- await new Promise((resolve) => setTimeout(resolve, 100));
313
- }
314
-
315
- throw new TransportError('Failed to connect to any relay');
316
- },
317
-
318
- async stop(): Promise<void> {
319
- stopped = true;
320
- logger.info('Stopping relay client');
321
-
322
- for (const conn of connections) {
323
- if (conn.reconnectTimer) {
324
- clearTimeout(conn.reconnectTimer);
325
- conn.reconnectTimer = null;
326
- }
327
- if (conn.stableTimer) {
328
- clearTimeout(conn.stableTimer);
329
- conn.stableTimer = null;
330
- }
331
- if (conn.pingTimer) {
332
- clearInterval(conn.pingTimer);
333
- conn.pingTimer = null;
334
- }
335
- if (conn.ws) {
336
- conn.ws.close();
337
- conn.ws = null;
338
- }
339
- conn.connected = false;
340
- }
341
-
342
- logger.info('Relay client stopped');
343
- },
344
-
345
- async sendEnvelope(toDid: string, envelopeBytes: Uint8Array): Promise<void> {
346
- const msg: RelayMessage = {
347
- type: 'SEND',
348
- to: toDid,
349
- envelope: envelopeBytes,
350
- };
351
-
352
- await sendToRelay(msg);
353
- logger.debug('Sent envelope', { to: toDid, size: envelopeBytes.length });
354
- },
355
-
356
- async discover(query: string, minTrust?: number, limit?: number): Promise<DiscoveredAgent[]> {
357
- const conn = getConnectedConnection();
358
- const ws = conn?.ws;
359
- if (!conn || !ws) {
360
- throw new TransportError('No connected relay');
361
- }
362
-
363
- return new Promise((resolve, reject) => {
364
- const timeout = setTimeout(() => {
365
- reject(new TransportError('Discover timeout'));
366
- }, 10000);
367
-
368
- const handler = (data: Buffer) => {
369
- try {
370
- const msg: RelayMessage = decodeCBOR(data);
371
- if (msg.type === 'DISCOVERED') {
372
- clearTimeout(timeout);
373
- ws.off('message', handler);
374
- resolve(msg.agents);
375
- }
376
- } catch (err) {
377
- // Ignore parse errors
378
- }
379
- };
380
-
381
- ws.on('message', handler);
382
- ws.send(encodeCBOR({ type: 'DISCOVER', query, minTrust, limit } as RelayMessage));
383
- });
384
- },
385
-
386
- async fetchCard(did: string): Promise<AgentCard | null> {
387
- const conn = getConnectedConnection();
388
- const ws = conn?.ws;
389
- if (!conn || !ws) {
390
- throw new TransportError('No connected relay');
391
- }
392
-
393
- return new Promise((resolve, reject) => {
394
- const timeout = setTimeout(() => {
395
- reject(new TransportError('Fetch card timeout'));
396
- }, 5000);
397
-
398
- const handler = (data: Buffer) => {
399
- try {
400
- const msg: RelayMessage = decodeCBOR(data);
401
- if (msg.type === 'CARD' && msg.did === did) {
402
- clearTimeout(timeout);
403
- ws.off('message', handler);
404
- resolve(msg.card);
405
- }
406
- } catch (err) {
407
- // Ignore parse errors
408
- }
409
- };
410
-
411
- ws.on('message', handler);
412
- ws.send(encodeCBOR({ type: 'FETCH_CARD', did } as RelayMessage));
413
- });
414
- },
415
-
416
- onDeliver(handler: MessageDeliveryHandler): void {
417
- deliveryHandler = handler;
418
- },
419
-
420
- onDeliveryReport(handler: DeliveryReportHandler): void {
421
- deliveryReportHandler = handler;
422
- },
423
-
424
- isConnected(): boolean {
425
- return getConnectedConnection() !== null;
426
- },
427
-
428
- getConnectedRelays(): string[] {
429
- return connections.filter((c) => c.connected).map((c) => c.url);
430
- },
431
-
432
- getPeerCount(): number {
433
- const conn = getConnectedConnection();
434
- return conn ? conn.peerCount : 0;
435
- },
436
- };
437
- }
@@ -1,196 +0,0 @@
1
- /**
2
- * CVP-0011: Relay wire protocol types (shared between relay server and relay client)
3
- */
4
-
5
- import type { AgentCard } from '../discovery/agent-card-types.js';
6
-
7
- export const RELAY_PROTOCOL_VERSION = 1;
8
-
9
- export type RelayMessageType =
10
- | 'HELLO'
11
- | 'WELCOME'
12
- | 'SEND'
13
- | 'DELIVER'
14
- | 'DISCOVER'
15
- | 'DISCOVERED'
16
- | 'PING'
17
- | 'PONG'
18
- | 'ACK'
19
- | 'DELIVERY_REPORT'
20
- | 'FETCH_CARD'
21
- | 'CARD'
22
- | 'GOODBYE'
23
- | 'INDEX_SYNC'
24
- | 'ROUTE'
25
- | 'SYNC_HELLO'
26
- | 'SYNC_REQUEST'
27
- | 'SYNC_RESPONSE'
28
- | 'SYNC_PUSH'
29
- | 'SYNC_RECONCILE';
30
-
31
- export interface HelloMessage {
32
- type: 'HELLO';
33
- protocolVersion: number;
34
- did: string;
35
- card: AgentCard;
36
- timestamp: number;
37
- signature: Uint8Array;
38
- extensions?: string[];
39
- }
40
-
41
- export interface WelcomeMessage {
42
- type: 'WELCOME';
43
- protocolVersion: number;
44
- relayId: string;
45
- peers: number;
46
- federatedRelays: string[];
47
- yourAddr: string;
48
- }
49
-
50
- export interface SendMessage {
51
- type: 'SEND';
52
- to: string;
53
- envelope: Uint8Array;
54
- }
55
-
56
- export interface DeliverMessage {
57
- type: 'DELIVER';
58
- messageId: string;
59
- from: string;
60
- envelope: Uint8Array;
61
- }
62
-
63
- export interface DiscoverMessage {
64
- type: 'DISCOVER';
65
- query: string;
66
- minTrust?: number;
67
- limit?: number;
68
- }
69
-
70
- export interface DiscoveredAgent {
71
- did: string;
72
- card: AgentCard;
73
- online: boolean;
74
- homeRelay?: string;
75
- }
76
-
77
- export interface DiscoveredMessage {
78
- type: 'DISCOVERED';
79
- agents: DiscoveredAgent[];
80
- }
81
-
82
- export interface AckMessage {
83
- type: 'ACK';
84
- messageId: string;
85
- }
86
-
87
- export interface DeliveryReportMessage {
88
- type: 'DELIVERY_REPORT';
89
- messageId: string;
90
- status: 'delivered' | 'expired' | 'queue_full' | 'unknown_recipient';
91
- timestamp: number;
92
- }
93
-
94
- export interface PingMessage {
95
- type: 'PING';
96
- }
97
-
98
- export interface PongMessage {
99
- type: 'PONG';
100
- peers: number;
101
- }
102
-
103
- export interface FetchCardMessage {
104
- type: 'FETCH_CARD';
105
- did: string;
106
- }
107
-
108
- export interface CardMessage {
109
- type: 'CARD';
110
- did: string;
111
- card: AgentCard | null;
112
- }
113
-
114
- export interface GoodbyeMessage {
115
- type: 'GOODBYE';
116
- reconnectAfter?: number; // CVP-0011 §2.6: Milliseconds to wait before reconnecting
117
- }
118
-
119
- export interface PresenceProof {
120
- did: string;
121
- homeRelay: string;
122
- expiry: number;
123
- signature: Uint8Array;
124
- }
125
-
126
- export interface SyncEvent {
127
- seq: number;
128
- type: 'JOIN' | 'LEAVE' | 'UPDATE';
129
- did: string;
130
- homeRelay: string;
131
- cardHash?: string;
132
- capabilityKeys?: string[];
133
- online: boolean;
134
- ts: number;
135
- presenceProof?: PresenceProof;
136
- }
137
-
138
- export interface IndexSyncMessage {
139
- type: 'INDEX_SYNC';
140
- events: SyncEvent[];
141
- }
142
-
143
- export interface RouteMessage {
144
- type: 'ROUTE';
145
- to: string;
146
- envelope: Uint8Array;
147
- ttl: number;
148
- }
149
-
150
- export interface SyncHelloMessage {
151
- type: 'SYNC_HELLO';
152
- relayId: string;
153
- seq: number;
154
- }
155
-
156
- export interface SyncRequestMessage {
157
- type: 'SYNC_REQUEST';
158
- fromSeq: number;
159
- }
160
-
161
- export interface SyncResponseMessage {
162
- type: 'SYNC_RESPONSE';
163
- events: SyncEvent[];
164
- }
165
-
166
- export interface SyncPushMessage {
167
- type: 'SYNC_PUSH';
168
- event: SyncEvent;
169
- }
170
-
171
- export interface SyncReconcileMessage {
172
- type: 'SYNC_RECONCILE';
173
- dids: string[];
174
- }
175
-
176
- export type RelayMessage =
177
- | HelloMessage
178
- | WelcomeMessage
179
- | SendMessage
180
- | DeliverMessage
181
- | DiscoverMessage
182
- | DiscoveredMessage
183
- | AckMessage
184
- | DeliveryReportMessage
185
- | PingMessage
186
- | PongMessage
187
- | FetchCardMessage
188
- | CardMessage
189
- | GoodbyeMessage
190
- | IndexSyncMessage
191
- | RouteMessage
192
- | SyncHelloMessage
193
- | SyncRequestMessage
194
- | SyncResponseMessage
195
- | SyncPushMessage
196
- | SyncReconcileMessage;