@serfab/cadre-core 0.7.1 → 0.9.0

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.
Files changed (170) hide show
  1. package/README.md +11 -10
  2. package/dist/cadre-node.d.ts +818 -19
  3. package/dist/cadre-node.js +1952 -99
  4. package/dist/cadre-node.js.map +1 -1
  5. package/dist/canonical-datetime.d.ts +33 -0
  6. package/dist/canonical-datetime.js +38 -0
  7. package/dist/canonical-datetime.js.map +1 -0
  8. package/dist/canonical-json.d.ts +12 -0
  9. package/dist/canonical-json.js +33 -0
  10. package/dist/canonical-json.js.map +1 -0
  11. package/dist/control-cohort.d.ts +63 -0
  12. package/dist/control-cohort.js +80 -0
  13. package/dist/control-cohort.js.map +1 -0
  14. package/dist/control-database.d.ts +279 -8
  15. package/dist/control-database.js +563 -134
  16. package/dist/control-database.js.map +1 -1
  17. package/dist/control-formation-recorder.d.ts +89 -0
  18. package/dist/control-formation-recorder.js +139 -0
  19. package/dist/control-formation-recorder.js.map +1 -0
  20. package/dist/control-schema.d.ts +12 -0
  21. package/dist/control-schema.js +223 -0
  22. package/dist/control-schema.js.map +1 -0
  23. package/dist/control-stream.d.ts +54 -0
  24. package/dist/control-stream.js +104 -0
  25. package/dist/control-stream.js.map +1 -0
  26. package/dist/device-token.d.ts +50 -0
  27. package/dist/device-token.js +66 -0
  28. package/dist/device-token.js.map +1 -0
  29. package/dist/diagnostics/connection-path.d.ts +118 -0
  30. package/dist/diagnostics/connection-path.js +204 -0
  31. package/dist/diagnostics/connection-path.js.map +1 -0
  32. package/dist/diagnostics/webrtc-turn-tracker.d.ts +50 -0
  33. package/dist/diagnostics/webrtc-turn-tracker.js +196 -0
  34. package/dist/diagnostics/webrtc-turn-tracker.js.map +1 -0
  35. package/dist/ed25519-key.d.ts +42 -0
  36. package/dist/ed25519-key.js +51 -0
  37. package/dist/ed25519-key.js.map +1 -0
  38. package/dist/hibernation-manager.d.ts +79 -0
  39. package/dist/hibernation-manager.js +173 -23
  40. package/dist/hibernation-manager.js.map +1 -1
  41. package/dist/index.d.ts +25 -4
  42. package/dist/index.js +59 -4
  43. package/dist/index.js.map +1 -1
  44. package/dist/key-store-file.d.ts +52 -0
  45. package/dist/key-store-file.js +194 -0
  46. package/dist/key-store-file.js.map +1 -0
  47. package/dist/key-store.d.ts +75 -0
  48. package/dist/key-store.js +66 -0
  49. package/dist/key-store.js.map +1 -0
  50. package/dist/peer-authorization.d.ts +48 -0
  51. package/dist/peer-authorization.js +66 -0
  52. package/dist/peer-authorization.js.map +1 -0
  53. package/dist/peer-record.d.ts +89 -0
  54. package/dist/peer-record.js +117 -0
  55. package/dist/peer-record.js.map +1 -0
  56. package/dist/push-credentials.d.ts +34 -0
  57. package/dist/push-credentials.js +81 -0
  58. package/dist/push-credentials.js.map +1 -0
  59. package/dist/push-fanout.d.ts +137 -0
  60. package/dist/push-fanout.js +197 -0
  61. package/dist/push-fanout.js.map +1 -0
  62. package/dist/push-node.d.ts +38 -0
  63. package/dist/push-node.js +48 -0
  64. package/dist/push-node.js.map +1 -0
  65. package/dist/push-notifier-apns.d.ts +61 -0
  66. package/dist/push-notifier-apns.js +199 -0
  67. package/dist/push-notifier-apns.js.map +1 -0
  68. package/dist/push-notifier-fcm.d.ts +44 -0
  69. package/dist/push-notifier-fcm.js +182 -0
  70. package/dist/push-notifier-fcm.js.map +1 -0
  71. package/dist/push-notifier-shared.d.ts +18 -0
  72. package/dist/push-notifier-shared.js +25 -0
  73. package/dist/push-notifier-shared.js.map +1 -0
  74. package/dist/push-notifier.d.ts +52 -0
  75. package/dist/push-notifier.js +22 -0
  76. package/dist/push-notifier.js.map +1 -0
  77. package/dist/schema-verification.d.ts +11 -2
  78. package/dist/schema-verification.js +15 -4
  79. package/dist/schema-verification.js.map +1 -1
  80. package/dist/seed-bootstrap.d.ts +228 -21
  81. package/dist/seed-bootstrap.js +489 -168
  82. package/dist/seed-bootstrap.js.map +1 -1
  83. package/dist/seed-trust-policy.d.ts +63 -0
  84. package/dist/seed-trust-policy.js +56 -0
  85. package/dist/seed-trust-policy.js.map +1 -0
  86. package/dist/strand-addr-protocol.d.ts +132 -0
  87. package/dist/strand-addr-protocol.js +313 -0
  88. package/dist/strand-addr-protocol.js.map +1 -0
  89. package/dist/strand-cohort.d.ts +54 -0
  90. package/dist/strand-cohort.js +38 -0
  91. package/dist/strand-cohort.js.map +1 -0
  92. package/dist/strand-database.d.ts +54 -6
  93. package/dist/strand-database.js +88 -100
  94. package/dist/strand-database.js.map +1 -1
  95. package/dist/strand-formation-manager.d.ts +79 -19
  96. package/dist/strand-formation-manager.js +196 -104
  97. package/dist/strand-formation-manager.js.map +1 -1
  98. package/dist/strand-formation-protocol.d.ts +162 -0
  99. package/dist/strand-formation-protocol.js +279 -0
  100. package/dist/strand-formation-protocol.js.map +1 -0
  101. package/dist/strand-instance-manager.d.ts +73 -4
  102. package/dist/strand-instance-manager.js +167 -41
  103. package/dist/strand-instance-manager.js.map +1 -1
  104. package/dist/strand-member-key.d.ts +29 -0
  105. package/dist/strand-member-key.js +38 -0
  106. package/dist/strand-member-key.js.map +1 -0
  107. package/dist/strand-member-registry.d.ts +98 -0
  108. package/dist/strand-member-registry.js +129 -0
  109. package/dist/strand-member-registry.js.map +1 -0
  110. package/dist/strand-membership-writer.d.ts +361 -0
  111. package/dist/strand-membership-writer.js +449 -0
  112. package/dist/strand-membership-writer.js.map +1 -0
  113. package/dist/strand-solicitation.d.ts +77 -2
  114. package/dist/strand-solicitation.js +25 -4
  115. package/dist/strand-solicitation.js.map +1 -1
  116. package/dist/strand-wake-payload.d.ts +30 -0
  117. package/dist/strand-wake-payload.js +20 -0
  118. package/dist/strand-wake-payload.js.map +1 -0
  119. package/dist/strand-wake-protocol.d.ts +114 -0
  120. package/dist/strand-wake-protocol.js +257 -0
  121. package/dist/strand-wake-protocol.js.map +1 -0
  122. package/dist/strand-watcher.d.ts +6 -2
  123. package/dist/strand-watcher.js +57 -17
  124. package/dist/strand-watcher.js.map +1 -1
  125. package/dist/types.d.ts +436 -33
  126. package/dist/types.js +22 -5
  127. package/dist/types.js.map +1 -1
  128. package/package.json +18 -8
  129. package/src/cadre-node.ts +2981 -948
  130. package/src/canonical-datetime.ts +39 -0
  131. package/src/canonical-json.ts +30 -0
  132. package/src/control-cohort.ts +103 -0
  133. package/src/control-database.ts +948 -383
  134. package/src/control-formation-recorder.ts +151 -0
  135. package/src/control-schema.ts +222 -0
  136. package/src/control-stream.ts +128 -0
  137. package/src/device-token.ts +89 -0
  138. package/src/diagnostics/connection-path.ts +277 -0
  139. package/src/diagnostics/webrtc-turn-tracker.ts +222 -0
  140. package/src/ed25519-key.ts +69 -0
  141. package/src/hibernation-manager.ts +197 -28
  142. package/src/index.ts +275 -79
  143. package/src/key-store-file.ts +203 -0
  144. package/src/key-store.ts +99 -0
  145. package/src/peer-authorization.ts +81 -0
  146. package/src/peer-record.ts +146 -0
  147. package/src/push-credentials.ts +84 -0
  148. package/src/push-fanout.ts +272 -0
  149. package/src/push-node.ts +64 -0
  150. package/src/push-notifier-apns.ts +247 -0
  151. package/src/push-notifier-fcm.ts +220 -0
  152. package/src/push-notifier-shared.ts +28 -0
  153. package/src/push-notifier.ts +52 -0
  154. package/src/schema-verification.ts +18 -4
  155. package/src/seed-bootstrap.ts +563 -223
  156. package/src/seed-trust-policy.ts +107 -0
  157. package/src/strand-addr-protocol.ts +410 -0
  158. package/src/strand-cohort.ts +77 -0
  159. package/src/strand-database.ts +113 -141
  160. package/src/strand-formation-manager.ts +233 -142
  161. package/src/strand-formation-protocol.ts +391 -0
  162. package/src/strand-instance-manager.ts +207 -56
  163. package/src/strand-member-key.ts +39 -0
  164. package/src/strand-member-registry.ts +177 -0
  165. package/src/strand-membership-writer.ts +640 -0
  166. package/src/strand-solicitation.ts +93 -4
  167. package/src/strand-wake-payload.ts +32 -0
  168. package/src/strand-wake-protocol.ts +331 -0
  169. package/src/strand-watcher.ts +61 -17
  170. package/src/types.ts +1032 -582
