@serfab/cadre-core 1.7.0 → 1.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.
- package/README.md +8 -6
- package/dist/cadre-node.d.ts +527 -206
- package/dist/cadre-node.js +1084 -528
- 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 +10 -10
- package/dist/index.js +25 -20
- package/dist/index.js.map +1 -1
- package/dist/joined-strand-store.d.ts +90 -25
- package/dist/joined-strand-store.js +223 -55
- package/dist/joined-strand-store.js.map +1 -1
- package/dist/key-store-file.js +14 -5
- package/dist/key-store-file.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 +6 -4
- package/dist/node-local-snapshot.js +7 -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 +2 -10
- package/dist/peer-join-backfill.js +8 -8
- 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 -46
- package/dist/strand-formation-protocol.js +62 -126
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-fret-addrs.d.ts +54 -0
- package/dist/strand-fret-addrs.js +108 -0
- package/dist/strand-fret-addrs.js.map +1 -0
- package/dist/strand-instance-manager.d.ts +122 -44
- package/dist/strand-instance-manager.js +270 -114
- 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-network-state-file.d.ts +12 -0
- package/dist/strand-network-state-file.js +38 -0
- package/dist/strand-network-state-file.js.map +1 -0
- package/dist/strand-network-state.d.ts +111 -0
- package/dist/strand-network-state.js +191 -0
- package/dist/strand-network-state.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 +148 -61
- package/dist/types.js +4 -3
- package/dist/types.js.map +1 -1
- package/package.json +11 -10
- package/src/cadre-node.ts +8400 -7776
- 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 +43 -65
- package/src/joined-strand-store.ts +247 -57
- package/src/key-store-file.ts +14 -5
- package/src/link-budget.ts +393 -74
- package/src/membership-connection-gater.ts +31 -45
- package/src/node-local-snapshot.ts +12 -7
- package/src/peer-dial.ts +81 -7
- package/src/peer-join-backfill.ts +10 -9
- 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 +106 -154
- package/src/strand-fret-addrs.ts +145 -0
- package/src/strand-instance-manager.ts +307 -140
- package/src/strand-membership-writer.ts +12 -0
- package/src/strand-network-state-file.ts +40 -0
- package/src/strand-network-state.ts +243 -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 +150 -61
- package/dist/strand-peer-book-file.d.ts +0 -12
- package/dist/strand-peer-book-file.js +0 -37
- package/dist/strand-peer-book-file.js.map +0 -1
- package/dist/strand-peer-book-protocol.d.ts +0 -159
- package/dist/strand-peer-book-protocol.js +0 -360
- package/dist/strand-peer-book-protocol.js.map +0 -1
- package/dist/strand-peer-book-swap.d.ts +0 -104
- package/dist/strand-peer-book-swap.js +0 -284
- package/dist/strand-peer-book-swap.js.map +0 -1
- package/dist/strand-peer-book.d.ts +0 -159
- package/dist/strand-peer-book.js +0 -354
- package/dist/strand-peer-book.js.map +0 -1
- package/dist/strand-peer-observer.d.ts +0 -88
- package/dist/strand-peer-observer.js +0 -180
- package/dist/strand-peer-observer.js.map +0 -1
- package/src/strand-peer-book-file.ts +0 -39
- package/src/strand-peer-book-protocol.ts +0 -463
- package/src/strand-peer-book-swap.ts +0 -338
- package/src/strand-peer-book.ts +0 -468
- package/src/strand-peer-observer.ts +0 -235
package/dist/cadre-node.d.ts
CHANGED
|
@@ -4,7 +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
|
|
7
|
+
import { type StrandNetworkStateStore } from './strand-network-state.js';
|
|
8
8
|
import { type EnrolledMachineStore } from './enrolled-machine-store.js';
|
|
9
9
|
import { type SAppIdLookup } from './strand-watcher.js';
|
|
10
10
|
import { type ControlCohortReconcileResult } from './control-cohort.js';
|
|
@@ -17,10 +17,10 @@ import { type RelayReservationState, type RelayReservationSupervisorOptions } fr
|
|
|
17
17
|
import type { WakeAck } from './types.js';
|
|
18
18
|
import { type ConnectionPathSummary } from './diagnostics/connection-path.js';
|
|
19
19
|
/**
|
|
20
|
-
* How often a running strand re-
|
|
21
|
-
* and re-merges them into
|
|
22
|
-
* ({@link CadreNode.refreshStrandPeerAddrs}).
|
|
23
|
-
* `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`.
|
|
24
24
|
*
|
|
25
25
|
* Ten minutes sits comfortably inside the peerStore's one-hour address expiry
|
|
26
26
|
* (`MAX_ADDRESS_AGE`, see `peer-addr-book.ts`) with headroom for several missed
|
|
@@ -28,6 +28,16 @@ import { type ConnectionPathSummary } from './diagnostics/connection-path.js';
|
|
|
28
28
|
* strand-addr RPC fan-out stays cheap.
|
|
29
29
|
*/
|
|
30
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;
|
|
31
41
|
type EventHandler<T> = (data: T) => void;
|
|
32
42
|
/**
|
|
33
43
|
* CadreNode is the main entry point for a cadre member.
|
|
@@ -161,6 +171,28 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
161
171
|
* {@link getDiscoveredStrands}.
|
|
162
172
|
*/
|
|
163
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;
|
|
164
196
|
/**
|
|
165
197
|
* Most-recently pushed invite addresses (see {@link setInviteAddresses}).
|
|
166
198
|
* When non-null these take priority over `libp2pNode.getMultiaddrs()` when
|
|
@@ -235,9 +267,20 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
235
267
|
* `denyInboundRelayReservation` policy); authorized members and announced
|
|
236
268
|
* delegates are admitted before it and never counted. Cap from
|
|
237
269
|
* `network.unauthorizedRelayReservationCap` (default
|
|
238
|
-
* `MAX_UNAUTHORIZED_RELAY_RESERVATIONS`)
|
|
270
|
+
* `MAX_UNAUTHORIZED_RELAY_RESERVATIONS`); entries expire on the relay
|
|
271
|
+
* server's own resolved `reservationTtl` ({@link relayServer}).
|
|
239
272
|
*/
|
|
240
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;
|
|
241
284
|
/**
|
|
242
285
|
* When this node last ANNOUNCED a delegate, keyed `<targetPeerId>\n<strandId>`
|
|
243
286
|
* (the peer announced TO, not the delegate). Throttles
|
|
@@ -248,46 +291,67 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
248
291
|
*/
|
|
249
292
|
private readonly delegateAnnounceAt;
|
|
250
293
|
/**
|
|
251
|
-
* When each
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
* entries alive past the peerStore's one-hour address expiry.
|
|
273
|
-
*
|
|
274
|
-
* This is the ONLY cross-party discovery input there is. The strand-addr RPC that
|
|
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
|
+
* The strand-network addresses a formation carried back
|
|
308
|
+
* (`FormationResultMessage.strandAddrs`), by strand id: the responder's live strand
|
|
309
|
+
* node addresses, kept so the strand's first attach has something to dial. Written by
|
|
310
|
+
* {@link recordFormationStrandAddrs}, read by {@link resolveCohortSeed} (launch and
|
|
311
|
+
* hibernation resume) and by every refresh pass for the peers the strand node's FRET
|
|
312
|
+
* table holds no record for yet ({@link remergeUnrecordedFormationAddrs}).
|
|
313
|
+
*
|
|
314
|
+
* This is the only cross-party input a FIRST attach has. The strand-addr RPC that
|
|
275
315
|
* resolves a strand's addresses is membership-gated and answers own-party siblings
|
|
276
|
-
* only, so without
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
316
|
+
* only, so without it a joiner's seed for a two-party strand is empty and the mesh
|
|
317
|
+
* never forms. Every later launch has the strand node's saved network state as well
|
|
318
|
+
* ({@link strandNetworkStateStore}), which is what a restart re-finds the other party
|
|
319
|
+
* from (gotchoices/sereus#18).
|
|
320
|
+
*
|
|
321
|
+
* A re-formation replaces the strand's list. An entry survives a {@link stopStrand}
|
|
322
|
+
* and a stop()→start() of this node instance ({@link cleanup} leaves the map alone),
|
|
323
|
+
* and is dropped where the saved network state is: {@link unpublishStrand},
|
|
324
|
+
* {@link forgetJoinedStrand} and self-revocation.
|
|
325
|
+
*
|
|
326
|
+
* NOTE: accepted tradeoff — in memory only, so a process restart between `formStrand`
|
|
327
|
+
* and the strand's first launch loses the carried addresses, and the strand then
|
|
328
|
+
* launches with no cross-party seed until it is re-formed. Persisting the list on the
|
|
329
|
+
* joined-strand record was weighed and declined: it mixes address state into the join
|
|
330
|
+
* record, and needs an "only until the first run" rule to stop dead addresses being
|
|
331
|
+
* dialled on every launch. Every embedder launches straight after forming, and once
|
|
332
|
+
* the strand has run the saved network state covers every later restart. Revisit if an
|
|
333
|
+
* embedder forms and launches in separate sessions.
|
|
334
|
+
*/
|
|
335
|
+
private readonly formationStrandAddrs;
|
|
336
|
+
/**
|
|
337
|
+
* Node-local strand network state (see `strand-network-state.ts`): per strand, the
|
|
338
|
+
* state db-p2p saves for the strand's libp2p node — its FRET routing table with each
|
|
339
|
+
* peer's signed address record. Handed to every strand launch as
|
|
340
|
+
* `StartStrandConfig.networkState`; the strand node saves into it on every connection
|
|
341
|
+
* and re-imports it when it is next built, which is what gives a restarted strand
|
|
342
|
+
* node addresses for the peers it was talking to.
|
|
343
|
+
*
|
|
344
|
+
* Constructed (or adopted from `config.strandNetworkState.store`) by
|
|
345
|
+
* {@link initializeStrandNetworkStateStore} during {@link start}, deliberately NOT
|
|
281
346
|
* cleared by {@link cleanup}, so it survives a stop()→start() cycle of the same node
|
|
282
347
|
* instance — same lifecycle as {@link bootstrapPeerStore}.
|
|
283
348
|
*
|
|
284
|
-
*
|
|
285
|
-
* {@link
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
* bounded by time and by the strands this node runs rather than by its history.
|
|
349
|
+
* A strand's state survives a {@link stopStrand} and a hibernation quiesce, and is
|
|
350
|
+
* forgotten by {@link unpublishStrand}, {@link forgetJoinedStrand} and
|
|
351
|
+
* self-revocation. A strand detached because the watcher saw its row gone keeps its
|
|
352
|
+
* state (`NOTE:` on `PersistentStrandNetworkStateStore`).
|
|
289
353
|
*/
|
|
290
|
-
private
|
|
354
|
+
private strandNetworkStateStore;
|
|
291
355
|
/**
|
|
292
356
|
* PENDING strand membership invitations learned at formation, keyed by strandId — the
|
|
293
357
|
* single-use `Strand.Invite` credential a closed-strand formation result carried back
|
|
@@ -315,7 +379,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
315
379
|
* NOTE: entries live for the node's lifetime (one small pair per formed closed
|
|
316
380
|
* strand); the number of keys is bounded only by the strands this node has ever
|
|
317
381
|
* formed, not by time — if a node ever forms strands at scale, evict on
|
|
318
|
-
* `unpublishStrand` / `forgetJoinedStrand` as
|
|
382
|
+
* `unpublishStrand` / `forgetJoinedStrand` as {@link formationStrandAddrs} is.
|
|
319
383
|
*/
|
|
320
384
|
private readonly pendingMembershipInvites;
|
|
321
385
|
/**
|
|
@@ -387,22 +451,22 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
387
451
|
*/
|
|
388
452
|
private enrolledMachineStore;
|
|
389
453
|
/**
|
|
390
|
-
* Node-local record of the strands this node joined from ANOTHER party
|
|
391
|
-
* `joined-strand-store.ts`)
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
* and on self-revocation. Adopted from `config.joinedStrands.store`, else built over
|
|
454
|
+
* Node-local record of the strands this node joined from ANOTHER party and has not yet
|
|
455
|
+
* published party-wide (see `joined-strand-store.ts`). Written by {@link formStrand}
|
|
456
|
+
* and by an {@link addStrand} of a foreign row; drained once the party-wide
|
|
457
|
+
* `JoinedStrand` row is visible. Adopted from `config.joinedStrands.store`, else built over
|
|
395
458
|
* `config.keyStore`, else in-memory, by {@link initializeJoinedStrandStore}. Like
|
|
396
459
|
* {@link bootstrapPeerStore} it is deliberately NOT cleared by {@link cleanup}.
|
|
397
460
|
*/
|
|
398
461
|
private joinedStrandStore;
|
|
399
462
|
/**
|
|
400
|
-
*
|
|
401
|
-
*
|
|
463
|
+
* This session's joined strands: {@link joinedStrandStore} and the party-wide
|
|
464
|
+
* `JoinedStrand` table, unioned with the control rows each watcher poll, published and
|
|
465
|
+
* removed by the reconcile pass. Per SESSION, unlike the store — rebuilt at every
|
|
402
466
|
* {@link start}, which is what ends a revoked join's "keep offering until this session
|
|
403
|
-
* ends" (see `
|
|
467
|
+
* ends" (see `JoinedStrandSession.forgetAfterThisSession`).
|
|
404
468
|
*/
|
|
405
|
-
private
|
|
469
|
+
private joinedStrands;
|
|
406
470
|
/**
|
|
407
471
|
* The enrolled-machine count this node's CONTROL libp2p node was (or will be)
|
|
408
472
|
* built with — read out of {@link enrolledMachineStore} during {@link start},
|
|
@@ -544,7 +608,8 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
544
608
|
private turnConnectionCloseHandler;
|
|
545
609
|
constructor(config: CadreNodeConfig);
|
|
546
610
|
/**
|
|
547
|
-
* SAppIdLookup implementation - get sAppId for a strand
|
|
611
|
+
* SAppIdLookup implementation - get sAppId for a strand: the claiming config's, else
|
|
612
|
+
* the one a storage replica read from the strand's own `Strand.Header`.
|
|
548
613
|
*/
|
|
549
614
|
getSAppId(strandId: string): string | undefined;
|
|
550
615
|
/**
|
|
@@ -712,7 +777,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
712
777
|
private initializeEnrolledMachineStore;
|
|
713
778
|
/**
|
|
714
779
|
* Construct (or adopt) the node-local joined-strand store (see
|
|
715
|
-
* {@link joinedStrandStore}) and this session's
|
|
780
|
+
* {@link joinedStrandStore}) and this session's {@link joinedStrands} over it.
|
|
716
781
|
*
|
|
717
782
|
* The store instance is kept across stop()→start(); the view is rebuilt. A store
|
|
718
783
|
* scoped to a different party is a configuration error, fail closed: its joins
|
|
@@ -720,15 +785,15 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
720
785
|
*/
|
|
721
786
|
private initializeJoinedStrandStore;
|
|
722
787
|
/**
|
|
723
|
-
* Construct (or adopt) the node-local strand
|
|
724
|
-
* {@link
|
|
788
|
+
* Construct (or adopt) the node-local strand network state (see
|
|
789
|
+
* {@link strandNetworkStateStore}). Synchronous for the reason
|
|
725
790
|
* {@link initializeBootstrapPeerStore} is: an injected store has already loaded its
|
|
726
791
|
* persisted entries by the time it is handed in, and the in-memory fallback has
|
|
727
792
|
* nothing to load. The instance is kept across stop()→start(). A store scoped to a
|
|
728
|
-
* different party is a configuration error, fail closed: another party's
|
|
729
|
-
*
|
|
793
|
+
* different party is a configuration error, fail closed: another party's routing
|
|
794
|
+
* tables must never be imported into this node's strands.
|
|
730
795
|
*/
|
|
731
|
-
private
|
|
796
|
+
private initializeStrandNetworkStateStore;
|
|
732
797
|
/**
|
|
733
798
|
* The node-local enrolled-machine store (null before {@link start}) — what this
|
|
734
799
|
* node last knew about its party's size. Exposed for diagnostics and for a host
|
|
@@ -743,12 +808,12 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
743
808
|
*/
|
|
744
809
|
getBootstrapPeerStore(): BootstrapPeerStore | null;
|
|
745
810
|
/**
|
|
746
|
-
* The node-local strand
|
|
747
|
-
*
|
|
748
|
-
* diagnostics and for a host that wants to show "
|
|
749
|
-
*
|
|
811
|
+
* The node-local strand network state (null before {@link start}) — per strand, the
|
|
812
|
+
* state its strand node last saved and will re-import when next built. Exposed for
|
|
813
|
+
* diagnostics and for a host that wants to show "what routing table would this
|
|
814
|
+
* strand restart with?".
|
|
750
815
|
*/
|
|
751
|
-
|
|
816
|
+
getStrandNetworkStateStore(): StrandNetworkStateStore | null;
|
|
752
817
|
/**
|
|
753
818
|
* The node-local trusted-owner anchor (null before {@link start}). This is
|
|
754
819
|
* the set the authorized-membership predicate and seed-trust anchor consult —
|
|
@@ -867,14 +932,12 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
867
932
|
*/
|
|
868
933
|
private buildControlNodeOptions;
|
|
869
934
|
/**
|
|
870
|
-
*
|
|
871
|
-
*
|
|
872
|
-
* (
|
|
873
|
-
*
|
|
874
|
-
* {@link admitInboundControlConnection} (whose deny/admit-for-relay branch
|
|
875
|
-
* must agree with whether a reservation is even servable here).
|
|
935
|
+
* One debug line naming what this machine's relay server forwards and holds —
|
|
936
|
+
* the same fields the dedicated relay container prints at boot
|
|
937
|
+
* (`ops/docker/libp2p-infra/src/main.ts`). Once per start, not per strand:
|
|
938
|
+
* strand nodes resolve the same settings from the same config.
|
|
876
939
|
*/
|
|
877
|
-
private
|
|
940
|
+
private logRelayServerSettings;
|
|
878
941
|
/**
|
|
879
942
|
* Decide whether an inbound CONTROL-network connection from `remotePeerId`
|
|
880
943
|
* should be admitted — the policy behind the connection gater wired in
|
|
@@ -922,7 +985,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
922
985
|
* accepted it, exactly like the unreplicated-membership-row case below.
|
|
923
986
|
*
|
|
924
987
|
* When every check falls through, the verdict depends on whether this node
|
|
925
|
-
* runs the circuit-relay server ({@link
|
|
988
|
+
* runs the circuit-relay server ({@link relayServer}): without one,
|
|
926
989
|
* `'deny'`; with one, `'admit-for-relay'` — a circuit-relay reservation is
|
|
927
990
|
* established by the reserving peer DIALING the relay, so a connection deny
|
|
928
991
|
* here kills the reservation, and that deny is NOT self-healing: an outbound
|
|
@@ -1089,11 +1152,24 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1089
1152
|
*/
|
|
1090
1153
|
private getBootstrapPeerIds;
|
|
1091
1154
|
/**
|
|
1092
|
-
* What the strand watcher polls: this party's control rows plus a row per
|
|
1093
|
-
*
|
|
1094
|
-
*
|
|
1155
|
+
* What the strand watcher polls: this party's control rows plus a row per cross-party
|
|
1156
|
+
* join (see {@link joinedStrands}), so a joined strand is offered and relaunched on
|
|
1157
|
+
* the same path as the party's own.
|
|
1095
1158
|
*/
|
|
1096
1159
|
private createStrandQueryable;
|
|
1160
|
+
/**
|
|
1161
|
+
* The party-wide `JoinedStrand` table as {@link joinedStrands} reads and writes it.
|
|
1162
|
+
* Reads {@link controlDatabase} at call time, so start-up order does not matter.
|
|
1163
|
+
*/
|
|
1164
|
+
private createPartyJoinedStrandLedger;
|
|
1165
|
+
/**
|
|
1166
|
+
* This machine's owner signing key when it can make owner-gated control writes: its seed
|
|
1167
|
+
* bootstrap holds an owner key (`SeedBootstrapService.canAuthorize`) and the party's
|
|
1168
|
+
* `OwnerKey` table enrolls it. A phone that ran self-genesis after another machine
|
|
1169
|
+
* founded the party passes the first test and not the second (`runOwnerGenesis` in the
|
|
1170
|
+
* reference apps), and a write it signed would only be refused.
|
|
1171
|
+
*/
|
|
1172
|
+
private enrolledOwnerSigningKey;
|
|
1097
1173
|
/**
|
|
1098
1174
|
* Schedule the node's initial self-record publish + ongoing refresh shortly
|
|
1099
1175
|
* after start (non-blocking). {@link registerSelf} is idempotent and safely
|
|
@@ -1417,6 +1493,17 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1417
1493
|
* test that drives dead addresses on purpose keeps the duration it chose.
|
|
1418
1494
|
*/
|
|
1419
1495
|
private controlDialBudget;
|
|
1496
|
+
/**
|
|
1497
|
+
* The link-derived limits shared by every {@link SeedBootstrapService} this node builds: its
|
|
1498
|
+
* owner and invite dials ({@link controlDialBudget}) and its seed delivery deadline, derived
|
|
1499
|
+
* from `network.linkRoundTripMs`. One helper so the four construction sites cannot drift.
|
|
1500
|
+
*/
|
|
1501
|
+
private seedServiceBudgets;
|
|
1502
|
+
/**
|
|
1503
|
+
* {@link collectStrandAddrs} at this node's declared `network.linkRoundTripMs`, so each ask's
|
|
1504
|
+
* deadline derives from it. One helper so the four call sites cannot drift.
|
|
1505
|
+
*/
|
|
1506
|
+
private collectSiblingStrandAddrs;
|
|
1420
1507
|
/**
|
|
1421
1508
|
* Dial one sibling from its already-resolved addresses, best-effort. Returns
|
|
1422
1509
|
* whether the dial resolved (false when no address resolves or the dial fails).
|
|
@@ -1425,7 +1512,9 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1425
1512
|
* the {@link controlDialBudget} expiring) is logged and swallowed so one
|
|
1426
1513
|
* unreachable sibling never aborts the pass — exactly like
|
|
1427
1514
|
* {@link SeedBootstrapService.applySeed}'s owner-dial loop. A failed dial is
|
|
1428
|
-
* simply retried on the next pass.
|
|
1515
|
+
* simply retried on the next pass. A sibling whose every address relays
|
|
1516
|
+
* through this node ({@link SelfRelayOnlyError}) is not dialed at all and gets
|
|
1517
|
+
* a one-line log instead: only the sibling can reconnect.
|
|
1429
1518
|
*/
|
|
1430
1519
|
private dialControlSibling;
|
|
1431
1520
|
/**
|
|
@@ -1815,6 +1904,37 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1815
1904
|
*/
|
|
1816
1905
|
expireDeviceToken(peerId: string): Promise<void>;
|
|
1817
1906
|
private handleStrandAdded;
|
|
1907
|
+
/**
|
|
1908
|
+
* Offer a strand no local config claims to the hosting app as `strand:discovered`, so
|
|
1909
|
+
* it can decide whether to join (register a config + addStrand); the strand-agnostic
|
|
1910
|
+
* seam keeps this class free of any app's join policy.
|
|
1911
|
+
*
|
|
1912
|
+
* Once per strand: a storage-replica launch that fails is retried by the watcher
|
|
1913
|
+
* (`StrandWatcher.forgetStrand` + backoff), and each retry comes back through
|
|
1914
|
+
* {@link handleStrandAdded} with no config — the app must still see one announcement.
|
|
1915
|
+
* The other thing that un-retains a strand in the watcher (a failed `addStrand`) leaves
|
|
1916
|
+
* the sApp config registered, so that retry takes the claimed branch instead.
|
|
1917
|
+
*
|
|
1918
|
+
* Recorded BEFORE the emit so a handler that synchronously drains
|
|
1919
|
+
* `getDiscoveredStrands()` sees this strand too. So this map — not the event — is what
|
|
1920
|
+
* a late subscriber reads. See the `strand:discovered` doc in types.ts.
|
|
1921
|
+
*
|
|
1922
|
+
* NOTE: a replica launch that keeps failing leaves the watcher not tracking the strand,
|
|
1923
|
+
* so if its control row is removed during the backoff no `handleStrandRemoved` arrives
|
|
1924
|
+
* and this entry outlives the row. Needs a persistently failing launch AND a removal
|
|
1925
|
+
* inside the backoff; if it is ever seen, have the watcher report removal of rows it
|
|
1926
|
+
* forgot, since `detachStrand` is already a no-op for an untracked instance.
|
|
1927
|
+
*/
|
|
1928
|
+
private announceDiscoveredStrand;
|
|
1929
|
+
/**
|
|
1930
|
+
* Record the sApp id a storage replica's `Strand.Header` names, for {@link getSAppId}.
|
|
1931
|
+
* Runs whenever a replica's database may have just been published (launch, the
|
|
1932
|
+
* first-sync gate opening, a wake); a no-op for a claimed strand, an unpublished
|
|
1933
|
+
* database, or an id already recorded. Never throws: a failed read logs and leaves the
|
|
1934
|
+
* id unknown, so an `sAppId` filter keeps the strand provisionally admitted — a
|
|
1935
|
+
* syncing replica, which is harmless.
|
|
1936
|
+
*/
|
|
1937
|
+
private recordReplicaSAppId;
|
|
1818
1938
|
private handleStrandRemoved;
|
|
1819
1939
|
/**
|
|
1820
1940
|
* Return this node to its pre-{@link start} state. The ONE teardown — `stop()` and
|
|
@@ -1835,10 +1955,10 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1835
1955
|
private handleStrandIdle;
|
|
1836
1956
|
/**
|
|
1837
1957
|
* Hibernate a strand: release its strand-network resources via the strand
|
|
1838
|
-
* manager (stop the libp2p node, close the StrandDatabase)
|
|
1958
|
+
* manager (stop the libp2p node, close the StrandDatabase), which marks it
|
|
1839
1959
|
* `hibernating`. A quiesced strand holds no open strand-network connections,
|
|
1840
|
-
* transports, or DB handles. No-ops if the strand is missing;
|
|
1841
|
-
*
|
|
1960
|
+
* transports, or DB handles. No-ops if the strand is missing; one already quiesced
|
|
1961
|
+
* is only marked, and still emits.
|
|
1842
1962
|
*/
|
|
1843
1963
|
private handleStrandHibernate;
|
|
1844
1964
|
/**
|
|
@@ -1847,15 +1967,42 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1847
1967
|
* its runtime via the strand manager. If it is still live (e.g. waking an idle
|
|
1848
1968
|
* strand, which retains its resources), just flip the status. Overlapping wake
|
|
1849
1969
|
* triggers are coalesced upstream by `HibernationManager`, so this runs once
|
|
1850
|
-
* per wake;
|
|
1970
|
+
* per wake; a wake racing a check-in joins the check-in's rebuild in `resumeStrand`.
|
|
1971
|
+
*
|
|
1972
|
+
* A failed rebuild re-hibernates the strand (see {@link rehibernateIfResumeFailed});
|
|
1973
|
+
* every failure rethrows, so the waker still sees the error.
|
|
1974
|
+
*
|
|
1975
|
+
* Records no activity itself: whoever asked for the wake did ({@link wakeStrand},
|
|
1976
|
+
* `HibernationManager.recordActivity`), and {@link serviceWake}'s own probe must not.
|
|
1851
1977
|
*/
|
|
1852
1978
|
private handleStrandWake;
|
|
1979
|
+
/**
|
|
1980
|
+
* Undo a failed {@link resumeStrandRuntime}, but only when the rebuild itself failed — this
|
|
1981
|
+
* caller's, or the one it joined in `resumeStrand` — which leaves the instance `'error'`. A
|
|
1982
|
+
* failure before the rebuild (the cohort seed read) built nothing, and may find the strand
|
|
1983
|
+
* mid-build or just brought up by a concurrent wake or check-in; that runtime belongs to
|
|
1984
|
+
* whoever is building or holding it, so quiescing it here would stop a node still in use.
|
|
1985
|
+
*/
|
|
1986
|
+
private rehibernateIfResumeFailed;
|
|
1987
|
+
/**
|
|
1988
|
+
* Put a strand whose rebuild or wake window failed back to `'hibernating'`, through a
|
|
1989
|
+
* best-effort quiesce that releases any runtime left up. `resumeStrand` leaves a failed
|
|
1990
|
+
* strand `'error'`, which nothing retries: `HibernationManager` wakes only
|
|
1991
|
+
* `idle`/`hibernating` strands, and reads any other status after a check-in as "woke",
|
|
1992
|
+
* ending its chain. Safe to run twice for one failure (a wake that joined a failed
|
|
1993
|
+
* check-in's rebuild): quiescing a quiesced strand only marks it.
|
|
1994
|
+
*
|
|
1995
|
+
* The quiesce writes the status itself, ordered before any resume requested meanwhile;
|
|
1996
|
+
* this writes it only when the quiesce failed, since a failed release leaves the status
|
|
1997
|
+
* alone.
|
|
1998
|
+
*/
|
|
1999
|
+
private rehibernateAfterFailedResume;
|
|
1853
2000
|
/**
|
|
1854
2001
|
* Rebuild a quiesced strand's runtime, re-resolving the volatile cohort input
|
|
1855
2002
|
* first: the discovery seed may have grown since the strand last ran. Shared by
|
|
1856
2003
|
* the wake (`handleStrandWake`) and check-in (`handleStrandCheckIn`) paths so both
|
|
1857
|
-
* apply the same fresh resolution. `resumeStrand`
|
|
1858
|
-
*
|
|
2004
|
+
* apply the same fresh resolution. `resumeStrand` returns a live instance unchanged
|
|
2005
|
+
* and joins a rebuild already in flight, so the two paths never build twice.
|
|
1859
2006
|
*/
|
|
1860
2007
|
private resumeStrandRuntime;
|
|
1861
2008
|
/**
|
|
@@ -1884,15 +2031,17 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1884
2031
|
* Window-then-decide for a just-resumed strand, shared by the check-in timer
|
|
1885
2032
|
* path ({@link handleStrandCheckIn}) and the on-demand {@link serviceWake}:
|
|
1886
2033
|
*
|
|
1887
|
-
* 1.
|
|
1888
|
-
* FRESH `Date`, so a changed reference after the window means real
|
|
1889
|
-
* activity landed during it — not millisecond-resolution noise.
|
|
1890
|
-
* 2. Hold the strand live for `windowMs` so its strand network reaches the
|
|
2034
|
+
* 1. Hold the strand live for `windowMs` so its strand network reaches the
|
|
1891
2035
|
* cohort and the app can drive pull-on-read activity.
|
|
1892
|
-
*
|
|
1893
|
-
* quiesce
|
|
2036
|
+
* 2. If activity landed since `activityMark`, leave the strand `active` (return
|
|
2037
|
+
* `true`); otherwise quiesce it, which marks it `hibernating` again (return `false`).
|
|
2038
|
+
* A wake or activity that lands during that quiesce rebuilds the strand after it.
|
|
1894
2039
|
*
|
|
1895
|
-
* @
|
|
2040
|
+
* @param activityMark - `instance.lastActivity` as the caller read it BEFORE bringing the
|
|
2041
|
+
* strand up. The bring-up records none, and every writer assigns a FRESH `Date`, so a
|
|
2042
|
+
* changed reference means a wake or activity landed during the resume or the window —
|
|
2043
|
+
* not millisecond-resolution noise.
|
|
2044
|
+
* @returns whether activity was observed (strand left active).
|
|
1896
2045
|
*/
|
|
1897
2046
|
private runWakeWindow;
|
|
1898
2047
|
/**
|
|
@@ -1923,9 +2072,10 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1923
2072
|
* published (e.g. re-attaching its own orphan after a restart) founds it, and attaching
|
|
1924
2073
|
* anyone else's row joins, without the caller needing to know which it is.
|
|
1925
2074
|
*
|
|
1926
|
-
* A joined row (no `founder: true`) this party's control database does not
|
|
1927
|
-
* strand joined from another party — is remembered in the node's joined-strand store, so it
|
|
1928
|
-
* `strand:discovered` after a restart
|
|
2075
|
+
* A joined row (no `founder: true`) this party's control database does not name — a
|
|
2076
|
+
* strand joined from another party — is remembered in the node's joined-strand store, so it
|
|
2077
|
+
* is re-offered as `strand:discovered` after a restart, and published party-wide by an
|
|
2078
|
+
* owner machine's reconcile pass (see {@link forgetJoinedStrand}).
|
|
1929
2079
|
*
|
|
1930
2080
|
* A rejected call leaves nothing running but DOES leave the sApp config
|
|
1931
2081
|
* registered, deliberately: both an explicit retry and the {@link StrandWatcher}'s
|
|
@@ -2104,7 +2254,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2104
2254
|
* — and with it the consent re-seat foreclosure — does propagate; but the physical row
|
|
2105
2255
|
* deletion cannot be replayed, and `queryStrands` reads raw (no retired-stamp filter),
|
|
2106
2256
|
* so siblings that already hold the row keep running the strand until the collection
|
|
2107
|
-
* itself converges (logged loudly; see
|
|
2257
|
+
* itself converges (logged loudly; see "Deletes made while alone" in
|
|
2108
2258
|
* docs/architecture.md).
|
|
2109
2259
|
*
|
|
2110
2260
|
* A no-op (no throw, no tombstone) when the row is already absent — but a
|
|
@@ -2167,10 +2317,10 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2167
2317
|
* NOTE: "founding machine" here is the ROW's provenance, not the launch's resolved
|
|
2168
2318
|
* `founder` flag — so an explicit `founder: false` over a row this machine published
|
|
2169
2319
|
* still mints, an owner-signed write a caller that said "I am not founding" did not ask
|
|
2170
|
-
* for.
|
|
2171
|
-
*
|
|
2172
|
-
*
|
|
2173
|
-
*
|
|
2320
|
+
* for. Reached by a storage replica of a row this machine published (always a joiner,
|
|
2321
|
+
* see {@link launchStrand}) and kept on purpose: the party needs that identity for the
|
|
2322
|
+
* strand either way, the mint is insert-if-absent, and a closed-strand replica runs its
|
|
2323
|
+
* membership reconciler — which seats its own `MemberPeer` binding — only with a party key.
|
|
2174
2324
|
*/
|
|
2175
2325
|
private resolveStrandPartyKey;
|
|
2176
2326
|
/**
|
|
@@ -2291,6 +2441,15 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2291
2441
|
* A founder launch refused as pre-split (`PreSplitStrandIdentityError`) is recorded in
|
|
2292
2442
|
* {@link strandLaunchRefusals} for the formation arm, then rethrown; a founder launch
|
|
2293
2443
|
* that succeeds clears the record (it ran the bootstrap's pre-split check and passed).
|
|
2444
|
+
*
|
|
2445
|
+
* An absent `sAppConfig` launches a storage replica ({@link CadreNodeConfig.hostUnclaimedStrands}),
|
|
2446
|
+
* which is ALWAYS a joiner whatever the row says: the founder bootstrap writes the sApp
|
|
2447
|
+
* into `Strand.Header`, and a replica has none. So a self-founded row whose watcher poll
|
|
2448
|
+
* wins the race against its app's `addStrand` after a restart comes up as a replica — and
|
|
2449
|
+
* so does a `foundStrand` whose publish a watcher poll saw before its attach. A claim
|
|
2450
|
+
* that finds a tracked replica upgrades it in place, over the same node and store
|
|
2451
|
+
* ({@link StrandInstanceManager.attachSApp}), BEFORE any founding: the founder bootstrap
|
|
2452
|
+
* needs the sApp the attach supplies.
|
|
2294
2453
|
*/
|
|
2295
2454
|
private launchStrand;
|
|
2296
2455
|
/**
|
|
@@ -2298,6 +2457,19 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2298
2457
|
* tracked instance in place, or start a fresh one.
|
|
2299
2458
|
*/
|
|
2300
2459
|
private startOrFoundStrand;
|
|
2460
|
+
/**
|
|
2461
|
+
* {@link startOrFoundStrand} for a strand the manager already tracks: a claim of a
|
|
2462
|
+
* storage replica gives it the app's schema in place, then a founder request founds it,
|
|
2463
|
+
* waking a quiesced instance so the bootstrap has run before this resolves. Emits
|
|
2464
|
+
* nothing — `strand:started` fired when the instance launched.
|
|
2465
|
+
*
|
|
2466
|
+
* A launch of the strand still in flight (the watcher's launch racing an app's
|
|
2467
|
+
* `addStrand`, in either order) is waited out first: until its build settles the tracked
|
|
2468
|
+
* instance is `'starting'` with no database, and returning it would resolve `addStrand`
|
|
2469
|
+
* with a strand the app cannot use — and skip that call's first-sync wait, which only a
|
|
2470
|
+
* `'syncing'` instance gets. If that launch fails, this rejects too: its record is gone.
|
|
2471
|
+
*/
|
|
2472
|
+
private claimTrackedStrand;
|
|
2301
2473
|
/**
|
|
2302
2474
|
* Resolve a strand's discovery seed — the dialable strand-network multiaddr
|
|
2303
2475
|
* strings for cohort siblings. Membership comes from the control network's
|
|
@@ -2320,18 +2492,11 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2320
2492
|
* relay reservation — the gate denial there is fatal, not degraded. A
|
|
2321
2493
|
* party-member relay answers the RPC (its control node admits us as a
|
|
2322
2494
|
* member); a dedicated ops/ relay does not speak the protocol and the
|
|
2323
|
-
* per-peer failure
|
|
2495
|
+
* per-peer failure comes back `unreachable` — harmless, it has no membership gate and
|
|
2324
2496
|
* needs no grant. The relay's direct addr rides along as the dial fallback
|
|
2325
2497
|
* for a relay we are not yet connected to.
|
|
2326
2498
|
*/
|
|
2327
2499
|
private resolveCohortSeed;
|
|
2328
|
-
/**
|
|
2329
|
-
* The strand peer book's contribution to a strand's seed: every live entry's
|
|
2330
|
-
* addresses, freshest peer first, minus this node's own entry when `selfPeerId` is
|
|
2331
|
-
* known (the book swap files one under the strand transport id; a node must never
|
|
2332
|
-
* dial itself). Empty before {@link start} builds the store.
|
|
2333
|
-
*/
|
|
2334
|
-
private strandPeerBookAddrs;
|
|
2335
2500
|
/**
|
|
2336
2501
|
* The own-party half of {@link resolveCohortSeed}: strand-network addresses resolved
|
|
2337
2502
|
* from CONNECTED cohort siblings over the control-mesh strand-addr RPC, doubling as
|
|
@@ -2401,9 +2566,10 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2401
2566
|
* Record the announce timestamps {@link refreshDelegateGrants} throttles on,
|
|
2402
2567
|
* for every relay a delegate-carrying announce pass dialed.
|
|
2403
2568
|
*
|
|
2404
|
-
* Recorded OPTIMISTICALLY at announce time
|
|
2405
|
-
* per
|
|
2406
|
-
*
|
|
2569
|
+
* Recorded OPTIMISTICALLY at announce time, whatever `collectStrandAddrs`
|
|
2570
|
+
* reports per peer: a dedicated `ops/` relay never speaks the strand-addr
|
|
2571
|
+
* protocol, so recording only on success would re-announce to it on every
|
|
2572
|
+
* reconcile tick, one wasted protocol negotiation each. A failed INITIAL announce
|
|
2407
2573
|
* costs the strand supervisor its first attempt only (the relay denies the
|
|
2408
2574
|
* reservation; every re-drive re-announces first through
|
|
2409
2575
|
* {@link announceDelegateToRelay}); a failed REFRESH retries within
|
|
@@ -2441,16 +2607,16 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2441
2607
|
* again right away.
|
|
2442
2608
|
*
|
|
2443
2609
|
* Against a dedicated ops relay (no strand-addr RPC) the request fails per-peer
|
|
2444
|
-
* and `collectStrandAddrs`
|
|
2610
|
+
* and `collectStrandAddrs` reports it `unreachable`: one wasted protocol negotiation per
|
|
2445
2611
|
* re-drive attempt, bounded by the supervisor's backoff. Never throws on that
|
|
2446
2612
|
* path; a relay addr that names no peer id is logged and skipped (the hook's
|
|
2447
2613
|
* caller runs the drive regardless).
|
|
2448
2614
|
*
|
|
2449
2615
|
* NOTE: against a relay that is DOWN this hook costs up to two strand-addr
|
|
2450
|
-
* timeouts (
|
|
2451
|
-
* starts, so one failed re-drive holds the supervisor
|
|
2452
|
-
* drive's own deadline —
|
|
2453
|
-
* that declared a slower one (`link-budget.ts`).
|
|
2616
|
+
* timeouts (dial by peer id, then by addr; 23 s each at the default declared link round trip)
|
|
2617
|
+
* before the reservation drive even starts, so one failed re-drive holds the supervisor
|
|
2618
|
+
* `driving` for those 46 s plus the drive's own deadline — 64 s at the default, and longer on
|
|
2619
|
+
* a host that declared a slower one (`link-budget.ts`).
|
|
2454
2620
|
* Bounded and harmless while the relay is unreachable anyway; if recovery
|
|
2455
2621
|
* latency after a relay comes back ever matters, skip the announce when the
|
|
2456
2622
|
* control node holds no connection to the relay (the drive's own dial fails
|
|
@@ -2460,17 +2626,25 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2460
2626
|
/** One strand's share of {@link refreshDelegateGrants}: announce to the relays whose grant is due. */
|
|
2461
2627
|
private announceDelegateToDueRelays;
|
|
2462
2628
|
/**
|
|
2463
|
-
*
|
|
2464
|
-
*
|
|
2465
|
-
* {@link
|
|
2629
|
+
* Keep each running strand's own libp2p address book warm: re-merge the address
|
|
2630
|
+
* records its FRET routing table holds on every pass
|
|
2631
|
+
* ({@link remergeStrandFretRecords}), and re-ask each connected SIBLING for its
|
|
2632
|
+
* strand addresses over the control mesh when that (sibling, strand) is due
|
|
2633
|
+
* ({@link strandAddrAskDueAt}).
|
|
2466
2634
|
*
|
|
2467
|
-
* Without this, a strand's address book
|
|
2635
|
+
* Without this, cadre-core writes a strand's address book exactly once — the
|
|
2468
2636
|
* launch/resume seed — and everything below cadre-core that dials a strand peer
|
|
2469
2637
|
* by bare peer id (Optimystic's cluster and repo clients, FRET ping/announce)
|
|
2470
|
-
* loses its address for any
|
|
2638
|
+
* loses its address for any peer it is not currently connected to: a sibling
|
|
2471
2639
|
* that restarted its strand node or rotated its relay reservation is never
|
|
2472
|
-
* re-resolved, and
|
|
2473
|
-
* one-hour expiry (see `peer-addr-book.ts`).
|
|
2640
|
+
* re-resolved, and every address, another party's included, falls off at the
|
|
2641
|
+
* peerStore's one-hour expiry (see `peer-addr-book.ts`).
|
|
2642
|
+
*
|
|
2643
|
+
* Due times are per (sibling, strand) and set from each sibling's own outcome, so
|
|
2644
|
+
* a sibling that connects late — a phone joining after the party's always-on
|
|
2645
|
+
* machines — is asked on the next tick, and one that could not answer or refused
|
|
2646
|
+
* (its view of the membership may not include us yet) is retried within
|
|
2647
|
+
* {@link STRAND_PEER_ADDR_RETRY_MS} rather than {@link STRAND_PEER_ADDR_REFRESH_MS}.
|
|
2474
2648
|
*
|
|
2475
2649
|
* Distinct from {@link refreshDelegateGrants}, deliberately: that pass covers
|
|
2476
2650
|
* RELAYS on a `DELEGATE_GRANT_TTL_MS / 2` throttle to keep circuit-relay
|
|
@@ -2485,30 +2659,101 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2485
2659
|
*/
|
|
2486
2660
|
private refreshStrandPeerAddrs;
|
|
2487
2661
|
/**
|
|
2488
|
-
*
|
|
2489
|
-
*
|
|
2490
|
-
*
|
|
2491
|
-
*
|
|
2662
|
+
* The siblings {@link refreshStrandPeerAddrs} may ask this pass: the connected
|
|
2663
|
+
* cohort, or none when enumeration fails (logged, never thrown).
|
|
2664
|
+
*
|
|
2665
|
+
* `connectedSiblingTargets`' membership read is unbounded, and with zero
|
|
2666
|
+
* connections its answer is empty whatever the table holds, so decide it from the
|
|
2667
|
+
* connection list instead of paying for the read once a tick.
|
|
2668
|
+
*
|
|
2669
|
+
* NOTE: otherwise the read runs on EVERY tick with a running strand, not only when
|
|
2670
|
+
* some (sibling, strand) is due: pruning a departed sibling needs the current
|
|
2671
|
+
* target set, and whether anyone is due cannot be told without it (a connected
|
|
2672
|
+
* non-member never gets a due time to compare). One more `CadrePeer` read per 15 s
|
|
2673
|
+
* tick, on top of the two `runReconcileControlCohort` already makes; if those reads
|
|
2674
|
+
* get costly, share one row-set across the pass (see the NOTE there) rather than
|
|
2675
|
+
* skipping ticks here.
|
|
2492
2676
|
*
|
|
2493
|
-
*
|
|
2494
|
-
*
|
|
2495
|
-
*
|
|
2496
|
-
*
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2677
|
+
* NOTE: one strand-addr RPC per (running strand × due sibling) — each a tiny
|
|
2678
|
+
* request/response on an already-open control connection. If a node ever runs
|
|
2679
|
+
* MANY strands at once, batch the RPC to carry several strand ids per request
|
|
2680
|
+
* rather than one fan-out per strand.
|
|
2681
|
+
*/
|
|
2682
|
+
private strandAddrRefreshTargets;
|
|
2683
|
+
/**
|
|
2684
|
+
* One strand's share of {@link refreshStrandPeerAddrs}: RPC the siblings that are
|
|
2685
|
+
* due and merge their answers into the strand's address book, then re-merge the
|
|
2686
|
+
* address records the strand node's FRET table holds. Errors are logged and
|
|
2687
|
+
* swallowed so one strand's failure never costs the others their refresh.
|
|
2688
|
+
*
|
|
2689
|
+
* The sibling half runs first, so a failure reading the FRET table cannot cost a
|
|
2690
|
+
* sibling answer its merge. The FRET half, and the formation-carried addresses of
|
|
2691
|
+
* the peers it holds no record for, run on EVERY pass, whether or not any sibling is
|
|
2692
|
+
* due or connected — see {@link remergeStrandFretRecords} and
|
|
2693
|
+
* {@link remergeUnrecordedFormationAddrs} for why.
|
|
2500
2694
|
*/
|
|
2501
2695
|
private refreshOneStrandPeerAddrs;
|
|
2696
|
+
/**
|
|
2697
|
+
* Is `strandNode` still the node `strandId` runs on? Asked again after every await
|
|
2698
|
+
* in a refresh pass: a strand stopped mid-pass, or one already restarted onto a new
|
|
2699
|
+
* node, must never have its store written to.
|
|
2700
|
+
*/
|
|
2701
|
+
private isRunningStrandNode;
|
|
2702
|
+
/**
|
|
2703
|
+
* Re-merge, into a running strand node's address book, the addresses of every peer
|
|
2704
|
+
* its FRET routing table holds a signed address record for (`strand-fret-addrs.ts`).
|
|
2705
|
+
*
|
|
2706
|
+
* This is what keeps ANOTHER PARTY's strand nodes dialable from a node that stays
|
|
2707
|
+
* up. Nothing re-resolves their addresses — the strand-addr RPC answers own-party
|
|
2708
|
+
* siblings only — and the peerStore hides an address one hour after it was first
|
|
2709
|
+
* observed. FRET keeps each peer's record but hands it to the peerStore only when it
|
|
2710
|
+
* arrives or is imported, so without this pass a connection to another party that
|
|
2711
|
+
* drops after the first hour could not be redialed by either side.
|
|
2712
|
+
*
|
|
2713
|
+
* A record is held while FRET holds the peer's table entry, and at most 14 days after
|
|
2714
|
+
* it was last confirmed, so that is also how long a wrong address can keep being
|
|
2715
|
+
* re-merged. Each record is signed by the peer it names and verified before use, so
|
|
2716
|
+
* the exposure is failed dials to an address that peer itself published.
|
|
2717
|
+
*
|
|
2718
|
+
* NOTE: every pass serialises the whole FRET table (`exportTable()`), verifies one
|
|
2719
|
+
* signature per record and makes one `peerStore.merge` per peer, per running strand
|
|
2720
|
+
* per 15 s tick. Fine at strand table sizes, and an unchanged address set writes
|
|
2721
|
+
* nothing in libp2p's persistent peer store. If many strands or large tables make it
|
|
2722
|
+
* show up, read only the entries whose address stamp is near the one-hour mark.
|
|
2723
|
+
*
|
|
2724
|
+
* @returns the ids of the peers the table holds a usable record for.
|
|
2725
|
+
*/
|
|
2726
|
+
private remergeStrandFretRecords;
|
|
2727
|
+
/**
|
|
2728
|
+
* Re-merge the addresses this strand's formation carried ({@link formationStrandAddrs})
|
|
2729
|
+
* for every peer the strand node's FRET table holds no record for (`recorded`).
|
|
2730
|
+
*
|
|
2731
|
+
* Until the first connection to the responder, the carried addresses are the only
|
|
2732
|
+
* thing naming it, and the launch seed writes them once. FRET's `bootstraps` keep only
|
|
2733
|
+
* the peer id, so if the responder stays unreachable past the peerStore's one-hour
|
|
2734
|
+
* expiry, nothing could dial it when it returns. Once FRET holds the peer's own signed
|
|
2735
|
+
* record, that record supersedes what the formation carried and this stops.
|
|
2736
|
+
*/
|
|
2737
|
+
private remergeUnrecordedFormationAddrs;
|
|
2738
|
+
/**
|
|
2739
|
+
* RPC `due` siblings for `strandId` and set each one's next due time from its own
|
|
2740
|
+
* outcome: an answer, even an empty one, waits the full refresh interval; anything
|
|
2741
|
+
* else retries in {@link STRAND_PEER_ADDR_RETRY_MS} (or the refresh interval, if
|
|
2742
|
+
* configured shorter). Stamped before the caller
|
|
2743
|
+
* re-checks that the strand is still running, so a strand torn down mid-pass still
|
|
2744
|
+
* records who was asked.
|
|
2745
|
+
*/
|
|
2746
|
+
private askSiblingsForStrandAddrs;
|
|
2502
2747
|
/**
|
|
2503
2748
|
* Merge strand-network addresses into ONE strand node's libp2p address book,
|
|
2504
2749
|
* attributed per peer. Best-effort throughout: an address-book write must never
|
|
2505
2750
|
* fail a launch, a resume, or a reconcile pass.
|
|
2506
2751
|
*
|
|
2507
|
-
* `addrs` is
|
|
2508
|
-
* {@link groupAddrsByPeerId} attributes each
|
|
2509
|
-
* peerId in its final `/p2p/` component — never
|
|
2510
|
-
* which names a different libp2p node entirely.
|
|
2511
|
-
* dropped there and counted here, once per pass.
|
|
2752
|
+
* `addrs` is a peer-agnostic list — the union the strand-addr RPC returns, or the
|
|
2753
|
+
* addresses a formation carried — so {@link groupAddrsByPeerId} attributes each
|
|
2754
|
+
* entry to the **strand transport** peerId in its final `/p2p/` component — never
|
|
2755
|
+
* the sibling's control peerId, which names a different libp2p node entirely.
|
|
2756
|
+
* Entries that name no peer are dropped there and counted here, once per pass.
|
|
2512
2757
|
*
|
|
2513
2758
|
* NOTE: a member could answer with arbitrary multiaddrs bound to arbitrary peer
|
|
2514
2759
|
* ids and poison this address book. That is the same exposure the launch-time
|
|
@@ -2518,14 +2763,13 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2518
2763
|
* peerStore's one-hour expiry. No new gating here — cross-party strand trust is
|
|
2519
2764
|
* `backlog/strand-network-nat-relay-reachability`.
|
|
2520
2765
|
*
|
|
2521
|
-
* NOTE: one input
|
|
2522
|
-
* ({@link
|
|
2523
|
-
*
|
|
2524
|
-
*
|
|
2525
|
-
*
|
|
2526
|
-
*
|
|
2527
|
-
*
|
|
2528
|
-
* exposure is a handful of failed dials, not a trust hole.
|
|
2766
|
+
* NOTE: one input outlives that hour — a formation's carried addresses
|
|
2767
|
+
* ({@link formationStrandAddrs}) are in every launch and resume seed for as long as
|
|
2768
|
+
* this process remembers the formation, and re-merged on every refresh pass until the
|
|
2769
|
+
* strand node's FRET table holds the peer's own record, so a junk address from a
|
|
2770
|
+
* responder that is never met keeps being re-merged. Bounded by `sanitizeStrandAddrs`,
|
|
2771
|
+
* replaced by a re-formation, dropped on `unpublishStrand` / `forgetJoinedStrand`, and
|
|
2772
|
+
* still authority-free — so the exposure is a handful of failed dials, not a trust hole.
|
|
2529
2773
|
*/
|
|
2530
2774
|
private mergeStrandPeerAddrs;
|
|
2531
2775
|
/**
|
|
@@ -2539,20 +2783,29 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2539
2783
|
*/
|
|
2540
2784
|
private getStrandMultiaddrs;
|
|
2541
2785
|
/**
|
|
2542
|
-
* Leave a strand joined from another party
|
|
2543
|
-
*
|
|
2544
|
-
*
|
|
2545
|
-
*
|
|
2786
|
+
* Leave a strand joined from another party, for the whole party: remove its party-wide
|
|
2787
|
+
* `JoinedStrand` row (owner-signed, with a `Revocation` tombstone), then this machine's
|
|
2788
|
+
* unpublished record, its formation-carried addresses, its saved network state and any
|
|
2789
|
+
* session-kept entry, then
|
|
2790
|
+
* {@link stopStrand} it here. Every other machine's watcher then sees the row gone and
|
|
2791
|
+
* detaches the strand, a storage replica included; a machine offline at the time reaps the
|
|
2792
|
+
* row once the tombstone reaches it. The joiner's counterpart of {@link unpublishStrand},
|
|
2793
|
+
* which only works on a row the party's own `Strand` table holds. For one of those, or for
|
|
2794
|
+
* a join still local-only, there is no party-wide row and this is a local forget plus
|
|
2795
|
+
* {@link stopStrand}.
|
|
2546
2796
|
*
|
|
2547
|
-
*
|
|
2548
|
-
*
|
|
2797
|
+
* The other members of the strand are not told, and this party's membership row in the
|
|
2798
|
+
* strand stays, so a later re-formation reuses the same identity.
|
|
2799
|
+
*
|
|
2800
|
+
* @throws when a party-wide row exists and this machine cannot sign its removal (not an
|
|
2801
|
+
* enrolled owner) — {@link stopStrand} stops the strand on this machine only — or when
|
|
2802
|
+
* the party-wide table cannot be read.
|
|
2549
2803
|
*/
|
|
2550
2804
|
forgetJoinedStrand(strandId: string): Promise<void>;
|
|
2551
2805
|
/**
|
|
2552
|
-
* Record `row` as a join from another party when this party's control database
|
|
2553
|
-
*
|
|
2554
|
-
*
|
|
2555
|
-
* every start), and keeps the first `joinedAt`.
|
|
2806
|
+
* Record `row` as a join from another party when this party's control database names it
|
|
2807
|
+
* neither as its own strand nor as a party-wide join — the one case nothing else would
|
|
2808
|
+
* re-offer after a restart (`JoinedStrandSession.rememberForeign`).
|
|
2556
2809
|
*
|
|
2557
2810
|
* Best-effort, for {@link addStrand}: a failure costs the strand its re-offer after the
|
|
2558
2811
|
* next restart, not this attach, so it is reported and the attach goes on.
|
|
@@ -2567,40 +2820,38 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2567
2820
|
*/
|
|
2568
2821
|
private rememberForeignStrand;
|
|
2569
2822
|
/**
|
|
2570
|
-
* Self-revocation arm of the joined-strand
|
|
2571
|
-
* not re-attach it on every launch. The strand keeps running this
|
|
2572
|
-
* `strand:revoked` contract is that nothing is torn down for the app —
|
|
2573
|
-
*
|
|
2574
|
-
*
|
|
2575
|
-
*
|
|
2823
|
+
* Self-revocation arm of the joined-strand records: a party removed from a strand must
|
|
2824
|
+
* not re-attach it on every launch of every machine. The strand keeps running this
|
|
2825
|
+
* session here — the `strand:revoked` contract is that nothing is torn down for the app —
|
|
2826
|
+
* and its party-wide row is queued for removal by the next connected owner reconcile pass
|
|
2827
|
+
* (`JoinedStrandSession.forgetAfterThisSession`). A no-op for this party's own strands,
|
|
2828
|
+
* which have neither record; the formation-carried addresses and the saved network
|
|
2829
|
+
* state are forgotten either way, since a removed party must not keep dialing the
|
|
2830
|
+
* strand's peers.
|
|
2831
|
+
*
|
|
2832
|
+
* NOTE: accepted tradeoff — a sibling machine that has not raised `strand:revoked` itself
|
|
2833
|
+
* detaches the strand (`strand:stopped`) when the party-wide removal reaches it, instead
|
|
2834
|
+
* of keeping it for the session, which weakens "nothing is stopped on the removed
|
|
2835
|
+
* machine's behalf" for siblings. Weighed against a party-wide row that brings a revoked
|
|
2836
|
+
* strand back on every start of every machine. Revisit if apps need the "you were
|
|
2837
|
+
* removed" screen on every device: the sibling could re-check its own revocation state
|
|
2838
|
+
* (`refreshRevocationEnforcement`) before detaching a vanished joined row.
|
|
2576
2839
|
*
|
|
2577
2840
|
* NOTE: a manager can re-admit a removed party directly (`addMemberByManager`), and the
|
|
2578
|
-
* membership loop then finishes the join on its own — but the
|
|
2579
|
-
* the next start does not re-attach the strand. Re-forming records it again. If direct
|
|
2841
|
+
* membership loop then finishes the join on its own — but the records are gone by then,
|
|
2842
|
+
* so the next start does not re-attach the strand. Re-forming records it again. If direct
|
|
2580
2843
|
* re-admission becomes a routine flow, re-record the join when the revoked-peer gate
|
|
2581
2844
|
* clears for this node.
|
|
2582
2845
|
*/
|
|
2583
2846
|
private forgetRevokedJoin;
|
|
2584
2847
|
/**
|
|
2585
|
-
* Forget a strand's
|
|
2586
|
-
*
|
|
2587
|
-
*
|
|
2848
|
+
* Forget what this node holds about a strand's peers: the addresses its formation
|
|
2849
|
+
* carried (see {@link formationStrandAddrs}) and its saved network state (see
|
|
2850
|
+
* {@link strandNetworkStateStore}). The store write is fire-and-log — the removal is
|
|
2851
|
+
* visible synchronously by the store's contract, and a failed persist only leaves the
|
|
2852
|
+
* entry on disk for the next start.
|
|
2588
2853
|
*/
|
|
2589
2854
|
private forgetStrandPeers;
|
|
2590
|
-
/**
|
|
2591
|
-
* The strand peer book's observation writer (`StartStrandConfig.onStrandPeerIdentified`):
|
|
2592
|
-
* a strand node identified a strand peer at `observation.addrs`, so remember it as an
|
|
2593
|
-
* unsigned entry seen just now. The observer never reports self, and the store binds
|
|
2594
|
-
* every address to the peer id before filing it.
|
|
2595
|
-
*/
|
|
2596
|
-
private observeStrandPeer;
|
|
2597
|
-
/**
|
|
2598
|
-
* Merge one entry into the peer book. Fire-and-log: the entry is visible
|
|
2599
|
-
* synchronously by the store's contract, and the promise tracks durability only. A
|
|
2600
|
-
* persist failure costs restart survival, never this session's seed — the same trade
|
|
2601
|
-
* {@link retainDialTarget} makes.
|
|
2602
|
-
*/
|
|
2603
|
-
private rememberStrandPeer;
|
|
2604
2855
|
/**
|
|
2605
2856
|
* Stop a strand on THIS node only: untrack it from hibernation, drop its sApp config,
|
|
2606
2857
|
* stop the local instance, and emit `strand:stopped`. The shared `Strand` row is left
|
|
@@ -2661,7 +2912,10 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2661
2912
|
*/
|
|
2662
2913
|
notifyStrandActivity(strandId: string, reason?: string): void;
|
|
2663
2914
|
/**
|
|
2664
|
-
* Force wake a hibernating strand
|
|
2915
|
+
* Force wake a hibernating strand. A requested wake is activity, recorded before the
|
|
2916
|
+
* wake starts: a check-in holding the strand — or rebuilding it, a rebuild this wake then
|
|
2917
|
+
* joins — leaves it up instead of re-quiescing it at the end of its window. Once awake the
|
|
2918
|
+
* strand's idle countdown runs, so it hibernates again if nothing uses it.
|
|
2665
2919
|
*/
|
|
2666
2920
|
wakeStrand(strandId: string): Promise<void>;
|
|
2667
2921
|
/**
|
|
@@ -2672,7 +2926,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2672
2926
|
* Routes through {@link HibernationManager.forceHibernate}, which cancels the
|
|
2673
2927
|
* strand's pending idle/hibernate (and check-in) timers — so a stale timer
|
|
2674
2928
|
* can't re-fire on or resurrect the strand — then runs the same `onHibernate`
|
|
2675
|
-
* path as the timer (`quiesceStrand
|
|
2929
|
+
* path as the timer (`quiesceStrand`, which marks it `hibernating`, then
|
|
2676
2930
|
* `strand:hibernating`). Unlike the timer path it does NOT re-arm check-ins:
|
|
2677
2931
|
* the strand stays down until the caller drives a wake (e.g. {@link serviceWake}).
|
|
2678
2932
|
*/
|
|
@@ -2697,9 +2951,14 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2697
2951
|
* strand share one in-flight operation ({@link serviceWakePromises}), and the
|
|
2698
2952
|
* underlying resume coalesces with a racing push-wake via
|
|
2699
2953
|
* {@link HibernationManager}'s wake coalescing — one runtime build, one window,
|
|
2700
|
-
* one re-hibernate decision.
|
|
2701
|
-
* the
|
|
2702
|
-
*
|
|
2954
|
+
* one re-hibernate decision. A wake or activity from elsewhere that lands during
|
|
2955
|
+
* the resume or the window leaves the strand up; this call's own wake does not.
|
|
2956
|
+
* Afterwards a strand left up has its idle countdown running, and a re-hibernated one
|
|
2957
|
+
* gets back the check-in chain this call interrupted — it never gains one it lacked,
|
|
2958
|
+
* so a strand the mobile runner force-hibernated stays down until the next push.
|
|
2959
|
+
* Returns `{ serviced: false }` (never throws) when
|
|
2960
|
+
* the node is not running or the strand is unknown, and surfaces a wake or
|
|
2961
|
+
* window failure as `{ serviced: true, hadActivity: false }`.
|
|
2703
2962
|
*
|
|
2704
2963
|
* @param strandId - the strand a push said has pending activity.
|
|
2705
2964
|
* @param opts.windowMs - override the live-window duration (defaults to the
|
|
@@ -2710,6 +2969,12 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2710
2969
|
}): Promise<ServiceWakeResult>;
|
|
2711
2970
|
/** Body of {@link serviceWake}; serialised per-strand by its coalescing guard. */
|
|
2712
2971
|
private runServiceWake;
|
|
2972
|
+
/**
|
|
2973
|
+
* The wake and window of {@link runServiceWake}. A failure in either — the network
|
|
2974
|
+
* unreachable inside a Doze grant, say — must not throw out of a background task, so it
|
|
2975
|
+
* reports no activity. Each stage cleans up only what it owns.
|
|
2976
|
+
*/
|
|
2977
|
+
private wakeAndHoldForService;
|
|
2713
2978
|
/**
|
|
2714
2979
|
* Get the control network node (for advanced use)
|
|
2715
2980
|
*/
|
|
@@ -2737,8 +3002,8 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2737
3002
|
*
|
|
2738
3003
|
* BUDGET: the supervisor's first attempt is what `start()` waits on — deliberately the drive's
|
|
2739
3004
|
* ordinary deadline rather than a boot-specific one. That deadline is now COUNTED, four link
|
|
2740
|
-
* round trips at the declared `network.linkRoundTripMs`
|
|
2741
|
-
* fixed 10 s): a healthy dial-plus-reserve is sub-second even over a WAN, so this is slack for
|
|
3005
|
+
* round trips at the declared `network.linkRoundTripMs` plus the relay's two admission
|
|
3006
|
+
* 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
|
|
2742
3007
|
* a slow link, while going much longer would make a dead relay indistinguishable from a hung
|
|
2743
3008
|
* start and much shorter would fail nodes on links that were merely slow. A host that declares
|
|
2744
3009
|
* a slower link lengthens it without touching this path — `link-budget.ts`. The retries carry
|
|
@@ -2819,7 +3084,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2819
3084
|
*
|
|
2820
3085
|
* @param ownerPrivateKey - The owner's private key (base64url encoded)
|
|
2821
3086
|
*/
|
|
2822
|
-
initializeSeedBootstrap(ownerPrivateKey: string): void
|
|
3087
|
+
initializeSeedBootstrap(ownerPrivateKey: string): Promise<void>;
|
|
2823
3088
|
/**
|
|
2824
3089
|
* Push the multiaddrs that future invites should advertise. Pass `null` to
|
|
2825
3090
|
* revert to the libp2p-reported addresses (the default). The host calls this
|
|
@@ -2954,7 +3219,8 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2954
3219
|
* is reachable through its circuit-relay address), dials `WAKE_PROTOCOL`, sends
|
|
2955
3220
|
* the {@link WakeRequest}, and returns the peer's {@link WakeAck}. The receiver
|
|
2956
3221
|
* gates the request on cadre membership and only resumes a strand it already
|
|
2957
|
-
* participates in.
|
|
3222
|
+
* participates in; it acks once it has decided, before the strand is up. The
|
|
3223
|
+
* dial deadlines derive from this node's `network.linkRoundTripMs`.
|
|
2958
3224
|
*
|
|
2959
3225
|
* @param targetPeerId - The hibernating cadre peer to wake.
|
|
2960
3226
|
* @param strandId - The strand the caller knows has pending activity.
|
|
@@ -2967,11 +3233,23 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2967
3233
|
* This is for drone nodes that need to receive seeds without being an owner.
|
|
2968
3234
|
* Does not require an owner key.
|
|
2969
3235
|
*/
|
|
2970
|
-
enableSeedListener(): void
|
|
3236
|
+
enableSeedListener(): Promise<void>;
|
|
2971
3237
|
/**
|
|
2972
3238
|
* Get the seed bootstrap service (for advanced use)
|
|
2973
3239
|
*/
|
|
2974
3240
|
getSeedBootstrapService(): SeedBootstrapService | null;
|
|
3241
|
+
/**
|
|
3242
|
+
* Make `service` this node's seed service and register its inbound seed handler.
|
|
3243
|
+
* The field is set before the registration is awaited, so a concurrent
|
|
3244
|
+
* {@link enableSeedListener} finds it and does not register a second handler; a
|
|
3245
|
+
* failed registration puts the previous service back and rethrows.
|
|
3246
|
+
*
|
|
3247
|
+
* NOTE: libp2p's registrar stores the handler before its peer-store merge, so a
|
|
3248
|
+
* failed merge leaves SEED_PROTOCOL registered with no service owning it and a retry
|
|
3249
|
+
* here rejects as a duplicate; if peer-store writes can fail in practice, unhandle on
|
|
3250
|
+
* a non-duplicate failure (never on a duplicate — that handler belongs to someone else).
|
|
3251
|
+
*/
|
|
3252
|
+
private installSeedBootstrapService;
|
|
2975
3253
|
/**
|
|
2976
3254
|
* The event callbacks every {@link SeedBootstrapService} this node owns is
|
|
2977
3255
|
* wired with — shared by the owner-capable service ({@link initializeSeedBootstrap})
|
|
@@ -3162,7 +3440,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3162
3440
|
*
|
|
3163
3441
|
* @param options - Configuration for the solicitation service
|
|
3164
3442
|
*/
|
|
3165
|
-
initializeStrandSolicitation(options?: StrandSolicitationServiceOptions): void
|
|
3443
|
+
initializeStrandSolicitation(options?: StrandSolicitationServiceOptions): Promise<void>;
|
|
3166
3444
|
/**
|
|
3167
3445
|
* Get the strand solicitation service (for advanced use)
|
|
3168
3446
|
*/
|
|
@@ -3185,7 +3463,9 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3185
3463
|
formStrand(invitation: OpenInvitation, disclosure?: StrandFormationDisclosure): Promise<FormStrandResult>;
|
|
3186
3464
|
/**
|
|
3187
3465
|
* Remember the strand a formation just joined (see {@link joinedStrandStore}), so it
|
|
3188
|
-
* is re-offered after a restart even when the app is killed before its `addStrand
|
|
3466
|
+
* is re-offered after a restart even when the app is killed before its `addStrand`, and
|
|
3467
|
+
* published party-wide by the next connected owner reconcile pass. A re-join also
|
|
3468
|
+
* cancels a party-wide removal a self-revocation queued for the strand.
|
|
3189
3469
|
* Last in {@link formStrand}, after the membership adoption, so a failure here leaves
|
|
3190
3470
|
* the party key and staged invitation in place for the re-formation it asks for.
|
|
3191
3471
|
*
|
|
@@ -3249,9 +3529,15 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3249
3529
|
* founder's publish/launch paths mint it, so this is a not-yet-converged sibling.)
|
|
3250
3530
|
* The manager maps the throw to a clean retryable rejection BEFORE the formation
|
|
3251
3531
|
* token is spent.
|
|
3252
|
-
* - Closed host strand
|
|
3253
|
-
*
|
|
3254
|
-
*
|
|
3532
|
+
* - Closed host strand whose runtime is HIBERNATING or being quiesced → woken first
|
|
3533
|
+
* ({@link wakeHostStrandForFormation}, bounded by `signal`), then issued as below. In
|
|
3534
|
+
* every state the redemption counts as activity, so the host stays up for the
|
|
3535
|
+
* joiner's first sync.
|
|
3536
|
+
* - Closed host strand with no running local instance/database (never launched, still
|
|
3537
|
+
* starting, or a hibernating one whose wake failed or outran `signal`) →
|
|
3538
|
+
* throw, same mapping: a joiner admitted without an invitation would look joined and
|
|
3539
|
+
* never become a member, and a responder not running the strand cannot serve its sync
|
|
3540
|
+
* anyway.
|
|
3255
3541
|
* - Closed host strand whose LIVE rows carry the pre-split fingerprint
|
|
3256
3542
|
* (`assertNotPreSplitStrand`) → throw `PreSplitStrandIdentityError`, same mapping as
|
|
3257
3543
|
* the recorded refusal. Covers the responders that never ran a refused founder launch:
|
|
@@ -3261,7 +3547,12 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3261
3547
|
* permanent diagnosis. Identity is checked BEFORE the runtime: it is the cheaper read
|
|
3262
3548
|
* and the more actionable diagnosis when both are missing (a missing runtime is
|
|
3263
3549
|
* transient, a missing identity is not), and it keeps the branch reachable without
|
|
3264
|
-
* standing a strand runtime up.
|
|
3550
|
+
* standing a strand runtime up. For the same reason every control-database check runs
|
|
3551
|
+
* before a wake: a strand that cannot issue anyway is not woken.
|
|
3552
|
+
*
|
|
3553
|
+
* `signal` is the formation's provisioning budget. Once it has aborted nothing is
|
|
3554
|
+
* issued — the joiner has already been told to retry, and an invitation written now
|
|
3555
|
+
* would only sit in the strand until it expires.
|
|
3265
3556
|
*
|
|
3266
3557
|
* The invitation expires `MEMBERSHIP_INVITE_TTL_MS` from now — see that constant for
|
|
3267
3558
|
* the slow-joiner / lost-result tradeoff.
|
|
@@ -3278,21 +3569,51 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3278
3569
|
*/
|
|
3279
3570
|
private issueStrandMembershipInvite;
|
|
3280
3571
|
/**
|
|
3281
|
-
*
|
|
3282
|
-
*
|
|
3283
|
-
*
|
|
3284
|
-
*
|
|
3572
|
+
* Count a bound closed-strand redemption as activity on the host strand, and wake the
|
|
3573
|
+
* strand when it is HIBERNATING or being quiesced (its database is closing) so the
|
|
3574
|
+
* membership invitation can be issued. Only reached after the formation manager has
|
|
3575
|
+
* authorized the redemption (token, disclosure, outside approval, seat pre-check), so only
|
|
3576
|
+
* a caller already entitled to the strand's member key can cause a wake. No other state is
|
|
3577
|
+
* woken — never launched or still starting is not something this node recovers from on
|
|
3578
|
+
* demand; the caller's live-database check refuses those.
|
|
3579
|
+
*
|
|
3580
|
+
* The activity is recorded in every state, through the hibernation manager rather than
|
|
3581
|
+
* {@link recordStrandActivity}, whose push fan-out would wake this party's phones for
|
|
3582
|
+
* nothing. It keeps the host up for the joiner's first sync: a live strand's idle timer
|
|
3583
|
+
* restarts, a check-in window that happens to have the strand live sees activity and
|
|
3584
|
+
* leaves it up instead of re-quiescing it, and a hibernating strand has its idle →
|
|
3585
|
+
* hibernate timers re-armed once the wake leaves it `active`. The explicit
|
|
3586
|
+
* {@link wakeStrand} coalesces onto the wake `recordActivity` began, and still wakes when
|
|
3587
|
+
* `recordActivity` is a no-op (hibernation disabled but the strand force-hibernated, or
|
|
3588
|
+
* the manager stopped).
|
|
3589
|
+
*
|
|
3590
|
+
* Bounded by `signal` (the formation's provisioning budget): when it aborts first this
|
|
3591
|
+
* throws — a retryable rejection, token unspent — and leaves the wake running, so the
|
|
3592
|
+
* joiner's retry finds the strand live.
|
|
3593
|
+
*/
|
|
3594
|
+
private wakeHostStrandForFormation;
|
|
3595
|
+
/**
|
|
3596
|
+
* Keep the responder's strand-network addresses for a strand this node just formed
|
|
3597
|
+
* (see {@link formationStrandAddrs}), so the strand's discovery seed has something to
|
|
3598
|
+
* dial when the app launches it.
|
|
3285
3599
|
*
|
|
3286
3600
|
* The carried list is peer-agnostic (`sanitizeStrandAddrs` bounds and parses it,
|
|
3287
3601
|
* nothing more), so it is attributed per peer here (`groupAddrsByPeerId`, the same
|
|
3288
|
-
* rule the address-book merge applies) and
|
|
3289
|
-
*
|
|
3290
|
-
*
|
|
3291
|
-
*
|
|
3292
|
-
*
|
|
3293
|
-
*
|
|
3294
|
-
|
|
3295
|
-
|
|
3602
|
+
* rule the address-book merge applies) and kept grouped by peer; an entry naming no
|
|
3603
|
+
* destination peer is dropped there. Scoped strictly to `strandId`: these addresses
|
|
3604
|
+
* reach ONE strand node of ONE other party and must never seed another strand's mesh
|
|
3605
|
+
* or the control peerStore.
|
|
3606
|
+
*
|
|
3607
|
+
* A list that names a peer REPLACES the strand's earlier one: the responder
|
|
3608
|
+
* disclosed its current addresses, so the older list is stale. An empty list, or one
|
|
3609
|
+
* naming no peer, records nothing, so it cannot wipe an earlier disclosure.
|
|
3610
|
+
*
|
|
3611
|
+
* A strand already running when this lands (a re-formation, which is the recovery
|
|
3612
|
+
* path for a dead address) gets the new addresses merged into its address book now.
|
|
3613
|
+
* Nothing else would deliver them: the seed is read only at launch and resume, and the
|
|
3614
|
+
* refresh pass skips a peer FRET already holds a record for — the stale one, here.
|
|
3615
|
+
*/
|
|
3616
|
+
private recordFormationStrandAddrs;
|
|
3296
3617
|
/**
|
|
3297
3618
|
* Encode an open invitation for out-of-band delivery (QR, link, etc.).
|
|
3298
3619
|
*/
|