dsh-mizar 0.8.6 → 0.8.7

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/README.md CHANGED
@@ -135,6 +135,10 @@ DSH 自己的 Web 接口暴露出去(那是 DSH 的安全边界,插件不碰
135
135
  漂到 19391、19392,手机配对里保存的直连地址就永远失效了。现在一个进程只保留**一个**挂载:
136
136
  新挂载要等旧挂载完全释放监听之后才绑端口;网络变化导致监听地址改变时,隧道也会立刻用新
137
137
  地址重拨,并把监听端口的变化同步给隧道(否则隧道健康、手机请求却全部 502)。
138
+ - **0.8.7 修了 0.8.6 单实例的漏洞。** 0.8.6 的注册表是模块级的,而 DSH 会在同一进程里多次
139
+ 加载插件模块,每份副本各有一张空注册表——实测一个进程因此挂了 8 个监听(19390–19397)。
140
+ 0.8.7 把注册表放到 `globalThis`(`Symbol.for('dsh-mizar.mount-registry')`),所有模块副本
141
+ 共用同一个槽位,一个进程只剩一个监听。
138
142
 
139
143
  日志里对应 `relay: the tunnel has been silent for …s — redialling it`、
140
144
  `relay: the network changed — redialling the tunnel` 和 `relay: the listener moved to port …`。
package/lib/index.js CHANGED
@@ -27,7 +27,7 @@ import { awaitRequestedPlan, bindKey, isRequested, needsRebind } from './net/wat
27
27
  import { PROTOCOL_VERSION } from './protocol/frames.js';
28
28
  import { MizarListener } from './server/listener.js';
29
29
  import { RelayClient, phoneBaseURL } from './relay/client.js';
30
- import { MountRegistry } from './mount.js';
30
+ import { sharedMountRegistry } from './mount.js';
31
31
  import { loadOrCreateTunnelSecret } from './relay/identity.js';
32
32
  import { runSpike } from './spike.js';
33
33
  /** Cordis plugin name. */
@@ -55,7 +55,7 @@ const systemLocale = (() => {
55
55
  * code in place and every symptom unchanged. One line at boot turns that from a
56
56
  * guess into a fact.
57
57
  */
58
- const MIZAR_VERSION = '0.8.6';
58
+ const MIZAR_VERSION = '0.8.7';
59
59
  /**
60
60
  * Services this plugin must observe before it activates.
61
61
  *
@@ -491,7 +491,7 @@ async function startListener(ctx, config, signal) {
491
491
  * finished shutting down, so the port is free and the address a phone saved is
492
492
  * still the address the computer listens on.
493
493
  */
494
- const mounts = new MountRegistry();
494
+ const mounts = sharedMountRegistry();
495
495
  /**
496
496
  * Mount the Mizar host half.
497
497
  * @param ctx - the plugin context.
package/lib/mount.d.ts CHANGED
@@ -1,3 +1,21 @@
1
+ /**
2
+ * The one registry every copy of this plugin in a process shares.
3
+ *
4
+ * Module-level state is not enough: the plugin can be loaded more than once in
5
+ * one process, and each load gets its own copy of this module -- which is how a
6
+ * single DSH process ended up with eight Mizar listeners (19390 through 19397)
7
+ * after 0.8.6 shipped a module-level registry. Each copy saw an empty slot and
8
+ * bound its own port, so the port a phone had saved was always one generation
9
+ * behind.
10
+ *
11
+ * `globalThis` is shared by every copy in the process, and `Symbol.for` gives
12
+ * the key a stable identity across them. The registry therefore outlives a
13
+ * module copy exactly as long as the process does, which is the lifetime that
14
+ * matters for a listening port.
15
+ *
16
+ * @returns the process-wide registry.
17
+ */
18
+ export declare function sharedMountRegistry(): MountRegistry;
1
19
  /** Something that can be shut down once, asynchronously. */
2
20
  export interface Mounted {
3
21
  /** Release the listener, the tunnel and anything else this mount owns. */
package/lib/mount.js CHANGED
@@ -21,6 +21,33 @@
21
21
  //
22
22
  // Pure, so the ordering is tested without a plugin host or an open port.
23
23
  //
24
+ /**
25
+ * The one registry every copy of this plugin in a process shares.
26
+ *
27
+ * Module-level state is not enough: the plugin can be loaded more than once in
28
+ * one process, and each load gets its own copy of this module -- which is how a
29
+ * single DSH process ended up with eight Mizar listeners (19390 through 19397)
30
+ * after 0.8.6 shipped a module-level registry. Each copy saw an empty slot and
31
+ * bound its own port, so the port a phone had saved was always one generation
32
+ * behind.
33
+ *
34
+ * `globalThis` is shared by every copy in the process, and `Symbol.for` gives
35
+ * the key a stable identity across them. The registry therefore outlives a
36
+ * module copy exactly as long as the process does, which is the lifetime that
37
+ * matters for a listening port.
38
+ *
39
+ * @returns the process-wide registry.
40
+ */
41
+ export function sharedMountRegistry() {
42
+ const key = Symbol.for('dsh-mizar.mount-registry');
43
+ const bag = globalThis;
44
+ let registry = bag[key];
45
+ if (registry === undefined) {
46
+ registry = new MountRegistry();
47
+ bag[key] = registry;
48
+ }
49
+ return registry;
50
+ }
24
51
  /**
25
52
  * The serialized, single-instance mount slot.
26
53
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-mizar",
3
- "version": "0.8.6",
3
+ "version": "0.8.7",
4
4
  "description": "Remote monitoring and guidance for DSH agents — host plugin backing the DSH-Mizar iOS app.",
5
5
  "license": "GPL-3.0-only",
6
6
  "type": "module",