@@ -1 +1 @@
1
- {"version":3,"file":"strand-solicitation.js","sourceRoot":"","sources":["../src/strand-solicitation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"strand-solicitation.js","sourceRoot":"","sources":["../src/strand-solicitation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * strand-wake-payload.ts — the canonical wire contract for a strand-wake data
3
+ * message, shared by the server-side sender (`PushNotifier`, this package) and
4
+ * the mobile receiver (`@serfab/reference-app-rn`'s `push-wake.ts`).
5
+ *
6
+ * A strand-wake is a data-only push delivered to an OS-suspended phone over its
7
+ * platform channel (FCM/APNs) when a control-network dial cannot reach the
8
+ * hibernating process. It names the strand that has pending activity so the phone
9
+ * can run one `serviceWake` cycle and re-hibernate.
10
+ *
11
+ * This module is intentionally dependency-free (no node/RN/browser-specific
12
+ * imports) so it sits cleanly on the cross-platform `cadre-core` entry and can be
13
+ * re-exported by the RN receive module without dragging any platform code across
14
+ * the send/receive boundary. The defensive `parseStrandWakePayload` parser stays
15
+ * on the receive side (it guards untrusted wire input); the sender constructs
16
+ * payloads from typed fields and never parses.
17
+ */
18
+ /** Discriminator value carried by a strand-wake data message. */
19
+ export declare const STRAND_WAKE_TYPE = "strand-wake";
20
+ /**
21
+ * A data-only push payload telling a suspended phone that `strandId` has pending
22
+ * activity. Delivered addressed to the phone's resolved `DeviceToken`.
23
+ */
24
+ export interface StrandWakePayload {
25
+ type: typeof STRAND_WAKE_TYPE;
26
+ /** The strand the wake is for. */
27
+ strandId: string;
28
+ /** Why the wake was sent (e.g. `'activity'`). Free-form, for diagnostics. */
29
+ reason: string;
30
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * strand-wake-payload.ts — the canonical wire contract for a strand-wake data
3
+ * message, shared by the server-side sender (`PushNotifier`, this package) and
4
+ * the mobile receiver (`@serfab/reference-app-rn`'s `push-wake.ts`).
5
+ *
6
+ * A strand-wake is a data-only push delivered to an OS-suspended phone over its
7
+ * platform channel (FCM/APNs) when a control-network dial cannot reach the
8
+ * hibernating process. It names the strand that has pending activity so the phone
9
+ * can run one `serviceWake` cycle and re-hibernate.
10
+ *
11
+ * This module is intentionally dependency-free (no node/RN/browser-specific
12
+ * imports) so it sits cleanly on the cross-platform `cadre-core` entry and can be
13
+ * re-exported by the RN receive module without dragging any platform code across
14
+ * the send/receive boundary. The defensive `parseStrandWakePayload` parser stays
15
+ * on the receive side (it guards untrusted wire input); the sender constructs
16
+ * payloads from typed fields and never parses.
17
+ */
18
+ /** Discriminator value carried by a strand-wake data message. */
19
+ export const STRAND_WAKE_TYPE = 'strand-wake';
20
+ //# sourceMappingURL=strand-wake-payload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strand-wake-payload.js","sourceRoot":"","sources":["../src/strand-wake-payload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,iEAAiE;AACjE,MAAM,CAAC,MAAM,gBAAgB,GAAG,aAAa,CAAC"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Control-network push-wake transport.
3
+ *
4
+ * Lets a same-cadre peer — typically an always-on server that participates in a
5
+ * strand and sees new activity — signal a hibernating cadre peer to bring that
6
+ * strand online, pull pending activity, and re-hibernate. Push-wake travels the
7
+ * **control network** (the per-party network connecting this party's own cadre
8
+ * nodes), which is the only network a hibernating peer keeps connected.
9
+ *
10
+ * Modeled directly on `seed-bootstrap.ts`: a dedicated libp2p protocol id,
11
+ * 4-byte big-endian length-prefixed JSON frames, `node.handle` for the receiver,
12
+ * `node.dialProtocol` for the sender, and the shared `ControlStream` primitives
13
+ * from `control-stream.ts`. The exchange is a single request → single ack on one
14
+ * stream (like seed delivery), so each side reads to EOF (under a read timeout)
15
+ * and decodes one frame via the shared {@link decodeLengthPrefixedFrame} guard.
16
+ *
17
+ * **Authorization (v1):** the control network already restricts membership —
18
+ * only this party's cadre peers connect (schema-gated `CadrePeer`). A wake is
19
+ * low-risk: it only causes the receiver to spend resources coming online for a
20
+ * strand it already participates in. So the receiver verifies the remote peer is
21
+ * a `CadrePeer` member (via the injected `isMember` gate) and requires no
22
+ * signature beyond control-network membership. A richer per-request signature is
23
+ * deliberately out of scope for v1.
24
+ */
25
+ import type { Libp2p } from '@libp2p/interface';
26
+ import type { Multiaddr } from '@multiformats/multiaddr';
27
+ import type { StrandInstance, WakeRequest, WakeAck } from './types.js';
28
+ /** Protocol id for control-network push-wake (parallel to `/sereus/seed/1.0.0`). */
29
+ export declare const WAKE_PROTOCOL = "/sereus/strand-wake/1.0.0";
30
+ /**
31
+ * Dependencies the {@link StrandWakeService} receiver needs from its host
32
+ * (`CadreNode`), injected so the service is testable without a full node.
33
+ */
34
+ export interface StrandWakeServiceOptions {
35
+ /** Membership gate: is the remote peer a `CadrePeer` member of this cadre? */
36
+ isMember(remotePeerId: string): Promise<boolean>;
37
+ /** Look up a local strand instance by id (undefined if not participated in). */
38
+ getStrand(strandId: string): StrandInstance | undefined;
39
+ /**
40
+ * Trigger the local wake path for a hibernating/idle strand. Wired to
41
+ * `CadreNode.wakeStrand` (→ `HibernationManager` → `resumeStrand`), whose
42
+ * resume coalescing prevents a push-wake racing a concurrent check-in.
43
+ */
44
+ wake(strandId: string): Promise<void>;
45
+ /**
46
+ * Time to wait for the inbound wake frame before aborting the read (ms).
47
+ * Defaults to {@link DEFAULT_WAKE_READ_TIMEOUT_MS}. Bounds a buggy/compromised
48
+ * own-cadre node that opens a stream and never half-closes its write end.
49
+ */
50
+ readTimeoutMs?: number;
51
+ /**
52
+ * Cap on concurrent inbound wake streams (defaults to
53
+ * {@link DEFAULT_MAX_CONCURRENT_WAKES}). Over the cap, a non-accepting ack is
54
+ * returned without invoking the wake path.
55
+ */
56
+ maxConcurrent?: number;
57
+ }
58
+ /**
59
+ * Receiver side of the push-wake protocol. Registers a `WAKE_PROTOCOL` handler
60
+ * on the control node and, for each inbound {@link WakeRequest}, gates on cadre
61
+ * membership, then resumes the named strand if it is hibernating/idle and we
62
+ * participate in it — replying with a {@link WakeAck}.
63
+ */
64
+ export declare class StrandWakeService {
65
+ private readonly options;
66
+ private readonly readTimeoutMs;
67
+ private readonly maxConcurrent;
68
+ private node;
69
+ /** In-flight inbound wake streams, used to enforce {@link maxConcurrent}. */
70
+ private activeStreams;
71
+ constructor(options: StrandWakeServiceOptions);
72
+ /** Number of in-flight inbound wake streams. */
73
+ get activeCount(): number;
74
+ /** Register the wake protocol handler on the control node. */
75
+ initialize(node: Libp2p): void;
76
+ /** Unregister the handler and release the node reference. */
77
+ shutdown(): Promise<void>;
78
+ /**
79
+ * Read the inbound request, decide + execute the wake, and write the ack.
80
+ *
81
+ * Three hardening layers, all reported as a non-accepting ack rather than a
82
+ * dropped/hung stream: a concurrency cap (over {@link maxConcurrent}, reply
83
+ * without touching the wake path), a read timeout (a peer that never
84
+ * half-closes is aborted inside {@link readFrame}/`readStreamToEnd`), and the
85
+ * existing malformed/oversized-frame guard.
86
+ */
87
+ private handleStream;
88
+ /**
89
+ * Decide and execute the wake for a decoded request. Exposed (not private) so
90
+ * the decision matrix can be unit-tested directly.
91
+ *
92
+ * - Non-member sender → rejected (`accepted: false`).
93
+ * - Unknown / not-participated strand → rejected.
94
+ * - Hibernating or idle strand → resumed via the wake path, then `accepted`.
95
+ * - Already-live strand → no-op, `accepted` with current status.
96
+ */
97
+ processWakeRequest(request: WakeRequest, remotePeerId: string): Promise<WakeAck>;
98
+ }
99
+ /** Options for {@link dialWake}. */
100
+ export interface DialWakeOptions {
101
+ /** Per-dial timeout in ms (default {@link DEFAULT_WAKE_TIMEOUT_MS}). */
102
+ timeoutMs?: number;
103
+ /** Override the protocol id (defaults to {@link WAKE_PROTOCOL}). */
104
+ protocolId?: string;
105
+ }
106
+ /**
107
+ * Sender side: dial a target's control-network address(es), send a
108
+ * {@link WakeRequest} over `WAKE_PROTOCOL`, and return the peer's {@link WakeAck}.
109
+ *
110
+ * Tries each candidate address in order (signaling/relay first, as produced by
111
+ * `CadreNode.resolvePeerAddrs`) until one dials, so a NAT'd peer is reachable via
112
+ * its circuit-relay address. Throws if no address is dialable.
113
+ */
114
+ export declare function dialWake(node: Libp2p, addrs: Multiaddr[], request: WakeRequest, options?: DialWakeOptions): Promise<WakeAck>;
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Control-network push-wake transport.
3
+ *
4
+ * Lets a same-cadre peer — typically an always-on server that participates in a
5
+ * strand and sees new activity — signal a hibernating cadre peer to bring that
6
+ * strand online, pull pending activity, and re-hibernate. Push-wake travels the
7
+ * **control network** (the per-party network connecting this party's own cadre
8
+ * nodes), which is the only network a hibernating peer keeps connected.
9
+ *
10
+ * Modeled directly on `seed-bootstrap.ts`: a dedicated libp2p protocol id,
11
+ * 4-byte big-endian length-prefixed JSON frames, `node.handle` for the receiver,
12
+ * `node.dialProtocol` for the sender, and the shared `ControlStream` primitives
13
+ * from `control-stream.ts`. The exchange is a single request → single ack on one
14
+ * stream (like seed delivery), so each side reads to EOF (under a read timeout)
15
+ * and decodes one frame via the shared {@link decodeLengthPrefixedFrame} guard.
16
+ *
17
+ * **Authorization (v1):** the control network already restricts membership —
18
+ * only this party's cadre peers connect (schema-gated `CadrePeer`). A wake is
19
+ * low-risk: it only causes the receiver to spend resources coming online for a
20
+ * strand it already participates in. So the receiver verifies the remote peer is
21
+ * a `CadrePeer` member (via the injected `isMember` gate) and requires no
22
+ * signature beyond control-network membership. A richer per-request signature is
23
+ * deliberately out of scope for v1.
24
+ */
25
+ import debug from 'debug';
26
+ import { decodeLengthPrefixedFrame } from './seed-bootstrap.js';
27
+ import { writeFrame, withTimeout, readStreamToEnd } from './control-stream.js';
28
+ const log = debug('sereus:cadre:strand-wake');
29
+ /** Protocol id for control-network push-wake (parallel to `/sereus/seed/1.0.0`). */
30
+ export const WAKE_PROTOCOL = '/sereus/strand-wake/1.0.0';
31
+ /**
32
+ * Maximum wake frame size. Wake messages are tiny (a strand id + short reason),
33
+ * so this is a defensive cap — far below the 1MB seed ceiling — that bounds the
34
+ * bytes a peer can make the receiver buffer per stream.
35
+ */
36
+ const MAX_WAKE_SIZE = 64 * 1024;
37
+ /** Default time to wait for the ack before abandoning a wake dial (ms). */
38
+ const DEFAULT_WAKE_TIMEOUT_MS = 10000;
39
+ /** Default time the receiver waits for an inbound wake frame before aborting (ms). */
40
+ const DEFAULT_WAKE_READ_TIMEOUT_MS = 10000;
41
+ /** Default cap on concurrent inbound wake streams a single peer can pin open. */
42
+ const DEFAULT_MAX_CONCURRENT_WAKES = 100;
43
+ /**
44
+ * Read a libp2p stream to EOF and decode the single length-prefixed JSON frame
45
+ * it carries. Bounded by `timeoutMs` (a never-half-closing peer is aborted, not
46
+ * awaited forever) and capped at {@link MAX_WAKE_SIZE}, reusing the shared
47
+ * {@link decodeLengthPrefixedFrame} guard for the prefix/length checks.
48
+ */
49
+ async function readFrame(stream, timeoutMs) {
50
+ const data = await readStreamToEnd(stream, { maxBytes: MAX_WAKE_SIZE, timeoutMs, label: 'Wake' });
51
+ const body = decodeLengthPrefixedFrame(data, MAX_WAKE_SIZE);
52
+ return JSON.parse(new TextDecoder().decode(body));
53
+ }
54
+ /**
55
+ * Receiver side of the push-wake protocol. Registers a `WAKE_PROTOCOL` handler
56
+ * on the control node and, for each inbound {@link WakeRequest}, gates on cadre
57
+ * membership, then resumes the named strand if it is hibernating/idle and we
58
+ * participate in it — replying with a {@link WakeAck}.
59
+ */
60
+ export class StrandWakeService {
61
+ constructor(options) {
62
+ this.node = null;
63
+ /** In-flight inbound wake streams, used to enforce {@link maxConcurrent}. */
64
+ this.activeStreams = 0;
65
+ this.options = options;
66
+ this.readTimeoutMs = options.readTimeoutMs ?? DEFAULT_WAKE_READ_TIMEOUT_MS;
67
+ this.maxConcurrent = options.maxConcurrent ?? DEFAULT_MAX_CONCURRENT_WAKES;
68
+ }
69
+ /** Number of in-flight inbound wake streams. */
70
+ get activeCount() {
71
+ return this.activeStreams;
72
+ }
73
+ /** Register the wake protocol handler on the control node. */
74
+ initialize(node) {
75
+ this.node = node;
76
+ // `runOnLimitedConnection: true` is REQUIRED for the relay path: a NAT'd
77
+ // receiver is reached over a circuit-relay connection, which libp2p marks
78
+ // "limited" (the relay caps its data/duration). Without this the receiver
79
+ // would refuse the inbound wake stream on exactly the connection the
80
+ // protocol is designed to use (see the relay note on `dialWake`).
81
+ void node.handle(WAKE_PROTOCOL, async (rawStream, rawConnection) => {
82
+ const remotePeerId = rawConnection.remotePeer.toString();
83
+ await this.handleStream(rawStream, remotePeerId);
84
+ }, { runOnLimitedConnection: true });
85
+ log('StrandWakeService registered handler: %s', WAKE_PROTOCOL);
86
+ }
87
+ /** Unregister the handler and release the node reference. */
88
+ async shutdown() {
89
+ if (this.node) {
90
+ await this.node.unhandle(WAKE_PROTOCOL);
91
+ this.node = null;
92
+ log('StrandWakeService shutdown');
93
+ }
94
+ }
95
+ /**
96
+ * Read the inbound request, decide + execute the wake, and write the ack.
97
+ *
98
+ * Three hardening layers, all reported as a non-accepting ack rather than a
99
+ * dropped/hung stream: a concurrency cap (over {@link maxConcurrent}, reply
100
+ * without touching the wake path), a read timeout (a peer that never
101
+ * half-closes is aborted inside {@link readFrame}/`readStreamToEnd`), and the
102
+ * existing malformed/oversized-frame guard.
103
+ */
104
+ async handleStream(stream, remotePeerId) {
105
+ log('Incoming wake request from: %s', remotePeerId);
106
+ if (this.activeStreams >= this.maxConcurrent) {
107
+ log('Rejecting wake from %s: %d concurrent streams at cap %d', remotePeerId, this.activeStreams, this.maxConcurrent);
108
+ const ack = { accepted: false, reason: 'Too many concurrent wake requests' };
109
+ try {
110
+ writeFrame(stream, ack);
111
+ }
112
+ catch {
113
+ // Ignore send errors on the reject path.
114
+ }
115
+ try {
116
+ await stream.close();
117
+ }
118
+ catch {
119
+ // Ignore close errors.
120
+ }
121
+ return;
122
+ }
123
+ this.activeStreams++;
124
+ try {
125
+ const request = await readFrame(stream, this.readTimeoutMs);
126
+ const ack = await this.processWakeRequest(request, remotePeerId);
127
+ writeFrame(stream, ack);
128
+ }
129
+ catch (err) {
130
+ log('Error handling wake request from %s: %o', remotePeerId, err);
131
+ const ack = {
132
+ accepted: false,
133
+ reason: err instanceof Error ? err.message : 'Unknown error',
134
+ };
135
+ try {
136
+ writeFrame(stream, ack);
137
+ }
138
+ catch {
139
+ // Ignore send errors on the error path.
140
+ }
141
+ }
142
+ finally {
143
+ this.activeStreams--;
144
+ try {
145
+ await stream.close();
146
+ }
147
+ catch {
148
+ // Ignore close errors.
149
+ }
150
+ }
151
+ }
152
+ /**
153
+ * Decide and execute the wake for a decoded request. Exposed (not private) so
154
+ * the decision matrix can be unit-tested directly.
155
+ *
156
+ * - Non-member sender → rejected (`accepted: false`).
157
+ * - Unknown / not-participated strand → rejected.
158
+ * - Hibernating or idle strand → resumed via the wake path, then `accepted`.
159
+ * - Already-live strand → no-op, `accepted` with current status.
160
+ */
161
+ async processWakeRequest(request, remotePeerId) {
162
+ // Control-network membership is the v1 authorization: only this party's
163
+ // cadre peers may ask us to wake.
164
+ if (!(await this.options.isMember(remotePeerId))) {
165
+ log('Rejecting wake from non-member %s', remotePeerId);
166
+ return { accepted: false, reason: 'Sender is not a cadre member' };
167
+ }
168
+ const instance = this.options.getStrand(request.strandId);
169
+ if (!instance) {
170
+ log('Rejecting wake for unknown/unparticipated strand %s', request.strandId);
171
+ return { accepted: false, reason: 'Strand not found or not participated in' };
172
+ }
173
+ if (instance.status === 'hibernating' || instance.status === 'idle') {
174
+ log('Waking strand %s on push from %s (reason=%s)', request.strandId, remotePeerId, request.reason ?? 'unspecified');
175
+ await this.options.wake(request.strandId);
176
+ }
177
+ else {
178
+ log('Strand %s already %s; push-wake is a no-op', request.strandId, instance.status);
179
+ }
180
+ // The wake path mutates the shared instance, so re-read its current status.
181
+ return { accepted: true, status: instance.status };
182
+ }
183
+ }
184
+ /**
185
+ * Sender side: dial a target's control-network address(es), send a
186
+ * {@link WakeRequest} over `WAKE_PROTOCOL`, and return the peer's {@link WakeAck}.
187
+ *
188
+ * Tries each candidate address in order (signaling/relay first, as produced by
189
+ * `CadreNode.resolvePeerAddrs`) until one dials, so a NAT'd peer is reachable via
190
+ * its circuit-relay address. Throws if no address is dialable.
191
+ */
192
+ export async function dialWake(node, addrs, request, options = {}) {
193
+ if (addrs.length === 0) {
194
+ throw new Error('No dialable address for wake target');
195
+ }
196
+ const protocolId = options.protocolId ?? WAKE_PROTOCOL;
197
+ const timeoutMs = options.timeoutMs ?? DEFAULT_WAKE_TIMEOUT_MS;
198
+ let lastError = null;
199
+ for (const addr of addrs) {
200
+ // One controller per attempt: on timeout, `withTimeout`'s `onTimeout` aborts
201
+ // it, which aborts the in-flight dialProtocol (via the dial `signal`) and the
202
+ // live stream — so neither the connect nor the unbounded ack-read leaks.
203
+ const controller = new AbortController();
204
+ try {
205
+ return await withTimeout(timeoutMs, `Wake dial ${addr.toString()}`, () => sendWake(node, addr, protocolId, request, timeoutMs, controller.signal), () => controller.abort());
206
+ }
207
+ catch (err) {
208
+ lastError = err instanceof Error ? err : new Error(String(err));
209
+ log('Wake dial to %s failed: %o', addr.toString(), err);
210
+ }
211
+ }
212
+ throw lastError ?? new Error('Wake dial failed');
213
+ }
214
+ /**
215
+ * Open one stream, send the request, half-close, and read the ack.
216
+ *
217
+ * `signal` is the per-attempt abort signal from {@link dialWake}: it is passed to
218
+ * `dialProtocol` so a timeout during connect aborts the dial, and once the stream
219
+ * is open an abort listener resets the live stream — releasing the otherwise
220
+ * unbounded ack-read. `readFrame` still applies `timeoutMs` as a backstop for the
221
+ * case where an abort does not propagate (e.g. a stream double whose `abort()` is
222
+ * a no-op).
223
+ */
224
+ async function sendWake(node, addr, protocolId, request, timeoutMs, signal) {
225
+ // `runOnLimitedConnection: true`: the target may be reachable only over a
226
+ // circuit-relay connection (the signaling-first addr), which libp2p marks
227
+ // "limited". The wake exchange is a single tiny request→ack well within the
228
+ // relay's data/duration cap, so opening it on the limited connection is safe
229
+ // and is the whole point of dialing the relay address.
230
+ const rawStream = await node.dialProtocol(addr, protocolId, { runOnLimitedConnection: true, signal });
231
+ const stream = rawStream;
232
+ const abortErr = new Error('Wake dial aborted by timeout');
233
+ const onAbort = () => stream.abort(abortErr);
234
+ // If the timeout already fired during connect, release the freshly-opened stream now.
235
+ if (signal.aborted) {
236
+ onAbort();
237
+ throw abortErr;
238
+ }
239
+ signal.addEventListener('abort', onAbort, { once: true });
240
+ try {
241
+ writeFrame(stream, request);
242
+ // close() half-closes the write end (EOF) while the read end stays open for
243
+ // the ack — the same libp2p 3.x pattern as seed delivery.
244
+ await stream.close();
245
+ const ack = await readFrame(stream, timeoutMs);
246
+ log('Wake ack from %s: accepted=%s status=%s', addr.toString(), ack.accepted, ack.status ?? '-');
247
+ return ack;
248
+ }
249
+ catch (err) {
250
+ stream.abort(err instanceof Error ? err : new Error(String(err)));
251
+ throw err;
252
+ }
253
+ finally {
254
+ signal.removeEventListener('abort', onAbort);
255
+ }
256
+ }
257
+ //# sourceMappingURL=strand-wake-protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strand-wake-protocol.js","sourceRoot":"","sources":["../src/strand-wake-protocol.ts"],"names":[],"mappings":"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"}
@@ -40,14 +40,18 @@ export declare class StrandWatcher {
40
40
  private readonly queryable;
41
41
  private readonly sAppIdLookup?;
42
42
  private knownStrands;
43
+ /** Ids admitted under a `defer` decision; re-evaluated each poll until they resolve. */
44
+ private provisional;
43
45
  private pollTimer;
44
46
  private initialPollTimer;
45
47
  private running;
46
48
  constructor(queryable: StrandQueryable, callbacks: StrandWatcherCallbacks, filter?: StrandFilter, pollInterval?: number, sAppIdLookup?: SAppIdLookup);
47
49
  /**
48
- * Check if a strand passes the current filter
50
+ * Evaluate a strand against the current filter, distinguishing a not-yet-known
51
+ * sAppId (`defer`) from a known non-match (`reject`). A `defer` admission is
52
+ * provisional and re-checked on subsequent polls.
49
53
  */
50
- private passesFilter;
54
+ private evaluateFilter;
51
55
  /**
52
56
  * Poll for strand changes
53
57
  */
@@ -9,6 +9,8 @@ const log = debug('sereus:cadre:strand-watcher');
9
9
  export class StrandWatcher {
10
10
  constructor(queryable, callbacks, filter = { mode: 'all' }, pollInterval = 5000, sAppIdLookup) {
11
11
  this.knownStrands = new Map();
12
+ /** Ids admitted under a `defer` decision; re-evaluated each poll until they resolve. */
13
+ this.provisional = new Set();
12
14
  this.pollTimer = null;
13
15
  this.initialPollTimer = null;
14
16
  this.running = false;
@@ -20,31 +22,35 @@ export class StrandWatcher {
20
22
  log('StrandWatcher created with filter: %o, interval: %dms', filter, pollInterval);
21
23
  }
22
24
  /**
23
- * Check if a strand passes the current filter
25
+ * Evaluate a strand against the current filter, distinguishing a not-yet-known
26
+ * sAppId (`defer`) from a known non-match (`reject`). A `defer` admission is
27
+ * provisional and re-checked on subsequent polls.
24
28
  */
25
- passesFilter(strand) {
29
+ evaluateFilter(strand) {
26
30
  switch (this.filter.mode) {
27
31
  case 'all':
28
- return true;
32
+ return 'pass';
29
33
  case 'none':
30
- return false;
34
+ return 'reject';
31
35
  case 'strandId':
32
- return strand.Id === this.filter.strandId;
36
+ return strand.Id === this.filter.strandId ? 'pass' : 'reject';
33
37
  case 'sAppId': {
34
- // Look up the sAppId for this strand
35
- const sAppId = this.sAppIdLookup?.getSAppId(strand.Id);
38
+ if (!this.sAppIdLookup) {
39
+ // No way to ever decide - admit permanently (no lookup configured).
40
+ return 'pass';
41
+ }
42
+ const sAppId = this.sAppIdLookup.getSAppId(strand.Id);
36
43
  if (sAppId === undefined) {
37
- // If no sAppId lookup configured or unknown strand, pass through
38
- // The strand will need to be re-evaluated when sApp info is available
44
+ // sAppId not yet known - admit provisionally and re-evaluate next poll.
39
45
  log('sAppId unknown for strand %s - deferring filter decision', strand.Id);
40
- return true;
46
+ return 'defer';
41
47
  }
42
48
  const matches = sAppId === this.filter.sAppId;
43
49
  log('sAppId filter: strand %s has sAppId %s, filter wants %s, match=%s', strand.Id, sAppId, this.filter.sAppId, matches);
44
- return matches;
50
+ return matches ? 'pass' : 'reject';
45
51
  }
46
52
  default:
47
- return true;
53
+ return 'pass';
48
54
  }
49
55
  }
50
56
  /**
@@ -58,22 +64,55 @@ export class StrandWatcher {
58
64
  const currentMap = new Map(currentStrands.map(s => [s.Id, s]));
59
65
  // Find added strands
60
66
  for (const strand of currentStrands) {
61
- if (!this.knownStrands.has(strand.Id) && this.passesFilter(strand)) {
62
- log('Strand added: %s', strand.Id);
63
- this.knownStrands.set(strand.Id, strand);
67
+ if (this.knownStrands.has(strand.Id))
68
+ continue;
69
+ const decision = this.evaluateFilter(strand);
70
+ if (decision === 'reject')
71
+ continue;
72
+ log('Strand added: %s', strand.Id);
73
+ this.knownStrands.set(strand.Id, strand);
74
+ if (decision === 'defer') {
75
+ // Provisional admission: sAppId unknown, re-check on later polls.
76
+ this.provisional.add(strand.Id);
77
+ }
78
+ try {
79
+ await this.callbacks.onStrandAdded(strand);
80
+ }
81
+ catch (error) {
82
+ log('Error handling strand add for %s: %o', strand.Id, error);
83
+ }
84
+ }
85
+ // Re-evaluate provisional admissions whose sAppId may now be resolvable.
86
+ // Snapshot ids so we can mutate provisional/knownStrands inside the loop.
87
+ for (const strandId of [...this.provisional]) {
88
+ const strand = currentMap.get(strandId);
89
+ if (!strand)
90
+ continue; // handled by the removed-strand loop below
91
+ const decision = this.evaluateFilter(strand);
92
+ if (decision === 'pass') {
93
+ // Resolved to a match - admission is now final.
94
+ this.provisional.delete(strandId);
95
+ }
96
+ else if (decision === 'reject') {
97
+ // Resolved to a non-match - stop the provisionally-admitted strand.
98
+ log('Provisional strand rejected on re-evaluation: %s', strandId);
99
+ this.knownStrands.delete(strandId);
100
+ this.provisional.delete(strandId);
64
101
  try {
65
- await this.callbacks.onStrandAdded(strand);
102
+ await this.callbacks.onStrandRemoved(strandId);
66
103
  }
67
104
  catch (error) {
68
- log('Error handling strand add for %s: %o', strand.Id, error);
105
+ log('Error handling strand remove for %s: %o', strandId, error);
69
106
  }
70
107
  }
108
+ // decision === 'defer': still unknown, leave provisional for next poll.
71
109
  }
72
110
  // Find removed strands
73
111
  for (const [strandId] of this.knownStrands) {
74
112
  if (!currentMap.has(strandId)) {
75
113
  log('Strand removed: %s', strandId);
76
114
  this.knownStrands.delete(strandId);
115
+ this.provisional.delete(strandId);
77
116
  try {
78
117
  await this.callbacks.onStrandRemoved(strandId);
79
118
  }
@@ -127,6 +166,7 @@ export class StrandWatcher {
127
166
  this.pollTimer = null;
128
167
  }
129
168
  this.knownStrands.clear();
169
+ this.provisional.clear();
130
170
  log('StrandWatcher stopped');
131
171
  }
132
172
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"strand-watcher.js","sourceRoot":"","sources":["../src/strand-watcher.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"strand-watcher.js","sourceRoot":"","sources":["../src/strand-watcher.ts"],"names":[],"mappings":"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"}