@decentnetwork/lan 0.1.132 → 0.1.134

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
@@ -73,9 +73,15 @@ function hostMatches(host, suffixes) {
73
73
  host = (host || "").toLowerCase();
74
74
  return suffixes.some((s) => host === (s.startsWith(".") ? s.slice(1) : s) || host.endsWith(s));
75
75
  }
76
- const HEALTH_INTERVAL_MS = 1000;
77
- const HEALTH_TIMEOUT_MS = 1500;
78
- const DOWN_AFTER_FAILS = 2;
76
+ const HEALTH_INTERVAL_MS = 1500;
77
+ // Probe connect timeout. A single remote exit reached over agentnet can sit at
78
+ // 300-900ms RTT and spike higher under load (CCTV opens dozens of tunnels), so a
79
+ // tight timeout makes a perfectly usable exit "flap". Keep this generously above
80
+ // the worst observed RTT.
81
+ const HEALTH_TIMEOUT_MS = 4000;
82
+ // Consecutive probe failures before marking an exit DOWN. Higher = more tolerant
83
+ // of transient jitter on a long-haul path (don't blackout a region on one blip).
84
+ const DOWN_AFTER_FAILS = 3;
79
85
  const CONNECT_TIMEOUT_MS = 8000;
80
86
  /**
81
87
  * Start the multi-exit router. Returns a stop() that closes the listener.
@@ -178,12 +184,21 @@ export function startMultiExitRouter(opts) {
178
184
  }
179
185
  // ---- exit selection (scoped to one region) --------------------------------
180
186
  function pickOrder(region) {
181
- const healthy = exits.filter((e) => e.healthy && e.region === region);
182
- if (healthy.length === 0)
187
+ const inRegion = exits.filter((e) => e.region === region);
188
+ const healthy = inRegion.filter((e) => e.healthy);
189
+ // Best-effort fallback: if NO exit currently passes health checks but the
190
+ // region HAS exits, try them anyway instead of 503-ing the user. A single
191
+ // high-latency exit under load makes the probe flap DOWN/UP every few
192
+ // seconds (observed: China exit at ~900ms RTT serving CCTV), yet the actual
193
+ // CONNECT tunnels keep working. The real forward() has its own timeout +
194
+ // retry, so trying a "down" exit costs little and avoids blacking out the
195
+ // whole region on a transient probe blip. Prefer healthy when we have them.
196
+ const pool = healthy.length > 0 ? healthy : inRegion;
197
+ if (pool.length === 0)
183
198
  return [];
184
199
  if (mode === "failover")
185
- return healthy; // priority order (input order)
186
- return [...healthy].sort((a, b) => a.active - b.active || a.served - b.served);
200
+ return pool; // priority order (input order)
201
+ return [...pool].sort((a, b) => a.active - b.active || a.served - b.served);
187
202
  }
188
203
  // Whether ANY exit is configured for a region (healthy or not). Lets us tell
189
204
  // "region has no exits at all → fall through" from "exits all down → 503".
@@ -43,6 +43,9 @@ paths:
43
43
  proxy:
44
44
  enabled: false # `agentnet proxy enable` flips this
45
45
  port: 8888
46
+ egress: "" # ""=OS default; "physical"=bind outbound dials to
47
+ # the real NIC (skip Tailscale/overlay); or an
48
+ # explicit source IPv4. See docs/CHINA-EXIT.md.
46
49
 
47
50
  friends:
48
51
  autoAccept: true # accept incoming friend-requests automatically
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/lan",
3
- "version": "0.1.132",
3
+ "version": "0.1.134",
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",
@@ -79,7 +79,7 @@
79
79
  },
80
80
  "dependencies": {
81
81
  "@decentnetwork/dora": "^0.1.11",
82
- "@decentnetwork/peer": "^0.1.69",
82
+ "@decentnetwork/peer": "^0.1.70",
83
83
  "ink": "^5.2.1",
84
84
  "js-yaml": "^4.1.0",
85
85
  "react": "^18.3.1",