@serfab/cadre-core 1.5.0 → 1.6.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.
@@ -41,7 +41,7 @@ import type {
41
41
  import { DEFAULT_CONNECTION_MONITOR, resolveStrandClusterSize, strandClusterPolicy } from './types.js';
42
42
  import { strandNodeAddrs } from './strand-network-config.js';
43
43
  import { superviseRelayReservation, type RelayReservationSupervisor } from './relay-reservation.js';
44
- import { peerJoinPushBudget, relayReservationBudgetMs, resolveLinkRoundTripMs } from './link-budget.js';
44
+ import { connectionManagerTimeouts, peerJoinPushBudget, relayReservationBudgetMs, resolveLinkRoundTripMs } from './link-budget.js';
45
45
 
46
46
  const log = debug('sereus:cadre:strand-manager');
47
47
  const timing = debug('sereus:cadre:timing');
@@ -711,6 +711,10 @@ export class StrandInstanceManager {
711
711
  // widened ping deadline has to reach the strand nodes too (see
712
712
  // DEFAULT_CONNECTION_MONITOR).
713
713
  connectionMonitor: config.network?.connectionMonitor ?? DEFAULT_CONNECTION_MONITOR,
714
+ // The same declared-link limits the control node takes, and for the same reason: a
715
+ // strand node is the listener for every other member's strand node (`link-budget.ts`,
716
+ // `connectionManagerTimeouts`).
717
+ connectionManager: connectionManagerTimeouts(config.network?.linkRoundTripMs),
714
718
  // Listen entries plus the WebSocket transport switch they imply — a strand node
715
719
  // announces nothing the operator configured (`strand-network-config.ts`), and
716
720
  // spreads AFTER `transports` above because the switch is a no-op whenever the
@@ -884,7 +888,7 @@ export class StrandInstanceManager {
884
888
  // failing here would only trade that for `StrandWatcher`'s full-rebuild retry.
885
889
  //
886
890
  // NOTE: a relay that is down costs this launch one full drive — the reservation budget
887
- // counted from the declared link round trip, 8 s at its default (`link-budget.ts`) — and
891
+ // counted from the declared link round trip, 14 s at its default (`link-budget.ts`) — and
888
892
  // `StrandWatcher` launches strands one at a time, so N strands cost N of those in
889
893
  // bring-up during a relay outage, and MORE on a host that declared a slower link. If that
890
894
  // ever matters, stop awaiting here (the circuit addr then lands after `active`) rather
package/src/types.ts CHANGED
@@ -317,7 +317,7 @@ export interface NetworkConfig {
317
317
  * It buys a node that BOOTS, not a node that boots fast: `start()` still waits
318
318
  * out that first attempt, which costs the drive's whole timeout
319
319
  * (`DEFAULT_RELAY_RESERVE_TIMEOUT_MS`, four link round trips at
320
- * {@link linkRoundTripMs} — 8 s at its default) against a relay that is unreachable
320
+ * {@link linkRoundTripMs} — 14 s at its default) against a relay that is unreachable
321
321
  * rather than merely refusing (`relay-reservation.ts` polls to the deadline, in
322
322
  * case libp2p's own discovery lands a reservation independently).
323
323
  *
@@ -434,22 +434,25 @@ export interface NetworkConfig {
434
434
  * The round trip this node assumes between itself and another machine, in milliseconds, for
435
435
  * the control node and every strand node — as {@link cohortQueryTimeoutMs} is, and for the
436
436
  * same reason: the setting describes the LINK, and a phone's control node and its strand
437
- * nodes ride the same one. Omitted takes {@link DECLARED_LINK_ROUND_TRIP_MS} (2000 ms).
437
+ * nodes ride the same one. Omitted takes {@link DECLARED_LINK_ROUND_TRIP_MS} (3500 ms).
438
438
  *
439
439
  * This is NOT a timeout. It is the one stated assumption that cadre's own dial and
440
440
  * reservation deadlines are DERIVED from, each by the number of round trips that operation
441
441
  * was measured to cost: a peer-join catch-up's dial to one peer, its push response, one relay
442
- * reservation drive, and the control-cohort dial budgets. Reaching another machine through a
443
- * relay costs a fixed number of exchanges, so a deadline written as milliseconds has a link
444
- * speed above which it can never open a connection — which is the defect this declaration
445
- * exists to make impossible to reintroduce one budget at a time. The counts, the measurement
446
- * behind them, and the ceiling that no declaration here can lift are in `link-budget.ts`.
442
+ * reservation drive, the control-cohort dial budgets, and libp2p's own `dialTimeout` and
443
+ * `inboundUpgradeTimeout` on every node. Reaching another machine through a relay costs a
444
+ * fixed number of exchanges, so a deadline written as milliseconds has a link speed above
445
+ * which it can never open a connection — which is the defect this declaration exists to make
446
+ * impossible to reintroduce one budget at a time. The counts, the measurement behind them,
447
+ * and what still fails at the supported link are in `link-budget.ts`.
447
448
  *
448
- * Raise it for a link slower than the relayed phone-to-phone band sereus assumes; the cost is
449
- * the ordinary cost of longer deadlines, a peer that is genuinely gone holding each operation
450
- * that much longer before it is abandoned and retried. Above about 2500 it buys nothing: two
451
- * libp2p budgets that sereus cannot reach abandon the connection first
452
- * (`tickets/blocked/how-slow-a-relayed-link-does-sereus-carry`).
449
+ * The default covers the slowest link sereus supports, a 3-second round trip through a relay.
450
+ * Raise it for a link slower still; the cost is the ordinary cost of longer deadlines, a peer
451
+ * that is genuinely gone holding each operation that much longer before it is abandoned and
452
+ * retried. Lower it only if EVERY machine of the party is that close: this node is also the
453
+ * listener for connections other machines open to it, and its `inboundUpgradeTimeout` comes
454
+ * from this value, so a node declaring a faster link than its peers discards their
455
+ * half-built connections — silently, from the dialer's side.
453
456
  *
454
457
  * Refused where the libp2p node is built — inside `CadreNode.start()` for the control
455
458
  * network, inside `CadreNode.addStrand` for a strand — if it is not a finite number above
@@ -569,6 +572,12 @@ export interface NetworkConfig {
569
572
  * kept it (the same pair aborted at a 200ms stall only when the deadline was 300ms). An
570
573
  * interval strictly above the deadline is what makes the 30 seconds real.
571
574
  *
575
+ * WHAT THE LINK NEEDS OF IT. One ping opens a fresh stream and echoes over it: a protocol
576
+ * negotiation plus the echo, two link round trips by `link-budget.ts`'s counts (a `newStream`
577
+ * over a relayed circuit measured 3016 ms at 1500 ms one-way). At the slowest link sereus
578
+ * supports, a 3-second relayed round trip, that is about 6 s — well inside the 30 s deadline,
579
+ * which exists for the phone's CPU rather than the link.
580
+ *
572
581
  * WHAT IT COSTS. A dead peer is reclaimed 30 to 65 seconds after it stops answering — the
573
582
  * deadline, plus up to one interval of waiting for the ping that will fail — where
574
583
  * libp2p's defaults took about 5 to 15 seconds. `db-p2p` caps a node at 16 connections,
@@ -807,11 +816,11 @@ export interface CadreNodeConfig {
807
816
  * `timeoutMs` bounds how long {@link CadreNode.addStrand} waits before rejecting with
808
817
  * `StrandAwaitingFirstSyncError` (retryable — the launch stays up and keeps probing);
809
818
  * `pollIntervalMs` is the probe cadence. Omit for the defaults (`DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS`,
810
- * 120 s — sized for a machine joining through a relay on a slow link; that constant's doc
811
- * comment carries the measurement behind the number and what the budget costs, and is the
812
- * only copy of it; `DEFAULT_STRAND_FIRST_SYNC_POLL_MS`, 500 ms). There is
813
- * deliberately no way to disable the gate: a machine that already holds the Header is never
814
- * gated, so nothing that works today is blocked by it.
819
+ * sized for a machine re-attaching through a relay on a slow link, the slowest attach
820
+ * measured; that constant's doc comment carries the number, the measurement behind it and
821
+ * what the budget costs, and is the only copy of them; `DEFAULT_STRAND_FIRST_SYNC_POLL_MS`,
822
+ * 500 ms). There is deliberately no way to disable the gate: a machine whose store already
823
+ * holds the Header is never gated, so nothing that works today is blocked by it.
815
824
  */
816
825
  strandFirstSync?: StrandFirstSyncConfig;
817
826