@hive-p2p/server 1.0.23 → 1.0.25

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/core/arbiter.mjs CHANGED
@@ -67,7 +67,7 @@ export class Arbiter {
67
67
  adjustTrust(peerId, delta, reason = 'na') { // Internal and API use - return true if peer isn't banished
68
68
  if (peerId === this.id) return; // self
69
69
  if (delta) this.trustBalances[peerId] = Math.min(MAX_TRUST, (this.trustBalances[peerId] || 0) + delta);
70
- if (delta && this.verbose > 2) console.log(`%c(Arbiter: ${this.id}) ${peerId} +${delta}ms (${reason}). Updated: ${this.trustBalances[peerId]}ms.`, LOG_CSS.ARBITER);
70
+ if (delta && this.verbose > 3) console.log(`%c(Arbiter: ${this.id}) ${peerId} +${delta}ms (${reason}). Updated: ${this.trustBalances[peerId]}ms.`, LOG_CSS.ARBITER);
71
71
  if (this.isBanished(peerId) && this.verbose > 1) console.log(`%c(Arbiter: ${this.id}) Peer ${peerId} is now banished.`, LOG_CSS.ARBITER);
72
72
  }
73
73
  isBanished(peerId = 'toto') { return (this.trustBalances[peerId] || 0) < 0; }
package/core/config.mjs CHANGED
@@ -104,7 +104,7 @@ export const TRANSPORTS = {
104
104
  /** Time to wait for signal before destroying WTRC connection | Default: 8_000 (8 seconds) */
105
105
  SIGNAL_CREATION_TIMEOUT: 8_000,
106
106
  /** Time to consider an SDP offer as valid | Default: 40_000 (40 seconds) */
107
- SDP_OFFER_EXPIRATION: 40_000,
107
+ SDP_OFFER_EXPIRATION: 40_000,
108
108
 
109
109
  WS_CLIENT: WebSocket,
110
110
  WS_SERVER: isNode ? (await import('ws')).WebSocketServer : null,
@@ -188,7 +188,7 @@ export class CryptoCodex {
188
188
  /** @param {Uint8Array | ArrayBuffer} serialized @return {GossipMessage | null } */
189
189
  readGossipMessage(serialized) {
190
190
  if (this.verbose > 3) console.log(`%creadGossipMessage ${serialized.byteLength} bytes`, LOG_CSS.CRYPTO_CODEX);
191
- if (this.verbose > 3) console.log(`%c${serialized}`, LOG_CSS.CRYPTO_CODEX);
191
+ if (this.verbose > 4) console.log(`%c${serialized}`, LOG_CSS.CRYPTO_CODEX);
192
192
  try { // 1, 1, 1, 8, 4, 32, X, 64, 1
193
193
  const { marker, dataCode, neighLength, timestamp, dataLength, pubkey, associatedId } = this.readBufferHeader(serialized);
194
194
  const topic = GOSSIP.MARKERS_BYTES[marker];
@@ -208,7 +208,7 @@ export class CryptoCodex {
208
208
  /** @param {Uint8Array | ArrayBuffer} serialized @return {DirectMessage | ReroutedDirectMessage | null} */
209
209
  readUnicastMessage(serialized) {
210
210
  if (this.verbose > 3) console.log(`%creadUnicastMessage ${serialized.byteLength} bytes`, LOG_CSS.CRYPTO_CODEX);
211
- if (this.verbose > 3) console.log(`%c${serialized}`, LOG_CSS.CRYPTO_CODEX);
211
+ if (this.verbose > 4) console.log(`%c${serialized}`, LOG_CSS.CRYPTO_CODEX);
212
212
  try { // 1, 1, 1, 8, 4, 32, X, 1, X, 64
213
213
  const { marker, dataCode, neighLength, timestamp, dataLength, pubkey } = this.readBufferHeader(serialized, false);
214
214
  const type = UNICAST.MARKERS_BYTES[marker];
@@ -59,27 +59,23 @@ export class NodeServices {
59
59
  ws.on('message', (data) => { // When peer proves his id, we can handle data normally
60
60
  if (remoteId) for (const cb of this.peerStore.callbacks.data) cb(remoteId, data);
61
61
  else { // FIRST MESSAGE SHOULD BE HANDSHAKE WITH ID
62
- try {
63
- const d = new Uint8Array(data); if (d[0] > 127) return; // not unicast, ignore
64
- const message = this.cryptoCodex.readUnicastMessage(d);
65
- if (!message) return; // invalid unicast message, ignore
66
-
67
- const { route, type, neighborsList } = message;
68
- if (type !== 'handshake' || route.length !== 2) return;
69
-
70
- const { signatureStart, pubkey, signature } = message;
71
- const signedData = d.subarray(0, signatureStart);
72
- if (!this.cryptoCodex.verifySignature(pubkey, signature, signedData)) return;
73
-
74
- remoteId = route[0];
75
- this.peerStore.digestPeerNeighbors(remoteId, neighborsList); // Update known store
76
- this.peerStore.connecting[remoteId]?.out?.close(); // close outgoing connection if any
77
- if (!this.peerStore.connecting[remoteId]) this.peerStore.connecting[remoteId] = {};
78
- this.peerStore.connecting[remoteId].in = new PeerConnection(remoteId, ws, 'in', true);
79
- for (const cb of this.peerStore.callbacks.connect) cb(remoteId, 'in');
80
- } catch (error) {
81
- console.error(`Error handling WebSocket message on Node #${this.id}:`, error);
82
- }
62
+ const d = new Uint8Array(data); if (d[0] > 127) return; // not unicast, ignore
63
+ const message = this.cryptoCodex.readUnicastMessage(d);
64
+ if (!message) return; // invalid unicast message, ignore
65
+
66
+ const { route, type, neighborsList } = message;
67
+ if (type !== 'handshake' || route.length !== 2) return;
68
+
69
+ const { signatureStart, pubkey, signature } = message;
70
+ const signedData = d.subarray(0, signatureStart);
71
+ if (!this.cryptoCodex.verifySignature(pubkey, signature, signedData)) return;
72
+
73
+ remoteId = route[0];
74
+ this.peerStore.digestPeerNeighbors(remoteId, neighborsList); // Update known store
75
+ this.peerStore.connecting[remoteId]?.out?.close(); // close outgoing connection if any
76
+ if (!this.peerStore.connecting[remoteId]) this.peerStore.connecting[remoteId] = {};
77
+ this.peerStore.connecting[remoteId].in = new PeerConnection(remoteId, ws, 'in', true);
78
+ for (const cb of this.peerStore.callbacks.connect) cb(remoteId, 'in');
83
79
  }
84
80
  });
85
81
  ws.send(this.cryptoCodex.createUnicastMessage('handshake', null, [this.id, this.id], this.peerStore.neighborsList));
@@ -93,6 +89,7 @@ export class NodeServices {
93
89
  this.stunServer.send(this.#buildSTUNResponse(msg, rinfo), rinfo.port, rinfo.address);
94
90
  });
95
91
  this.stunServer.bind(port, host);
92
+ if (this.verbose > 2) console.log(`%cSTUN server listening on ${host}:${port}`, LOG_CSS.SERVICE);
96
93
  }
97
94
  #isValidSTUNRequest(msg) {
98
95
  if (msg.length < 20) return false;
package/core/node.mjs CHANGED
@@ -16,7 +16,8 @@ import { NodeServices } from './node-services.mjs';
16
16
  * @param {string} [options.domain] If provided, the node will operate as a public node and start necessary services (e.g., WebSocket server)
17
17
  * @param {number} [options.port] If provided, the node will listen on this port (default: SERVICE.PORT)
18
18
  * @param {number} [options.verbose] Verbosity level for logging (default: NODE.DEFAULT_VERBOSE) */
19
- export async function createPublicNode(options) {
19
+ export async function createPublicNode(options) {
20
+ await CLOCK.sync(this.verbose);
20
21
  const verbose = options.verbose !== undefined ? options.verbose : NODE.DEFAULT_VERBOSE;
21
22
  const domain = options.domain || undefined;
22
23
  const codex = options.cryptoCodex || new CryptoCodex(undefined, verbose);
@@ -144,9 +144,18 @@ export class Topologist {
144
144
  }
145
145
  /** Get overlap information for multiple peers @param {string[]} peerIds */
146
146
  #getOverlaps(peerIds = []) { return peerIds.map(id => ({ id, ...this.#getOverlap(id) })); }
147
+ #getFullWsUrl(url) {
148
+ // Auto-detect protocol: use wss:// if in browser + HTTPS
149
+ const isBrowser = typeof window !== 'undefined';
150
+ const isSecure = isBrowser && window.location.protocol === 'https:';
151
+ const protocol = isSecure ? 'wss://' : 'ws://';
152
+
153
+ // Build full URL if not already prefixed
154
+ return url.startsWith('ws') ? url : `${protocol}${url}`;
155
+ }
147
156
  #connectToPublicNode(publicUrl = 'localhost:8080') {
148
157
  let remoteId = null;
149
- const ws = new TRANSPORTS.WS_CLIENT(publicUrl); ws.binaryType = 'arraybuffer';
158
+ const ws = new TRANSPORTS.WS_CLIENT(this.#getFullWsUrl(publicUrl)); ws.binaryType = 'arraybuffer';
150
159
  ws.onerror = (error) => console.error(`WebSocket error:`, error.stack);
151
160
  ws.onopen = () => {
152
161
  this.bootstrapsConnectionState.set(publicUrl, true);
@@ -157,27 +166,23 @@ export class Topologist {
157
166
  ws.onmessage = (data) => {
158
167
  if (remoteId) for (const cb of this.peerStore.callbacks.data) cb(remoteId, data.data);
159
168
  else { // FIRST MESSAGE SHOULD BE HANDSHAKE WITH ID
160
- try {
161
- const d = new Uint8Array(data.data); if (d[0] > 127) return; // not unicast, ignore
162
- const message = this.cryptoCodex.readUnicastMessage(d);
163
- if (!message) return; // invalid unicast message, ignore
164
-
165
- const { route, type, neighborsList } = message;
166
- if (type !== 'handshake' || route.length !== 2) return;
167
-
168
- const { signatureStart, pubkey, signature } = message;
169
- const signedData = d.subarray(0, signatureStart);
170
- if (!this.cryptoCodex.verifySignature(pubkey, signature, signedData)) return;
171
-
172
- remoteId = route[0];
173
- this.peerStore.digestPeerNeighbors(remoteId, neighborsList); // Update known store
174
- this.peerStore.connecting[remoteId]?.in?.close(); // close incoming connection if any
175
- if (!this.peerStore.connecting[remoteId]) this.peerStore.connecting[remoteId] = {};
176
- this.peerStore.connecting[remoteId].out = new PeerConnection(remoteId, ws, 'out', true);
177
- for (const cb of this.peerStore.callbacks.connect) cb(remoteId, 'out');
178
- } catch (error) {
179
- console.error(`Error handling WebSocket message on Node #${this.id}:`, error);
180
- }
169
+ const d = new Uint8Array(data.data); if (d[0] > 127) return; // not unicast, ignore
170
+ const message = this.cryptoCodex.readUnicastMessage(d);
171
+ if (!message) return; // invalid unicast message, ignore
172
+
173
+ const { route, type, neighborsList } = message;
174
+ if (type !== 'handshake' || route.length !== 2) return;
175
+
176
+ const { signatureStart, pubkey, signature } = message;
177
+ const signedData = d.subarray(0, signatureStart);
178
+ if (!this.cryptoCodex.verifySignature(pubkey, signature, signedData)) return;
179
+
180
+ remoteId = route[0];
181
+ this.peerStore.digestPeerNeighbors(remoteId, neighborsList); // Update known store
182
+ this.peerStore.connecting[remoteId]?.in?.close(); // close incoming connection if any
183
+ if (!this.peerStore.connecting[remoteId]) this.peerStore.connecting[remoteId] = {};
184
+ this.peerStore.connecting[remoteId].out = new PeerConnection(remoteId, ws, 'out', true);
185
+ for (const cb of this.peerStore.callbacks.connect) cb(remoteId, 'out');
181
186
  }
182
187
  };
183
188
  ws.send(this.cryptoCodex.createUnicastMessage('handshake', null, [this.id, this.id], this.peerStore.neighborsList));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hive-p2p/server",
3
- "version": "1.0.23",
3
+ "version": "1.0.25",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },