@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/src/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ConnectionGater, Libp2p, PeerId, PrivateKey } from '@libp2p/interface';
|
|
2
|
+
import type { CircuitRelayServerInit } from '@libp2p/circuit-relay-v2';
|
|
2
3
|
import type { IRawStorage, Libp2pConnectionMonitorInit, Libp2pTransports, NoiseCryptoInterface } from '@optimystic/db-p2p';
|
|
3
4
|
import type { IPeerNetwork, IRepo } from '@optimystic/db-core';
|
|
4
5
|
import type { PeerJoinBackfillConfig } from './peer-join-backfill.js';
|
|
@@ -10,7 +11,9 @@ import type { SeedTrustPolicy } from './seed-trust-policy.js';
|
|
|
10
11
|
import type { KeyStore, KeyId } from './key-store.js';
|
|
11
12
|
import type { TrustedOwnerStore, TrustSource } from './trusted-owner-store.js';
|
|
12
13
|
import type { BootstrapPeerStore } from './bootstrap-peer-store.js';
|
|
14
|
+
import type { StrandPeerBookStore } from './strand-peer-book.js';
|
|
13
15
|
import type { EnrolledMachineStore } from './enrolled-machine-store.js';
|
|
16
|
+
import type { JoinedStrandStore } from './joined-strand-store.js';
|
|
14
17
|
import type { PushNotifier } from './push-notifier.js';
|
|
15
18
|
import type { RevocableTable } from './control-authorization.js';
|
|
16
19
|
import type { ControlRetryAbandonment } from './control-retry.js';
|
|
@@ -121,10 +124,12 @@ export const HIBERNATION_TIMEOUTS: Record<LatencyHint, HibernationTimeouts> = {
|
|
|
121
124
|
* Factory functions are useful for creating per-scope storage instances.
|
|
122
125
|
*
|
|
123
126
|
* **The argument is an opaque scope key.** Use it directly as a file name, directory
|
|
124
|
-
* name or database name: every key stays within `[
|
|
125
|
-
* needed and none should be assumed
|
|
126
|
-
*
|
|
127
|
-
*
|
|
127
|
+
* name or database name: every key stays within `[a-z0-9._-]`, so no escaping is
|
|
128
|
+
* needed and none should be assumed, and two different keys are two different names
|
|
129
|
+
* even on a filesystem that ignores case (Windows, macOS). Do not parse it;
|
|
130
|
+
* `controlStorageScope` / `isControlStorageScope` (`storage-scope.ts`) are the supported
|
|
131
|
+
* way to mint and recognize the control key. The control key holds the charset by
|
|
132
|
+
* lowercase hex encoding;
|
|
128
133
|
* a strand's key — its strand id, which may have replicated in from another node in
|
|
129
134
|
* the party — holds it because `StrandInstanceManager.startStrand` runs
|
|
130
135
|
* `assertStrandScopeKey` on every launch and refuses a strand that fails it.
|
|
@@ -169,7 +174,7 @@ export interface StorageConfig {
|
|
|
169
174
|
* the single-instance form cannot serve more than one party.
|
|
170
175
|
*
|
|
171
176
|
* The `scope` a factory receives is an opaque key already safe as a path or
|
|
172
|
-
* database-name segment (always within `[
|
|
177
|
+
* database-name segment (always within `[a-z0-9._-]`), so each example below
|
|
173
178
|
* interpolates it directly.
|
|
174
179
|
*
|
|
175
180
|
* For Node.js environments, use FileRawStorage from @optimystic/db-p2p-storage-fs:
|
|
@@ -317,7 +322,8 @@ export interface NetworkConfig {
|
|
|
317
322
|
* It buys a node that BOOTS, not a node that boots fast: `start()` still waits
|
|
318
323
|
* out that first attempt, which costs the drive's whole timeout
|
|
319
324
|
* (`DEFAULT_RELAY_RESERVE_TIMEOUT_MS`, four link round trips at
|
|
320
|
-
* {@link linkRoundTripMs} —
|
|
325
|
+
* {@link linkRoundTripMs} plus two admission decisions — 18 s at its default) against a relay
|
|
326
|
+
* that is unreachable
|
|
321
327
|
* rather than merely refusing (`relay-reservation.ts` polls to the deadline, in
|
|
322
328
|
* case libp2p's own discovery lands a reservation independently).
|
|
323
329
|
*
|
|
@@ -340,6 +346,31 @@ export interface NetworkConfig {
|
|
|
340
346
|
* better connectivity and uptime), false for transaction profile nodes.
|
|
341
347
|
*/
|
|
342
348
|
enableRelay?: boolean;
|
|
349
|
+
/**
|
|
350
|
+
* Settings for this machine's circuit-relay SERVER, handed to `@libp2p/circuit-relay-v2`'s
|
|
351
|
+
* `circuitRelayServer(...)` (through db-p2p's `NodeOptions.relayServerInit`) on the control
|
|
352
|
+
* node and on every strand node. Only meaningful while {@link enableRelay} is on.
|
|
353
|
+
*
|
|
354
|
+
* Omitted, a party-run relay FORWARDS WITHOUT LIMIT: `reservations.applyDefaultLimit` is
|
|
355
|
+
* `false`, where libp2p's own default caps every relayed connection at 128 KiB or two
|
|
356
|
+
* minutes and resets it — which cut off any database sync or chat history forwarded
|
|
357
|
+
* through a party's own always-on machine. The store also holds
|
|
358
|
+
* `PARTY_RELAY_MAX_RESERVATIONS` (128) reservations instead of libp2p's 15, and each lives
|
|
359
|
+
* `PARTY_RELAY_RESERVATION_TTL_MS` (2 h) unrefreshed. See `relay-server.ts`.
|
|
360
|
+
*
|
|
361
|
+
* A value is MERGED over those defaults, not substituted for them: `reservations` key by
|
|
362
|
+
* key, every other top-level key as given. Setting only `reservations.maxReservations`
|
|
363
|
+
* keeps the limit off; `reservations: { applyDefaultLimit: true }` is how to turn libp2p's
|
|
364
|
+
* cap back on.
|
|
365
|
+
*
|
|
366
|
+
* The accepted tradeoff of the unlimited default: once a slot is granted, nothing caps what
|
|
367
|
+
* is forwarded through it, and a holder that keeps refreshing keeps it indefinitely — the
|
|
368
|
+
* 2 h lifetime only reclaims a slot whose holder went away. On the control node a peer it
|
|
369
|
+
* cannot place as a party member is bounded by COUNT ({@link unauthorizedRelayReservationCap}).
|
|
370
|
+
* A strand node's relay server has no such budget (its peers are cross-party by design), so
|
|
371
|
+
* any peer that reaches it is bounded only by `maxReservations`.
|
|
372
|
+
*/
|
|
373
|
+
relayServerInit?: CircuitRelayServerInit;
|
|
343
374
|
/**
|
|
344
375
|
* Cap on concurrent circuit-relay reservations this node's relay server grants to
|
|
345
376
|
* peers it cannot (yet) recognize as authorized members — the boot-ordering window
|
|
@@ -411,16 +442,21 @@ export interface NetworkConfig {
|
|
|
411
442
|
* How long ONE cohort peer gets to answer ONE read-path request, in milliseconds, for the
|
|
412
443
|
* control node and every strand node — as {@link connectionMonitor} is, and for the same
|
|
413
444
|
* reason: the setting describes the LINK, and a phone's control node and its strand nodes
|
|
414
|
-
* ride the same one. Omitted
|
|
415
|
-
*
|
|
445
|
+
* ride the same one. Omitted, the deadline is DERIVED from {@link linkRoundTripMs}: one
|
|
446
|
+
* request and its answer over an open circuit costs two link round trips, so 7 000 ms at the
|
|
447
|
+
* default declaration (`cohortReadDeadlineMs` in `link-budget.ts`; the plugin's
|
|
448
|
+
* {@link COHORT_READ_DEADLINE_MS} is the same number). Optimystic's own default is 1000 ms.
|
|
416
449
|
*
|
|
417
|
-
*
|
|
418
|
-
*
|
|
419
|
-
*
|
|
420
|
-
*
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
*
|
|
450
|
+
* Set this only to break the derivation on purpose: an explicit value wins over whatever
|
|
451
|
+
* {@link linkRoundTripMs} would derive, so a deployment that is all LAN can make a departed
|
|
452
|
+
* peer stop holding up a read sooner without declaring a faster link (which would also
|
|
453
|
+
* shorten the dial budgets). A deployment on a slower link should raise
|
|
454
|
+
* {@link linkRoundTripMs} instead, which moves this and every dial budget together. The
|
|
455
|
+
* cost of a larger value is that a peer which is truly gone holds a read of a block missing
|
|
456
|
+
* locally for that long before the read is declined and retried, and a joining machine's
|
|
457
|
+
* first sync runs several such consults — so a change here should be weighed against
|
|
458
|
+
* {@link CadreNodeConfig.strandFirstSync}'s budget. The derivation, its history and what it
|
|
459
|
+
* costs are on {@link COHORT_READ_DEADLINE_MS}.
|
|
424
460
|
*
|
|
425
461
|
* Handed to db-p2p's `clusterPolicy.cohortQueryTimeoutMs` unchanged and NOT re-validated
|
|
426
462
|
* here. Optimystic refuses a value that is not a finite number above zero, or is above its
|
|
@@ -439,11 +475,14 @@ export interface NetworkConfig {
|
|
|
439
475
|
* This is NOT a timeout. It is the one stated assumption that cadre's own dial and
|
|
440
476
|
* reservation deadlines are DERIVED from, each by the number of round trips that operation
|
|
441
477
|
* was measured to cost: a peer-join catch-up's dial to one peer, its push response, one relay
|
|
442
|
-
* reservation drive, the control-cohort dial budgets, and
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
*
|
|
446
|
-
*
|
|
478
|
+
* reservation drive, the control-cohort dial budgets, and the per-peer cohort read deadline
|
|
479
|
+
* ({@link cohortQueryTimeoutMs}, unless set explicitly). It is also handed to Optimystic
|
|
480
|
+
* (`NodeOptions.linkRoundTripMs`), default included, which derives its own deadlines from it:
|
|
481
|
+
* its request dials and responses, its block pushes, and libp2p's `dialTimeout` and
|
|
482
|
+
* `inboundUpgradeTimeout` on every node. Reaching another machine through a
|
|
483
|
+
* relay costs a fixed number of exchanges, so a deadline written as milliseconds has a link
|
|
484
|
+
* speed above which it can never open a connection — which is the defect this declaration
|
|
485
|
+
* exists to make impossible to reintroduce one budget at a time. The counts, the measurement behind them,
|
|
447
486
|
* and what still fails at the supported link are in `link-budget.ts`.
|
|
448
487
|
*
|
|
449
488
|
* The default covers the slowest link sereus supports, a 3-second round trip through a relay.
|
|
@@ -457,7 +496,8 @@ export interface NetworkConfig {
|
|
|
457
496
|
* Refused where the libp2p node is built — inside `CadreNode.start()` for the control
|
|
458
497
|
* network, inside `CadreNode.addStrand` for a strand — if it is not a finite number above
|
|
459
498
|
* zero, because every consumer multiplies it into a deadline where a zero means "give up at
|
|
460
|
-
* once" and a `NaN` means "never".
|
|
499
|
+
* once" and a `NaN` means "never". Optimystic refuses, in the same place, a value above its
|
|
500
|
+
* `MAX_LINK_ROUND_TRIP_MS` (about 1.66 days).
|
|
461
501
|
*/
|
|
462
502
|
linkRoundTripMs?: number;
|
|
463
503
|
/**
|
|
@@ -528,14 +568,19 @@ export interface NetworkConfig {
|
|
|
528
568
|
* {@link dialTimeoutMs}. Each address is dialed on its own under this limit,
|
|
529
569
|
* so an address that never answers cannot use up the time the peer's other
|
|
530
570
|
* addresses needed. Defaults to
|
|
531
|
-
* {@link DEFAULT_CONTROL_COHORT_PER_ADDRESS_DIAL_TIMEOUT_MS}.
|
|
571
|
+
* {@link DEFAULT_CONTROL_COHORT_PER_ADDRESS_DIAL_TIMEOUT_MS}. A value given
|
|
572
|
+
* here replaces that whole derived budget, including its allowance for the
|
|
573
|
+
* called machine's admission decision (`relayedDialBudgetMs`).
|
|
532
574
|
*/
|
|
533
575
|
perAddressDialTimeoutMs?: number;
|
|
534
576
|
/**
|
|
535
|
-
* How
|
|
536
|
-
*
|
|
537
|
-
*
|
|
538
|
-
*
|
|
577
|
+
* How long a running strand waits, after a sibling answers its strand-addr
|
|
578
|
+
* RPC, before asking that sibling again for its strand-network addresses — a
|
|
579
|
+
* step of the reconcile pass, scheduled per (sibling, strand). A sibling that
|
|
580
|
+
* did not answer is retried sooner, on `STRAND_PEER_ADDR_RETRY_MS` (or this
|
|
581
|
+
* interval, when it is set shorter). Defaults to
|
|
582
|
+
* `STRAND_PEER_ADDR_REFRESH_MS` (10 min); must stay well under the peerStore's
|
|
583
|
+
* one-hour address expiry.
|
|
539
584
|
*/
|
|
540
585
|
strandAddrRefreshMs?: number;
|
|
541
586
|
};
|
|
@@ -654,9 +699,10 @@ export interface ControlNetworkConfig {
|
|
|
654
699
|
*
|
|
655
700
|
* The two `*ClusterPolicy` BUILDERS are the same objects with the block-repair
|
|
656
701
|
* corroboration yardstick declared from the machines enrolled in this party
|
|
657
|
-
* ({@link resolveRepairYardstick}) and, if the host set one
|
|
658
|
-
* deadline in place of {@link COHORT_READ_DEADLINE_MS}
|
|
659
|
-
*
|
|
702
|
+
* ({@link resolveRepairYardstick}) and, if the host set one or declared its link, a
|
|
703
|
+
* per-peer read deadline in place of {@link COHORT_READ_DEADLINE_MS}
|
|
704
|
+
* (`declaredCohortReadDeadlineMs` in `link-budget.ts` settles which). Both arrive in one
|
|
705
|
+
* named declarations object, because both are plain numbers meaning unrelated things;
|
|
660
706
|
* declaring neither returns the frozen base constant unchanged. A network picks the
|
|
661
707
|
* derived numbers up when its libp2p node is built, which for a strand is every wake
|
|
662
708
|
* from hibernation.
|
|
@@ -720,6 +766,22 @@ export interface CadreNodeConfig {
|
|
|
720
766
|
/** Which strands to participate in */
|
|
721
767
|
strandFilter?: StrandFilter;
|
|
722
768
|
|
|
769
|
+
/**
|
|
770
|
+
* Run every strand {@link strandFilter} admits, including ones no app on this machine
|
|
771
|
+
* has claimed with `addStrand`, as a **storage replica**: the strand's own libp2p node,
|
|
772
|
+
* storage and `Strand` membership schema, storing and serving its blocks, without the
|
|
773
|
+
* app's schema (no sApp config needed, none of the app's code or schema runs). This is
|
|
774
|
+
* how an always-on machine keeps a copy of every shared workspace its party runs, so a
|
|
775
|
+
* lost phone loses nothing that had reached it.
|
|
776
|
+
*
|
|
777
|
+
* The strand is still announced as `strand:discovered` (once) and stays in
|
|
778
|
+
* `getDiscoveredStrands()` until an app claims it. A replica is always a joiner.
|
|
779
|
+
*
|
|
780
|
+
* Default: `profile === 'storage'` — always-on machines host replicas, phones
|
|
781
|
+
* (`'transaction'`) keep today's announce-only behaviour.
|
|
782
|
+
*/
|
|
783
|
+
hostUnclaimedStrands?: boolean;
|
|
784
|
+
|
|
723
785
|
/** Storage configuration (only for storage profile) */
|
|
724
786
|
storage?: StorageConfig;
|
|
725
787
|
|
|
@@ -947,6 +1009,55 @@ export interface CadreNodeConfig {
|
|
|
947
1009
|
store?: EnrolledMachineStore;
|
|
948
1010
|
};
|
|
949
1011
|
|
|
1012
|
+
/**
|
|
1013
|
+
* Node-local record of the strands this node joined from ANOTHER party and has not yet
|
|
1014
|
+
* published to the party-wide `JoinedStrand` table (see `joined-strand-store.ts`): id,
|
|
1015
|
+
* type and the closed strand's read secret, recorded by `formStrand` and by a joining
|
|
1016
|
+
* (not `founder: true`) `addStrand` whose row this party's control database does not
|
|
1017
|
+
* name, offered on every start as `strand:discovered` until an owner machine's
|
|
1018
|
+
* reconcile pass publishes it — so an embedding app keeps no list of its own.
|
|
1019
|
+
*
|
|
1020
|
+
* Absent ⇒ a `KeyStoreJoinedStrandStore` over {@link keyStore} when one is
|
|
1021
|
+
* configured (the record carries a secret, and the KeyStore is where every platform
|
|
1022
|
+
* already keeps secrets), else an in-memory store that warns once and forgets every
|
|
1023
|
+
* join on restart. An embedder that passes {@link privateKey} rather than a
|
|
1024
|
+
* `keyStore` injects a durable store here, e.g.
|
|
1025
|
+
* `new KeyStoreJoinedStrandStore(new FileKeyStore(dir), partyId)`.
|
|
1026
|
+
*/
|
|
1027
|
+
joinedStrands?: {
|
|
1028
|
+
/** Its `partyId` must match `controlNetwork.partyId`; start() fails closed on a mismatch. */
|
|
1029
|
+
store?: JoinedStrandStore;
|
|
1030
|
+
};
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* Node-local strand peer book (see `strand-peer-book.ts`): per strand, the strand
|
|
1034
|
+
* peers this node has met — the other parties' strand transport peers a formation
|
|
1035
|
+
* carried back, and every strand peer observed on a live connection — with their
|
|
1036
|
+
* last-known addresses. Read on every launch, hibernation resume and periodic
|
|
1037
|
+
* address refresh, so a restarted machine dials the peers it was talking to before
|
|
1038
|
+
* anything else, instead of coming back up alone (gotchoices/sereus#18). Sibling
|
|
1039
|
+
* of {@link bootstrapPeers} — same NON-replicated, per-party, injected-backend
|
|
1040
|
+
* shape — and, like it, nothing here is trust-bearing: dial hints only, each
|
|
1041
|
+
* bound to its peer id. Entries age out after 14 days (`STRAND_PEER_MAX_AGE_MS`,
|
|
1042
|
+
* overridable per store via `StrandPeerBookOptions.maxAgeMs`).
|
|
1043
|
+
*
|
|
1044
|
+
* Absent ⇒ an in-memory store is created at start() (ephemeral: the book dies
|
|
1045
|
+
* with the process, and a cross-party strand whose only contact was the other
|
|
1046
|
+
* party does not re-mesh after a restart until a fresh formation). Every
|
|
1047
|
+
* reference embedder injects a durable backend over the same slot kind as its
|
|
1048
|
+
* bootstrap-peer store. A new platform needs no new store class: supply a
|
|
1049
|
+
* `DurableSlot` and inject `PersistentStrandPeerBookStore.open(slot, partyId)`.
|
|
1050
|
+
*/
|
|
1051
|
+
strandPeers?: {
|
|
1052
|
+
/**
|
|
1053
|
+
* Injected store instance — e.g. a `FileStrandPeerBookStore` from the
|
|
1054
|
+
* Node-only subpath `@serfab/cadre-core/strand-peer-book-file`, persisted in
|
|
1055
|
+
* the node's state directory. Its `partyId` must match
|
|
1056
|
+
* `controlNetwork.partyId`; start() fails closed on a mismatch.
|
|
1057
|
+
*/
|
|
1058
|
+
store?: StrandPeerBookStore;
|
|
1059
|
+
};
|
|
1060
|
+
|
|
950
1061
|
/**
|
|
951
1062
|
* Platform push-delivery for suspended mobile peers. When present, the node's
|
|
952
1063
|
* server fan-out can deliver strand-wake data messages over the platform push
|
|
@@ -1032,6 +1143,12 @@ export interface StrandInstance {
|
|
|
1032
1143
|
|
|
1033
1144
|
/** Activity tracking */
|
|
1034
1145
|
connectedPeers: number;
|
|
1146
|
+
/**
|
|
1147
|
+
* When the strand last saw activity: the app's (`recordStrandActivity`), a requested wake
|
|
1148
|
+
* (`wakeStrand`), or a peer delivering a gated joiner's `Strand.Header`. Always a fresh
|
|
1149
|
+
* `Date`. Bringing the runtime up is NOT activity — a check-in window decides whether to
|
|
1150
|
+
* re-hibernate by whether this changed since before its resume.
|
|
1151
|
+
*/
|
|
1035
1152
|
lastActivity: Date;
|
|
1036
1153
|
nextCheckIn?: Date;
|
|
1037
1154
|
|
|
@@ -1394,6 +1511,10 @@ export interface CadreNodeEvents {
|
|
|
1394
1511
|
* re-admits this party, so a second removal is reported again — and a
|
|
1395
1512
|
* hibernation wake rebuilds the gate, which may re-emit for a strand still
|
|
1396
1513
|
* revoked.
|
|
1514
|
+
*
|
|
1515
|
+
* For a strand joined from another party, the node also forgets its remembered
|
|
1516
|
+
* join (see {@link CadreNodeConfig.joinedStrands}): it keeps running this session
|
|
1517
|
+
* and is not re-offered after the next start.
|
|
1397
1518
|
*/
|
|
1398
1519
|
'strand:revoked': { strandId: string };
|
|
1399
1520
|
/**
|
|
@@ -1427,6 +1548,12 @@ export interface CadreNodeEvents {
|
|
|
1427
1548
|
* Carries the full {@link StrandRow} so the app can join without re-querying
|
|
1428
1549
|
* the control DB.
|
|
1429
1550
|
*
|
|
1551
|
+
* Also emitted for a strand this node joined from ANOTHER party, which no control
|
|
1552
|
+
* row names: the node remembers every such join (see
|
|
1553
|
+
* {@link CadreNodeConfig.joinedStrands}) and offers it here on each start, as a row
|
|
1554
|
+
* with `FounderOwnerKey: null` carrying the closed strand's `MemberPrivateKey`. That
|
|
1555
|
+
* row is the product of the formation's consent, so claiming it needs no second one.
|
|
1556
|
+
*
|
|
1430
1557
|
* **Fired once per strand per session, and it can fire before your listener is
|
|
1431
1558
|
* attached.** The strand watcher's first poll runs inside `CadreNode.start()`
|
|
1432
1559
|
* (100 ms after the watcher starts), so every strand already stored for this
|
|
@@ -1448,6 +1575,12 @@ export interface CadreNodeEvents {
|
|
|
1448
1575
|
* be idempotent (guard on an in-flight set, not only on
|
|
1449
1576
|
* `getStrands().has(id)` — the strand manager tracks an instance only once
|
|
1450
1577
|
* `addStrand` has resolved).
|
|
1578
|
+
*
|
|
1579
|
+
* On a node that hosts storage replicas ({@link CadreNodeConfig.hostUnclaimedStrands},
|
|
1580
|
+
* the default for `profile: 'storage'`) the event still fires once, and the node launches
|
|
1581
|
+
* the strand as a replica right after it — so there `getStrands().has(id)` soon turns
|
|
1582
|
+
* true for a strand no app has claimed; an unclaimed instance has no `sAppInfo`. A
|
|
1583
|
+
* launch that fails is retried by the watcher without a second `strand:discovered`.
|
|
1451
1584
|
*/
|
|
1452
1585
|
'strand:discovered': { strandId: string; strand: StrandRow };
|
|
1453
1586
|
'control:connected': void;
|
|
@@ -1826,7 +1959,11 @@ export interface WakeRequest {
|
|
|
1826
1959
|
export interface WakeAck {
|
|
1827
1960
|
/** Whether the receiver honored the wake (member + participated strand). */
|
|
1828
1961
|
accepted: boolean;
|
|
1829
|
-
/**
|
|
1962
|
+
/**
|
|
1963
|
+
* The strand's status when the receiver accepted the wake (present when `accepted`). The
|
|
1964
|
+
* receiver replies before the wake runs, so `hibernating` or `idle` here means "a wake was
|
|
1965
|
+
* started", not "the strand is now up".
|
|
1966
|
+
*/
|
|
1830
1967
|
status?: StrandStatus;
|
|
1831
1968
|
/** Reason for rejection (non-member, unknown strand, malformed frame). */
|
|
1832
1969
|
reason?: string;
|
|
@@ -1855,17 +1992,32 @@ export interface StrandAddrRequest {
|
|
|
1855
1992
|
delegatePeerId?: string;
|
|
1856
1993
|
}
|
|
1857
1994
|
|
|
1995
|
+
/**
|
|
1996
|
+
* How a strand-addr responder handled a {@link StrandAddrRequest}. Carried so the
|
|
1997
|
+
* asker can tell "I have nothing" from "I could not answer" — the two need
|
|
1998
|
+
* different retry timing, and an empty address list alone cannot say which.
|
|
1999
|
+
*
|
|
2000
|
+
* - `ok` — looked up; `multiaddrs` is the truth, possibly empty (strand not running here).
|
|
2001
|
+
* - `unavailable` — the responder could not answer: over its concurrency cap, the
|
|
2002
|
+
* request was unreadable, or its own lookup threw (e.g. a control-database read failed).
|
|
2003
|
+
* - `refused` — the requester is not an authorized member in the responder's
|
|
2004
|
+
* current view (possibly because its `CadrePeer` row has not replicated there yet);
|
|
2005
|
+
* any `delegatePeerId` it carried was NOT recorded.
|
|
2006
|
+
*/
|
|
2007
|
+
export type StrandAddrStatus = 'ok' | 'unavailable' | 'refused';
|
|
2008
|
+
|
|
1858
2009
|
/**
|
|
1859
2010
|
* Response to a {@link StrandAddrRequest}, carrying the responder's strand-node
|
|
1860
2011
|
* multiaddrs, returned on the same stream.
|
|
1861
2012
|
*/
|
|
1862
2013
|
export interface StrandAddrResponse {
|
|
1863
|
-
/**
|
|
2014
|
+
/** How the responder handled the request; a reply without one is malformed. */
|
|
2015
|
+
status: StrandAddrStatus;
|
|
2016
|
+
/** Echoes the requested strand id; always empty on an `unavailable` reply. */
|
|
1864
2017
|
strandId: string;
|
|
1865
2018
|
/**
|
|
1866
2019
|
* Dialable strand-network multiaddr strings (signaling/`p2p-circuit` first);
|
|
1867
|
-
* empty
|
|
1868
|
-
* strand, or the exchange failed.
|
|
2020
|
+
* empty unless `status` is `ok` and the responder runs that strand.
|
|
1869
2021
|
*/
|
|
1870
2022
|
multiaddrs: string[];
|
|
1871
2023
|
}
|