@decentnetwork/lan 0.1.71 → 0.1.73
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/config/default-exits.yaml +55 -0
- package/dist/cli/commands.js +56 -17
- package/dist/config/loader.d.ts +22 -0
- package/dist/config/loader.js +30 -7
- package/dist/proxy/multi-exit-router.js +20 -2
- package/package.json +2 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Official exit / infrastructure nodes shipped with @decentnetwork/lan.
|
|
2
|
+
#
|
|
3
|
+
# A fresh install auto-friends ONLY the dora registries (config/default-doras.yaml)
|
|
4
|
+
# and the exits listed here — NOT every machine that happens to be in the dora
|
|
5
|
+
# roster. This keeps a new client's friend list to infrastructure (doras + exits)
|
|
6
|
+
# instead of filling up with other people's personal/compute boxes.
|
|
7
|
+
#
|
|
8
|
+
# Each exit carries a `region`, so `agentnet proxy router` (with no routes.yaml)
|
|
9
|
+
# auto-builds the routing table: China sites → china exits, Japan/Binance →
|
|
10
|
+
# japan exit, Google/YouTube/GitHub/etc → us exits, everything else direct.
|
|
11
|
+
#
|
|
12
|
+
# Mechanism: these userids (plus the dora userids) become the default
|
|
13
|
+
# `dora.autoFriend` whitelist for a freshly-initialised config. The client
|
|
14
|
+
# still LEARNS every roster entry into IPAM (so names/IPs resolve), but only
|
|
15
|
+
# proactively friends infrastructure. Exits still serve arbitrary clients
|
|
16
|
+
# because the daemon auto-ACCEPTS incoming friend-requests — so hub-and-spoke
|
|
17
|
+
# works without meshing every personal machine.
|
|
18
|
+
#
|
|
19
|
+
# To run a full mesh instead (friend everyone in the roster):
|
|
20
|
+
# agentnet dora autofriend all
|
|
21
|
+
# To add/replace an exit: edit this file (data, not code) and republish, or
|
|
22
|
+
# locally: agentnet dora autofriend allow <name|userid> ...
|
|
23
|
+
#
|
|
24
|
+
# `region` is one of: china | japan | us (matches the router's built-in
|
|
25
|
+
# domain lists; see BUILTIN_REGION_DOMAINS in src/proxy/multi-exit-router.ts).
|
|
26
|
+
exits:
|
|
27
|
+
# --- China (China-only / GFW-blocked-from-outside sites) ---
|
|
28
|
+
- name: cn
|
|
29
|
+
userid: 5Aj6uQMd1cNRb9cGT4AwHCN4RdVZjfaGLtgKGhdP1LzN
|
|
30
|
+
virtual_ip: 10.86.1.15
|
|
31
|
+
region: china
|
|
32
|
+
- name: sh
|
|
33
|
+
userid: 6D1PLSVqbSpcxnDMAd8ZXLWycYveeP2hU4g3igDQEqA7
|
|
34
|
+
virtual_ip: 10.86.1.16
|
|
35
|
+
region: china
|
|
36
|
+
- name: callpass
|
|
37
|
+
userid: DZN2L9RV1YkHjqGHMTA6juZhskKA65AD2RyPt4umZVc7
|
|
38
|
+
virtual_ip: 10.86.1.17
|
|
39
|
+
region: china
|
|
40
|
+
# --- Japan (host "lico", dora name node-91) — Binance routes here ---
|
|
41
|
+
- name: tokyo
|
|
42
|
+
userid: 4uNnLnAQHVJ7td657MDaz4UnEuhnTW9sRAPSVeAEEQe4
|
|
43
|
+
virtual_ip: 10.86.68.90
|
|
44
|
+
region: japan
|
|
45
|
+
# --- US (Google, YouTube, GitHub, X/Twitter, LinkedIn, OpenAI) ---
|
|
46
|
+
- name: gojipower
|
|
47
|
+
userid: 2wErj1XreXt1UchE3FGhuvkZ4GoBpo8JGMn8X49nm2ec
|
|
48
|
+
virtual_ip: 10.86.166.16
|
|
49
|
+
region: us
|
|
50
|
+
# snoopy has NO public IP (LAN 10.0.0.115) — proves a NAT'd node can serve
|
|
51
|
+
# as an exit purely over the Carrier tunnel.
|
|
52
|
+
- name: snoopy
|
|
53
|
+
userid: BeMbuKf1poh3U4rjw3eeUAKZsbN2qBSjVZdPSvehmHsw
|
|
54
|
+
virtual_ip: 10.86.156.164
|
|
55
|
+
region: us
|
package/dist/cli/commands.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { resolve, dirname } from "path";
|
|
5
5
|
import { existsSync, mkdirSync, readFileSync } from "fs";
|
|
6
6
|
import { createConnection } from "net";
|
|
7
|
-
import { ConfigLoader, DEFAULT_DORAS } from "../config/loader.js";
|
|
7
|
+
import { ConfigLoader, DEFAULT_DORAS, DEFAULT_EXITS } from "../config/loader.js";
|
|
8
8
|
import { ipcSocketPath } from "../daemon/ipc.js";
|
|
9
9
|
import { DaemonServer } from "../daemon/server.js";
|
|
10
10
|
import { Ipam } from "../ipam/ipam.js";
|
|
@@ -1104,24 +1104,63 @@ export async function cmdProxyRouter(args) {
|
|
|
1104
1104
|
defaultRegion = routesFile.default ?? "direct";
|
|
1105
1105
|
console.log(`Loaded ${regions.length} region(s) from ${routesPath}`);
|
|
1106
1106
|
}
|
|
1107
|
-
else {
|
|
1108
|
-
// ----
|
|
1109
|
-
const discovered = peers
|
|
1110
|
-
|
|
1111
|
-
.
|
|
1112
|
-
|
|
1113
|
-
if (
|
|
1114
|
-
console.error(`No exits
|
|
1115
|
-
console.error(`Pass exits explicitly, e.g.:`);
|
|
1116
|
-
console.error(` agentnet proxy router --exit cn --exit node-5123`);
|
|
1117
|
-
console.error(`Or define regions in ${routesPath} (see docs/INSTALL-NODES.md).`);
|
|
1107
|
+
else if (args.all) {
|
|
1108
|
+
// ---- --all: every discovered peer through exits, single region --------
|
|
1109
|
+
const discovered = peers.filter((p) => p.virtualIp);
|
|
1110
|
+
for (const p of discovered) {
|
|
1111
|
+
exits.push({ name: p.name || p.virtualIp, host: p.virtualIp, port: defaultExitPort, region: "all" });
|
|
1112
|
+
}
|
|
1113
|
+
if (exits.length === 0) {
|
|
1114
|
+
console.error(`No exits discovered from IPAM (${source}). Is the daemon up?`);
|
|
1118
1115
|
process.exit(1);
|
|
1119
1116
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1117
|
+
regions.push({ name: "all", domains: [] });
|
|
1118
|
+
defaultRegion = "all";
|
|
1119
|
+
console.log(`Auto-discovered ${exits.length} exit(s) from IPAM (${source}); routing ALL hosts through them.\n`);
|
|
1120
|
+
}
|
|
1121
|
+
else {
|
|
1122
|
+
// ---- Auto-discovery: curated exits, each in its OWN region -----------
|
|
1123
|
+
// Match live IPAM peers against the shipped exit list (by userid, then
|
|
1124
|
+
// name) and assign each its region (china/japan/us). Non-exit peers
|
|
1125
|
+
// (personal machines) are skipped, so the router never sends traffic
|
|
1126
|
+
// through someone's laptop.
|
|
1127
|
+
const exitByUserid = new Map(DEFAULT_EXITS.map((e) => [e.userid, e]));
|
|
1128
|
+
const exitByName = new Map(DEFAULT_EXITS.map((e) => [e.name, e]));
|
|
1129
|
+
const regionsSeen = new Set();
|
|
1130
|
+
for (const p of peers) {
|
|
1131
|
+
if (!p.virtualIp)
|
|
1132
|
+
continue;
|
|
1133
|
+
const curated = exitByUserid.get(p.carrierId) ?? exitByName.get(p.name);
|
|
1134
|
+
if (!curated)
|
|
1135
|
+
continue;
|
|
1136
|
+
const region = curated.region ?? "china";
|
|
1137
|
+
exits.push({ name: curated.name, host: p.virtualIp, port: defaultExitPort, region });
|
|
1138
|
+
regionsSeen.add(region);
|
|
1139
|
+
}
|
|
1140
|
+
if (exits.length > 0) {
|
|
1141
|
+
for (const r of regionsSeen)
|
|
1142
|
+
regions.push({ name: r, domains: [] });
|
|
1143
|
+
console.log(`Auto-discovered ${exits.length} curated exit(s) from IPAM (${source}): ` +
|
|
1144
|
+
[...regionsSeen].map((r) => `${r}=[${exits.filter((e) => e.region === r).map((e) => e.name).join(",")}]`).join(" "));
|
|
1145
|
+
console.log(`(China→china exits, Japan/Binance→japan, Google/YouTube/etc→us, rest→direct)\n`);
|
|
1146
|
+
}
|
|
1147
|
+
else {
|
|
1148
|
+
// No curated exits in this roster (a different/private network) — fall
|
|
1149
|
+
// back to treating every discovered peer as a China exit.
|
|
1150
|
+
const discovered = peers
|
|
1151
|
+
.filter((p) => p.virtualIp)
|
|
1152
|
+
.map((p) => ({ name: p.name || p.virtualIp, host: p.virtualIp, port: defaultExitPort, region: "china" }));
|
|
1153
|
+
if (discovered.length === 0) {
|
|
1154
|
+
console.error(`No exits given and none discovered from IPAM (${source}).`);
|
|
1155
|
+
console.error(`Pass exits explicitly, e.g.: agentnet proxy router --exit cn --exit sh`);
|
|
1156
|
+
console.error(`Or define regions in ${routesPath} (see docs/INSTALL-NODES.md).`);
|
|
1157
|
+
process.exit(1);
|
|
1158
|
+
}
|
|
1159
|
+
exits.push(...discovered);
|
|
1160
|
+
regions.push({ name: "china", domains: [] });
|
|
1161
|
+
console.log(`Auto-discovered ${discovered.length} exit(s) from IPAM (${source}); none curated, routing China-split.\n`);
|
|
1162
|
+
}
|
|
1163
|
+
defaultRegion = "direct";
|
|
1125
1164
|
}
|
|
1126
1165
|
startMultiExitRouter({
|
|
1127
1166
|
exits,
|
package/dist/config/loader.d.ts
CHANGED
|
@@ -26,6 +26,28 @@ export interface DefaultDora {
|
|
|
26
26
|
export declare const DEFAULT_DORAS: DefaultDora[];
|
|
27
27
|
export declare const DEFAULT_DORA_USERID: string;
|
|
28
28
|
export declare const DEFAULT_DORA_ADDRESS: string;
|
|
29
|
+
/**
|
|
30
|
+
* Official exit / infrastructure nodes (config/default-exits.yaml). These,
|
|
31
|
+
* together with the dora userids, form the default `autoFriend` whitelist so
|
|
32
|
+
* a fresh install only friends infrastructure — not every personal/compute
|
|
33
|
+
* box that shows up in the dora roster. Editable data, not code.
|
|
34
|
+
*/
|
|
35
|
+
export interface DefaultExit {
|
|
36
|
+
name: string;
|
|
37
|
+
userid: string;
|
|
38
|
+
virtual_ip?: string;
|
|
39
|
+
/** Routing region: china | japan | us. Used by `agentnet proxy router` to
|
|
40
|
+
* auto-build the domain→region table with zero config. */
|
|
41
|
+
region?: string;
|
|
42
|
+
}
|
|
43
|
+
export declare const DEFAULT_EXITS: DefaultExit[];
|
|
44
|
+
/**
|
|
45
|
+
* The default `dora.autoFriend` whitelist for a fresh config: every dora plus
|
|
46
|
+
* every official exit, by userid. A new client friends only these (the dora
|
|
47
|
+
* roster is still fully learned into IPAM for name/IP resolution). Override
|
|
48
|
+
* with `agentnet dora autofriend all` for a full mesh.
|
|
49
|
+
*/
|
|
50
|
+
export declare const DEFAULT_AUTOFRIEND: string[];
|
|
29
51
|
export declare class ConfigLoader {
|
|
30
52
|
static defaultConfigPath(): string;
|
|
31
53
|
static defaultConfigDir(): string;
|
package/dist/config/loader.js
CHANGED
|
@@ -72,6 +72,27 @@ export const DEFAULT_DORAS = loadDefaultDoras();
|
|
|
72
72
|
// Back-compat single-value exports (first/primary dora).
|
|
73
73
|
export const DEFAULT_DORA_USERID = DEFAULT_DORAS[0].userid;
|
|
74
74
|
export const DEFAULT_DORA_ADDRESS = DEFAULT_DORAS[0].address;
|
|
75
|
+
function loadDefaultExits() {
|
|
76
|
+
try {
|
|
77
|
+
const file = resolve(dirname(new URL(import.meta.url).pathname), "../../config/default-exits.yaml");
|
|
78
|
+
const parsed = yaml.load(readFileSync(file, "utf-8"));
|
|
79
|
+
return (parsed?.exits ?? []).filter((e) => e && e.userid);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export const DEFAULT_EXITS = loadDefaultExits();
|
|
86
|
+
/**
|
|
87
|
+
* The default `dora.autoFriend` whitelist for a fresh config: every dora plus
|
|
88
|
+
* every official exit, by userid. A new client friends only these (the dora
|
|
89
|
+
* roster is still fully learned into IPAM for name/IP resolution). Override
|
|
90
|
+
* with `agentnet dora autofriend all` for a full mesh.
|
|
91
|
+
*/
|
|
92
|
+
export const DEFAULT_AUTOFRIEND = [
|
|
93
|
+
...DEFAULT_DORAS.map((d) => d.userid),
|
|
94
|
+
...DEFAULT_EXITS.map((e) => e.userid),
|
|
95
|
+
];
|
|
75
96
|
export class ConfigLoader {
|
|
76
97
|
static defaultConfigPath() {
|
|
77
98
|
return resolve(this.defaultConfigDir(), "config.yaml");
|
|
@@ -174,13 +195,15 @@ export class ConfigLoader {
|
|
|
174
195
|
enabled: true,
|
|
175
196
|
userids: DEFAULT_DORAS.map((d) => d.userid),
|
|
176
197
|
refreshIntervalMs: 60_000,
|
|
177
|
-
// Default: auto-friend
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
|
|
198
|
+
// Default: auto-friend ONLY infrastructure — the dora registries and
|
|
199
|
+
// the official exit nodes (config/default-exits.yaml) — not every
|
|
200
|
+
// personal/compute box in the roster. Hub-and-spoke: a client connects
|
|
201
|
+
// to doras + exits; exits still serve arbitrary clients because the
|
|
202
|
+
// daemon auto-ACCEPTS incoming friend-requests. The full roster is
|
|
203
|
+
// still learned into IPAM for name/IP resolution. Run a full mesh with
|
|
204
|
+
// `agentnet dora autofriend all`, lock down with `... none`, or curate
|
|
205
|
+
// with `agentnet dora autofriend allow <peer>...`.
|
|
206
|
+
autoFriend: DEFAULT_AUTOFRIEND,
|
|
184
207
|
},
|
|
185
208
|
};
|
|
186
209
|
}
|
|
@@ -33,15 +33,33 @@ export const BUILTIN_REGION_DOMAINS = {
|
|
|
33
33
|
".volcfcdn.com", ".volccdn.com", ".byteimg.com", ".bytedance.com",
|
|
34
34
|
".weibo.com", ".weibocdn.com", ".sinaimg.cn", ".youku.cn",
|
|
35
35
|
],
|
|
36
|
-
// Japan-region sites / services that geo-fence to a JP IP
|
|
36
|
+
// Japan-region sites / services that geo-fence to a JP IP, plus Binance
|
|
37
|
+
// (routed through the Japan exit — it's blocked/geo-restricted on many
|
|
38
|
+
// other paths). .bnbstatic.com is Binance's asset CDN; without it the
|
|
39
|
+
// site loads blank.
|
|
37
40
|
japan: [
|
|
38
41
|
".jp",
|
|
39
42
|
".nicovideo.jp", ".nimg.jp", ".dmm.com", ".dmm.co.jp",
|
|
40
43
|
".abema.tv", ".abema.io", ".tver.jp", ".unext.jp",
|
|
41
44
|
".radiko.jp", ".pixiv.net", ".pximg.net",
|
|
45
|
+
".binance.com", ".binance.org", ".bnbstatic.com", ".binancecnt.com",
|
|
42
46
|
],
|
|
43
|
-
// US
|
|
47
|
+
// US region: Western sites commonly blocked/throttled from China (route via
|
|
48
|
+
// a US exit), plus US-geo-fenced streaming.
|
|
44
49
|
us: [
|
|
50
|
+
// Google / YouTube
|
|
51
|
+
".google.com", ".google.com.hk", ".gstatic.com", ".googleapis.com",
|
|
52
|
+
".googleusercontent.com", ".googlevideo.com", ".ggpht.com", ".withgoogle.com", ".goo.gl",
|
|
53
|
+
".youtube.com", ".youtu.be", ".ytimg.com",
|
|
54
|
+
// GitHub
|
|
55
|
+
".github.com", ".github.io", ".githubusercontent.com", ".githubassets.com", ".ghcr.io",
|
|
56
|
+
// X / Twitter
|
|
57
|
+
".twitter.com", ".x.com", ".twimg.com", ".t.co",
|
|
58
|
+
// LinkedIn
|
|
59
|
+
".linkedin.com", ".licdn.com",
|
|
60
|
+
// OpenAI / ChatGPT
|
|
61
|
+
".openai.com", ".chatgpt.com", ".oaistatic.com", ".oaiusercontent.com",
|
|
62
|
+
// US-geo-fenced streaming
|
|
45
63
|
".hulu.com", ".huluim.com", ".hulustream.com",
|
|
46
64
|
".peacocktv.com", ".max.com", ".hbomax.com",
|
|
47
65
|
".pluto.tv", ".tubi.tv", ".tubitv.com",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/lan",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.73",
|
|
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",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"dist",
|
|
20
20
|
"bin",
|
|
21
21
|
"config/default-doras.yaml",
|
|
22
|
+
"config/default-exits.yaml",
|
|
22
23
|
"config/routes.example.yaml",
|
|
23
24
|
"README.md",
|
|
24
25
|
"LICENSE",
|