@serfab/cadre-core 1.6.0 → 1.8.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.
- package/README.md +8 -6
- package/dist/cadre-node.d.ts +548 -137
- package/dist/cadre-node.js +1208 -383
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-authorization.d.ts +2 -2
- package/dist/control-authorization.js +1 -0
- package/dist/control-authorization.js.map +1 -1
- package/dist/control-cohort.d.ts +6 -0
- package/dist/control-cohort.js +6 -0
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +46 -8
- package/dist/control-database.js +97 -18
- package/dist/control-database.js.map +1 -1
- package/dist/control-formation-recorder.d.ts +37 -14
- package/dist/control-formation-recorder.js +60 -10
- package/dist/control-formation-recorder.js.map +1 -1
- package/dist/control-read-retry.d.ts +13 -12
- package/dist/control-read-retry.js +14 -12
- package/dist/control-read-retry.js.map +1 -1
- package/dist/control-schema.d.ts +1 -1
- package/dist/control-schema.js +67 -5
- package/dist/control-schema.js.map +1 -1
- package/dist/control-stream.d.ts +9 -1
- package/dist/control-stream.js +24 -1
- package/dist/control-stream.js.map +1 -1
- package/dist/control-write-retry.d.ts +12 -2
- package/dist/control-write-retry.js +13 -2
- package/dist/control-write-retry.js.map +1 -1
- package/dist/delegate-admission.d.ts +11 -8
- package/dist/delegate-admission.js +19 -11
- package/dist/delegate-admission.js.map +1 -1
- package/dist/diagnostics/connection-path.d.ts +0 -7
- package/dist/diagnostics/connection-path.js +0 -7
- package/dist/diagnostics/connection-path.js.map +1 -1
- package/dist/formation-approval.d.ts +7 -0
- package/dist/formation-approval.js +9 -3
- package/dist/formation-approval.js.map +1 -1
- package/dist/fs-atomic.d.ts +11 -2
- package/dist/fs-atomic.js +13 -3
- package/dist/fs-atomic.js.map +1 -1
- package/dist/hibernation-manager.d.ts +111 -8
- package/dist/hibernation-manager.js +234 -49
- package/dist/hibernation-manager.js.map +1 -1
- package/dist/index.d.ts +13 -6
- package/dist/index.js +27 -5
- package/dist/index.js.map +1 -1
- package/dist/joined-strand-store.d.ts +177 -0
- package/dist/joined-strand-store.js +431 -0
- package/dist/joined-strand-store.js.map +1 -0
- package/dist/key-store-file.js +14 -5
- package/dist/key-store-file.js.map +1 -1
- package/dist/key-store.d.ts +6 -0
- package/dist/key-store.js.map +1 -1
- package/dist/link-budget.d.ts +367 -69
- package/dist/link-budget.js +375 -72
- package/dist/link-budget.js.map +1 -1
- package/dist/membership-connection-gater.d.ts +30 -43
- package/dist/membership-connection-gater.js +31 -42
- package/dist/membership-connection-gater.js.map +1 -1
- package/dist/node-local-snapshot.d.ts +5 -4
- package/dist/node-local-snapshot.js +6 -3
- package/dist/node-local-snapshot.js.map +1 -1
- package/dist/peer-dial.d.ts +24 -4
- package/dist/peer-dial.js +74 -5
- package/dist/peer-dial.js.map +1 -1
- package/dist/peer-join-backfill.d.ts +35 -15
- package/dist/peer-join-backfill.js +115 -55
- package/dist/peer-join-backfill.js.map +1 -1
- package/dist/push-fanout.d.ts +6 -0
- package/dist/push-fanout.js +6 -0
- package/dist/push-fanout.js.map +1 -1
- package/dist/relay-reservation.d.ts +3 -1
- package/dist/relay-reservation.js +3 -1
- package/dist/relay-reservation.js.map +1 -1
- package/dist/relay-server.d.ts +79 -0
- package/dist/relay-server.js +74 -0
- package/dist/relay-server.js.map +1 -0
- package/dist/seed-bootstrap.d.ts +57 -4
- package/dist/seed-bootstrap.js +107 -83
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/storage-scope.d.ts +33 -17
- package/dist/storage-scope.js +44 -23
- package/dist/storage-scope.js.map +1 -1
- package/dist/strand-addr-protocol.d.ts +58 -21
- package/dist/strand-addr-protocol.js +133 -85
- package/dist/strand-addr-protocol.js.map +1 -1
- package/dist/strand-database.d.ts +20 -2
- package/dist/strand-database.js +42 -5
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-first-sync-gate.d.ts +59 -21
- package/dist/strand-first-sync-gate.js +58 -19
- package/dist/strand-first-sync-gate.js.map +1 -1
- package/dist/strand-formation-deadlines.d.ts +151 -0
- package/dist/strand-formation-deadlines.js +171 -0
- package/dist/strand-formation-deadlines.js.map +1 -0
- package/dist/strand-formation-manager.d.ts +63 -30
- package/dist/strand-formation-manager.js +67 -41
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-formation-protocol.d.ts +45 -35
- package/dist/strand-formation-protocol.js +59 -122
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +152 -6
- package/dist/strand-instance-manager.js +331 -42
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-membership-writer.d.ts +6 -0
- package/dist/strand-membership-writer.js +11 -0
- package/dist/strand-membership-writer.js.map +1 -1
- package/dist/strand-peer-book-file.d.ts +12 -0
- package/dist/strand-peer-book-file.js +37 -0
- package/dist/strand-peer-book-file.js.map +1 -0
- package/dist/strand-peer-book-protocol.d.ts +168 -0
- package/dist/strand-peer-book-protocol.js +356 -0
- package/dist/strand-peer-book-protocol.js.map +1 -0
- package/dist/strand-peer-book-swap.d.ts +104 -0
- package/dist/strand-peer-book-swap.js +284 -0
- package/dist/strand-peer-book-swap.js.map +1 -0
- package/dist/strand-peer-book.d.ts +159 -0
- package/dist/strand-peer-book.js +361 -0
- package/dist/strand-peer-book.js.map +1 -0
- package/dist/strand-peer-observer.d.ts +88 -0
- package/dist/strand-peer-observer.js +180 -0
- package/dist/strand-peer-observer.js.map +1 -0
- package/dist/strand-revocation-enforcer.js +2 -1
- package/dist/strand-revocation-enforcer.js.map +1 -1
- package/dist/strand-solicitation.d.ts +36 -20
- package/dist/strand-solicitation.js +4 -4
- package/dist/strand-solicitation.js.map +1 -1
- package/dist/strand-wake-protocol.d.ts +80 -23
- package/dist/strand-wake-protocol.js +89 -64
- package/dist/strand-wake-protocol.js.map +1 -1
- package/dist/types.d.ts +181 -33
- package/dist/types.js +4 -3
- package/dist/types.js.map +1 -1
- package/package.json +11 -7
- package/src/cadre-node.ts +1316 -398
- package/src/control-authorization.ts +2 -1
- package/src/control-cohort.ts +6 -0
- package/src/control-database.ts +122 -18
- package/src/control-formation-recorder.ts +354 -305
- package/src/control-read-retry.ts +14 -12
- package/src/control-schema.ts +67 -5
- package/src/control-stream.ts +25 -1
- package/src/control-write-retry.ts +13 -2
- package/src/delegate-admission.ts +23 -13
- package/src/diagnostics/connection-path.ts +0 -7
- package/src/formation-approval.ts +610 -604
- package/src/fs-atomic.ts +14 -4
- package/src/hibernation-manager.ts +638 -437
- package/src/index.ts +91 -6
- package/src/joined-strand-store.ts +498 -0
- package/src/key-store-file.ts +14 -5
- package/src/key-store.ts +6 -0
- package/src/link-budget.ts +393 -74
- package/src/membership-connection-gater.ts +31 -45
- package/src/node-local-snapshot.ts +293 -289
- package/src/peer-dial.ts +81 -7
- package/src/peer-join-backfill.ts +120 -57
- package/src/push-fanout.ts +6 -0
- package/src/relay-reservation.ts +3 -1
- package/src/relay-server.ts +98 -0
- package/src/seed-bootstrap.ts +133 -91
- package/src/storage-scope.ts +44 -23
- package/src/strand-addr-protocol.ts +547 -458
- package/src/strand-database.ts +287 -242
- package/src/strand-first-sync-gate.ts +58 -19
- package/src/strand-formation-deadlines.ts +227 -0
- package/src/strand-formation-manager.ts +700 -647
- package/src/strand-formation-protocol.ts +103 -150
- package/src/strand-instance-manager.ts +387 -45
- package/src/strand-membership-writer.ts +12 -0
- package/src/strand-peer-book-file.ts +39 -0
- package/src/strand-peer-book-protocol.ts +466 -0
- package/src/strand-peer-book-swap.ts +338 -0
- package/src/strand-peer-book.ts +475 -0
- package/src/strand-peer-observer.ts +235 -0
- package/src/strand-revocation-enforcer.ts +2 -1
- package/src/strand-solicitation.ts +519 -500
- package/src/strand-wake-protocol.ts +401 -355
- package/src/types.ts +185 -33
package/dist/cadre-node.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { CadreNodeConfig, StrandInstance, StrandRow, StrandConfig, FoundStr
|
|
|
4
4
|
import { type Ed25519KeyPair } from './ed25519-key.js';
|
|
5
5
|
import { type TrustedOwnerStore, type TrustSource } from './trusted-owner-store.js';
|
|
6
6
|
import { type BootstrapPeerStore } from './bootstrap-peer-store.js';
|
|
7
|
+
import { type StrandPeerBookStore } from './strand-peer-book.js';
|
|
7
8
|
import { type EnrolledMachineStore } from './enrolled-machine-store.js';
|
|
8
9
|
import { type SAppIdLookup } from './strand-watcher.js';
|
|
9
10
|
import { type ControlCohortReconcileResult } from './control-cohort.js';
|
|
@@ -16,10 +17,10 @@ import { type RelayReservationState, type RelayReservationSupervisorOptions } fr
|
|
|
16
17
|
import type { WakeAck } from './types.js';
|
|
17
18
|
import { type ConnectionPathSummary } from './diagnostics/connection-path.js';
|
|
18
19
|
/**
|
|
19
|
-
* How often a running strand re-
|
|
20
|
-
* and re-merges them into
|
|
21
|
-
* ({@link CadreNode.refreshStrandPeerAddrs}).
|
|
22
|
-
* `network.controlCohort.strandAddrRefreshMs`.
|
|
20
|
+
* How often a running strand re-asks each connected sibling that last ANSWERED
|
|
21
|
+
* (even with nothing) for its strand-network addresses, and re-merges them into
|
|
22
|
+
* its own libp2p address book ({@link CadreNode.refreshStrandPeerAddrs}).
|
|
23
|
+
* Overridable per node via `network.controlCohort.strandAddrRefreshMs`.
|
|
23
24
|
*
|
|
24
25
|
* Ten minutes sits comfortably inside the peerStore's one-hour address expiry
|
|
25
26
|
* (`MAX_ADDRESS_AGE`, see `peer-addr-book.ts`) with headroom for several missed
|
|
@@ -27,6 +28,16 @@ import { type ConnectionPathSummary } from './diagnostics/connection-path.js';
|
|
|
27
28
|
* strand-addr RPC fan-out stays cheap.
|
|
28
29
|
*/
|
|
29
30
|
export declare const STRAND_PEER_ADDR_REFRESH_MS: number;
|
|
31
|
+
/**
|
|
32
|
+
* How soon {@link CadreNode.refreshStrandPeerAddrs} re-asks a sibling that did NOT
|
|
33
|
+
* answer — unreachable, `unavailable`, or `refused`. A refusal matters most: a
|
|
34
|
+
* phone asking before its `CadrePeer` row has replicated to the sibling is refused
|
|
35
|
+
* and its delegate grant goes unrecorded, and waiting the full
|
|
36
|
+
* {@link STRAND_PEER_ADDR_REFRESH_MS} for the next try is the delay this bounds.
|
|
37
|
+
* Four reconcile ticks, so a sibling that keeps failing costs one timed-out RPC a
|
|
38
|
+
* minute rather than one a tick.
|
|
39
|
+
*/
|
|
40
|
+
export declare const STRAND_PEER_ADDR_RETRY_MS: number;
|
|
30
41
|
type EventHandler<T> = (data: T) => void;
|
|
31
42
|
/**
|
|
32
43
|
* CadreNode is the main entry point for a cadre member.
|
|
@@ -160,6 +171,28 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
160
171
|
* {@link getDiscoveredStrands}.
|
|
161
172
|
*/
|
|
162
173
|
private discoveredStrands;
|
|
174
|
+
/**
|
|
175
|
+
* Whether an unclaimed strand the filter admits is launched as a storage replica
|
|
176
|
+
* ({@link CadreNodeConfig.hostUnclaimedStrands}), resolved once from the config.
|
|
177
|
+
*
|
|
178
|
+
* NOTE: a replica host stores every admitted strand of its party with no quota (Arachnode
|
|
179
|
+
* quotas are unimplemented). Fine at a party's handful of strands; if always-on nodes come
|
|
180
|
+
* to host strands by the hundred, per-strand quotas or a narrower default filter is the lever.
|
|
181
|
+
*/
|
|
182
|
+
private readonly hostUnclaimedStrands;
|
|
183
|
+
/**
|
|
184
|
+
* The sApp id each storage replica read from its own `Strand.Header` — {@link getSAppId}'s
|
|
185
|
+
* answer for a strand no local config claims, so an `sAppId` strand filter can reject a
|
|
186
|
+
* replica of some other app instead of admitting it provisionally forever.
|
|
187
|
+
*
|
|
188
|
+
* Cleared by {@link cleanup} only, deliberately NOT by {@link detachStrand}: the watcher's
|
|
189
|
+
* filter rejection stops a replica through that very method, and forgetting the id there
|
|
190
|
+
* would have the next poll find it unknown again, re-admit the strand provisionally and
|
|
191
|
+
* relaunch the replica — once every other poll, forever. A strand id's sApp never changes,
|
|
192
|
+
* and the map is bounded by the strands this party has published, like
|
|
193
|
+
* {@link discoveredStrands}.
|
|
194
|
+
*/
|
|
195
|
+
private replicaSAppIds;
|
|
163
196
|
/**
|
|
164
197
|
* Most-recently pushed invite addresses (see {@link setInviteAddresses}).
|
|
165
198
|
* When non-null these take priority over `libp2pNode.getMultiaddrs()` when
|
|
@@ -234,9 +267,20 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
234
267
|
* `denyInboundRelayReservation` policy); authorized members and announced
|
|
235
268
|
* delegates are admitted before it and never counted. Cap from
|
|
236
269
|
* `network.unauthorizedRelayReservationCap` (default
|
|
237
|
-
* `MAX_UNAUTHORIZED_RELAY_RESERVATIONS`)
|
|
270
|
+
* `MAX_UNAUTHORIZED_RELAY_RESERVATIONS`); entries expire on the relay
|
|
271
|
+
* server's own resolved `reservationTtl` ({@link relayServer}).
|
|
238
272
|
*/
|
|
239
273
|
private readonly unauthorizedRelayReservations;
|
|
274
|
+
/**
|
|
275
|
+
* Does this node's CONTROL libp2p run the circuit-relay server, and with which
|
|
276
|
+
* init? Resolved once from `network` and `profile` by the same function every
|
|
277
|
+
* strand node's build uses (`relay-server.ts`). Read by
|
|
278
|
+
* {@link buildControlNodeOptions} (which configures the server from it),
|
|
279
|
+
* {@link admitInboundControlConnection} (whose deny/admit-for-relay branch must
|
|
280
|
+
* agree with whether a reservation is even servable here), and the
|
|
281
|
+
* unauthorized budget above (whose TTL is the server's).
|
|
282
|
+
*/
|
|
283
|
+
private readonly relayServer;
|
|
240
284
|
/**
|
|
241
285
|
* When this node last ANNOUNCED a delegate, keyed `<targetPeerId>\n<strandId>`
|
|
242
286
|
* (the peer announced TO, not the delegate). Throttles
|
|
@@ -247,47 +291,49 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
247
291
|
*/
|
|
248
292
|
private readonly delegateAnnounceAt;
|
|
249
293
|
/**
|
|
250
|
-
* When each
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
294
|
+
* When each connected sibling is next due a strand-addr RPC for each running
|
|
295
|
+
* strand, keyed `peerStrandKey(siblingControlPeerId, strandId)` → epoch ms; a
|
|
296
|
+
* missing key is due. Bounds {@link refreshStrandPeerAddrs}'s fan-out per
|
|
297
|
+
* (sibling, strand) rather than per strand, so a sibling that connects after a
|
|
298
|
+
* pass is asked on the next 15 s tick instead of waiting out a stamp another
|
|
299
|
+
* sibling's answer set. An answer (even an empty one) makes the sibling due again
|
|
300
|
+
* in {@link STRAND_PEER_ADDR_REFRESH_MS}; no answer, in
|
|
301
|
+
* {@link STRAND_PEER_ADDR_RETRY_MS}. Keys whose strand stopped running or whose
|
|
302
|
+
* sibling is no longer connected are pruned every pass, so a resumed strand and a
|
|
303
|
+
* reconnected sibling are both asked at once.
|
|
304
|
+
*/
|
|
305
|
+
private readonly strandAddrAskDueAt;
|
|
306
|
+
/**
|
|
307
|
+
* Node-local strand peer book (see `strand-peer-book.ts`): per strand, the strand
|
|
308
|
+
* peers this node has met and their last-known strand-network addresses. Three
|
|
309
|
+
* writers: {@link recordFormationStrandPeers} files the responder's live strand
|
|
310
|
+
* addrs a formation result carried back (`FormationResultMessage.strandAddrs`),
|
|
311
|
+
* {@link observeStrandPeer} files every strand peer a running strand node
|
|
312
|
+
* identifies, and the per-strand book swap (`strand-peer-book-swap.ts`, handed the
|
|
313
|
+
* store as `StartStrandConfig.strandPeerBook`) files the node's own signed entry
|
|
314
|
+
* plus every signed entry a strand peer sends it — the only writer whose entries
|
|
315
|
+
* carry a signature, and the only one that can refresh a peer's addresses without
|
|
316
|
+
* meeting that peer. Read by {@link resolveCohortSeed} (launch + hibernation resume) and
|
|
317
|
+
* re-merged by every {@link refreshStrandPeerAddrs} pass, which is what keeps the
|
|
318
|
+
* entries alive past the peerStore's one-hour address expiry.
|
|
263
319
|
*
|
|
264
320
|
* This is the ONLY cross-party discovery input there is. The strand-addr RPC that
|
|
265
321
|
* resolves a strand's addresses is membership-gated and answers own-party siblings
|
|
266
|
-
* only, so without
|
|
267
|
-
* the mesh never forms
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
* NOTE: entries are kept for the node's lifetime, including across a
|
|
282
|
-
* {@link stopStrand} — a stopped strand may be claimed again with {@link addStrand}
|
|
283
|
-
* (the watcher itself will not re-offer it: the stop suppresses the id for the rest of
|
|
284
|
-
* the session) and must still get its seed. Each strand's list is capped at
|
|
285
|
-
* `MAX_STRAND_ADDRS`, but the
|
|
286
|
-
* number of KEYS is bounded only by the strands this node has ever formed as an
|
|
287
|
-
* initiator (not by time); if a node ever forms strands at scale, evict on
|
|
288
|
-
* `unpublishStrand` or cap the map.
|
|
289
|
-
*/
|
|
290
|
-
private readonly crossPartyStrandAddrs;
|
|
322
|
+
* only, so without the book a joiner's cohort seed for a two-party strand is empty
|
|
323
|
+
* and the mesh never forms — and, with an in-memory book, never RE-forms after a
|
|
324
|
+
* restart (gotchoices/sereus#18). A STORE rather than a map so an embedder can make
|
|
325
|
+
* it outlive the process; constructed (or adopted from `config.strandPeers.store`)
|
|
326
|
+
* by {@link initializeStrandPeerBookStore} during {@link start}, deliberately NOT
|
|
327
|
+
* cleared by {@link cleanup}, so it survives a stop()→start() cycle of the same node
|
|
328
|
+
* instance — same lifecycle as {@link bootstrapPeerStore}.
|
|
329
|
+
*
|
|
330
|
+
* Entries survive a {@link stopStrand} (a stopped strand may be claimed again with
|
|
331
|
+
* {@link addStrand}, and is rediscovered on restart) and are forgotten by
|
|
332
|
+
* {@link unpublishStrand}, {@link forgetJoinedStrand} and self-revocation; the store
|
|
333
|
+
* ages the rest out after 14 days and caps each strand at 16 peers, so the book is
|
|
334
|
+
* bounded by time and by the strands this node runs rather than by its history.
|
|
335
|
+
*/
|
|
336
|
+
private strandPeerBookStore;
|
|
291
337
|
/**
|
|
292
338
|
* PENDING strand membership invitations learned at formation, keyed by strandId — the
|
|
293
339
|
* single-use `Strand.Invite` credential a closed-strand formation result carried back
|
|
@@ -295,10 +341,9 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
295
341
|
* to redeem (`consumeInvite` seats the `Strand.Member` row under this party's own key
|
|
296
342
|
* — the `strand-node-binds-member-peer` half of the party-identity chain).
|
|
297
343
|
*
|
|
298
|
-
* IN-MEMORY, deliberately
|
|
299
|
-
* re-forms from scratch
|
|
300
|
-
*
|
|
301
|
-
* a fresh invitation. A re-formation against the same strand REPLACES the entry — the
|
|
344
|
+
* IN-MEMORY, deliberately: an invitation is single-use and short-lived, a restarted
|
|
345
|
+
* joiner that never redeemed it re-forms from scratch, and re-forming issues a fresh
|
|
346
|
+
* one. A re-formation against the same strand REPLACES the entry — the
|
|
302
347
|
* fresh invitation supersedes one that may have expired. The party key the invitation
|
|
303
348
|
* admits, by contrast, IS persisted (`StrandPartyKey`, seated by
|
|
304
349
|
* {@link adoptFormationMembershipInvite} before the entry lands here), so a lost
|
|
@@ -314,8 +359,9 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
314
359
|
* just waits; once launched, staging notifies the manager, which re-arms a finished loop.
|
|
315
360
|
*
|
|
316
361
|
* NOTE: entries live for the node's lifetime (one small pair per formed closed
|
|
317
|
-
* strand)
|
|
318
|
-
* if a node ever forms strands at scale, evict
|
|
362
|
+
* strand); the number of keys is bounded only by the strands this node has ever
|
|
363
|
+
* formed, not by time — if a node ever forms strands at scale, evict on
|
|
364
|
+
* `unpublishStrand` / `forgetJoinedStrand` as the strand peer book does.
|
|
319
365
|
*/
|
|
320
366
|
private readonly pendingMembershipInvites;
|
|
321
367
|
/**
|
|
@@ -386,6 +432,23 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
386
432
|
* so it survives a stop()→start() cycle of the same node instance.
|
|
387
433
|
*/
|
|
388
434
|
private enrolledMachineStore;
|
|
435
|
+
/**
|
|
436
|
+
* Node-local record of the strands this node joined from ANOTHER party and has not yet
|
|
437
|
+
* published party-wide (see `joined-strand-store.ts`). Written by {@link formStrand}
|
|
438
|
+
* and by an {@link addStrand} of a foreign row; drained once the party-wide
|
|
439
|
+
* `JoinedStrand` row is visible. Adopted from `config.joinedStrands.store`, else built over
|
|
440
|
+
* `config.keyStore`, else in-memory, by {@link initializeJoinedStrandStore}. Like
|
|
441
|
+
* {@link bootstrapPeerStore} it is deliberately NOT cleared by {@link cleanup}.
|
|
442
|
+
*/
|
|
443
|
+
private joinedStrandStore;
|
|
444
|
+
/**
|
|
445
|
+
* This session's joined strands: {@link joinedStrandStore} and the party-wide
|
|
446
|
+
* `JoinedStrand` table, unioned with the control rows each watcher poll, published and
|
|
447
|
+
* removed by the reconcile pass. Per SESSION, unlike the store — rebuilt at every
|
|
448
|
+
* {@link start}, which is what ends a revoked join's "keep offering until this session
|
|
449
|
+
* ends" (see `JoinedStrandSession.forgetAfterThisSession`).
|
|
450
|
+
*/
|
|
451
|
+
private joinedStrands;
|
|
389
452
|
/**
|
|
390
453
|
* The enrolled-machine count this node's CONTROL libp2p node was (or will be)
|
|
391
454
|
* built with — read out of {@link enrolledMachineStore} during {@link start},
|
|
@@ -527,7 +590,8 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
527
590
|
private turnConnectionCloseHandler;
|
|
528
591
|
constructor(config: CadreNodeConfig);
|
|
529
592
|
/**
|
|
530
|
-
* SAppIdLookup implementation - get sAppId for a strand
|
|
593
|
+
* SAppIdLookup implementation - get sAppId for a strand: the claiming config's, else
|
|
594
|
+
* the one a storage replica read from the strand's own `Strand.Header`.
|
|
531
595
|
*/
|
|
532
596
|
getSAppId(strandId: string): string | undefined;
|
|
533
597
|
/**
|
|
@@ -693,6 +757,25 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
693
757
|
* number nobody chose, and the mismatch always means a miswired embedder.
|
|
694
758
|
*/
|
|
695
759
|
private initializeEnrolledMachineStore;
|
|
760
|
+
/**
|
|
761
|
+
* Construct (or adopt) the node-local joined-strand store (see
|
|
762
|
+
* {@link joinedStrandStore}) and this session's {@link joinedStrands} over it.
|
|
763
|
+
*
|
|
764
|
+
* The store instance is kept across stop()→start(); the view is rebuilt. A store
|
|
765
|
+
* scoped to a different party is a configuration error, fail closed: its joins
|
|
766
|
+
* would be offered to this party's app as if this party had made them.
|
|
767
|
+
*/
|
|
768
|
+
private initializeJoinedStrandStore;
|
|
769
|
+
/**
|
|
770
|
+
* Construct (or adopt) the node-local strand peer book (see
|
|
771
|
+
* {@link strandPeerBookStore}). Synchronous for the reason
|
|
772
|
+
* {@link initializeBootstrapPeerStore} is: an injected store has already loaded its
|
|
773
|
+
* persisted entries by the time it is handed in, and the in-memory fallback has
|
|
774
|
+
* nothing to load. The instance is kept across stop()→start(). A store scoped to a
|
|
775
|
+
* different party is a configuration error, fail closed: another party's strand
|
|
776
|
+
* peers must never enter this node's dial loop.
|
|
777
|
+
*/
|
|
778
|
+
private initializeStrandPeerBookStore;
|
|
696
779
|
/**
|
|
697
780
|
* The node-local enrolled-machine store (null before {@link start}) — what this
|
|
698
781
|
* node last knew about its party's size. Exposed for diagnostics and for a host
|
|
@@ -706,6 +789,13 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
706
789
|
* node dial if it is stranded?".
|
|
707
790
|
*/
|
|
708
791
|
getBootstrapPeerStore(): BootstrapPeerStore | null;
|
|
792
|
+
/**
|
|
793
|
+
* The node-local strand peer book (null before {@link start}) — per strand, the
|
|
794
|
+
* strand peers this node dials first on launch, resume and refresh. Exposed for
|
|
795
|
+
* diagnostics and for a host that wants to show "whom would this strand re-find
|
|
796
|
+
* after a restart?".
|
|
797
|
+
*/
|
|
798
|
+
getStrandPeerBookStore(): StrandPeerBookStore | null;
|
|
709
799
|
/**
|
|
710
800
|
* The node-local trusted-owner anchor (null before {@link start}). This is
|
|
711
801
|
* the set the authorized-membership predicate and seed-trust anchor consult —
|
|
@@ -765,10 +855,10 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
765
855
|
* needs no work of its own: `createLibp2pNode` registers the block-transfer
|
|
766
856
|
* handler on every node it builds, control node included, and this node's
|
|
767
857
|
* per-stream gate (`authorizeInboundControlStream`) covers the inbound
|
|
768
|
-
* direction. A denied peer is retried on its next `
|
|
769
|
-
* because the production join order is
|
|
770
|
-
* committed membership change via
|
|
771
|
-
* `scheduleConnectedPeers()` call.
|
|
858
|
+
* direction. A denied peer is retried on its next `peer:identify` (a
|
|
859
|
+
* reconnect re-runs identify), and — because the production join order is
|
|
860
|
+
* connect-then-authorize — on every committed membership change via
|
|
861
|
+
* {@link refreshAuthorizedControlPeers}'s `scheduleConnectedPeers()` call.
|
|
772
862
|
*
|
|
773
863
|
* No-ops (logged) when the embedder configured no control storage or the
|
|
774
864
|
* node exposes no key network — the backfill would have nothing to read or
|
|
@@ -824,14 +914,12 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
824
914
|
*/
|
|
825
915
|
private buildControlNodeOptions;
|
|
826
916
|
/**
|
|
827
|
-
*
|
|
828
|
-
*
|
|
829
|
-
* (
|
|
830
|
-
*
|
|
831
|
-
* {@link admitInboundControlConnection} (whose deny/admit-for-relay branch
|
|
832
|
-
* must agree with whether a reservation is even servable here).
|
|
917
|
+
* One debug line naming what this machine's relay server forwards and holds —
|
|
918
|
+
* the same fields the dedicated relay container prints at boot
|
|
919
|
+
* (`ops/docker/libp2p-infra/src/main.ts`). Once per start, not per strand:
|
|
920
|
+
* strand nodes resolve the same settings from the same config.
|
|
833
921
|
*/
|
|
834
|
-
private
|
|
922
|
+
private logRelayServerSettings;
|
|
835
923
|
/**
|
|
836
924
|
* Decide whether an inbound CONTROL-network connection from `remotePeerId`
|
|
837
925
|
* should be admitted — the policy behind the connection gater wired in
|
|
@@ -879,7 +967,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
879
967
|
* accepted it, exactly like the unreplicated-membership-row case below.
|
|
880
968
|
*
|
|
881
969
|
* When every check falls through, the verdict depends on whether this node
|
|
882
|
-
* runs the circuit-relay server ({@link
|
|
970
|
+
* runs the circuit-relay server ({@link relayServer}): without one,
|
|
883
971
|
* `'deny'`; with one, `'admit-for-relay'` — a circuit-relay reservation is
|
|
884
972
|
* established by the reserving peer DIALING the relay, so a connection deny
|
|
885
973
|
* here kills the reservation, and that deny is NOT self-healing: an outbound
|
|
@@ -1045,7 +1133,25 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1045
1133
|
* address without a `/p2p/<id>` component contributes nothing.
|
|
1046
1134
|
*/
|
|
1047
1135
|
private getBootstrapPeerIds;
|
|
1136
|
+
/**
|
|
1137
|
+
* What the strand watcher polls: this party's control rows plus a row per cross-party
|
|
1138
|
+
* join (see {@link joinedStrands}), so a joined strand is offered and relaunched on
|
|
1139
|
+
* the same path as the party's own.
|
|
1140
|
+
*/
|
|
1048
1141
|
private createStrandQueryable;
|
|
1142
|
+
/**
|
|
1143
|
+
* The party-wide `JoinedStrand` table as {@link joinedStrands} reads and writes it.
|
|
1144
|
+
* Reads {@link controlDatabase} at call time, so start-up order does not matter.
|
|
1145
|
+
*/
|
|
1146
|
+
private createPartyJoinedStrandLedger;
|
|
1147
|
+
/**
|
|
1148
|
+
* This machine's owner signing key when it can make owner-gated control writes: its seed
|
|
1149
|
+
* bootstrap holds an owner key (`SeedBootstrapService.canAuthorize`) and the party's
|
|
1150
|
+
* `OwnerKey` table enrolls it. A phone that ran self-genesis after another machine
|
|
1151
|
+
* founded the party passes the first test and not the second (`runOwnerGenesis` in the
|
|
1152
|
+
* reference apps), and a write it signed would only be refused.
|
|
1153
|
+
*/
|
|
1154
|
+
private enrolledOwnerSigningKey;
|
|
1049
1155
|
/**
|
|
1050
1156
|
* Schedule the node's initial self-record publish + ongoing refresh shortly
|
|
1051
1157
|
* after start (non-blocking). {@link registerSelf} is idempotent and safely
|
|
@@ -1369,6 +1475,17 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1369
1475
|
* test that drives dead addresses on purpose keeps the duration it chose.
|
|
1370
1476
|
*/
|
|
1371
1477
|
private controlDialBudget;
|
|
1478
|
+
/**
|
|
1479
|
+
* The link-derived limits shared by every {@link SeedBootstrapService} this node builds: its
|
|
1480
|
+
* owner and invite dials ({@link controlDialBudget}) and its seed delivery deadline, derived
|
|
1481
|
+
* from `network.linkRoundTripMs`. One helper so the four construction sites cannot drift.
|
|
1482
|
+
*/
|
|
1483
|
+
private seedServiceBudgets;
|
|
1484
|
+
/**
|
|
1485
|
+
* {@link collectStrandAddrs} at this node's declared `network.linkRoundTripMs`, so each ask's
|
|
1486
|
+
* deadline derives from it. One helper so the four call sites cannot drift.
|
|
1487
|
+
*/
|
|
1488
|
+
private collectSiblingStrandAddrs;
|
|
1372
1489
|
/**
|
|
1373
1490
|
* Dial one sibling from its already-resolved addresses, best-effort. Returns
|
|
1374
1491
|
* whether the dial resolved (false when no address resolves or the dial fails).
|
|
@@ -1377,7 +1494,9 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1377
1494
|
* the {@link controlDialBudget} expiring) is logged and swallowed so one
|
|
1378
1495
|
* unreachable sibling never aborts the pass — exactly like
|
|
1379
1496
|
* {@link SeedBootstrapService.applySeed}'s owner-dial loop. A failed dial is
|
|
1380
|
-
* simply retried on the next pass.
|
|
1497
|
+
* simply retried on the next pass. A sibling whose every address relays
|
|
1498
|
+
* through this node ({@link SelfRelayOnlyError}) is not dialed at all and gets
|
|
1499
|
+
* a one-line log instead: only the sibling can reconnect.
|
|
1381
1500
|
*/
|
|
1382
1501
|
private dialControlSibling;
|
|
1383
1502
|
/**
|
|
@@ -1767,6 +1886,37 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1767
1886
|
*/
|
|
1768
1887
|
expireDeviceToken(peerId: string): Promise<void>;
|
|
1769
1888
|
private handleStrandAdded;
|
|
1889
|
+
/**
|
|
1890
|
+
* Offer a strand no local config claims to the hosting app as `strand:discovered`, so
|
|
1891
|
+
* it can decide whether to join (register a config + addStrand); the strand-agnostic
|
|
1892
|
+
* seam keeps this class free of any app's join policy.
|
|
1893
|
+
*
|
|
1894
|
+
* Once per strand: a storage-replica launch that fails is retried by the watcher
|
|
1895
|
+
* (`StrandWatcher.forgetStrand` + backoff), and each retry comes back through
|
|
1896
|
+
* {@link handleStrandAdded} with no config — the app must still see one announcement.
|
|
1897
|
+
* The other thing that un-retains a strand in the watcher (a failed `addStrand`) leaves
|
|
1898
|
+
* the sApp config registered, so that retry takes the claimed branch instead.
|
|
1899
|
+
*
|
|
1900
|
+
* Recorded BEFORE the emit so a handler that synchronously drains
|
|
1901
|
+
* `getDiscoveredStrands()` sees this strand too. So this map — not the event — is what
|
|
1902
|
+
* a late subscriber reads. See the `strand:discovered` doc in types.ts.
|
|
1903
|
+
*
|
|
1904
|
+
* NOTE: a replica launch that keeps failing leaves the watcher not tracking the strand,
|
|
1905
|
+
* so if its control row is removed during the backoff no `handleStrandRemoved` arrives
|
|
1906
|
+
* and this entry outlives the row. Needs a persistently failing launch AND a removal
|
|
1907
|
+
* inside the backoff; if it is ever seen, have the watcher report removal of rows it
|
|
1908
|
+
* forgot, since `detachStrand` is already a no-op for an untracked instance.
|
|
1909
|
+
*/
|
|
1910
|
+
private announceDiscoveredStrand;
|
|
1911
|
+
/**
|
|
1912
|
+
* Record the sApp id a storage replica's `Strand.Header` names, for {@link getSAppId}.
|
|
1913
|
+
* Runs whenever a replica's database may have just been published (launch, the
|
|
1914
|
+
* first-sync gate opening, a wake); a no-op for a claimed strand, an unpublished
|
|
1915
|
+
* database, or an id already recorded. Never throws: a failed read logs and leaves the
|
|
1916
|
+
* id unknown, so an `sAppId` filter keeps the strand provisionally admitted — a
|
|
1917
|
+
* syncing replica, which is harmless.
|
|
1918
|
+
*/
|
|
1919
|
+
private recordReplicaSAppId;
|
|
1770
1920
|
private handleStrandRemoved;
|
|
1771
1921
|
/**
|
|
1772
1922
|
* Return this node to its pre-{@link start} state. The ONE teardown — `stop()` and
|
|
@@ -1787,10 +1937,10 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1787
1937
|
private handleStrandIdle;
|
|
1788
1938
|
/**
|
|
1789
1939
|
* Hibernate a strand: release its strand-network resources via the strand
|
|
1790
|
-
* manager (stop the libp2p node, close the StrandDatabase)
|
|
1940
|
+
* manager (stop the libp2p node, close the StrandDatabase), which marks it
|
|
1791
1941
|
* `hibernating`. A quiesced strand holds no open strand-network connections,
|
|
1792
|
-
* transports, or DB handles. No-ops if the strand is missing;
|
|
1793
|
-
*
|
|
1942
|
+
* transports, or DB handles. No-ops if the strand is missing; one already quiesced
|
|
1943
|
+
* is only marked, and still emits.
|
|
1794
1944
|
*/
|
|
1795
1945
|
private handleStrandHibernate;
|
|
1796
1946
|
/**
|
|
@@ -1799,15 +1949,42 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1799
1949
|
* its runtime via the strand manager. If it is still live (e.g. waking an idle
|
|
1800
1950
|
* strand, which retains its resources), just flip the status. Overlapping wake
|
|
1801
1951
|
* triggers are coalesced upstream by `HibernationManager`, so this runs once
|
|
1802
|
-
* per wake;
|
|
1952
|
+
* per wake; a wake racing a check-in joins the check-in's rebuild in `resumeStrand`.
|
|
1953
|
+
*
|
|
1954
|
+
* A failed rebuild re-hibernates the strand (see {@link rehibernateIfResumeFailed});
|
|
1955
|
+
* every failure rethrows, so the waker still sees the error.
|
|
1956
|
+
*
|
|
1957
|
+
* Records no activity itself: whoever asked for the wake did ({@link wakeStrand},
|
|
1958
|
+
* `HibernationManager.recordActivity`), and {@link serviceWake}'s own probe must not.
|
|
1803
1959
|
*/
|
|
1804
1960
|
private handleStrandWake;
|
|
1961
|
+
/**
|
|
1962
|
+
* Undo a failed {@link resumeStrandRuntime}, but only when the rebuild itself failed — this
|
|
1963
|
+
* caller's, or the one it joined in `resumeStrand` — which leaves the instance `'error'`. A
|
|
1964
|
+
* failure before the rebuild (the cohort seed read) built nothing, and may find the strand
|
|
1965
|
+
* mid-build or just brought up by a concurrent wake or check-in; that runtime belongs to
|
|
1966
|
+
* whoever is building or holding it, so quiescing it here would stop a node still in use.
|
|
1967
|
+
*/
|
|
1968
|
+
private rehibernateIfResumeFailed;
|
|
1969
|
+
/**
|
|
1970
|
+
* Put a strand whose rebuild or wake window failed back to `'hibernating'`, through a
|
|
1971
|
+
* best-effort quiesce that releases any runtime left up. `resumeStrand` leaves a failed
|
|
1972
|
+
* strand `'error'`, which nothing retries: `HibernationManager` wakes only
|
|
1973
|
+
* `idle`/`hibernating` strands, and reads any other status after a check-in as "woke",
|
|
1974
|
+
* ending its chain. Safe to run twice for one failure (a wake that joined a failed
|
|
1975
|
+
* check-in's rebuild): quiescing a quiesced strand only marks it.
|
|
1976
|
+
*
|
|
1977
|
+
* The quiesce writes the status itself, ordered before any resume requested meanwhile;
|
|
1978
|
+
* this writes it only when the quiesce failed, since a failed release leaves the status
|
|
1979
|
+
* alone.
|
|
1980
|
+
*/
|
|
1981
|
+
private rehibernateAfterFailedResume;
|
|
1805
1982
|
/**
|
|
1806
1983
|
* Rebuild a quiesced strand's runtime, re-resolving the volatile cohort input
|
|
1807
1984
|
* first: the discovery seed may have grown since the strand last ran. Shared by
|
|
1808
1985
|
* the wake (`handleStrandWake`) and check-in (`handleStrandCheckIn`) paths so both
|
|
1809
|
-
* apply the same fresh resolution. `resumeStrand`
|
|
1810
|
-
*
|
|
1986
|
+
* apply the same fresh resolution. `resumeStrand` returns a live instance unchanged
|
|
1987
|
+
* and joins a rebuild already in flight, so the two paths never build twice.
|
|
1811
1988
|
*/
|
|
1812
1989
|
private resumeStrandRuntime;
|
|
1813
1990
|
/**
|
|
@@ -1836,15 +2013,17 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1836
2013
|
* Window-then-decide for a just-resumed strand, shared by the check-in timer
|
|
1837
2014
|
* path ({@link handleStrandCheckIn}) and the on-demand {@link serviceWake}:
|
|
1838
2015
|
*
|
|
1839
|
-
* 1.
|
|
1840
|
-
* FRESH `Date`, so a changed reference after the window means real
|
|
1841
|
-
* activity landed during it — not millisecond-resolution noise.
|
|
1842
|
-
* 2. Hold the strand live for `windowMs` so its strand network reaches the
|
|
2016
|
+
* 1. Hold the strand live for `windowMs` so its strand network reaches the
|
|
1843
2017
|
* cohort and the app can drive pull-on-read activity.
|
|
1844
|
-
*
|
|
1845
|
-
* quiesce
|
|
2018
|
+
* 2. If activity landed since `activityMark`, leave the strand `active` (return
|
|
2019
|
+
* `true`); otherwise quiesce it, which marks it `hibernating` again (return `false`).
|
|
2020
|
+
* A wake or activity that lands during that quiesce rebuilds the strand after it.
|
|
1846
2021
|
*
|
|
1847
|
-
* @
|
|
2022
|
+
* @param activityMark - `instance.lastActivity` as the caller read it BEFORE bringing the
|
|
2023
|
+
* strand up. The bring-up records none, and every writer assigns a FRESH `Date`, so a
|
|
2024
|
+
* changed reference means a wake or activity landed during the resume or the window —
|
|
2025
|
+
* not millisecond-resolution noise.
|
|
2026
|
+
* @returns whether activity was observed (strand left active).
|
|
1848
2027
|
*/
|
|
1849
2028
|
private runWakeWindow;
|
|
1850
2029
|
/**
|
|
@@ -1875,6 +2054,11 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1875
2054
|
* published (e.g. re-attaching its own orphan after a restart) founds it, and attaching
|
|
1876
2055
|
* anyone else's row joins, without the caller needing to know which it is.
|
|
1877
2056
|
*
|
|
2057
|
+
* A joined row (no `founder: true`) this party's control database does not name — a
|
|
2058
|
+
* strand joined from another party — is remembered in the node's joined-strand store, so it
|
|
2059
|
+
* is re-offered as `strand:discovered` after a restart, and published party-wide by an
|
|
2060
|
+
* owner machine's reconcile pass (see {@link forgetJoinedStrand}).
|
|
2061
|
+
*
|
|
1878
2062
|
* A rejected call leaves nothing running but DOES leave the sApp config
|
|
1879
2063
|
* registered, deliberately: both an explicit retry and the {@link StrandWatcher}'s
|
|
1880
2064
|
* automatic relaunch need it. A failed launch here hands the strand back to that
|
|
@@ -2052,7 +2236,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2052
2236
|
* — and with it the consent re-seat foreclosure — does propagate; but the physical row
|
|
2053
2237
|
* deletion cannot be replayed, and `queryStrands` reads raw (no retired-stamp filter),
|
|
2054
2238
|
* so siblings that already hold the row keep running the strand until the collection
|
|
2055
|
-
* itself converges (logged loudly; see
|
|
2239
|
+
* itself converges (logged loudly; see "Deletes made while alone" in
|
|
2056
2240
|
* docs/architecture.md).
|
|
2057
2241
|
*
|
|
2058
2242
|
* A no-op (no throw, no tombstone) when the row is already absent — but a
|
|
@@ -2115,10 +2299,10 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2115
2299
|
* NOTE: "founding machine" here is the ROW's provenance, not the launch's resolved
|
|
2116
2300
|
* `founder` flag — so an explicit `founder: false` over a row this machine published
|
|
2117
2301
|
* still mints, an owner-signed write a caller that said "I am not founding" did not ask
|
|
2118
|
-
* for.
|
|
2119
|
-
*
|
|
2120
|
-
*
|
|
2121
|
-
*
|
|
2302
|
+
* for. Reached by a storage replica of a row this machine published (always a joiner,
|
|
2303
|
+
* see {@link launchStrand}) and kept on purpose: the party needs that identity for the
|
|
2304
|
+
* strand either way, the mint is insert-if-absent, and a closed-strand replica runs its
|
|
2305
|
+
* membership reconciler — which seats its own `MemberPeer` binding — only with a party key.
|
|
2122
2306
|
*/
|
|
2123
2307
|
private resolveStrandPartyKey;
|
|
2124
2308
|
/**
|
|
@@ -2239,6 +2423,15 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2239
2423
|
* A founder launch refused as pre-split (`PreSplitStrandIdentityError`) is recorded in
|
|
2240
2424
|
* {@link strandLaunchRefusals} for the formation arm, then rethrown; a founder launch
|
|
2241
2425
|
* that succeeds clears the record (it ran the bootstrap's pre-split check and passed).
|
|
2426
|
+
*
|
|
2427
|
+
* An absent `sAppConfig` launches a storage replica ({@link CadreNodeConfig.hostUnclaimedStrands}),
|
|
2428
|
+
* which is ALWAYS a joiner whatever the row says: the founder bootstrap writes the sApp
|
|
2429
|
+
* into `Strand.Header`, and a replica has none. So a self-founded row whose watcher poll
|
|
2430
|
+
* wins the race against its app's `addStrand` after a restart comes up as a replica — and
|
|
2431
|
+
* so does a `foundStrand` whose publish a watcher poll saw before its attach. A claim
|
|
2432
|
+
* that finds a tracked replica upgrades it in place, over the same node and store
|
|
2433
|
+
* ({@link StrandInstanceManager.attachSApp}), BEFORE any founding: the founder bootstrap
|
|
2434
|
+
* needs the sApp the attach supplies.
|
|
2242
2435
|
*/
|
|
2243
2436
|
private launchStrand;
|
|
2244
2437
|
/**
|
|
@@ -2246,6 +2439,19 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2246
2439
|
* tracked instance in place, or start a fresh one.
|
|
2247
2440
|
*/
|
|
2248
2441
|
private startOrFoundStrand;
|
|
2442
|
+
/**
|
|
2443
|
+
* {@link startOrFoundStrand} for a strand the manager already tracks: a claim of a
|
|
2444
|
+
* storage replica gives it the app's schema in place, then a founder request founds it,
|
|
2445
|
+
* waking a quiesced instance so the bootstrap has run before this resolves. Emits
|
|
2446
|
+
* nothing — `strand:started` fired when the instance launched.
|
|
2447
|
+
*
|
|
2448
|
+
* A launch of the strand still in flight (the watcher's launch racing an app's
|
|
2449
|
+
* `addStrand`, in either order) is waited out first: until its build settles the tracked
|
|
2450
|
+
* instance is `'starting'` with no database, and returning it would resolve `addStrand`
|
|
2451
|
+
* with a strand the app cannot use — and skip that call's first-sync wait, which only a
|
|
2452
|
+
* `'syncing'` instance gets. If that launch fails, this rejects too: its record is gone.
|
|
2453
|
+
*/
|
|
2454
|
+
private claimTrackedStrand;
|
|
2249
2455
|
/**
|
|
2250
2456
|
* Resolve a strand's discovery seed — the dialable strand-network multiaddr
|
|
2251
2457
|
* strings for cohort siblings. Membership comes from the control network's
|
|
@@ -2268,11 +2474,18 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2268
2474
|
* relay reservation — the gate denial there is fatal, not degraded. A
|
|
2269
2475
|
* party-member relay answers the RPC (its control node admits us as a
|
|
2270
2476
|
* member); a dedicated ops/ relay does not speak the protocol and the
|
|
2271
|
-
* per-peer failure
|
|
2477
|
+
* per-peer failure comes back `unreachable` — harmless, it has no membership gate and
|
|
2272
2478
|
* needs no grant. The relay's direct addr rides along as the dial fallback
|
|
2273
2479
|
* for a relay we are not yet connected to.
|
|
2274
2480
|
*/
|
|
2275
2481
|
private resolveCohortSeed;
|
|
2482
|
+
/**
|
|
2483
|
+
* The strand peer book's contribution to a strand's seed: every live entry's
|
|
2484
|
+
* addresses, freshest peer first, minus this node's own entry when `selfPeerId` is
|
|
2485
|
+
* known (the book swap files one under the strand transport id; a node must never
|
|
2486
|
+
* dial itself). Empty before {@link start} builds the store.
|
|
2487
|
+
*/
|
|
2488
|
+
private strandPeerBookAddrs;
|
|
2276
2489
|
/**
|
|
2277
2490
|
* The own-party half of {@link resolveCohortSeed}: strand-network addresses resolved
|
|
2278
2491
|
* from CONNECTED cohort siblings over the control-mesh strand-addr RPC, doubling as
|
|
@@ -2342,9 +2555,10 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2342
2555
|
* Record the announce timestamps {@link refreshDelegateGrants} throttles on,
|
|
2343
2556
|
* for every relay a delegate-carrying announce pass dialed.
|
|
2344
2557
|
*
|
|
2345
|
-
* Recorded OPTIMISTICALLY at announce time
|
|
2346
|
-
* per
|
|
2347
|
-
*
|
|
2558
|
+
* Recorded OPTIMISTICALLY at announce time, whatever `collectStrandAddrs`
|
|
2559
|
+
* reports per peer: a dedicated `ops/` relay never speaks the strand-addr
|
|
2560
|
+
* protocol, so recording only on success would re-announce to it on every
|
|
2561
|
+
* reconcile tick, one wasted protocol negotiation each. A failed INITIAL announce
|
|
2348
2562
|
* costs the strand supervisor its first attempt only (the relay denies the
|
|
2349
2563
|
* reservation; every re-drive re-announces first through
|
|
2350
2564
|
* {@link announceDelegateToRelay}); a failed REFRESH retries within
|
|
@@ -2382,16 +2596,16 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2382
2596
|
* again right away.
|
|
2383
2597
|
*
|
|
2384
2598
|
* Against a dedicated ops relay (no strand-addr RPC) the request fails per-peer
|
|
2385
|
-
* and `collectStrandAddrs`
|
|
2599
|
+
* and `collectStrandAddrs` reports it `unreachable`: one wasted protocol negotiation per
|
|
2386
2600
|
* re-drive attempt, bounded by the supervisor's backoff. Never throws on that
|
|
2387
2601
|
* path; a relay addr that names no peer id is logged and skipped (the hook's
|
|
2388
2602
|
* caller runs the drive regardless).
|
|
2389
2603
|
*
|
|
2390
2604
|
* NOTE: against a relay that is DOWN this hook costs up to two strand-addr
|
|
2391
|
-
* timeouts (
|
|
2392
|
-
* starts, so one failed re-drive holds the supervisor
|
|
2393
|
-
* drive's own deadline —
|
|
2394
|
-
* that declared a slower one (`link-budget.ts`).
|
|
2605
|
+
* timeouts (dial by peer id, then by addr; 23 s each at the default declared link round trip)
|
|
2606
|
+
* before the reservation drive even starts, so one failed re-drive holds the supervisor
|
|
2607
|
+
* `driving` for those 46 s plus the drive's own deadline — 64 s at the default, and longer on
|
|
2608
|
+
* a host that declared a slower one (`link-budget.ts`).
|
|
2395
2609
|
* Bounded and harmless while the relay is unreachable anyway; if recovery
|
|
2396
2610
|
* latency after a relay comes back ever matters, skip the announce when the
|
|
2397
2611
|
* control node holds no connection to the relay (the drive's own dial fails
|
|
@@ -2401,9 +2615,10 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2401
2615
|
/** One strand's share of {@link refreshDelegateGrants}: announce to the relays whose grant is due. */
|
|
2402
2616
|
private announceDelegateToDueRelays;
|
|
2403
2617
|
/**
|
|
2404
|
-
*
|
|
2405
|
-
*
|
|
2406
|
-
*
|
|
2618
|
+
* Keep each running strand's own libp2p address book warm: re-merge its strand
|
|
2619
|
+
* peer book on every pass, and re-ask each connected SIBLING for its strand
|
|
2620
|
+
* addresses over the control mesh when that (sibling, strand) is due
|
|
2621
|
+
* ({@link strandAddrAskDueAt}).
|
|
2407
2622
|
*
|
|
2408
2623
|
* Without this, a strand's address book is written exactly once — the
|
|
2409
2624
|
* launch/resume seed — and everything below cadre-core that dials a strand peer
|
|
@@ -2413,6 +2628,12 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2413
2628
|
* re-resolved, and even the original seed addresses fall off at the peerStore's
|
|
2414
2629
|
* one-hour expiry (see `peer-addr-book.ts`).
|
|
2415
2630
|
*
|
|
2631
|
+
* Due times are per (sibling, strand) and set from each sibling's own outcome, so
|
|
2632
|
+
* a sibling that connects late — a phone joining after the party's always-on
|
|
2633
|
+
* machines — is asked on the next tick, and one that could not answer or refused
|
|
2634
|
+
* (its view of the membership may not include us yet) is retried within
|
|
2635
|
+
* {@link STRAND_PEER_ADDR_RETRY_MS} rather than {@link STRAND_PEER_ADDR_REFRESH_MS}.
|
|
2636
|
+
*
|
|
2416
2637
|
* Distinct from {@link refreshDelegateGrants}, deliberately: that pass covers
|
|
2417
2638
|
* RELAYS on a `DELEGATE_GRANT_TTL_MS / 2` throttle to keep circuit-relay
|
|
2418
2639
|
* admission grants alive, this one covers SIBLINGS on an address-expiry
|
|
@@ -2426,20 +2647,49 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2426
2647
|
*/
|
|
2427
2648
|
private refreshStrandPeerAddrs;
|
|
2428
2649
|
/**
|
|
2429
|
-
*
|
|
2430
|
-
*
|
|
2431
|
-
*
|
|
2432
|
-
*
|
|
2650
|
+
* The siblings {@link refreshStrandPeerAddrs} may ask this pass: the connected
|
|
2651
|
+
* cohort, or none when enumeration fails (logged, never thrown).
|
|
2652
|
+
*
|
|
2653
|
+
* `connectedSiblingTargets`' membership read is unbounded, and with zero
|
|
2654
|
+
* connections its answer is empty whatever the table holds, so decide it from the
|
|
2655
|
+
* connection list instead of paying for the read once a tick.
|
|
2656
|
+
*
|
|
2657
|
+
* NOTE: otherwise the read runs on EVERY tick with a running strand, not only when
|
|
2658
|
+
* some (sibling, strand) is due: pruning a departed sibling needs the current
|
|
2659
|
+
* target set, and whether anyone is due cannot be told without it (a connected
|
|
2660
|
+
* non-member never gets a due time to compare). One more `CadrePeer` read per 15 s
|
|
2661
|
+
* tick, on top of the two `runReconcileControlCohort` already makes; if those reads
|
|
2662
|
+
* get costly, share one row-set across the pass (see the NOTE there) rather than
|
|
2663
|
+
* skipping ticks here.
|
|
2433
2664
|
*
|
|
2434
|
-
*
|
|
2435
|
-
*
|
|
2436
|
-
*
|
|
2437
|
-
*
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2665
|
+
* NOTE: one strand-addr RPC per (running strand × due sibling) — each a tiny
|
|
2666
|
+
* request/response on an already-open control connection. If a node ever runs
|
|
2667
|
+
* MANY strands at once, batch the RPC to carry several strand ids per request
|
|
2668
|
+
* rather than one fan-out per strand.
|
|
2669
|
+
*/
|
|
2670
|
+
private strandAddrRefreshTargets;
|
|
2671
|
+
/**
|
|
2672
|
+
* One strand's share of {@link refreshStrandPeerAddrs}: RPC the siblings that are
|
|
2673
|
+
* due, union their answers with this strand's peer-book addresses, and merge the
|
|
2674
|
+
* lot into the strand's address book. Errors are logged and swallowed so one
|
|
2675
|
+
* strand's failure never costs the others their refresh.
|
|
2676
|
+
*
|
|
2677
|
+
* The peer book merges on EVERY pass, whether or not any sibling is due or
|
|
2678
|
+
* connected: nothing re-resolves another party's addresses, so this re-merge is the
|
|
2679
|
+
* ONLY thing standing between them and the peerStore's one-hour expiry — and it is
|
|
2680
|
+
* also how a re-formation's freshly carried addresses reach a running strand on the
|
|
2681
|
+
* next tick. A strand with nobody due and no book entry does nothing.
|
|
2441
2682
|
*/
|
|
2442
2683
|
private refreshOneStrandPeerAddrs;
|
|
2684
|
+
/**
|
|
2685
|
+
* RPC `due` siblings for `strandId` and set each one's next due time from its own
|
|
2686
|
+
* outcome: an answer, even an empty one, waits the full refresh interval; anything
|
|
2687
|
+
* else retries in {@link STRAND_PEER_ADDR_RETRY_MS} (or the refresh interval, if
|
|
2688
|
+
* configured shorter). Stamped before the caller
|
|
2689
|
+
* re-checks that the strand is still running, so a strand torn down mid-pass still
|
|
2690
|
+
* records who was asked.
|
|
2691
|
+
*/
|
|
2692
|
+
private askSiblingsForStrandAddrs;
|
|
2443
2693
|
/**
|
|
2444
2694
|
* Merge strand-network addresses into ONE strand node's libp2p address book,
|
|
2445
2695
|
* attributed per peer. Best-effort throughout: an address-book write must never
|
|
@@ -2459,14 +2709,14 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2459
2709
|
* peerStore's one-hour expiry. No new gating here — cross-party strand trust is
|
|
2460
2710
|
* `backlog/strand-network-nat-relay-reachability`.
|
|
2461
2711
|
*
|
|
2462
|
-
* NOTE: one input
|
|
2463
|
-
*
|
|
2464
|
-
* {@link refreshStrandPeerAddrs} re-merges the
|
|
2465
|
-
*
|
|
2466
|
-
*
|
|
2467
|
-
*
|
|
2468
|
-
*
|
|
2469
|
-
*
|
|
2712
|
+
* NOTE: one input ages out on a slower clock — the strand peer book's entries
|
|
2713
|
+
* ({@link strandPeerBookStore}). Nothing can re-resolve another party's addresses,
|
|
2714
|
+
* so {@link refreshStrandPeerAddrs} re-merges the book every pass, which means a
|
|
2715
|
+
* junk entry from a responder or a peer's identify survives until the book drops it
|
|
2716
|
+
* (14 days unrefreshed, or the peer's next connection replacing the entry) rather
|
|
2717
|
+
* than an hour. Bounded to 16 peers × 16 addrs per strand, forgotten on
|
|
2718
|
+
* `unpublishStrand` / `forgetJoinedStrand`, and still authority-free — so the
|
|
2719
|
+
* exposure is a handful of failed dials, not a trust hole.
|
|
2470
2720
|
*/
|
|
2471
2721
|
private mergeStrandPeerAddrs;
|
|
2472
2722
|
/**
|
|
@@ -2479,6 +2729,86 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2479
2729
|
* live node, so it answers and a later sibling can dial in.
|
|
2480
2730
|
*/
|
|
2481
2731
|
private getStrandMultiaddrs;
|
|
2732
|
+
/**
|
|
2733
|
+
* Leave a strand joined from another party, for the whole party: remove its party-wide
|
|
2734
|
+
* `JoinedStrand` row (owner-signed, with a `Revocation` tombstone), then this machine's
|
|
2735
|
+
* unpublished record, its strand peer book entries and any session-kept entry, then
|
|
2736
|
+
* {@link stopStrand} it here. Every other machine's watcher then sees the row gone and
|
|
2737
|
+
* detaches the strand, a storage replica included; a machine offline at the time reaps the
|
|
2738
|
+
* row once the tombstone reaches it. The joiner's counterpart of {@link unpublishStrand},
|
|
2739
|
+
* which only works on a row the party's own `Strand` table holds. For one of those, or for
|
|
2740
|
+
* a join still local-only, there is no party-wide row and this is a local forget plus
|
|
2741
|
+
* {@link stopStrand}.
|
|
2742
|
+
*
|
|
2743
|
+
* The other members of the strand are not told, and this party's membership row in the
|
|
2744
|
+
* strand stays, so a later re-formation reuses the same identity.
|
|
2745
|
+
*
|
|
2746
|
+
* @throws when a party-wide row exists and this machine cannot sign its removal (not an
|
|
2747
|
+
* enrolled owner) — {@link stopStrand} stops the strand on this machine only — or when
|
|
2748
|
+
* the party-wide table cannot be read.
|
|
2749
|
+
*/
|
|
2750
|
+
forgetJoinedStrand(strandId: string): Promise<void>;
|
|
2751
|
+
/**
|
|
2752
|
+
* Record `row` as a join from another party when this party's control database names it
|
|
2753
|
+
* neither as its own strand nor as a party-wide join — the one case nothing else would
|
|
2754
|
+
* re-offer after a restart (`JoinedStrandSession.rememberForeign`).
|
|
2755
|
+
*
|
|
2756
|
+
* Best-effort, for {@link addStrand}: a failure costs the strand its re-offer after the
|
|
2757
|
+
* next restart, not this attach, so it is reported and the attach goes on.
|
|
2758
|
+
* {@link formStrand}, whose join nothing else would name, fails loudly instead.
|
|
2759
|
+
*
|
|
2760
|
+
* NOTE: "no control row" also describes a row the party has just unpublished. A claim
|
|
2761
|
+
* of such a row the app kept from an earlier offer, made after the watcher already
|
|
2762
|
+
* withdrew it, is remembered as a join and survives the removal on this machine until
|
|
2763
|
+
* {@link forgetJoinedStrand}. Contrived today (a claim of a row the node itself is still
|
|
2764
|
+
* offering skips this method); if it shows up, check the strand's `Revocation`
|
|
2765
|
+
* tombstone here before recording.
|
|
2766
|
+
*/
|
|
2767
|
+
private rememberForeignStrand;
|
|
2768
|
+
/**
|
|
2769
|
+
* Self-revocation arm of the joined-strand records: a party removed from a strand must
|
|
2770
|
+
* not re-attach it on every launch of every machine. The strand keeps running this
|
|
2771
|
+
* session here — the `strand:revoked` contract is that nothing is torn down for the app —
|
|
2772
|
+
* and its party-wide row is queued for removal by the next connected owner reconcile pass
|
|
2773
|
+
* (`JoinedStrandSession.forgetAfterThisSession`). A no-op for this party's own strands,
|
|
2774
|
+
* which have neither record; the strand peer book is forgotten either way, since a
|
|
2775
|
+
* removed party must not keep dialing the strand's peers.
|
|
2776
|
+
*
|
|
2777
|
+
* NOTE: accepted tradeoff — a sibling machine that has not raised `strand:revoked` itself
|
|
2778
|
+
* detaches the strand (`strand:stopped`) when the party-wide removal reaches it, instead
|
|
2779
|
+
* of keeping it for the session, which weakens "nothing is stopped on the removed
|
|
2780
|
+
* machine's behalf" for siblings. Weighed against a party-wide row that brings a revoked
|
|
2781
|
+
* strand back on every start of every machine. Revisit if apps need the "you were
|
|
2782
|
+
* removed" screen on every device: the sibling could re-check its own revocation state
|
|
2783
|
+
* (`refreshRevocationEnforcement`) before detaching a vanished joined row.
|
|
2784
|
+
*
|
|
2785
|
+
* NOTE: a manager can re-admit a removed party directly (`addMemberByManager`), and the
|
|
2786
|
+
* membership loop then finishes the join on its own — but the records are gone by then,
|
|
2787
|
+
* so the next start does not re-attach the strand. Re-forming records it again. If direct
|
|
2788
|
+
* re-admission becomes a routine flow, re-record the join when the revoked-peer gate
|
|
2789
|
+
* clears for this node.
|
|
2790
|
+
*/
|
|
2791
|
+
private forgetRevokedJoin;
|
|
2792
|
+
/**
|
|
2793
|
+
* Forget a strand's entries in the peer book (see {@link strandPeerBookStore}):
|
|
2794
|
+
* fire-and-log, like every book write — the removal is visible synchronously by the
|
|
2795
|
+
* store's contract, and a failed persist costs restart survival only.
|
|
2796
|
+
*/
|
|
2797
|
+
private forgetStrandPeers;
|
|
2798
|
+
/**
|
|
2799
|
+
* The strand peer book's observation writer (`StartStrandConfig.onStrandPeerIdentified`):
|
|
2800
|
+
* a strand node identified a strand peer at `observation.addrs`, so remember it as an
|
|
2801
|
+
* unsigned entry seen just now. The observer never reports self, and the store binds
|
|
2802
|
+
* every address to the peer id before filing it.
|
|
2803
|
+
*/
|
|
2804
|
+
private observeStrandPeer;
|
|
2805
|
+
/**
|
|
2806
|
+
* Merge one entry into the peer book. Fire-and-log: the entry is visible
|
|
2807
|
+
* synchronously by the store's contract, and the promise tracks durability only. A
|
|
2808
|
+
* persist failure costs restart survival, never this session's seed — the same trade
|
|
2809
|
+
* {@link retainDialTarget} makes.
|
|
2810
|
+
*/
|
|
2811
|
+
private rememberStrandPeer;
|
|
2482
2812
|
/**
|
|
2483
2813
|
* Stop a strand on THIS node only: untrack it from hibernation, drop its sApp config,
|
|
2484
2814
|
* stop the local instance, and emit `strand:stopped`. The shared `Strand` row is left
|
|
@@ -2487,7 +2817,9 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2487
2817
|
* id in the watcher (`StrandWatcher.suppressStrand`) and drops it from
|
|
2488
2818
|
* {@link getDiscoveredStrands}, so neither a later poll nor a drain can undo a
|
|
2489
2819
|
* deliberate stop. Only an explicit {@link addStrand} does, which is the caller
|
|
2490
|
-
* reversing its own decision. Party-wide removal is {@link unpublishStrand}.
|
|
2820
|
+
* reversing its own decision. Party-wide removal is {@link unpublishStrand}. A strand
|
|
2821
|
+
* joined from another party comes back the same way, from its remembered join;
|
|
2822
|
+
* {@link forgetJoinedStrand} is how to leave one for good.
|
|
2491
2823
|
*/
|
|
2492
2824
|
stopStrand(strandId: string): Promise<void>;
|
|
2493
2825
|
/**
|
|
@@ -2537,7 +2869,10 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2537
2869
|
*/
|
|
2538
2870
|
notifyStrandActivity(strandId: string, reason?: string): void;
|
|
2539
2871
|
/**
|
|
2540
|
-
* Force wake a hibernating strand
|
|
2872
|
+
* Force wake a hibernating strand. A requested wake is activity, recorded before the
|
|
2873
|
+
* wake starts: a check-in holding the strand — or rebuilding it, a rebuild this wake then
|
|
2874
|
+
* joins — leaves it up instead of re-quiescing it at the end of its window. Once awake the
|
|
2875
|
+
* strand's idle countdown runs, so it hibernates again if nothing uses it.
|
|
2541
2876
|
*/
|
|
2542
2877
|
wakeStrand(strandId: string): Promise<void>;
|
|
2543
2878
|
/**
|
|
@@ -2548,7 +2883,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2548
2883
|
* Routes through {@link HibernationManager.forceHibernate}, which cancels the
|
|
2549
2884
|
* strand's pending idle/hibernate (and check-in) timers — so a stale timer
|
|
2550
2885
|
* can't re-fire on or resurrect the strand — then runs the same `onHibernate`
|
|
2551
|
-
* path as the timer (`quiesceStrand
|
|
2886
|
+
* path as the timer (`quiesceStrand`, which marks it `hibernating`, then
|
|
2552
2887
|
* `strand:hibernating`). Unlike the timer path it does NOT re-arm check-ins:
|
|
2553
2888
|
* the strand stays down until the caller drives a wake (e.g. {@link serviceWake}).
|
|
2554
2889
|
*/
|
|
@@ -2573,9 +2908,14 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2573
2908
|
* strand share one in-flight operation ({@link serviceWakePromises}), and the
|
|
2574
2909
|
* underlying resume coalesces with a racing push-wake via
|
|
2575
2910
|
* {@link HibernationManager}'s wake coalescing — one runtime build, one window,
|
|
2576
|
-
* one re-hibernate decision.
|
|
2577
|
-
* the
|
|
2578
|
-
*
|
|
2911
|
+
* one re-hibernate decision. A wake or activity from elsewhere that lands during
|
|
2912
|
+
* the resume or the window leaves the strand up; this call's own wake does not.
|
|
2913
|
+
* Afterwards a strand left up has its idle countdown running, and a re-hibernated one
|
|
2914
|
+
* gets back the check-in chain this call interrupted — it never gains one it lacked,
|
|
2915
|
+
* so a strand the mobile runner force-hibernated stays down until the next push.
|
|
2916
|
+
* Returns `{ serviced: false }` (never throws) when
|
|
2917
|
+
* the node is not running or the strand is unknown, and surfaces a wake or
|
|
2918
|
+
* window failure as `{ serviced: true, hadActivity: false }`.
|
|
2579
2919
|
*
|
|
2580
2920
|
* @param strandId - the strand a push said has pending activity.
|
|
2581
2921
|
* @param opts.windowMs - override the live-window duration (defaults to the
|
|
@@ -2586,6 +2926,12 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2586
2926
|
}): Promise<ServiceWakeResult>;
|
|
2587
2927
|
/** Body of {@link serviceWake}; serialised per-strand by its coalescing guard. */
|
|
2588
2928
|
private runServiceWake;
|
|
2929
|
+
/**
|
|
2930
|
+
* The wake and window of {@link runServiceWake}. A failure in either — the network
|
|
2931
|
+
* unreachable inside a Doze grant, say — must not throw out of a background task, so it
|
|
2932
|
+
* reports no activity. Each stage cleans up only what it owns.
|
|
2933
|
+
*/
|
|
2934
|
+
private wakeAndHoldForService;
|
|
2589
2935
|
/**
|
|
2590
2936
|
* Get the control network node (for advanced use)
|
|
2591
2937
|
*/
|
|
@@ -2613,8 +2959,8 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2613
2959
|
*
|
|
2614
2960
|
* BUDGET: the supervisor's first attempt is what `start()` waits on — deliberately the drive's
|
|
2615
2961
|
* ordinary deadline rather than a boot-specific one. That deadline is now COUNTED, four link
|
|
2616
|
-
* round trips at the declared `network.linkRoundTripMs`
|
|
2617
|
-
* fixed 10 s): a healthy dial-plus-reserve is sub-second even over a WAN, so this is slack for
|
|
2962
|
+
* round trips at the declared `network.linkRoundTripMs` plus the relay's two admission
|
|
2963
|
+
* decisions (18 s at its default, where it was a fixed 10 s): a healthy dial-plus-reserve is sub-second even over a WAN, so this is slack for
|
|
2618
2964
|
* a slow link, while going much longer would make a dead relay indistinguishable from a hung
|
|
2619
2965
|
* start and much shorter would fail nodes on links that were merely slow. A host that declares
|
|
2620
2966
|
* a slower link lengthens it without touching this path — `link-budget.ts`. The retries carry
|
|
@@ -2695,7 +3041,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2695
3041
|
*
|
|
2696
3042
|
* @param ownerPrivateKey - The owner's private key (base64url encoded)
|
|
2697
3043
|
*/
|
|
2698
|
-
initializeSeedBootstrap(ownerPrivateKey: string): void
|
|
3044
|
+
initializeSeedBootstrap(ownerPrivateKey: string): Promise<void>;
|
|
2699
3045
|
/**
|
|
2700
3046
|
* Push the multiaddrs that future invites should advertise. Pass `null` to
|
|
2701
3047
|
* revert to the libp2p-reported addresses (the default). The host calls this
|
|
@@ -2830,7 +3176,8 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2830
3176
|
* is reachable through its circuit-relay address), dials `WAKE_PROTOCOL`, sends
|
|
2831
3177
|
* the {@link WakeRequest}, and returns the peer's {@link WakeAck}. The receiver
|
|
2832
3178
|
* gates the request on cadre membership and only resumes a strand it already
|
|
2833
|
-
* participates in.
|
|
3179
|
+
* participates in; it acks once it has decided, before the strand is up. The
|
|
3180
|
+
* dial deadlines derive from this node's `network.linkRoundTripMs`.
|
|
2834
3181
|
*
|
|
2835
3182
|
* @param targetPeerId - The hibernating cadre peer to wake.
|
|
2836
3183
|
* @param strandId - The strand the caller knows has pending activity.
|
|
@@ -2843,11 +3190,23 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2843
3190
|
* This is for drone nodes that need to receive seeds without being an owner.
|
|
2844
3191
|
* Does not require an owner key.
|
|
2845
3192
|
*/
|
|
2846
|
-
enableSeedListener(): void
|
|
3193
|
+
enableSeedListener(): Promise<void>;
|
|
2847
3194
|
/**
|
|
2848
3195
|
* Get the seed bootstrap service (for advanced use)
|
|
2849
3196
|
*/
|
|
2850
3197
|
getSeedBootstrapService(): SeedBootstrapService | null;
|
|
3198
|
+
/**
|
|
3199
|
+
* Make `service` this node's seed service and register its inbound seed handler.
|
|
3200
|
+
* The field is set before the registration is awaited, so a concurrent
|
|
3201
|
+
* {@link enableSeedListener} finds it and does not register a second handler; a
|
|
3202
|
+
* failed registration puts the previous service back and rethrows.
|
|
3203
|
+
*
|
|
3204
|
+
* NOTE: libp2p's registrar stores the handler before its peer-store merge, so a
|
|
3205
|
+
* failed merge leaves SEED_PROTOCOL registered with no service owning it and a retry
|
|
3206
|
+
* here rejects as a duplicate; if peer-store writes can fail in practice, unhandle on
|
|
3207
|
+
* a non-duplicate failure (never on a duplicate — that handler belongs to someone else).
|
|
3208
|
+
*/
|
|
3209
|
+
private installSeedBootstrapService;
|
|
2851
3210
|
/**
|
|
2852
3211
|
* The event callbacks every {@link SeedBootstrapService} this node owns is
|
|
2853
3212
|
* wired with — shared by the owner-capable service ({@link initializeSeedBootstrap})
|
|
@@ -3038,7 +3397,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3038
3397
|
*
|
|
3039
3398
|
* @param options - Configuration for the solicitation service
|
|
3040
3399
|
*/
|
|
3041
|
-
initializeStrandSolicitation(options?: StrandSolicitationServiceOptions): void
|
|
3400
|
+
initializeStrandSolicitation(options?: StrandSolicitationServiceOptions): Promise<void>;
|
|
3042
3401
|
/**
|
|
3043
3402
|
* Get the strand solicitation service (for advanced use)
|
|
3044
3403
|
*/
|
|
@@ -3059,6 +3418,18 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3059
3418
|
* @returns The member key and strand info if successful
|
|
3060
3419
|
*/
|
|
3061
3420
|
formStrand(invitation: OpenInvitation, disclosure?: StrandFormationDisclosure): Promise<FormStrandResult>;
|
|
3421
|
+
/**
|
|
3422
|
+
* Remember the strand a formation just joined (see {@link joinedStrandStore}), so it
|
|
3423
|
+
* is re-offered after a restart even when the app is killed before its `addStrand`, and
|
|
3424
|
+
* published party-wide by the next connected owner reconcile pass. A re-join also
|
|
3425
|
+
* cancels a party-wide removal a self-revocation queued for the strand.
|
|
3426
|
+
* Last in {@link formStrand}, after the membership adoption, so a failure here leaves
|
|
3427
|
+
* the party key and staged invitation in place for the re-formation it asks for.
|
|
3428
|
+
*
|
|
3429
|
+
* Throws on failure, like {@link adoptFormationMembershipInvite}: the formation's
|
|
3430
|
+
* token is spent, and a join no store names would vanish at the next restart.
|
|
3431
|
+
*/
|
|
3432
|
+
private rememberFormedStrand;
|
|
3062
3433
|
/**
|
|
3063
3434
|
* Adopt an approved closed-strand formation's membership half on the JOINER:
|
|
3064
3435
|
*
|
|
@@ -3115,9 +3486,15 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3115
3486
|
* founder's publish/launch paths mint it, so this is a not-yet-converged sibling.)
|
|
3116
3487
|
* The manager maps the throw to a clean retryable rejection BEFORE the formation
|
|
3117
3488
|
* token is spent.
|
|
3118
|
-
* - Closed host strand
|
|
3119
|
-
*
|
|
3120
|
-
*
|
|
3489
|
+
* - Closed host strand whose runtime is HIBERNATING or being quiesced → woken first
|
|
3490
|
+
* ({@link wakeHostStrandForFormation}, bounded by `signal`), then issued as below. In
|
|
3491
|
+
* every state the redemption counts as activity, so the host stays up for the
|
|
3492
|
+
* joiner's first sync.
|
|
3493
|
+
* - Closed host strand with no running local instance/database (never launched, still
|
|
3494
|
+
* starting, or a hibernating one whose wake failed or outran `signal`) →
|
|
3495
|
+
* throw, same mapping: a joiner admitted without an invitation would look joined and
|
|
3496
|
+
* never become a member, and a responder not running the strand cannot serve its sync
|
|
3497
|
+
* anyway.
|
|
3121
3498
|
* - Closed host strand whose LIVE rows carry the pre-split fingerprint
|
|
3122
3499
|
* (`assertNotPreSplitStrand`) → throw `PreSplitStrandIdentityError`, same mapping as
|
|
3123
3500
|
* the recorded refusal. Covers the responders that never ran a refused founder launch:
|
|
@@ -3127,7 +3504,12 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3127
3504
|
* permanent diagnosis. Identity is checked BEFORE the runtime: it is the cheaper read
|
|
3128
3505
|
* and the more actionable diagnosis when both are missing (a missing runtime is
|
|
3129
3506
|
* transient, a missing identity is not), and it keeps the branch reachable without
|
|
3130
|
-
* standing a strand runtime up.
|
|
3507
|
+
* standing a strand runtime up. For the same reason every control-database check runs
|
|
3508
|
+
* before a wake: a strand that cannot issue anyway is not woken.
|
|
3509
|
+
*
|
|
3510
|
+
* `signal` is the formation's provisioning budget. Once it has aborted nothing is
|
|
3511
|
+
* issued — the joiner has already been told to retry, and an invitation written now
|
|
3512
|
+
* would only sit in the strand until it expires.
|
|
3131
3513
|
*
|
|
3132
3514
|
* The invitation expires `MEMBERSHIP_INVITE_TTL_MS` from now — see that constant for
|
|
3133
3515
|
* the slow-joiner / lost-result tradeoff.
|
|
@@ -3143,17 +3525,46 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3143
3525
|
* not a retry.
|
|
3144
3526
|
*/
|
|
3145
3527
|
private issueStrandMembershipInvite;
|
|
3528
|
+
/**
|
|
3529
|
+
* Count a bound closed-strand redemption as activity on the host strand, and wake the
|
|
3530
|
+
* strand when it is HIBERNATING or being quiesced (its database is closing) so the
|
|
3531
|
+
* membership invitation can be issued. Only reached after the formation manager has
|
|
3532
|
+
* authorized the redemption (token, disclosure, outside approval, seat pre-check), so only
|
|
3533
|
+
* a caller already entitled to the strand's member key can cause a wake. No other state is
|
|
3534
|
+
* woken — never launched or still starting is not something this node recovers from on
|
|
3535
|
+
* demand; the caller's live-database check refuses those.
|
|
3536
|
+
*
|
|
3537
|
+
* The activity is recorded in every state, through the hibernation manager rather than
|
|
3538
|
+
* {@link recordStrandActivity}, whose push fan-out would wake this party's phones for
|
|
3539
|
+
* nothing. It keeps the host up for the joiner's first sync: a live strand's idle timer
|
|
3540
|
+
* restarts, a check-in window that happens to have the strand live sees activity and
|
|
3541
|
+
* leaves it up instead of re-quiescing it, and a hibernating strand has its idle →
|
|
3542
|
+
* hibernate timers re-armed once the wake leaves it `active`. The explicit
|
|
3543
|
+
* {@link wakeStrand} coalesces onto the wake `recordActivity` began, and still wakes when
|
|
3544
|
+
* `recordActivity` is a no-op (hibernation disabled but the strand force-hibernated, or
|
|
3545
|
+
* the manager stopped).
|
|
3546
|
+
*
|
|
3547
|
+
* Bounded by `signal` (the formation's provisioning budget): when it aborts first this
|
|
3548
|
+
* throws — a retryable rejection, token unspent — and leaves the wake running, so the
|
|
3549
|
+
* joiner's retry finds the strand live.
|
|
3550
|
+
*/
|
|
3551
|
+
private wakeHostStrandForFormation;
|
|
3146
3552
|
/**
|
|
3147
3553
|
* Remember the responder's strand-network addresses for a strand this node just
|
|
3148
|
-
* formed
|
|
3149
|
-
* launches it
|
|
3150
|
-
*
|
|
3151
|
-
*
|
|
3152
|
-
*
|
|
3153
|
-
*
|
|
3154
|
-
*
|
|
3155
|
-
|
|
3156
|
-
|
|
3554
|
+
* formed in the strand peer book (see {@link strandPeerBookStore}), so the strand's
|
|
3555
|
+
* discovery seed has something to work with when the app launches it — and again
|
|
3556
|
+
* after every restart.
|
|
3557
|
+
*
|
|
3558
|
+
* The carried list is peer-agnostic (`sanitizeStrandAddrs` bounds and parses it,
|
|
3559
|
+
* nothing more), so it is attributed per peer here (`groupAddrsByPeerId`, the same
|
|
3560
|
+
* rule the address-book merge applies) and each peer becomes one unsigned entry seen
|
|
3561
|
+
* just now: the responder disclosed the addresses live a moment ago. An entry naming
|
|
3562
|
+
* no destination peer is dropped there. Scoped strictly to `strandId`: these
|
|
3563
|
+
* addresses reach ONE strand node of ONE other party and must never seed another
|
|
3564
|
+
* strand's mesh or the control peerStore. An empty list records nothing, so a later
|
|
3565
|
+
* formation against the same strand that DOES disclose addresses is not shadowed.
|
|
3566
|
+
*/
|
|
3567
|
+
private recordFormationStrandPeers;
|
|
3157
3568
|
/**
|
|
3158
3569
|
* Encode an open invitation for out-of-band delivery (QR, link, etc.).
|
|
3159
3570
|
*/
|