@decentnetwork/lan 0.1.3 → 0.1.5
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/bin/tun-helper-darwin-amd64 +0 -0
- package/bin/tun-helper-darwin-arm64 +0 -0
- package/bin/tun-helper-linux-amd64 +0 -0
- package/bin/tun-helper-linux-arm64 +0 -0
- package/dist/cli/commands.d.ts +11 -0
- package/dist/cli/commands.js +59 -0
- package/dist/cli/index.js +20 -1
- package/dist/config/loader.js +5 -0
- package/dist/dora/dora-integration.d.ts +6 -0
- package/dist/dora/dora-integration.js +47 -1
- package/dist/types.d.ts +19 -0
- package/docs/CONFIGURATION.md +21 -1
- package/package.json +1 -1
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/cli/commands.d.ts
CHANGED
|
@@ -254,3 +254,14 @@ export declare function cmdDoraDisable(args: {
|
|
|
254
254
|
export declare function cmdDoraStatus(args: {
|
|
255
255
|
configDir?: string;
|
|
256
256
|
}): Promise<void>;
|
|
257
|
+
/**
|
|
258
|
+
* Set the dora auto-friend policy. Three modes:
|
|
259
|
+
* - 'none' (default): never auto-friend roster peers
|
|
260
|
+
* - 'all': friend every roster entry that carries an address
|
|
261
|
+
* - 'allow <id-or-name>...' / 'remove <id-or-name>...': whitelist
|
|
262
|
+
*/
|
|
263
|
+
export declare function cmdDoraAutofriend(args: {
|
|
264
|
+
mode: "none" | "all" | "allow" | "remove" | "list";
|
|
265
|
+
values?: string[];
|
|
266
|
+
configDir?: string;
|
|
267
|
+
}): Promise<void>;
|
package/dist/cli/commands.js
CHANGED
|
@@ -1045,6 +1045,65 @@ export async function cmdDoraStatus(args) {
|
|
|
1045
1045
|
}
|
|
1046
1046
|
const refresh = dora.refreshIntervalMs ?? 60_000;
|
|
1047
1047
|
console.log(` refresh: every ${Math.round(refresh / 1000)}s`);
|
|
1048
|
+
const af = dora.autoFriend ?? "none";
|
|
1049
|
+
if (af === "none" || af === "all") {
|
|
1050
|
+
console.log(` auto-friend: ${af}`);
|
|
1051
|
+
}
|
|
1052
|
+
else {
|
|
1053
|
+
console.log(` auto-friend: whitelist [${af.length}]: ${af.join(", ")}`);
|
|
1054
|
+
}
|
|
1048
1055
|
console.log("");
|
|
1049
1056
|
console.log("Note: live state (allocated IP, last refresh) requires querying a running daemon — not yet wired.");
|
|
1050
1057
|
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Set the dora auto-friend policy. Three modes:
|
|
1060
|
+
* - 'none' (default): never auto-friend roster peers
|
|
1061
|
+
* - 'all': friend every roster entry that carries an address
|
|
1062
|
+
* - 'allow <id-or-name>...' / 'remove <id-or-name>...': whitelist
|
|
1063
|
+
*/
|
|
1064
|
+
export async function cmdDoraAutofriend(args) {
|
|
1065
|
+
const dir = args.configDir || ConfigLoader.defaultConfigDir();
|
|
1066
|
+
const configPath = resolve(dir, "config.yaml");
|
|
1067
|
+
const config = await ConfigLoader.load(configPath);
|
|
1068
|
+
const dora = config.dora ?? { enabled: false, userids: [], refreshIntervalMs: 60_000 };
|
|
1069
|
+
const current = dora.autoFriend ?? "none";
|
|
1070
|
+
if (args.mode === "list") {
|
|
1071
|
+
if (current === "none" || current === "all") {
|
|
1072
|
+
console.log(`auto-friend: ${current}`);
|
|
1073
|
+
}
|
|
1074
|
+
else {
|
|
1075
|
+
console.log(`auto-friend: whitelist (${current.length})`);
|
|
1076
|
+
for (const t of current)
|
|
1077
|
+
console.log(` - ${t}`);
|
|
1078
|
+
}
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
let next;
|
|
1082
|
+
if (args.mode === "none" || args.mode === "all") {
|
|
1083
|
+
next = args.mode;
|
|
1084
|
+
}
|
|
1085
|
+
else if (args.mode === "allow") {
|
|
1086
|
+
const list = Array.isArray(current) ? new Set(current) : new Set();
|
|
1087
|
+
for (const v of args.values ?? [])
|
|
1088
|
+
list.add(v);
|
|
1089
|
+
next = [...list];
|
|
1090
|
+
}
|
|
1091
|
+
else if (args.mode === "remove") {
|
|
1092
|
+
const list = Array.isArray(current) ? new Set(current) : new Set();
|
|
1093
|
+
for (const v of args.values ?? [])
|
|
1094
|
+
list.delete(v);
|
|
1095
|
+
next = list.size === 0 ? "none" : [...list];
|
|
1096
|
+
}
|
|
1097
|
+
else {
|
|
1098
|
+
throw new Error(`unknown autofriend mode: ${args.mode}`);
|
|
1099
|
+
}
|
|
1100
|
+
config.dora = { ...dora, autoFriend: next };
|
|
1101
|
+
await ConfigLoader.save(config, configPath);
|
|
1102
|
+
if (next === "none" || next === "all") {
|
|
1103
|
+
console.log(`auto-friend set to '${next}'.`);
|
|
1104
|
+
}
|
|
1105
|
+
else {
|
|
1106
|
+
console.log(`auto-friend whitelist now (${next.length}): ${next.join(", ")}`);
|
|
1107
|
+
}
|
|
1108
|
+
console.log(`Takes effect on the next roster refresh (~60s) or daemon restart.`);
|
|
1109
|
+
}
|
package/dist/cli/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { hideBin } from "yargs/helpers";
|
|
|
10
10
|
// Belt-and-braces — also raise it here in case the CLI is run directly
|
|
11
11
|
// (e.g. `node dist/cli/index.js` rather than via dist/index.js).
|
|
12
12
|
EventEmitter.defaultMaxListeners = 100;
|
|
13
|
-
import { cmdInit, cmdIdentityShow, cmdPeersList, cmdIpamAssign, cmdGrant, cmdRevoke, cmdResolve, cmdStatus, cmdUp, cmdAuditLog, cmdFriendRequest, cmdFriendAccept, cmdFriendsList, cmdFriendsPending, cmdFriendsAccept, cmdFriendsReject, cmdProxyEnable, cmdProxyDisable, cmdProxyStatus, cmdProxyAllowHost, cmdProxyRevokeHost, cmdProxyListHosts, cmdProxyUse, cmdDoraEnable, cmdDoraDisable, cmdDoraStatus, cmdDiag, cmdDnsInstall, cmdDnsHosts, } from "./commands.js";
|
|
13
|
+
import { cmdInit, cmdIdentityShow, cmdPeersList, cmdIpamAssign, cmdGrant, cmdRevoke, cmdResolve, cmdStatus, cmdUp, cmdAuditLog, cmdFriendRequest, cmdFriendAccept, cmdFriendsList, cmdFriendsPending, cmdFriendsAccept, cmdFriendsReject, cmdProxyEnable, cmdProxyDisable, cmdProxyStatus, cmdProxyAllowHost, cmdProxyRevokeHost, cmdProxyListHosts, cmdProxyUse, cmdDoraEnable, cmdDoraDisable, cmdDoraStatus, cmdDoraAutofriend, cmdDiag, cmdDnsInstall, cmdDnsHosts, } from "./commands.js";
|
|
14
14
|
async function main() {
|
|
15
15
|
await yargs(hideBin(process.argv))
|
|
16
16
|
.scriptName("agentnet")
|
|
@@ -204,6 +204,25 @@ async function main() {
|
|
|
204
204
|
})
|
|
205
205
|
.command("status", "Show dora configuration", (yy) => yy.option("config-dir", { type: "string" }), async (argv) => {
|
|
206
206
|
await cmdDoraStatus({ configDir: argv["config-dir"] });
|
|
207
|
+
})
|
|
208
|
+
.command("autofriend <mode> [values..]", "Auto-friend policy: 'none' (default), 'all', 'allow <name|userid>...', 'remove <name|userid>...', or 'list'", (yy) => yy
|
|
209
|
+
.positional("mode", {
|
|
210
|
+
type: "string",
|
|
211
|
+
demandOption: true,
|
|
212
|
+
choices: ["none", "all", "allow", "remove", "list"],
|
|
213
|
+
describe: "Policy mode",
|
|
214
|
+
})
|
|
215
|
+
.positional("values", {
|
|
216
|
+
type: "string",
|
|
217
|
+
array: true,
|
|
218
|
+
describe: "For 'allow'/'remove': one or more names or userids",
|
|
219
|
+
})
|
|
220
|
+
.option("config-dir", { type: "string" }), async (argv) => {
|
|
221
|
+
await cmdDoraAutofriend({
|
|
222
|
+
mode: argv.mode,
|
|
223
|
+
values: argv.values,
|
|
224
|
+
configDir: argv["config-dir"],
|
|
225
|
+
});
|
|
207
226
|
})
|
|
208
227
|
.demandCommand(1, "Specify a dora subcommand (run 'agentnet dora --help')"), () => {
|
|
209
228
|
// parent handler — never invoked because demandCommand above
|
package/dist/config/loader.js
CHANGED
|
@@ -108,6 +108,11 @@ export class ConfigLoader {
|
|
|
108
108
|
enabled: false,
|
|
109
109
|
userids: [],
|
|
110
110
|
refreshIntervalMs: 60_000,
|
|
111
|
+
// Default: do NOT auto-friend roster peers. Dora is a naming
|
|
112
|
+
// / IP service, not a trust statement. Operators opt in with
|
|
113
|
+
// `agentnet dora autofriend all` (closed labs) or
|
|
114
|
+
// `agentnet dora autofriend allow <name|userid>` (whitelist).
|
|
115
|
+
autoFriend: "none",
|
|
111
116
|
},
|
|
112
117
|
};
|
|
113
118
|
}
|
|
@@ -87,4 +87,10 @@ export declare class DoraIntegration {
|
|
|
87
87
|
* re-register the peer or fall back to a manual friend-request.
|
|
88
88
|
*/
|
|
89
89
|
private maybeFriend;
|
|
90
|
+
/**
|
|
91
|
+
* Decide whether a roster entry is allowed by the autoFriend
|
|
92
|
+
* policy. Names match case-insensitively against the entry's
|
|
93
|
+
* `name`; userids match exact base58.
|
|
94
|
+
*/
|
|
95
|
+
private policyAllows;
|
|
90
96
|
}
|
|
@@ -76,7 +76,16 @@ export class DoraIntegration {
|
|
|
76
76
|
new Promise((r) => setTimeout(() => r(null), 30_000)),
|
|
77
77
|
]);
|
|
78
78
|
if (!anyOnline) {
|
|
79
|
-
this.logger.warn("No dora server became reachable within 30s — falling back to config IP");
|
|
79
|
+
this.logger.warn("No dora server became reachable within 30s — falling back to config IP. Will retry in background.");
|
|
80
|
+
// Without this the daemon would never re-attempt registration
|
|
81
|
+
// after a cold-start where the dora friend session was slow
|
|
82
|
+
// to come up. Symptom: friends.status=online for dora a
|
|
83
|
+
// moment later, but ipam stays empty and packetsForwarded=0
|
|
84
|
+
// forever because we never called register/list. The 30s
|
|
85
|
+
// window expiring is the SAME failure class as register
|
|
86
|
+
// throwing AllRegistriesUnavailableError, so reuse the same
|
|
87
|
+
// retry timer.
|
|
88
|
+
this.scheduleBootstrapRetry();
|
|
80
89
|
return this.opts.preferredIp ?? "";
|
|
81
90
|
}
|
|
82
91
|
this.logger.debug(`Dora friend online: ${anyOnline.id}`);
|
|
@@ -304,6 +313,22 @@ export class DoraIntegration {
|
|
|
304
313
|
this.friendRequested.add(entry.userid);
|
|
305
314
|
return;
|
|
306
315
|
}
|
|
316
|
+
// Policy gate. Dora is a name service, not a trust statement.
|
|
317
|
+
// Default ("none") means we DON'T auto-friend roster peers —
|
|
318
|
+
// an operator on a multi-tenant dora isn't asking to be
|
|
319
|
+
// mutually-friended with everyone else just because they share
|
|
320
|
+
// a name service. "all" reproduces the old behavior (closed
|
|
321
|
+
// labs). Otherwise the policy is a whitelist of names or
|
|
322
|
+
// userids; only matches are friended.
|
|
323
|
+
const policy = this.opts.config.autoFriend ?? "none";
|
|
324
|
+
if (!this.policyAllows(entry, policy)) {
|
|
325
|
+
// Mark as "seen" so we don't re-evaluate the policy every
|
|
326
|
+
// 60s for entries we deliberately skip — but DON'T mark as
|
|
327
|
+
// "friendRequested" since the operator may flip the policy
|
|
328
|
+
// later and we should pick them up on next refresh after a
|
|
329
|
+
// daemon restart.
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
307
332
|
if (!entry.address) {
|
|
308
333
|
this.logger.warn(`Roster entry ${entry.name} (${entry.userid.slice(0, 12)}...) has no address — can't auto-friend. ` +
|
|
309
334
|
`Have them re-register against a newer dora server, or run 'agentnet friend-request' manually.`);
|
|
@@ -322,4 +347,25 @@ export class DoraIntegration {
|
|
|
322
347
|
this.friendRequested.delete(entry.userid);
|
|
323
348
|
});
|
|
324
349
|
}
|
|
350
|
+
/**
|
|
351
|
+
* Decide whether a roster entry is allowed by the autoFriend
|
|
352
|
+
* policy. Names match case-insensitively against the entry's
|
|
353
|
+
* `name`; userids match exact base58.
|
|
354
|
+
*/
|
|
355
|
+
policyAllows(entry, policy) {
|
|
356
|
+
if (policy === "all")
|
|
357
|
+
return true;
|
|
358
|
+
if (policy === "none")
|
|
359
|
+
return false;
|
|
360
|
+
if (!Array.isArray(policy) || policy.length === 0)
|
|
361
|
+
return false;
|
|
362
|
+
const nameLower = entry.name.toLowerCase();
|
|
363
|
+
for (const token of policy) {
|
|
364
|
+
if (token === entry.userid)
|
|
365
|
+
return true;
|
|
366
|
+
if (token.toLowerCase() === nameLower)
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
325
371
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -142,6 +142,25 @@ export interface DoraConfig {
|
|
|
142
142
|
userids?: string[];
|
|
143
143
|
/** How often to re-fetch the full roster from dora. Default 60_000ms. */
|
|
144
144
|
refreshIntervalMs?: number;
|
|
145
|
+
/**
|
|
146
|
+
* Policy for auto-friending peers found in the dora roster. Dora is
|
|
147
|
+
* a NAMING and IP-ALLOCATION service — it does NOT imply trust.
|
|
148
|
+
* On a large dora (many tenants, customer machines, etc.) you do
|
|
149
|
+
* NOT want to auto-friend everyone.
|
|
150
|
+
*
|
|
151
|
+
* - `"none"` (default): never auto-friend roster peers. The operator
|
|
152
|
+
* initiates friend-requests explicitly with `agentnet
|
|
153
|
+
* friend-request --address <addr>`. The dora server itself is
|
|
154
|
+
* always friended out-of-band when you run `agentnet dora enable
|
|
155
|
+
* --address`.
|
|
156
|
+
* - `"all"`: friend every roster entry that carries an address.
|
|
157
|
+
* Useful for a closed lab of trusted peers; **dangerous on a
|
|
158
|
+
* shared / public dora.**
|
|
159
|
+
* - `string[]`: whitelist by peer NAME (matches `name` in the roster
|
|
160
|
+
* record) or by userid (base58). Matching entries are auto-
|
|
161
|
+
* friended on every refresh; everything else is left alone.
|
|
162
|
+
*/
|
|
163
|
+
autoFriend?: "none" | "all" | string[];
|
|
145
164
|
}
|
|
146
165
|
export interface DecentAgentNetConfig {
|
|
147
166
|
node: NodeConfig;
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -112,7 +112,27 @@ ACL-gated; see `agentnet proxy --help`.
|
|
|
112
112
|
- **`userids`** — list of dora servers to try, in order. First
|
|
113
113
|
responder wins.
|
|
114
114
|
- **`refreshIntervalMs`** — how often to re-pull the roster from
|
|
115
|
-
dora (default 60s).
|
|
115
|
+
dora (default 60s).
|
|
116
|
+
- **`autoFriend`** — policy for friending peers found in the dora
|
|
117
|
+
roster. Dora is a name service, NOT a trust statement; on a
|
|
118
|
+
multi-tenant dora you do not want the daemon to silently friend
|
|
119
|
+
every other peer.
|
|
120
|
+
- `"none"` (default) — never auto-friend roster peers; only the
|
|
121
|
+
dora server itself is friended (you do that explicitly with
|
|
122
|
+
`agentnet dora enable --address`).
|
|
123
|
+
- `"all"` — friend every roster entry that carries an address.
|
|
124
|
+
Reasonable for closed labs of mutually-trusted peers.
|
|
125
|
+
- `string[]` — whitelist by peer name OR userid. Only matching
|
|
126
|
+
entries get auto-friended.
|
|
127
|
+
|
|
128
|
+
CLI:
|
|
129
|
+
```
|
|
130
|
+
agentnet dora autofriend none # default
|
|
131
|
+
agentnet dora autofriend all # closed lab
|
|
132
|
+
agentnet dora autofriend allow mac-dev snoopy peerX-userid # whitelist
|
|
133
|
+
agentnet dora autofriend remove snoopy
|
|
134
|
+
agentnet dora autofriend list
|
|
135
|
+
```
|
|
116
136
|
|
|
117
137
|
## `policy.yaml` reference
|
|
118
138
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/lan",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
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",
|