@decentnetwork/lan 0.1.138 → 0.1.140

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.
Binary file
Binary file
Binary file
Binary file
@@ -18,6 +18,9 @@ export interface PeerManagerOptions {
18
18
  nickname?: string;
19
19
  /** Status-message / short bio advertised to friends (Carrier USERINFO descr). */
20
20
  statusMessage?: string;
21
+ /** Directory for persisting partially-received files so a dropped/restarted
22
+ * transfer resumes instead of restarting (断点续传). */
23
+ fileResumeDir?: string;
21
24
  }
22
25
  export declare class PeerManager extends EventEmitter {
23
26
  private peer;
@@ -185,6 +188,13 @@ export declare class PeerManager extends EventEmitter {
185
188
  * (162, lossless). Replaces the old `DORA:`-prefixed text on packet 64.
186
189
  */
187
190
  sendDora(userid: string, text: string): Promise<void>;
191
+ /**
192
+ * Report to `pubkey` the delivered rate (bytes/sec) we're measuring on their
193
+ * inbound IP flow, so they can pace their sends to us toward it. Lossless +
194
+ * tiny (4 bytes). Best-effort: a failed report just means the sender keeps
195
+ * its previous pace estimate.
196
+ */
197
+ sendRateReport(pubkey: string, bytesPerSec: number): Promise<void>;
188
198
  /**
189
199
  * Route a decoded decentlan frame to its packet-session, creating or
190
200
  * replacing the session on a HANDSHAKE_REQ (handles the Carrier re-pair
@@ -6,7 +6,7 @@ import { Peer } from "@decentnetwork/peer";
6
6
  import { EventEmitter } from "events";
7
7
  import { PacketSession } from "./packet-session.js";
8
8
  import { FrameCodec } from "./frame.js";
9
- import { FRAME_OPCODE_HANDSHAKE_ACK, PACKET_ID_DL_SESSION, PACKET_ID_DL_DORA, PACKET_ID_DL_IP, } from "./types.js";
9
+ import { FRAME_OPCODE_HANDSHAKE_ACK, PACKET_ID_DL_SESSION, PACKET_ID_DL_DORA, PACKET_ID_DL_IP, PACKET_ID_DL_RATE, } from "./types.js";
10
10
  import { Logger } from "../utils/logger.js";
11
11
  export class PeerManager extends EventEmitter {
12
12
  peer = null;
@@ -32,6 +32,7 @@ export class PeerManager extends EventEmitter {
32
32
  // rides a single transport instead of fanning out over UDP + relay +
33
33
  // TCP relay (which delivered 3-4 duplicates of every IP packet).
34
34
  bulkDataPacketId: PACKET_ID_DL_IP,
35
+ fileResumeDir: opts.fileResumeDir,
35
36
  });
36
37
  this.setupEventHandlers();
37
38
  this.logger.info("Peer instance created (not yet started)");
@@ -421,6 +422,22 @@ export class PeerManager extends EventEmitter {
421
422
  }
422
423
  await this.peer.sendCustomPacket(userid, PACKET_ID_DL_DORA, Buffer.from(text, "utf-8"));
423
424
  }
425
+ /**
426
+ * Report to `pubkey` the delivered rate (bytes/sec) we're measuring on their
427
+ * inbound IP flow, so they can pace their sends to us toward it. Lossless +
428
+ * tiny (4 bytes). Best-effort: a failed report just means the sender keeps
429
+ * its previous pace estimate.
430
+ */
431
+ async sendRateReport(pubkey, bytesPerSec) {
432
+ if (!this.peer)
433
+ return;
434
+ const buf = Buffer.alloc(4);
435
+ buf.writeUInt32BE(Math.max(0, Math.min(0xffffffff, Math.round(bytesPerSec))), 0);
436
+ try {
437
+ await this.peer.sendCustomPacket(pubkey, PACKET_ID_DL_RATE, buf);
438
+ }
439
+ catch { /* best-effort */ }
440
+ }
424
441
  /**
425
442
  * Route a decoded decentlan frame to its packet-session, creating or
426
443
  * replacing the session on a HANDSHAKE_REQ (handles the Carrier re-pair
@@ -511,6 +528,13 @@ export class PeerManager extends EventEmitter {
511
528
  this.emit("dora-message", pubkey, Buffer.from(data).toString("utf-8"));
512
529
  return;
513
530
  }
531
+ if (id === PACKET_ID_DL_RATE) {
532
+ if (data.length >= 4) {
533
+ const bytesPerSec = Buffer.from(data.buffer, data.byteOffset, 4).readUInt32BE(0);
534
+ this.emit("rate-report", pubkey, bytesPerSec);
535
+ }
536
+ return;
537
+ }
514
538
  if (id === PACKET_ID_DL_IP || id === PACKET_ID_DL_SESSION) {
515
539
  const frame = FrameCodec.decode(data);
516
540
  if (!frame) {
@@ -11,3 +11,4 @@ export declare const FRAME_HEADER_SIZE = 6;
11
11
  export declare const PACKET_ID_DL_SESSION = 161;
12
12
  export declare const PACKET_ID_DL_DORA = 162;
13
13
  export declare const PACKET_ID_DL_IP = 163;
14
+ export declare const PACKET_ID_DL_RATE = 164;
@@ -25,3 +25,9 @@ export const PACKET_ID_DL_DORA = 162; // lossless: dora control (must arrive)
25
25
  // The TCP-over-TCP cost on a direct path is the accepted trade — relay delivery
26
26
  // is non-negotiable. (The handshake at 161 already exempts IP from chat-64.)
27
27
  export const PACKET_ID_DL_IP = 163; // lossless: IP data frames (must traverse relays)
28
+ // lossless: egress pacing feedback. The RECEIVER of an IP flow measures how many
29
+ // bytes/sec it actually received from a peer and reports that number back; the
30
+ // SENDER paces its outbound to that peer toward the reported (delivered) rate,
31
+ // so it stops overrunning the path and inducing congestion drops. Payload: a
32
+ // single uint32be = delivered bytes/sec. Rare + tiny, so lossless is free.
33
+ export const PACKET_ID_DL_RATE = 164;
@@ -330,6 +330,7 @@ export declare function cmdProxyRouter(args: {
330
330
  all?: boolean;
331
331
  routes?: string;
332
332
  egress?: string;
333
+ hlsAccel?: boolean;
333
334
  configDir?: string;
334
335
  }): Promise<void>;
335
336
  /**
@@ -1480,6 +1480,9 @@ export async function cmdProxyRouter(args) {
1480
1480
  listenPort: args.port ?? 8889,
1481
1481
  mode,
1482
1482
  egressBindIp,
1483
+ learnedRoutesPath: resolve(dir, "learned-routes.json"),
1484
+ hlsAccel: args.hlsAccel ?? false,
1485
+ configDir: dir,
1483
1486
  });
1484
1487
  // Keep the process alive; the router runs until Ctrl-C / signal.
1485
1488
  await new Promise(() => { });
package/dist/cli/index.js CHANGED
@@ -348,6 +348,7 @@ async function main() {
348
348
  .option("mode", { choices: ["loadbalance", "failover"], default: "loadbalance" })
349
349
  .option("all", { type: "boolean", default: false, describe: "Route ALL hosts through exits (default: China-only split tunnel)" })
350
350
  .option("egress", { type: "string", describe: "Bind the DIRECT internet dial to the physical NIC: 'physical' (auto) or an explicit source IPv4 — keeps egress off Tailscale/overlay" })
351
+ .option("hls-accel", { type: "boolean", default: false, describe: "Accelerate China video streams: selectively MITM video-CDN HTTPS to parallel-prefetch HLS segments across all exits (prints a CA cert to trust once)" })
351
352
  .option("config-dir", { type: "string" }), async (argv) => {
352
353
  await cmdProxyRouter({
353
354
  exit: argv.exit,
@@ -357,6 +358,7 @@ async function main() {
357
358
  mode: argv.mode,
358
359
  all: argv.all,
359
360
  egress: argv.egress,
361
+ hlsAccel: argv["hls-accel"],
360
362
  configDir: argv["config-dir"],
361
363
  });
362
364
  })
@@ -283,6 +283,9 @@ export class DaemonServer {
283
283
  // instead of the generic "@decentnetwork/peer".
284
284
  nickname: this.config.node.name,
285
285
  statusMessage: this.config.node.statusMessage,
286
+ // Persist partially-received files so a dropped/restarted transfer
287
+ // resumes instead of restarting from 0 (断点续传).
288
+ fileResumeDir: resolve(this.configDir, "downloads", ".partial"),
286
289
  });
287
290
  await this.peerManager.start();
288
291
  this.logger.info(`Identity: ${this.peerManager.getAddress()}`);
@@ -908,6 +911,10 @@ export class DaemonServer {
908
911
  peerManager: this.peerManager,
909
912
  ipam: this.ipam,
910
913
  acl: this.aclEngine,
914
+ // Pace + fair-queue egress IP data (default on). Disable with
915
+ // `forwarding: { paced: false }` in config.yaml to restore the old
916
+ // fire-immediately behavior.
917
+ pacedForwarding: this.config.forwarding?.paced !== false,
911
918
  });
912
919
  await this.packetRouter.start();
913
920
  // 7b. In-process DNS server. Answers A/PTR for *.{dnsDomain}
@@ -0,0 +1 @@
1
+ export declare const CN_IPV4_RANGES: readonly number[];