@decentnetwork/lan 0.1.15 → 0.1.16

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.
@@ -212,8 +212,15 @@ export class DaemonServer {
212
212
  this.logger.info(`Reconfiguring TUN: dora-allocated IP changed ${currentIp} -> ${newIp}`);
213
213
  const ifname = this.tunDevice.getInterface();
214
214
  try {
215
- await this.routeManager.cleanup(ifname, this.config.network.subnet, currentIp);
215
+ // ORDER MATTERS: close TunDevice FIRST so the helper
216
+ // subprocess fully exits and releases agentnet0.
217
+ // routeManager.cleanup's `ip tuntap del` would otherwise
218
+ // fail with EBUSY and the subsequent helper spawn would
219
+ // race the kernel for the device name, exit code=1, and
220
+ // leave the daemon TUN-less. tunDevice.close() now waits
221
+ // for actual process exit.
216
222
  await this.tunDevice.close();
223
+ await this.routeManager.cleanup(ifname, this.config.network.subnet, currentIp);
217
224
  this.tunDevice = new TunDevice({
218
225
  config: {
219
226
  name: this.config.network.interface,
@@ -54,8 +54,35 @@ export class TunDevice extends EventEmitter {
54
54
  this.isOpen = false;
55
55
  this.packetQueue = [];
56
56
  if (this.helper) {
57
- this.helper.kill("SIGTERM");
57
+ // Wait for the helper subprocess to actually exit before
58
+ // returning. SIGTERM is async — without this await, a caller
59
+ // that immediately spawns a new helper for the same device
60
+ // name races the kernel: the old helper still owns
61
+ // `agentnet0`, the new helper's TUNSETIFF ioctl returns
62
+ // EBUSY, the helper exits with code=1, the daemon ends up
63
+ // with NO TUN device at all even though `systemctl is-active`
64
+ // reports green. Symptom: every outbound packet routes via
65
+ // the default gateway instead of the daemon, 100% packet
66
+ // loss with no obvious error. Force-kill after 2s in case
67
+ // SIGTERM is swallowed.
68
+ const helper = this.helper;
58
69
  this.helper = undefined;
70
+ await new Promise((res) => {
71
+ if (helper.exitCode !== null || helper.signalCode !== null) {
72
+ res();
73
+ return;
74
+ }
75
+ const timer = setTimeout(() => {
76
+ if (helper.exitCode === null && helper.signalCode === null) {
77
+ helper.kill("SIGKILL");
78
+ }
79
+ }, 2000);
80
+ helper.once("exit", () => {
81
+ clearTimeout(timer);
82
+ res();
83
+ });
84
+ helper.kill("SIGTERM");
85
+ });
59
86
  }
60
87
  this.emit("closed");
61
88
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/lan",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "Private virtual LAN for self-hosted services and AI agents, built on Elastos Carrier. NAT-traversal, name service, ACL, all over a peer-to-peer mesh — no public IP required.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",