@serfab/cadre-core 1.9.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/dist/cadre-node.d.ts +148 -44
  2. package/dist/cadre-node.js +363 -139
  3. package/dist/cadre-node.js.map +1 -1
  4. package/dist/control-authorization.d.ts +2 -2
  5. package/dist/control-authorization.js +1 -0
  6. package/dist/control-authorization.js.map +1 -1
  7. package/dist/control-cohort.d.ts +1 -1
  8. package/dist/control-cohort.js +1 -1
  9. package/dist/control-database.d.ts +105 -14
  10. package/dist/control-database.js +242 -73
  11. package/dist/control-database.js.map +1 -1
  12. package/dist/control-formation-recorder.d.ts +6 -6
  13. package/dist/control-formation-recorder.js +7 -8
  14. package/dist/control-formation-recorder.js.map +1 -1
  15. package/dist/control-schema.d.ts +1 -1
  16. package/dist/control-schema.js +1066 -979
  17. package/dist/control-schema.js.map +1 -1
  18. package/dist/control-write-retry.d.ts +21 -15
  19. package/dist/control-write-retry.js +21 -15
  20. package/dist/control-write-retry.js.map +1 -1
  21. package/dist/diagnostics/connection-path.d.ts +1 -18
  22. package/dist/diagnostics/connection-path.js +1 -20
  23. package/dist/diagnostics/connection-path.js.map +1 -1
  24. package/dist/identity-key.d.ts +0 -30
  25. package/dist/identity-key.js +7 -40
  26. package/dist/identity-key.js.map +1 -1
  27. package/dist/index.d.ts +8 -5
  28. package/dist/index.js +12 -6
  29. package/dist/index.js.map +1 -1
  30. package/dist/invitation-bootstrap.d.ts +32 -0
  31. package/dist/invitation-bootstrap.js +30 -0
  32. package/dist/invitation-bootstrap.js.map +1 -0
  33. package/dist/link-budget.d.ts +204 -119
  34. package/dist/link-budget.js +208 -120
  35. package/dist/link-budget.js.map +1 -1
  36. package/dist/membership-connection-gater.d.ts +3 -3
  37. package/dist/membership-connection-gater.js +3 -3
  38. package/dist/peer-dial.d.ts +18 -14
  39. package/dist/peer-dial.js +18 -14
  40. package/dist/peer-dial.js.map +1 -1
  41. package/dist/peer-join-backfill.d.ts +3 -3
  42. package/dist/pending-join-runner.d.ts +186 -0
  43. package/dist/pending-join-runner.js +649 -0
  44. package/dist/pending-join-runner.js.map +1 -0
  45. package/dist/push-fanout.d.ts +1 -1
  46. package/dist/push-fanout.js +1 -1
  47. package/dist/relay-stun.d.ts +24 -0
  48. package/dist/relay-stun.js +51 -0
  49. package/dist/relay-stun.js.map +1 -0
  50. package/dist/seed-bootstrap.d.ts +2 -2
  51. package/dist/seed-bootstrap.js +5 -5
  52. package/dist/strand-addr-protocol.js +2 -2
  53. package/dist/strand-formation-deadlines.d.ts +5 -5
  54. package/dist/strand-formation-deadlines.js +5 -5
  55. package/dist/strand-formation-manager.d.ts +18 -4
  56. package/dist/strand-formation-manager.js +48 -33
  57. package/dist/strand-formation-manager.js.map +1 -1
  58. package/dist/strand-formation-protocol.d.ts +71 -14
  59. package/dist/strand-formation-protocol.js +222 -47
  60. package/dist/strand-formation-protocol.js.map +1 -1
  61. package/dist/strand-formation-rejection.d.ts +94 -0
  62. package/dist/strand-formation-rejection.js +77 -0
  63. package/dist/strand-formation-rejection.js.map +1 -0
  64. package/dist/strand-instance-manager.js +4 -1
  65. package/dist/strand-instance-manager.js.map +1 -1
  66. package/dist/strand-membership-writer.js +32 -32
  67. package/dist/strand-solicitation.d.ts +11 -3
  68. package/dist/strand-solicitation.js +9 -0
  69. package/dist/strand-solicitation.js.map +1 -1
  70. package/dist/types.d.ts +103 -17
  71. package/dist/types.js +5 -9
  72. package/dist/types.js.map +1 -1
  73. package/package.json +22 -22
  74. package/src/cadre-node.ts +8654 -8400
  75. package/src/control-authorization.ts +2 -1
  76. package/src/control-cohort.ts +128 -128
  77. package/src/control-database.ts +303 -82
  78. package/src/control-formation-recorder.ts +353 -354
  79. package/src/control-read-retry.ts +216 -216
  80. package/src/control-schema.ts +1078 -991
  81. package/src/control-write-retry.ts +567 -561
  82. package/src/diagnostics/connection-path.ts +1 -29
  83. package/src/formation-approval.ts +610 -610
  84. package/src/hibernation-manager.ts +638 -638
  85. package/src/identity-key.ts +7 -62
  86. package/src/index.ts +644 -627
  87. package/src/invitation-bootstrap.ts +40 -0
  88. package/src/joined-strand-store.ts +498 -498
  89. package/src/link-budget.ts +684 -583
  90. package/src/membership-connection-gater.ts +570 -570
  91. package/src/node-local-snapshot.ts +298 -298
  92. package/src/peer-dial.ts +315 -311
  93. package/src/peer-join-backfill.ts +710 -710
  94. package/src/pending-join-runner.ts +735 -0
  95. package/src/push-fanout.ts +1 -1
  96. package/src/relay-reservation.ts +1121 -1121
  97. package/src/relay-server.ts +98 -98
  98. package/src/relay-stun.ts +58 -0
  99. package/src/seed-bootstrap.ts +1448 -1448
  100. package/src/strand-addr-protocol.ts +547 -547
  101. package/src/strand-database.ts +287 -287
  102. package/src/strand-first-sync-gate.ts +391 -391
  103. package/src/strand-formation-deadlines.ts +227 -227
  104. package/src/strand-formation-manager.ts +725 -700
  105. package/src/strand-formation-protocol.ts +1036 -802
  106. package/src/strand-formation-rejection.ts +129 -0
  107. package/src/strand-fret-addrs.ts +145 -145
  108. package/src/strand-instance-manager.ts +1789 -1786
  109. package/src/strand-membership-writer.ts +1974 -1974
  110. package/src/strand-network-state-file.ts +40 -40
  111. package/src/strand-network-state.ts +243 -243
  112. package/src/strand-revocation-enforcer.ts +654 -654
  113. package/src/strand-solicitation.ts +532 -519
  114. package/src/strand-wake-protocol.ts +401 -401
  115. package/src/types.ts +2222 -2140
  116. package/dist/diagnostics/webrtc-turn-tracker.d.ts +0 -50
  117. package/dist/diagnostics/webrtc-turn-tracker.js +0 -196
  118. package/dist/diagnostics/webrtc-turn-tracker.js.map +0 -1
  119. package/src/diagnostics/webrtc-turn-tracker.ts +0 -222
