@decentnetwork/peer 0.1.37 → 0.1.38

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.
@@ -64,7 +64,7 @@ export declare class TcpRelayPool extends EventEmitter {
64
64
  * online. Returns the count of relays that accepted the write — 0 if
65
65
  * the friend isn't online on any TCP relay.
66
66
  */
67
- sendToFriend(friendPublicKey: Uint8Array, payload: Uint8Array): number;
67
+ sendToFriend(friendPublicKey: Uint8Array, payload: Uint8Array, droppable?: boolean): number;
68
68
  /** Diagnostics. */
69
69
  connectedCount(): number;
70
70
  /**
@@ -173,7 +173,7 @@ export class TcpRelayPool extends EventEmitter {
173
173
  * online. Returns the count of relays that accepted the write — 0 if
174
174
  * the friend isn't online on any TCP relay.
175
175
  */
176
- sendToFriend(friendPublicKey, payload) {
176
+ sendToFriend(friendPublicKey, payload, droppable = false) {
177
177
  if (friendPublicKey.length !== KEY_SIZE)
178
178
  return 0;
179
179
  // Send via the FIRST connected relay where the friend is online; only
@@ -191,7 +191,7 @@ export class TcpRelayPool extends EventEmitter {
191
191
  continue;
192
192
  if (!r.client.hasFriendOnline(friendPublicKey))
193
193
  continue;
194
- if (r.client.sendToFriend(friendPublicKey, payload)) {
194
+ if (r.client.sendToFriend(friendPublicKey, payload, droppable)) {
195
195
  return 1;
196
196
  }
197
197
  }
@@ -82,11 +82,11 @@ export declare class TcpRelayClient extends EventEmitter {
82
82
  * if the relay says they're offline. Caller should treat false as
83
83
  * "try another transport".
84
84
  */
85
- sendToFriend(friendPublicKey: Uint8Array, payload: Uint8Array): boolean;
85
+ sendToFriend(friendPublicKey: Uint8Array, payload: Uint8Array, droppable?: boolean): boolean;
86
86
  /** Send a PING; relay echoes it as PONG. ping_id is opaque 8 bytes. */
87
87
  sendPing(): boolean;
88
88
  /** Send DATA payload to the friend at `connectionId`. */
89
- sendData(connectionId: number, payload: Uint8Array): boolean;
89
+ sendData(connectionId: number, payload: Uint8Array, droppable?: boolean): boolean;
90
90
  /** Send OOB_SEND (used for delivering friend requests via TCP relay). */
91
91
  sendOob(receiverPublicKey: Uint8Array, payload: Uint8Array): boolean;
92
92
  close(reason?: string): void;
@@ -37,6 +37,15 @@ import { incrementNonce } from "./net-crypto.js";
37
37
  const KEY_SIZE = 32;
38
38
  const NONCE_SIZE = 24;
39
39
  const MAC_SIZE = 16;
40
+ // Max bytes we let pile up in the relay socket's write buffer before we start
41
+ // DROPPING droppable (bulk IP) frames. Node's socket.write() buffers without
42
+ // bound when the kernel send buffer is full, so a congested relay link would
43
+ // otherwise queue SECONDS of IP packets — the multi-second bufferbloat behind
44
+ // the snoopy/lili 13s pings. ~64KB is well under a second of queue even on a
45
+ // slow transpacific relay; past it we behave like a real congested network and
46
+ // drop, letting the inner protocol (TCP/QUIC) retransmit instead of delivering
47
+ // stale packets late. Control frames (droppable=false) always go through.
48
+ const RELAY_SEND_BUFFER_CAP = 64 * 1024;
40
49
  /** TCP_HANDSHAKE_PLAIN_SIZE in toxcore — temp pubkey + initial nonce. */
41
50
  const TCP_HANDSHAKE_PLAIN_SIZE = KEY_SIZE + NONCE_SIZE;
42
51
  /** Outbound handshake: self_dht_pk(32) + nonce(24) + box(plain)(KEY_SIZE+NONCE_SIZE+MAC_SIZE = 72) = 128. */
@@ -276,13 +285,18 @@ export class TcpRelayClient extends EventEmitter {
276
285
  return opened;
277
286
  }
278
287
  /** Encrypt a payload and write it as one framed packet. */
279
- #sendFrame(payload) {
288
+ #sendFrame(payload, droppable = false) {
280
289
  if (this.#state !== "connected" || !this.#sessionKey || !this.#sentNonce || !this.#socket) {
281
290
  return false;
282
291
  }
283
292
  if (payload.length === 0 || payload.length > MAX_PACKET_SIZE - MAC_SIZE) {
284
293
  return false;
285
294
  }
295
+ // Bound the relay queue: drop bulk IP frames once the socket buffer is
296
+ // backed up rather than letting Node buffer seconds of packets (bufferbloat).
297
+ if (droppable && this.#socket.writableLength > RELAY_SEND_BUFFER_CAP) {
298
+ return false;
299
+ }
286
300
  const cipher = nacl.secretbox(payload, this.#sentNonce, this.#sessionKey);
287
301
  incrementNonce(this.#sentNonce);
288
302
  // Wire layout: [4-byte iveg][2-byte BE length][cipher].
@@ -418,13 +432,13 @@ export class TcpRelayClient extends EventEmitter {
418
432
  * if the relay says they're offline. Caller should treat false as
419
433
  * "try another transport".
420
434
  */
421
- sendToFriend(friendPublicKey, payload) {
435
+ sendToFriend(friendPublicKey, payload, droppable = false) {
422
436
  if (friendPublicKey.length !== KEY_SIZE)
423
437
  return false;
424
438
  const cid = this.#cidByFriend.get(Buffer.from(friendPublicKey).toString("hex"));
425
439
  if (cid === undefined)
426
440
  return false;
427
- return this.sendData(cid, payload);
441
+ return this.sendData(cid, payload, droppable);
428
442
  }
429
443
  /** Send a PING; relay echoes it as PONG. ping_id is opaque 8 bytes. */
430
444
  sendPing() {
@@ -432,12 +446,12 @@ export class TcpRelayClient extends EventEmitter {
432
446
  return this.#sendFrame(concatBytes([Uint8Array.of(TCP_PACKET_PING), pingId]));
433
447
  }
434
448
  /** Send DATA payload to the friend at `connectionId`. */
435
- sendData(connectionId, payload) {
449
+ sendData(connectionId, payload, droppable = false) {
436
450
  if (connectionId < 16 || connectionId > 0xff)
437
451
  return false;
438
452
  if (payload.length === 0)
439
453
  return false;
440
- return this.#sendFrame(concatBytes([Uint8Array.of(connectionId), payload]));
454
+ return this.#sendFrame(concatBytes([Uint8Array.of(connectionId), payload]), droppable);
441
455
  }
442
456
  /** Send OOB_SEND (used for delivering friend requests via TCP relay). */
443
457
  sendOob(receiverPublicKey, payload) {
package/dist/peer.js CHANGED
@@ -2775,7 +2775,10 @@ export class Peer {
2775
2775
  // would silently drop on iPad's side and leave the session
2776
2776
  // looking healthy on our side while iPad never sees the
2777
2777
  // packets.
2778
- const sent = this.#tcpRelays.sendToFriend(tcpKey, packet);
2778
+ // Bulk IP data is droppable over the relay: under backpressure the
2779
+ // relay client drops it (bounded queue) rather than buffering seconds
2780
+ // of packets. Control frames pass isBulkData=false and always queue.
2781
+ const sent = this.#tcpRelays.sendToFriend(tcpKey, packet, isBulkData);
2779
2782
  if (sent > 0) {
2780
2783
  tcpOk = true;
2781
2784
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/peer",
3
- "version": "0.1.37",
3
+ "version": "0.1.38",
4
4
  "description": "Pure TypeScript port of Elastos Carrier (toxcore-derived) P2P messaging. DHT, onion routing, TCP relay, FlatBuffers app payloads, Express offline relay. Wire-compatible with iOS Beagle and the Carrier C SDK.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",