@decentnetwork/lan 0.1.268 → 0.1.269
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/dist/daemon/ipc.d.ts
CHANGED
|
@@ -100,6 +100,11 @@ export interface IpcHandlers {
|
|
|
100
100
|
* without adding an op per feature. Inbound ones arrive as
|
|
101
101
|
* `{type:"app", userid, packetId, data}` events on `subscribe`. */
|
|
102
102
|
appSend: (userid: string, packetId: number, data: Uint8Array) => Promise<void>;
|
|
103
|
+
/** Per-registry dora health: asks each configured dora for its roster
|
|
104
|
+
* INDIVIDUALLY, so one healthy registry cannot mask three dead ones the way
|
|
105
|
+
* the merged resolve path does. Backs external monitoring, which would
|
|
106
|
+
* otherwise need its own Carrier peer and a cold session per check. */
|
|
107
|
+
doraStatus: () => Promise<Record<string, unknown>>;
|
|
103
108
|
/** Long-poll for inbound call-signaling payloads. Resolves with any queued
|
|
104
109
|
* signals immediately, otherwise holds the connection until one arrives or a
|
|
105
110
|
* ~20s timeout elapses (then resolves empty). The UI re-polls to get
|
|
@@ -127,7 +132,7 @@ export interface IpcHandlers {
|
|
|
127
132
|
selfRestart: () => Promise<Record<string, unknown>>;
|
|
128
133
|
}
|
|
129
134
|
export interface IpcRequest {
|
|
130
|
-
op: "friend-request" | "ping" | "diag" | "friends-pending" | "friends-accept" | "friends-reject" | "chat-send" | "chat-log-local" | "file-log-local" | "chat-history" | "friends-list" | "friend-remove" | "friend-set-alias" | "friends-autoaccept" | "set-profile" | "file-send" | "file-delete" | "file-cancel" | "file-retry" | "chat-mark-read" | "subscribe" | "sign" | "call-signal" | "call-poll" | "proxy-reload" | "proxy-access" | "app-send" | "self-restart";
|
|
135
|
+
op: "friend-request" | "ping" | "diag" | "friends-pending" | "friends-accept" | "friends-reject" | "chat-send" | "chat-log-local" | "file-log-local" | "chat-history" | "friends-list" | "friend-remove" | "friend-set-alias" | "friends-autoaccept" | "set-profile" | "file-send" | "file-delete" | "file-cancel" | "file-retry" | "chat-mark-read" | "subscribe" | "sign" | "call-signal" | "call-poll" | "proxy-reload" | "proxy-access" | "app-send" | "dora-status" | "self-restart";
|
|
131
136
|
address?: string;
|
|
132
137
|
hello?: string;
|
|
133
138
|
userid?: string;
|
package/dist/daemon/ipc.js
CHANGED
|
@@ -294,6 +294,8 @@ export class IpcServer {
|
|
|
294
294
|
await this.handlers.appSend(req.userid, req.packetId, new Uint8Array(bytes));
|
|
295
295
|
return { sent: bytes.length };
|
|
296
296
|
}
|
|
297
|
+
case "dora-status":
|
|
298
|
+
return await this.handlers.doraStatus();
|
|
297
299
|
case "call-poll":
|
|
298
300
|
return await this.handlers.callPoll();
|
|
299
301
|
case "proxy-reload":
|
package/dist/daemon/server.js
CHANGED
|
@@ -703,6 +703,11 @@ export class DaemonServer {
|
|
|
703
703
|
// applies to every caller, not just this socket.
|
|
704
704
|
await this.peerManager.sendAppPacket(userid, packetId, data);
|
|
705
705
|
},
|
|
706
|
+
doraStatus: async () => {
|
|
707
|
+
if (!this.doraIntegration)
|
|
708
|
+
return { checkedAt: new Date().toISOString(), registries: [], note: "dora not enabled on this node" };
|
|
709
|
+
return await this.doraIntegration.doraStatus();
|
|
710
|
+
},
|
|
706
711
|
callPoll: async () => {
|
|
707
712
|
// Drain immediately if signals are queued; otherwise hold up to ~20s
|
|
708
713
|
// for one to arrive (near-instant delivery without a WebSocket). The
|
|
@@ -114,6 +114,26 @@ export declare class DoraIntegration {
|
|
|
114
114
|
*/
|
|
115
115
|
private tryRegister;
|
|
116
116
|
stop(): void;
|
|
117
|
+
/**
|
|
118
|
+
* Per-dora health, by asking each one for its roster.
|
|
119
|
+
*
|
|
120
|
+
* This lives in the daemon rather than in a monitoring tool with its own peer
|
|
121
|
+
* for one reason: reaching a dora needs a warm net_crypto session, and the
|
|
122
|
+
* daemon already holds one to every configured registry. A separate process
|
|
123
|
+
* has to build that from scratch on every check, which is slow, adds a second
|
|
124
|
+
* identity for the same operator, and fails in ways that look exactly like
|
|
125
|
+
* the outage it is trying to detect.
|
|
126
|
+
*
|
|
127
|
+
* Each registry is asked INDIVIDUALLY. The normal list() merges every
|
|
128
|
+
* registry's answer, which is right for resolving names and useless for
|
|
129
|
+
* health: one dora answering makes the merged call succeed while three sit
|
|
130
|
+
* dead — which is precisely how a dora stayed broken for ten days unnoticed.
|
|
131
|
+
*
|
|
132
|
+
* Results are cached briefly so a polling dashboard cannot turn into load on
|
|
133
|
+
* the registries it is watching.
|
|
134
|
+
*/
|
|
135
|
+
private doraStatusCache?;
|
|
136
|
+
doraStatus(maxAgeMs?: number): Promise<Record<string, unknown>>;
|
|
117
137
|
getAllocatedIp(): string | undefined;
|
|
118
138
|
private refreshRoster;
|
|
119
139
|
/**
|
|
@@ -384,6 +384,58 @@ export class DoraIntegration {
|
|
|
384
384
|
this.retryBootstrapTimer = undefined;
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
|
+
/**
|
|
388
|
+
* Per-dora health, by asking each one for its roster.
|
|
389
|
+
*
|
|
390
|
+
* This lives in the daemon rather than in a monitoring tool with its own peer
|
|
391
|
+
* for one reason: reaching a dora needs a warm net_crypto session, and the
|
|
392
|
+
* daemon already holds one to every configured registry. A separate process
|
|
393
|
+
* has to build that from scratch on every check, which is slow, adds a second
|
|
394
|
+
* identity for the same operator, and fails in ways that look exactly like
|
|
395
|
+
* the outage it is trying to detect.
|
|
396
|
+
*
|
|
397
|
+
* Each registry is asked INDIVIDUALLY. The normal list() merges every
|
|
398
|
+
* registry's answer, which is right for resolving names and useless for
|
|
399
|
+
* health: one dora answering makes the merged call succeed while three sit
|
|
400
|
+
* dead — which is precisely how a dora stayed broken for ten days unnoticed.
|
|
401
|
+
*
|
|
402
|
+
* Results are cached briefly so a polling dashboard cannot turn into load on
|
|
403
|
+
* the registries it is watching.
|
|
404
|
+
*/
|
|
405
|
+
doraStatusCache;
|
|
406
|
+
async doraStatus(maxAgeMs = 20_000) {
|
|
407
|
+
const cached = this.doraStatusCache;
|
|
408
|
+
if (cached && Date.now() - cached.at < maxAgeMs)
|
|
409
|
+
return cached.value;
|
|
410
|
+
const userids = this.opts.config.userids ?? [];
|
|
411
|
+
const checks = await Promise.all(userids.map(async (id) => {
|
|
412
|
+
const started = Date.now();
|
|
413
|
+
try {
|
|
414
|
+
// One client per registry: this is the whole point — a merged call
|
|
415
|
+
// would hide exactly the failure we are looking for.
|
|
416
|
+
const one = new DoraClient({
|
|
417
|
+
registryUserids: [id],
|
|
418
|
+
sendText: (to, text) => this.opts.peerManager.sendText(to, text),
|
|
419
|
+
onText: (h) => this.opts.peerManager.on("message", (pubkey, text) => h(pubkey, text)),
|
|
420
|
+
timeoutMs: 8000,
|
|
421
|
+
});
|
|
422
|
+
const roster = await one.list();
|
|
423
|
+
return { userid: id, ok: Array.isArray(roster) && roster.length > 0,
|
|
424
|
+
entries: Array.isArray(roster) ? roster.length : 0,
|
|
425
|
+
ms: Date.now() - started,
|
|
426
|
+
...(Array.isArray(roster) && roster.length === 0
|
|
427
|
+
? { error: "roster empty — answering but holding no records" }
|
|
428
|
+
: {}) };
|
|
429
|
+
}
|
|
430
|
+
catch (err) {
|
|
431
|
+
return { userid: id, ok: false, entries: 0, ms: Date.now() - started,
|
|
432
|
+
error: err instanceof Error ? err.message : String(err) };
|
|
433
|
+
}
|
|
434
|
+
}));
|
|
435
|
+
const value = { checkedAt: new Date().toISOString(), registries: checks };
|
|
436
|
+
this.doraStatusCache = { at: Date.now(), value };
|
|
437
|
+
return value;
|
|
438
|
+
}
|
|
387
439
|
getAllocatedIp() {
|
|
388
440
|
return this.allocatedIp;
|
|
389
441
|
}
|
package/dist/ui/desktop/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
window.__DK_UI_VERSION="0.1.
|
|
1
|
+
window.__DK_UI_VERSION="0.1.269";
|
|
2
2
|
const ICON_PATHS = {
|
|
3
3
|
// ---- tab bar (the four must feel like one set) ----
|
|
4
4
|
users: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/lan",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.269",
|
|
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",
|