@@ -1,98 +1,98 @@
1
- /**
2
- * The circuit-relay SERVER a party-run node runs — whether it runs one, and with which
3
- * `@libp2p/circuit-relay-v2` init — resolved once from `NetworkConfig` for both node
4
- * kinds: the control node (`cadre-node.ts` → `buildControlNodeOptions`) and every strand
5
- * node (`strand-instance-manager.ts` → `buildStrandRuntime`). One function decides both
6
- * answers so the two build sites cannot drift apart.
7
- *
8
- * WHY cadre sets an init at all. libp2p's server defaults `applyDefaultLimit` to true,
9
- * which stamps every reservation with `Limit { data: 128 KiB, duration: 2 min }`; libp2p
10
- * marks each connection relayed under it "limited" and resets it once either cap is
11
- * reached, and db-p2p's database protocols refuse a limited connection outright (they do
12
- * not set `runOnLimitedConnection`). A party-run relay exists to carry that party's own
13
- * database traffic, so the cap cut off every sync and chat history forwarded through it
14
- * (gotchoices/sereus#19). The dedicated relays (`ops/docker/libp2p-infra`, and the
15
- * `startDedicatedRelay` test fixture that stands in for it) already turn the limit off;
16
- * this is the same policy for the party-run kind.
17
- */
18
-
19
- import type { CircuitRelayServerInit, ServerReservationStoreInit } from '@libp2p/circuit-relay-v2';
20
- import type { NetworkConfig, NodeProfile } from './types.js';
21
-
22
- /**
23
- * Default size of a party-run relay's reservation store, raised from libp2p's 15.
24
- *
25
- * A NAT'd machine reserves one slot for its control node and one for each strand it
26
- * serves, all on the same relay (`strand-network-config.ts` hands strand nodes the
27
- * control node's `network.relayAddrs`), so the store needs
28
- * `NAT'd machines × (1 + strands each)` slots. At libp2p's 15, three phones serving four
29
- * strands each already fill it. 128 covers, for example, ten NAT'd machines serving eleven
30
- * strands each with the unauthorized-reservation budget's 8 slots
31
- * (`MAX_UNAUTHORIZED_RELAY_RESERVATIONS`) still free on top — one party's worth, where the
32
- * dedicated relay's 500 is sized for many parties. A reservation costs the server a map
33
- * entry and a timer, not a connection's worth of state, so the headroom is cheap.
34
- */
35
- export const PARTY_RELAY_MAX_RESERVATIONS = 128;
36
-
37
- /**
38
- * How long a party-run relay holds a reservation that is not refreshed: libp2p's own
39
- * default (2 h), set EXPLICITLY so that cadre, not a libp2p constant it cannot import,
40
- * owns the value the unauthorized-reservation budget has to agree with (see
41
- * {@link ResolvedRelayServer.init}).
42
- */
43
- export const PARTY_RELAY_RESERVATION_TTL_MS = 2 * 60 * 60 * 1000;
44
-
45
- /**
46
- * The reservation-store settings every party-run relay resolves unless the caller
47
- * overrides them one key at a time through `network.relayServerInit.reservations`.
48
- * `applyDefaultLimit: false` is the point of this module — see the module doc.
49
- */
50
- const PARTY_RELAY_RESERVATION_DEFAULTS = {
51
- applyDefaultLimit: false,
52
- maxReservations: PARTY_RELAY_MAX_RESERVATIONS,
53
- reservationTtl: PARTY_RELAY_RESERVATION_TTL_MS
54
- } as const satisfies ServerReservationStoreInit;
55
-
56
- /** Reservation settings with the three keys cadre always resolves made definite. */
57
- export type ResolvedRelayReservations = ServerReservationStoreInit
58
- & Required<Pick<ServerReservationStoreInit, keyof typeof PARTY_RELAY_RESERVATION_DEFAULTS>>;
59
-
60
- /** What {@link resolveRelayServer} decides for one node. */
61
- export interface ResolvedRelayServer {
62
- /** Does this node run the relay server? Passed as db-p2p's `relay`. */
63
- enabled: boolean;
64
- /**
65
- * The server's init, passed as db-p2p's `relayServerInit` when {@link enabled}: the
66
- * party-run defaults with the caller's `network.relayServerInit` merged over them.
67
- * `reservations.reservationTtl` is always set, which is what lets the control node's
68
- * unauthorized-reservation budget expire its entries on the server's own clock.
69
- */
70
- init: CircuitRelayServerInit & { reservations: ResolvedRelayReservations };
71
- }
72
-
73
- /**
74
- * Resolve the relay server for a node built from `network` with `profile`.
75
- *
76
- * `enabled`: an explicit `network.enableRelay` wins; otherwise storage-profile nodes run
77
- * the server (they are the machines with the connectivity and uptime to be a relay).
78
- *
79
- * `init`: MERGED, not replaced. `reservations` is merged key by key over the party-run
80
- * defaults, so a caller who sets only `maxReservations` does not silently get the data
81
- * limit back; every other top-level key is taken from the caller as given. A key set to
82
- * `undefined` counts as unset, so the default beneath it holds.
83
- */
84
- export function resolveRelayServer(network: NetworkConfig | undefined, profile: NodeProfile): ResolvedRelayServer {
85
- const override = network?.relayServerInit;
86
- return {
87
- enabled: network?.enableRelay ?? (profile === 'storage'),
88
- init: {
89
- ...override,
90
- reservations: { ...PARTY_RELAY_RESERVATION_DEFAULTS, ...definedEntries(override?.reservations) }
91
- }
92
- };
93
- }
94
-
95
- /** `obj` without its `undefined`-valued keys, so spreading it never erases a default. */
96
- function definedEntries<T extends object>(obj: T | undefined): Partial<T> {
97
- return Object.fromEntries(Object.entries(obj ?? {}).filter(([, value]) => value !== undefined)) as Partial<T>;
98
- }
1
+ /**
2
+ * The circuit-relay SERVER a party-run node runs — whether it runs one, and with which
3
+ * `@libp2p/circuit-relay-v2` init — resolved once from `NetworkConfig` for both node
4
+ * kinds: the control node (`cadre-node.ts` → `buildControlNodeOptions`) and every strand
5
+ * node (`strand-instance-manager.ts` → `buildStrandRuntime`). One function decides both
6
+ * answers so the two build sites cannot drift apart.
7
+ *
8
+ * WHY cadre sets an init at all. libp2p's server defaults `applyDefaultLimit` to true,
9
+ * which stamps every reservation with `Limit { data: 128 KiB, duration: 2 min }`; libp2p
10
+ * marks each connection relayed under it "limited" and resets it once either cap is
11
+ * reached, and db-p2p's database protocols refuse a limited connection outright (they do
12
+ * not set `runOnLimitedConnection`). A party-run relay exists to carry that party's own
13
+ * database traffic, so the cap cut off every sync and chat history forwarded through it
14
+ * (gotchoices/sereus#19). The dedicated relays (`ops/docker/libp2p-infra`, and the
15
+ * `startDedicatedRelay` test fixture that stands in for it) already turn the limit off;
16
+ * this is the same policy for the party-run kind.
17
+ */
18
+
19
+ import type { CircuitRelayServerInit, ServerReservationStoreInit } from '@libp2p/circuit-relay-v2';
20
+ import type { NetworkConfig, NodeProfile } from './types.js';
21
+
22
+ /**
23
+ * Default size of a party-run relay's reservation store, raised from libp2p's 15.
24
+ *
25
+ * A NAT'd machine reserves one slot for its control node and one for each strand it
26
+ * serves, all on the same relay (`strand-network-config.ts` hands strand nodes the
27
+ * control node's `network.relayAddrs`), so the store needs
28
+ * `NAT'd machines × (1 + strands each)` slots. At libp2p's 15, three phones serving four
29
+ * strands each already fill it. 128 covers, for example, ten NAT'd machines serving eleven
30
+ * strands each with the unauthorized-reservation budget's 8 slots
31
+ * (`MAX_UNAUTHORIZED_RELAY_RESERVATIONS`) still free on top — one party's worth, where the
32
+ * dedicated relay's 500 is sized for many parties. A reservation costs the server a map
33
+ * entry and a timer, not a connection's worth of state, so the headroom is cheap.
34
+ */
35
+ export const PARTY_RELAY_MAX_RESERVATIONS = 128;
36
+
37
+ /**
38
+ * How long a party-run relay holds a reservation that is not refreshed: libp2p's own
39
+ * default (2 h), set EXPLICITLY so that cadre, not a libp2p constant it cannot import,
40
+ * owns the value the unauthorized-reservation budget has to agree with (see
41
+ * {@link ResolvedRelayServer.init}).
42
+ */
43
+ export const PARTY_RELAY_RESERVATION_TTL_MS = 2 * 60 * 60 * 1000;
44
+
45
+ /**
46
+ * The reservation-store settings every party-run relay resolves unless the caller
47
+ * overrides them one key at a time through `network.relayServerInit.reservations`.
48
+ * `applyDefaultLimit: false` is the point of this module — see the module doc.
49
+ */
50
+ const PARTY_RELAY_RESERVATION_DEFAULTS = {
51
+ applyDefaultLimit: false,
52
+ maxReservations: PARTY_RELAY_MAX_RESERVATIONS,
53
+ reservationTtl: PARTY_RELAY_RESERVATION_TTL_MS
54
+ } as const satisfies ServerReservationStoreInit;
55
+
56
+ /** Reservation settings with the three keys cadre always resolves made definite. */
57
+ export type ResolvedRelayReservations = ServerReservationStoreInit
58
+ & Required<Pick<ServerReservationStoreInit, keyof typeof PARTY_RELAY_RESERVATION_DEFAULTS>>;
59
+
60
+ /** What {@link resolveRelayServer} decides for one node. */
61
+ export interface ResolvedRelayServer {
62
+ /** Does this node run the relay server? Passed as db-p2p's `relay`. */
63
+ enabled: boolean;
64
+ /**
65
+ * The server's init, passed as db-p2p's `relayServerInit` when {@link enabled}: the
66
+ * party-run defaults with the caller's `network.relayServerInit` merged over them.
67
+ * `reservations.reservationTtl` is always set, which is what lets the control node's
68
+ * unauthorized-reservation budget expire its entries on the server's own clock.
69
+ */
70
+ init: CircuitRelayServerInit & { reservations: ResolvedRelayReservations };
71
+ }
72
+
73
+ /**
74
+ * Resolve the relay server for a node built from `network` with `profile`.
75
+ *
76
+ * `enabled`: an explicit `network.enableRelay` wins; otherwise storage-profile nodes run
77
+ * the server (they are the machines with the connectivity and uptime to be a relay).
78
+ *
79
+ * `init`: MERGED, not replaced. `reservations` is merged key by key over the party-run
80
+ * defaults, so a caller who sets only `maxReservations` does not silently get the data
81
+ * limit back; every other top-level key is taken from the caller as given. A key set to
82
+ * `undefined` counts as unset, so the default beneath it holds.
83
+ */
84
+ export function resolveRelayServer(network: NetworkConfig | undefined, profile: NodeProfile): ResolvedRelayServer {
85
+ const override = network?.relayServerInit;
86
+ return {
87
+ enabled: network?.enableRelay ?? (profile === 'storage'),
88
+ init: {
89
+ ...override,
90
+ reservations: { ...PARTY_RELAY_RESERVATION_DEFAULTS, ...definedEntries(override?.reservations) }
91
+ }
92
+ };
93
+ }
94
+
95
+ /** `obj` without its `undefined`-valued keys, so spreading it never erases a default. */
96
+ function definedEntries<T extends object>(obj: T | undefined): Partial<T> {
97
+ return Object.fromEntries(Object.entries(obj ?? {}).filter(([, value]) => value !== undefined)) as Partial<T>;
98
+ }
@@ -0,0 +1,58 @@
1
+ import debug from 'debug';
2
+ import { multiaddr } from '@multiformats/multiaddr';
3
+
4
+ const log = debug('sereus:cadre:relay-stun');
5
+
6
+ /**
7
+ * The UDP port a Sereus relay answers STUN on (`STUN_PORT` in `ops/docker/libp2p-infra`).
8
+ * Fixed rather than discovered: a relay advertises only its libp2p listeners, so a relay
9
+ * that publishes STUN elsewhere needs the app's explicit override.
10
+ */
11
+ export const RELAY_STUN_PORT = 3478;
12
+
13
+ /** A STUN-only `RTCIceServer`, declared structurally so non-DOM builds can use it. */
14
+ export interface StunServer {
15
+ urls: string;
16
+ }
17
+
18
+ /**
19
+ * Multiaddr protocols that name a host a `stun:` URL can carry. For `dnsaddr` that is the
20
+ * name the TXT records sit under, which is the relay's own host when one relay publishes
21
+ * `_dnsaddr.<host>` for itself (`ops/docs/dnsaddr.md`). A pool published under one name
22
+ * (`relay-1.…`, `relay-2.…`) needs that name to resolve to one of its relays, or the
23
+ * embedder's override.
24
+ */
25
+ const HOST_PROTOCOLS = new Set(['dns', 'dns4', 'dns6', 'dnsaddr', 'ip4', 'ip6']);
26
+
27
+ /**
28
+ * The `stun:` URL for the relay at `relayAddr`, or `undefined` when the address names no
29
+ * host. An unparsable address only logs: `CadreNode.start()` rejects the same `relayAddrs`
30
+ * entry with an error naming it.
31
+ */
32
+ export function relayStunUrl(relayAddr: string): string | undefined {
33
+ let first;
34
+ try {
35
+ first = multiaddr(relayAddr).getComponents()[0];
36
+ } catch (err) {
37
+ log('no STUN server for unparsable relay address %s: %o', relayAddr, err);
38
+ return undefined;
39
+ }
40
+ if (first?.value === undefined || !HOST_PROTOCOLS.has(first.name)) return undefined;
41
+ const host = first.name === 'ip6' ? `[${first.value}]` : first.value;
42
+ return `stun:${host}:${RELAY_STUN_PORT}`;
43
+ }
44
+
45
+ /**
46
+ * The STUN servers for a node's WebRTC transport: the comma-separated `override` URLs when
47
+ * it names any, otherwise one per distinct relay host — each Sereus relay also answers
48
+ * STUN. Empty with no relay and no override, which leaves host/LAN candidates only and
49
+ * tells no third party who this node talks to. No TURN: a WebRTC upgrade that fails stays
50
+ * on the circuit relay.
51
+ */
52
+ export function resolveStunServers(relayAddrs: readonly string[], override?: string): StunServer[] {
53
+ const overrideUrls = (override ?? '').split(',').map((s) => s.trim()).filter((s) => s.length > 0);
54
+ const urls = overrideUrls.length > 0
55
+ ? overrideUrls
56
+ : relayAddrs.map(relayStunUrl).filter((url): url is string => url !== undefined);
57
+ return [...new Set(urls)].map((url) => ({ urls: url }));
58
+ }