@cryptforge/key-exchange 0.1.0

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/server.js ADDED
@@ -0,0 +1,940 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/server.ts
31
+ var server_exports = {};
32
+ __export(server_exports, {
33
+ KeyTransportServer: () => KeyTransportServer,
34
+ useKeyExchangeServer: () => useKeyExchangeServer
35
+ });
36
+ module.exports = __toCommonJS(server_exports);
37
+
38
+ // src/server/useKeyExchangeServer.ts
39
+ var import_hyperdht = __toESM(require("hyperdht"));
40
+ var import_b4a2 = __toESM(require("b4a"));
41
+
42
+ // src/server/messages.ts
43
+ var import_b4a = __toESM(require("b4a"));
44
+
45
+ // src/types/messages.ts
46
+ var MESSAGES = {
47
+ enableBroadcast: "admin:broadcast:enable",
48
+ connect: "client:topic:connect",
49
+ onClientConnection: "admin:topic:connected",
50
+ requestKeystore: "client:keystore:request",
51
+ onKeystoreRequestInvalidPIN: "admin:pin:invalid",
52
+ onKeystoreRequestValidPIN: "admin:pin:valid",
53
+ onKeystoreRequest: "admin:request:received",
54
+ approveRequest: "admin:request:approve",
55
+ onClientApproval: "client:request:approved",
56
+ denyRequest: "admin:request:deny",
57
+ onClientDenial: "client:request:denied",
58
+ onCompletion: "server:link:complete",
59
+ disableBroadcast: "admin:broadcast:disable",
60
+ onBroadcastDisable: "client:broadcast:disabled",
61
+ getDeviceInfo: "client:request:deviceInfo",
62
+ getHostname: "client:request:hostname",
63
+ // Presence/Network messages
64
+ connectPresence: "network:connect",
65
+ broadcastClientState: "network:broadcast",
66
+ onClientStateRequest: "network:request",
67
+ onClientStateUpdate: "network:update"
68
+ };
69
+
70
+ // src/server/messages.ts
71
+ var syncSetupEventRoutes = {
72
+ onClientConnection: MESSAGES.onClientConnection,
73
+ onKeystoreRequest: MESSAGES.onKeystoreRequest,
74
+ onClientApproval: MESSAGES.onClientApproval,
75
+ onClientDenial: MESSAGES.onClientDenial,
76
+ onCompletion: MESSAGES.onCompletion,
77
+ onBroadcastDisable: MESSAGES.onBroadcastDisable,
78
+ onKeystoreRequestInvalidPIN: MESSAGES.onKeystoreRequestInvalidPIN,
79
+ onKeystoreRequestValidPIN: MESSAGES.onKeystoreRequestValidPIN
80
+ };
81
+ var requestHeaders = [
82
+ {
83
+ code: 1e3,
84
+ name: "Request PIN",
85
+ description: "The Client is asking the Server to show UI of a PIN screen for the user to enter on the Client"
86
+ },
87
+ {
88
+ code: 2e3,
89
+ name: "Request Keystore",
90
+ description: "The Client is asking the Server to show UI for the user to approve or reject the request"
91
+ }
92
+ ];
93
+ var sendRequest = async (options) => {
94
+ const index = requestHeaders.findIndex(
95
+ (response) => response.name === options.header
96
+ );
97
+ if (index !== -1) {
98
+ const header = requestHeaders[index];
99
+ const body = options.body();
100
+ const request = {
101
+ header,
102
+ body
103
+ };
104
+ const payload = JSON.stringify(request);
105
+ for (const socket of options.to) {
106
+ console.log(
107
+ `hyperswarm: sending ${header.name} to connection ${import_b4a.default.toString(
108
+ socket.remotePublicKey,
109
+ "hex"
110
+ )}`
111
+ );
112
+ socket.write(payload);
113
+ }
114
+ } else {
115
+ console.log(`Request with name \`${options.header}\` not found`);
116
+ }
117
+ };
118
+ var responseHeaders = [
119
+ {
120
+ code: 1501,
121
+ name: "Connection Failure: Broadcast disabled",
122
+ type: "Failure"
123
+ },
124
+ { code: 2200, name: "Authentication Succeeded", type: "Success" },
125
+ { code: 2400, name: "Authentication Error: Invalid PIN", type: "Error" },
126
+ { code: 3200, name: "Transmition Succeeded: Approved", type: "Success" },
127
+ { code: 3201, name: "Transmition Succeeded: Rejected", type: "Success" },
128
+ { code: 4200, name: "Setup Succeeded", type: "Success" }
129
+ ];
130
+ var sendResponse = (options) => {
131
+ const index = responseHeaders.findIndex(
132
+ (response) => response.name === options.header
133
+ );
134
+ if (index !== -1) {
135
+ const header = responseHeaders[index];
136
+ const data = options.body();
137
+ const response = {
138
+ header,
139
+ body: data
140
+ };
141
+ const payload = JSON.stringify(response);
142
+ for (const socket of options.to) {
143
+ console.log(
144
+ `hyperswarm: sending ${header.name} to connection ${import_b4a.default.toString(
145
+ socket.remotePublicKey,
146
+ "hex"
147
+ )}`
148
+ );
149
+ socket.write(payload);
150
+ }
151
+ } else {
152
+ console.log(`Response with name \`${options.header}\` not found`);
153
+ }
154
+ };
155
+
156
+ // src/server/useKeyExchangeServer.ts
157
+ function useKeyExchangeServer(onEvent) {
158
+ let client;
159
+ let dht;
160
+ const keyPair = import_hyperdht.default.keyPair();
161
+ const getKey = () => {
162
+ return import_b4a2.default.toString(keyPair.publicKey, "hex");
163
+ };
164
+ const generateRandomPIN = () => {
165
+ return Math.floor(1e5 + Math.random() * 9e5);
166
+ };
167
+ const pin = generateRandomPIN();
168
+ const getPIN = () => {
169
+ return `${pin}`;
170
+ };
171
+ let server;
172
+ const createServer = async () => {
173
+ dht = new import_hyperdht.default();
174
+ server = await dht.createServer((socket) => {
175
+ const name = import_b4a2.default.toString(socket.remotePublicKey, "hex");
176
+ console.log(`server: got a connection on \`${name}\``);
177
+ socket.on("data", (data) => {
178
+ parseRequest(data);
179
+ });
180
+ socket.on("error", (error) => {
181
+ onConnectionError(error);
182
+ });
183
+ client = socket;
184
+ });
185
+ };
186
+ const parseRequest = (data) => {
187
+ console.log(`server: got data: ${data}`);
188
+ const parsedData = JSON.parse(data);
189
+ const payload = parsedData;
190
+ console.log("payload");
191
+ console.log(payload);
192
+ switch (payload.header.name) {
193
+ case "Request PIN":
194
+ onRequestPin(payload.body);
195
+ break;
196
+ case "Request Keystore":
197
+ onRequestKeystore(payload.body);
198
+ break;
199
+ }
200
+ };
201
+ const onConnectionError = (error) => {
202
+ console.log(`server: Connection error: ${error}`);
203
+ };
204
+ const onRequestPin = (body) => {
205
+ const route = syncSetupEventRoutes["onClientConnection"];
206
+ onEvent({
207
+ route,
208
+ data: {
209
+ pin: getPIN(),
210
+ name: body.deviceName,
211
+ app: body.appName
212
+ }
213
+ });
214
+ };
215
+ const onRequestKeystore = (body) => {
216
+ const pin2 = body.pin;
217
+ if (pin2 !== getPIN()) {
218
+ sendResponse({
219
+ to: [client],
220
+ header: "Authentication Error: Invalid PIN",
221
+ body: () => {
222
+ return {};
223
+ }
224
+ });
225
+ return;
226
+ } else {
227
+ sendResponse({
228
+ to: [client],
229
+ header: "Authentication Succeeded",
230
+ body: () => {
231
+ return {};
232
+ }
233
+ });
234
+ }
235
+ const route = syncSetupEventRoutes["onKeystoreRequest"];
236
+ onEvent({
237
+ route,
238
+ data: {}
239
+ });
240
+ };
241
+ const onRequestDenied = () => {
242
+ const route = syncSetupEventRoutes["onClientDenial"];
243
+ onEvent({ route, data: {} });
244
+ };
245
+ const onSetupComplete = () => {
246
+ const route = syncSetupEventRoutes["onCompletion"];
247
+ onEvent({ route, data: {} });
248
+ };
249
+ const beginBroadcast = async () => {
250
+ await createServer();
251
+ await server.listen(keyPair);
252
+ await server.refresh();
253
+ console.log(`server: opened connection to ${getKey()}`);
254
+ };
255
+ const endBroadcast = async () => {
256
+ if (server) {
257
+ await server.close();
258
+ console.log(`server: closed connection to ${getKey()}`);
259
+ }
260
+ if (dht) {
261
+ await dht.destroy();
262
+ console.log(`server: destroyed dht`);
263
+ }
264
+ };
265
+ const approveRequest = async (identity, appId) => {
266
+ sendResponse({
267
+ to: [client],
268
+ header: "Transmition Succeeded: Approved",
269
+ body: () => {
270
+ return {
271
+ identity,
272
+ appId
273
+ };
274
+ }
275
+ });
276
+ };
277
+ const completeSetupSuccess = async () => {
278
+ console.log("sending compltion message to client");
279
+ sendResponse({
280
+ to: [client],
281
+ header: "Setup Succeeded",
282
+ body: () => {
283
+ return {};
284
+ }
285
+ });
286
+ onSetupComplete();
287
+ };
288
+ const rejectRequest = async () => {
289
+ sendResponse({
290
+ to: [client],
291
+ header: "Transmition Succeeded: Rejected",
292
+ body: () => {
293
+ return {};
294
+ }
295
+ });
296
+ onRequestDenied();
297
+ };
298
+ const broadcastDisabled = async () => {
299
+ if (client) {
300
+ sendResponse({
301
+ to: [client],
302
+ header: "Connection Failure: Broadcast disabled",
303
+ body: () => {
304
+ return {};
305
+ }
306
+ });
307
+ }
308
+ };
309
+ return {
310
+ getKey,
311
+ getPIN,
312
+ beginBroadcast,
313
+ endBroadcast,
314
+ approveRequest,
315
+ completeSetupSuccess,
316
+ rejectRequest,
317
+ broadcastDisabled
318
+ };
319
+ }
320
+
321
+ // src/server/keyTransportServer.ts
322
+ var import_ws = __toESM(require("ws"));
323
+
324
+ // src/useNetworkPresenceServer.ts
325
+ var import_hyperswarm = __toESM(require("hyperswarm"));
326
+ var import_b4a3 = __toESM(require("b4a"));
327
+ function useNetworkPresence(onUpdate, onRequest) {
328
+ const deviceIDs = /* @__PURE__ */ new Map();
329
+ const swarm = new import_hyperswarm.default();
330
+ swarm.on("connection", (connection) => {
331
+ const name = import_b4a3.default.toString(connection.remotePublicKey, "hex");
332
+ console.log("Presence: * got a connection from:", name, "*");
333
+ connections.push(connection);
334
+ sendStateRequestMessage(connection);
335
+ connection.once("close", () => {
336
+ console.log(
337
+ `Presence: Connection closed. Removing connection ${import_b4a3.default.toString(
338
+ connection.remotePublicKey,
339
+ "hex"
340
+ )}`
341
+ );
342
+ connections.splice(connections.indexOf(connection), 1);
343
+ const id = deviceIDs.get(import_b4a3.default.toString(connection.remotePublicKey, "hex"));
344
+ if (id) {
345
+ broadcastStatusMessage(id, "unknown");
346
+ }
347
+ deviceIDs.delete(import_b4a3.default.toString(connection.remotePublicKey, "hex"));
348
+ });
349
+ connection.on("data", (data) => {
350
+ let parsedData;
351
+ try {
352
+ parsedData = JSON.parse(data);
353
+ } catch (error) {
354
+ console.error("Error parsing data:", error);
355
+ return;
356
+ }
357
+ const header = parsedData;
358
+ console.log(
359
+ "Presence: got data for ",
360
+ connection.publicKey.toString("hex")
361
+ );
362
+ if (header.type === "update") {
363
+ const payload = parsedData;
364
+ onUpdate({ id: payload.id, state: payload.state });
365
+ deviceIDs.set(
366
+ import_b4a3.default.toString(connection.remotePublicKey, "hex"),
367
+ payload.id
368
+ );
369
+ } else if (header.type === "request") {
370
+ onRequest();
371
+ }
372
+ });
373
+ connection.on("error", (error) => {
374
+ console.error(
375
+ `Presence: Connection error on connection ${import_b4a3.default.toString(
376
+ connection.remotePublicKey,
377
+ "hex"
378
+ )}: ${error}`
379
+ );
380
+ });
381
+ });
382
+ const connections = [];
383
+ const connect = async (topic, id) => {
384
+ console.log("Presence: connecting to topic: ", topic);
385
+ const key = import_b4a3.default.from(topic, "hex");
386
+ console.log("key: ", key);
387
+ const discovery = swarm.join(key, { client: true, server: true });
388
+ console.log("got discovery: ");
389
+ const publicKey = import_b4a3.default.toString(
390
+ discovery.swarm.keyPair?.publicKey || discovery.swarm.publicKey || Buffer.alloc(32),
391
+ "hex"
392
+ );
393
+ console.log("Presence: public key is ", publicKey);
394
+ deviceIDs.set(publicKey, id);
395
+ discovery.flushed().then(() => {
396
+ console.log("Presence: joined topic:", import_b4a3.default.toString(key, "hex"));
397
+ });
398
+ };
399
+ const sendStateRequestMessage = async (connection) => {
400
+ const message = {
401
+ type: "request"
402
+ };
403
+ const payload = JSON.stringify(message);
404
+ console.log(
405
+ "sending state request message to: ",
406
+ connection.publicKey.toString("hex")
407
+ );
408
+ connection.write(payload);
409
+ };
410
+ const receiveStatusMessage = async (id, state) => {
411
+ return broadcastStatusMessage(id, state);
412
+ };
413
+ const broadcastStatusMessage = (id, state) => {
414
+ console.log(`server got connection state message for ${id}: `, state);
415
+ const message = {
416
+ type: "update",
417
+ id,
418
+ state
419
+ };
420
+ console.log("active connections: ", connections.length);
421
+ const payload = JSON.stringify(message);
422
+ for (const connection of connections) {
423
+ console.log("sending message to: ", connection.publicKey.toString("hex"));
424
+ connection.write(payload);
425
+ }
426
+ onUpdate(message);
427
+ };
428
+ return {
429
+ connect,
430
+ receiveStatusMessage
431
+ };
432
+ }
433
+
434
+ // src/client/useKeyExchangeClient.ts
435
+ var import_hyperdht2 = __toESM(require("hyperdht"));
436
+ var import_b4a4 = __toESM(require("b4a"));
437
+ function useKeyExchangeClient(onEvent) {
438
+ let socket;
439
+ let deviceName;
440
+ let appName;
441
+ const connect = async (options) => {
442
+ deviceName = options.name;
443
+ appName = options.app;
444
+ return new Promise((resolve) => {
445
+ console.log(`client: connecting to server on ${options.key}`);
446
+ const publicKey = import_b4a4.default.from(options.key, "hex");
447
+ const dht = new import_hyperdht2.default();
448
+ socket = dht.connect(publicKey);
449
+ socket.once("open", () => {
450
+ const name = import_b4a4.default.toString(socket.remotePublicKey, "hex");
451
+ console.log(`client: got a connection on ${name}`);
452
+ resolve();
453
+ });
454
+ socket.on("data", (data) => {
455
+ parseResponse(data);
456
+ });
457
+ socket.on("error", (error) => {
458
+ onConnectionError(error);
459
+ });
460
+ });
461
+ };
462
+ const parseResponse = async (data) => {
463
+ console.log(`client: got data: ${data}`);
464
+ const parsedData = JSON.parse(data);
465
+ const payload = parsedData;
466
+ console.log("payload");
467
+ console.log(payload);
468
+ switch (payload.header.name) {
469
+ case "Connection Failure: Broadcast disabled":
470
+ onEvent({
471
+ route: syncSetupEventRoutes["onBroadcastDisable"],
472
+ data: {}
473
+ });
474
+ break;
475
+ case "Transmition Succeeded: Approved":
476
+ const identity = payload.body.identity;
477
+ const appId = payload.body.appId;
478
+ onEvent({
479
+ route: syncSetupEventRoutes["onClientApproval"],
480
+ data: { identity, appId }
481
+ });
482
+ break;
483
+ case "Transmition Succeeded: Rejected":
484
+ onEvent({ route: syncSetupEventRoutes["onClientDenial"], data: {} });
485
+ break;
486
+ case "Setup Succeeded":
487
+ onEvent({ route: syncSetupEventRoutes["onCompletion"], data: {} });
488
+ break;
489
+ case "Authentication Error: Invalid PIN":
490
+ onEvent({
491
+ route: syncSetupEventRoutes["onKeystoreRequestInvalidPIN"],
492
+ data: {
493
+ reason: "Invalid PIN Entered"
494
+ }
495
+ });
496
+ break;
497
+ case "Authentication Succeeded":
498
+ onEvent({
499
+ route: syncSetupEventRoutes["onKeystoreRequestValidPIN"],
500
+ data: {}
501
+ });
502
+ break;
503
+ default:
504
+ console.error(`error: response ${payload} not handled in client`);
505
+ break;
506
+ }
507
+ };
508
+ const onConnectionError = async (error) => {
509
+ console.log(`client: Connection error: ${error}`);
510
+ const route = syncSetupEventRoutes["onBroadcastDisable"];
511
+ switch (error.code) {
512
+ case "PEER_NOT_FOUND":
513
+ onEvent({
514
+ route,
515
+ data: {
516
+ title: "Peer Not Found",
517
+ description: "Another device with the specified broadcast key could not be located."
518
+ }
519
+ });
520
+ break;
521
+ default:
522
+ onEvent({
523
+ route,
524
+ data: {
525
+ title: "Client Disconnected",
526
+ description: "The connection to your other device was lost. Please ensure you have another device that is broadcasting"
527
+ }
528
+ });
529
+ break;
530
+ }
531
+ };
532
+ const requestPIN = async () => {
533
+ await sendRequest({
534
+ to: [socket],
535
+ header: "Request PIN",
536
+ body: () => {
537
+ return { deviceName, appName };
538
+ }
539
+ });
540
+ };
541
+ const requestKeystore = async (pin) => {
542
+ await sendRequest({
543
+ to: [socket],
544
+ header: "Request Keystore",
545
+ body: () => {
546
+ return {
547
+ pin
548
+ };
549
+ }
550
+ });
551
+ const route = syncSetupEventRoutes["onKeystoreRequest"];
552
+ onEvent({ route, data: {} });
553
+ };
554
+ return {
555
+ connect,
556
+ requestPIN,
557
+ requestKeystore
558
+ };
559
+ }
560
+
561
+ // src/CoreTransportLogic.ts
562
+ var import_crypto = __toESM(require("crypto"));
563
+ var import_os = __toESM(require("os"));
564
+ var import_systeminformation = __toESM(require("systeminformation"));
565
+ var CoreTransportLogic = class {
566
+ syncServer;
567
+ syncClient;
568
+ presence;
569
+ deviceInfo = null;
570
+ handleEvent;
571
+ presenceConnected = false;
572
+ constructor(handleEvent = () => {
573
+ }) {
574
+ this.handleEvent = handleEvent;
575
+ this.syncServer = useKeyExchangeServer(this.handleEvent);
576
+ this.syncClient = useKeyExchangeClient(this.handleEvent);
577
+ this.presence = useNetworkPresence(
578
+ this.onStateUpdate.bind(this),
579
+ this.onStateRequest.bind(this)
580
+ );
581
+ this.initializeDeviceInfo();
582
+ }
583
+ async initializeDeviceInfo() {
584
+ this.deviceInfo = await this.getDeviceInfo();
585
+ }
586
+ onStateUpdate(response) {
587
+ console.log("received state message for:", response.id);
588
+ this.handleEvent({ route: MESSAGES.onClientStateUpdate, data: response });
589
+ }
590
+ onStateRequest() {
591
+ console.log("received state request");
592
+ this.handleEvent({ route: MESSAGES.onClientStateRequest, data: {} });
593
+ }
594
+ delay(ms) {
595
+ return new Promise((resolve) => setTimeout(resolve, ms));
596
+ }
597
+ // Sync methods
598
+ async enableBroadcast() {
599
+ console.log(`admin: enabled broadcast`);
600
+ try {
601
+ await this.syncServer.beginBroadcast();
602
+ return this.syncServer.getKey();
603
+ } catch (error) {
604
+ const code = error && typeof error === "object" && "code" in error ? error.code : "UNKNOWN";
605
+ console.error("Error on broadcast enable during topic sync setup:", code);
606
+ if (code === "ALREADY_LISTENING") {
607
+ return this.syncServer.getKey();
608
+ }
609
+ throw error;
610
+ }
611
+ }
612
+ async connect(topic, name, app) {
613
+ console.log(`client: requested connection to ${topic}`);
614
+ try {
615
+ await this.syncClient.connect({ key: topic, name, app });
616
+ await this.syncClient.requestPIN();
617
+ } catch (error) {
618
+ console.error("Error on topic connect during sync setup:", error);
619
+ throw error;
620
+ }
621
+ }
622
+ async requestKeystore(pin) {
623
+ console.log(`client: requested keystore using pin \`${pin}\``);
624
+ try {
625
+ await this.syncClient.requestKeystore(pin);
626
+ } catch (error) {
627
+ console.error("Error on keystore request during sync setup:", error);
628
+ throw error;
629
+ }
630
+ }
631
+ async approveRequest(data) {
632
+ console.log(`admin: request approved`);
633
+ try {
634
+ await this.syncServer.approveRequest(data.identity, data.appId);
635
+ await this.delay(3e3);
636
+ await this.syncServer.completeSetupSuccess();
637
+ } catch (error) {
638
+ console.error("Error on request approve during sync setup:", error);
639
+ throw error;
640
+ }
641
+ }
642
+ async denyRequest() {
643
+ console.log(`admin: request denied`);
644
+ try {
645
+ await this.syncServer.rejectRequest();
646
+ } catch (error) {
647
+ console.error("Error on request deny during sync setup:", error);
648
+ throw error;
649
+ }
650
+ }
651
+ async disableBroadcast() {
652
+ try {
653
+ await this.syncServer.endBroadcast();
654
+ await this.syncServer.broadcastDisabled();
655
+ } catch (error) {
656
+ console.error("Error on broadcast disable during sync setup:", error);
657
+ throw error;
658
+ }
659
+ }
660
+ // Device methods
661
+ async getDeviceInfo() {
662
+ if (this.deviceInfo) {
663
+ return this.deviceInfo;
664
+ }
665
+ const hostname = import_os.default.hostname();
666
+ const osInfo = await import_systeminformation.default.osInfo();
667
+ const uuid = await import_systeminformation.default.uuid();
668
+ const serial = osInfo.serial;
669
+ const distro = osInfo.distro;
670
+ const arch = osInfo.arch;
671
+ const hardwareUUID = uuid.hardware;
672
+ const key = serial + distro + arch + hardwareUUID;
673
+ const hash = import_crypto.default.createHash("sha256").update(key).digest("hex");
674
+ return { id: hash, name: hostname };
675
+ }
676
+ async getHostname() {
677
+ return import_os.default.hostname();
678
+ }
679
+ // Network methods
680
+ async connectPresence(topic) {
681
+ if (this.presenceConnected) {
682
+ console.log("Presence already connected, skipping");
683
+ return;
684
+ }
685
+ const deviceInfo = await this.getDeviceInfo();
686
+ const topicHash = import_crypto.default.createHash("sha256").update(topic).digest("hex");
687
+ console.log(`Connecting presence network to topic: ${topicHash}`);
688
+ await this.presence.connect(topicHash, deviceInfo.id);
689
+ this.presenceConnected = true;
690
+ }
691
+ broadcastClientState(id, state) {
692
+ this.presence.receiveStatusMessage(id, state);
693
+ }
694
+ };
695
+
696
+ // src/server/keyTransportServer.ts
697
+ var KeyTransportServer = class {
698
+ wss;
699
+ activeConnections = /* @__PURE__ */ new Set();
700
+ core;
701
+ port;
702
+ logger;
703
+ constructor(config) {
704
+ this.port = config.port || 3001;
705
+ this.logger = config.logger || console;
706
+ this.wss = new import_ws.WebSocketServer({ port: this.port });
707
+ this.core = new CoreTransportLogic(this.handleEvent);
708
+ this.setupServerHandlers();
709
+ this.logger.log(
710
+ `\u{1F680} CryptForge WebSocket Server running on ws://localhost:${this.port}`
711
+ );
712
+ }
713
+ // Event handler for CoreTransportLogic
714
+ handleEvent = (options) => {
715
+ this.logger?.log(
716
+ `Core event with route: \`${options.route}\` data:`,
717
+ options.data
718
+ );
719
+ this.broadcastEvent(options.route, options.data);
720
+ };
721
+ // Helper function to send a response
722
+ sendResponse = (ws, type, requestId, data, error) => {
723
+ const response = {
724
+ type,
725
+ requestId,
726
+ ...data && { data },
727
+ ...error && { error }
728
+ };
729
+ ws.send(JSON.stringify(response));
730
+ this.logger?.log("\u{1F4E4} Sent response:", response);
731
+ };
732
+ // Helper function to broadcast events to all connected clients
733
+ broadcastEvent = (route, data) => {
734
+ const message = JSON.stringify({
735
+ type: route,
736
+ data
737
+ });
738
+ this.activeConnections.forEach((ws) => {
739
+ if (ws.readyState === import_ws.default.OPEN) {
740
+ ws.send(message);
741
+ }
742
+ });
743
+ this.logger?.log(`\u{1F4E1} Broadcast event: ${route}`, data);
744
+ };
745
+ setupServerHandlers = () => {
746
+ this.wss.on("connection", (ws) => {
747
+ this.logger?.log("\u2705 Client connected");
748
+ this.activeConnections.add(ws);
749
+ ws.on("message", async (data) => {
750
+ await this.handleMessage(ws, data);
751
+ });
752
+ ws.on("close", () => {
753
+ this.logger?.log("\u{1F44B} Client disconnected");
754
+ this.activeConnections.delete(ws);
755
+ });
756
+ ws.on("error", (error) => {
757
+ this.logger?.error("\u274C WebSocket error:", error);
758
+ this.activeConnections.delete(ws);
759
+ });
760
+ });
761
+ this.wss.on("error", (error) => {
762
+ this.logger?.error("\u274C Server error:", error);
763
+ });
764
+ };
765
+ handleMessage = async (ws, data) => {
766
+ try {
767
+ const message = JSON.parse(data.toString());
768
+ this.logger?.log("\u{1F4E8} Received:", message);
769
+ const { type, requestId, data: requestData } = message;
770
+ switch (type) {
771
+ // Sync messages
772
+ case MESSAGES.enableBroadcast:
773
+ this.logger?.log("\u{1F4E1} Enabling broadcast...");
774
+ try {
775
+ const broadcastId = await this.core.enableBroadcast();
776
+ this.sendResponse(ws, type, requestId, { broadcastId });
777
+ } catch (error) {
778
+ this.sendResponse(ws, type, requestId, null, {
779
+ message: error instanceof Error ? error.message : "Unknown error"
780
+ });
781
+ }
782
+ break;
783
+ case MESSAGES.connect:
784
+ this.logger?.log("\u{1F50C} Connecting to topic...");
785
+ try {
786
+ await this.core.connect(
787
+ requestData.topic,
788
+ requestData.name,
789
+ requestData.app
790
+ );
791
+ this.sendResponse(ws, type, requestId, {
792
+ connected: true,
793
+ topic: requestData.topic,
794
+ name: requestData.name,
795
+ app: requestData.app
796
+ });
797
+ } catch (error) {
798
+ this.sendResponse(ws, type, requestId, null, {
799
+ message: error instanceof Error ? error.message : "Unknown error"
800
+ });
801
+ }
802
+ break;
803
+ case MESSAGES.requestKeystore:
804
+ this.logger?.log("\u{1F4E8} Requesting keystore...");
805
+ try {
806
+ await this.core.requestKeystore(requestData.pin);
807
+ this.sendResponse(ws, type, requestId, {
808
+ requested: true,
809
+ pin: requestData.pin
810
+ });
811
+ } catch (error) {
812
+ this.sendResponse(ws, type, requestId, null, {
813
+ message: error instanceof Error ? error.message : "Unknown error"
814
+ });
815
+ }
816
+ break;
817
+ case MESSAGES.approveRequest:
818
+ this.logger?.log("\u2705 Approving request...");
819
+ try {
820
+ await this.core.approveRequest({
821
+ identity: requestData.identity,
822
+ appId: requestData.appId
823
+ });
824
+ this.sendResponse(ws, type, requestId, {
825
+ approved: true
826
+ });
827
+ } catch (error) {
828
+ this.sendResponse(ws, type, requestId, null, {
829
+ message: error instanceof Error ? error.message : "Unknown error"
830
+ });
831
+ }
832
+ break;
833
+ case MESSAGES.denyRequest:
834
+ this.logger?.log("\u274C Denying request...");
835
+ try {
836
+ await this.core.denyRequest();
837
+ this.sendResponse(ws, type, requestId, {
838
+ denied: true
839
+ });
840
+ } catch (error) {
841
+ this.sendResponse(ws, type, requestId, null, {
842
+ message: error instanceof Error ? error.message : "Unknown error"
843
+ });
844
+ }
845
+ break;
846
+ case MESSAGES.disableBroadcast:
847
+ this.logger?.log("\u{1F4F4} Disabling broadcast...");
848
+ try {
849
+ await this.core.disableBroadcast();
850
+ this.sendResponse(ws, type, requestId, {
851
+ disabled: true
852
+ });
853
+ } catch (error) {
854
+ this.sendResponse(ws, type, requestId, null, {
855
+ message: error instanceof Error ? error.message : "Unknown error"
856
+ });
857
+ }
858
+ break;
859
+ case MESSAGES.getDeviceInfo:
860
+ this.logger?.log("\u{1F50D} Getting device info...");
861
+ try {
862
+ const deviceInfo = await this.core.getDeviceInfo();
863
+ this.sendResponse(ws, type, requestId, deviceInfo);
864
+ } catch (error) {
865
+ this.sendResponse(ws, type, requestId, null, {
866
+ message: error instanceof Error ? error.message : "Unknown error"
867
+ });
868
+ }
869
+ break;
870
+ case MESSAGES.getHostname:
871
+ this.logger?.log("\u{1F50D} Getting hostname...");
872
+ try {
873
+ const hostname = await this.core.getHostname();
874
+ this.sendResponse(ws, type, requestId, { hostname });
875
+ } catch (error) {
876
+ this.sendResponse(ws, type, requestId, null, {
877
+ message: error instanceof Error ? error.message : "Unknown error"
878
+ });
879
+ }
880
+ break;
881
+ case MESSAGES.connectPresence:
882
+ this.logger?.log("\u{1F50C} Connecting presence network...");
883
+ try {
884
+ await this.core.connectPresence(requestData.topic);
885
+ this.sendResponse(ws, type, requestId, { success: true });
886
+ } catch (error) {
887
+ this.sendResponse(ws, type, requestId, null, {
888
+ message: error instanceof Error ? error.message : "Unknown error"
889
+ });
890
+ }
891
+ break;
892
+ case MESSAGES.broadcastClientState:
893
+ this.logger?.log("\u{1F4E1} Broadcasting client state...");
894
+ try {
895
+ this.core.broadcastClientState(requestData.id, requestData.state);
896
+ this.sendResponse(ws, type, requestId, { success: true });
897
+ } catch (error) {
898
+ this.sendResponse(ws, type, requestId, null, {
899
+ message: error instanceof Error ? error.message : "Unknown error"
900
+ });
901
+ }
902
+ break;
903
+ default:
904
+ this.logger?.log(`\u26A0\uFE0F Unknown message type: ${type}`);
905
+ if (requestId) {
906
+ this.sendResponse(ws, type, requestId, null, {
907
+ message: `Unknown message type: ${type}`
908
+ });
909
+ }
910
+ }
911
+ } catch (error) {
912
+ this.logger?.error("\u274C Error processing message:", error);
913
+ try {
914
+ const parsed = JSON.parse(data.toString());
915
+ if (parsed.requestId) {
916
+ this.sendResponse(ws, parsed.type, parsed.requestId, null, {
917
+ message: error instanceof Error ? error.message : "Unknown error"
918
+ });
919
+ }
920
+ } catch (e) {
921
+ this.logger?.error("\u274C Could not send error response:", e);
922
+ }
923
+ }
924
+ };
925
+ // Public methods for managing the server
926
+ close = (callback) => {
927
+ this.wss.close(callback);
928
+ };
929
+ getActiveConnections = () => {
930
+ return new Set(this.activeConnections);
931
+ };
932
+ getPort = () => {
933
+ return this.port;
934
+ };
935
+ };
936
+ // Annotate the CommonJS export names for ESM import in node:
937
+ 0 && (module.exports = {
938
+ KeyTransportServer,
939
+ useKeyExchangeServer
940
+ });