@serfab/cadre-core 0.13.0 → 1.1.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 +20 -6
- package/dist/bootstrap-peer-store-file.js +2 -2
- package/dist/bootstrap-peer-store.d.ts +32 -12
- package/dist/bootstrap-peer-store.js +41 -15
- package/dist/bootstrap-peer-store.js.map +1 -1
- package/dist/cadre-node.d.ts +558 -80
- package/dist/cadre-node.js +1060 -170
- 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 +13 -26
- package/dist/control-cohort.js +0 -26
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +299 -44
- package/dist/control-database.js +469 -69
- package/dist/control-database.js.map +1 -1
- package/dist/control-read-retry.d.ts +40 -11
- package/dist/control-read-retry.js +48 -15
- package/dist/control-read-retry.js.map +1 -1
- package/dist/control-retry.d.ts +58 -3
- package/dist/control-retry.js +58 -3
- package/dist/control-retry.js.map +1 -1
- package/dist/control-schema.d.ts +1 -1
- package/dist/control-schema.js +103 -19
- package/dist/control-schema.js.map +1 -1
- package/dist/control-write-retry.d.ts +80 -11
- package/dist/control-write-retry.js +176 -29
- package/dist/control-write-retry.js.map +1 -1
- package/dist/enrolled-machine-store.js +2 -2
- package/dist/index.d.ts +14 -7
- package/dist/index.js +33 -6
- package/dist/index.js.map +1 -1
- package/dist/node-local-snapshot.d.ts +15 -7
- package/dist/node-local-snapshot.js +23 -8
- package/dist/node-local-snapshot.js.map +1 -1
- package/dist/peer-dial.d.ts +143 -0
- package/dist/peer-dial.js +176 -0
- package/dist/peer-dial.js.map +1 -0
- package/dist/relay-addrs.d.ts +90 -52
- package/dist/relay-addrs.js +91 -59
- package/dist/relay-addrs.js.map +1 -1
- package/dist/relay-reservation.d.ts +71 -3
- package/dist/relay-reservation.js +116 -8
- package/dist/relay-reservation.js.map +1 -1
- package/dist/seed-bootstrap.d.ts +30 -1
- package/dist/seed-bootstrap.js +62 -17
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/storage-scope.d.ts +62 -0
- package/dist/storage-scope.js +75 -0
- package/dist/storage-scope.js.map +1 -0
- package/dist/strand-database.d.ts +34 -16
- package/dist/strand-database.js +38 -24
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-first-sync-gate.d.ts +152 -0
- package/dist/strand-first-sync-gate.js +208 -0
- package/dist/strand-first-sync-gate.js.map +1 -0
- package/dist/strand-formation-manager.d.ts +87 -5
- package/dist/strand-formation-manager.js +109 -5
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-formation-protocol.d.ts +21 -1
- package/dist/strand-formation-protocol.js +30 -0
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +243 -6
- package/dist/strand-instance-manager.js +470 -37
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-member-key.d.ts +20 -12
- package/dist/strand-member-key.js +20 -12
- package/dist/strand-member-key.js.map +1 -1
- package/dist/strand-member-registry.js +3 -0
- package/dist/strand-member-registry.js.map +1 -1
- package/dist/strand-membership-reconciler.d.ts +361 -0
- package/dist/strand-membership-reconciler.js +548 -0
- package/dist/strand-membership-reconciler.js.map +1 -0
- package/dist/strand-membership-writer.d.ts +131 -11
- package/dist/strand-membership-writer.js +396 -134
- package/dist/strand-membership-writer.js.map +1 -1
- package/dist/strand-network-config.d.ts +46 -14
- package/dist/strand-network-config.js +79 -23
- package/dist/strand-network-config.js.map +1 -1
- package/dist/strand-solicitation.d.ts +10 -1
- package/dist/strand-solicitation.js +5 -0
- package/dist/strand-solicitation.js.map +1 -1
- package/dist/strand-wake-protocol.d.ts +11 -7
- package/dist/strand-wake-protocol.js +20 -52
- package/dist/strand-wake-protocol.js.map +1 -1
- package/dist/strand-watcher.js +6 -0
- package/dist/strand-watcher.js.map +1 -1
- package/dist/timed-step.d.ts +12 -0
- package/dist/timed-step.js +28 -0
- package/dist/timed-step.js.map +1 -0
- package/dist/timeout-scheduler.d.ts +19 -0
- package/dist/timeout-scheduler.js +22 -0
- package/dist/timeout-scheduler.js.map +1 -0
- package/dist/types.d.ts +262 -30
- package/dist/types.js.map +1 -1
- package/package.json +8 -8
- package/src/bootstrap-peer-store-file.ts +2 -2
- package/src/bootstrap-peer-store.ts +69 -27
- package/src/cadre-node.ts +7301 -6353
- package/src/control-authorization.ts +2 -1
- package/src/control-cohort.ts +122 -130
- package/src/control-database.ts +537 -69
- package/src/control-read-retry.ts +202 -167
- package/src/control-retry.ts +297 -190
- package/src/control-schema.ts +103 -19
- package/src/control-write-retry.ts +533 -383
- package/src/enrolled-machine-store.ts +2 -2
- package/src/index.ts +83 -6
- package/src/node-local-snapshot.ts +25 -8
- package/src/peer-dial.ts +215 -0
- package/src/relay-addrs.ts +458 -436
- package/src/relay-reservation.ts +951 -828
- package/src/seed-bootstrap.ts +76 -18
- package/src/storage-scope.ts +78 -0
- package/src/strand-database.ts +50 -27
- package/src/strand-first-sync-gate.ts +258 -0
- package/src/strand-formation-manager.ts +140 -5
- package/src/strand-formation-protocol.ts +43 -1
- package/src/strand-instance-manager.ts +577 -37
- package/src/strand-member-key.ts +20 -12
- package/src/strand-member-registry.ts +3 -0
- package/src/strand-membership-reconciler.ts +680 -0
- package/src/strand-membership-writer.ts +527 -189
- package/src/strand-network-config.ts +102 -24
- package/src/strand-solicitation.ts +16 -1
- package/src/strand-wake-protocol.ts +21 -66
- package/src/strand-watcher.ts +6 -0
- package/src/timed-step.ts +28 -0
- package/src/timeout-scheduler.ts +28 -0
- package/src/types.ts +1789 -1554
package/dist/relay-addrs.d.ts
CHANGED
|
@@ -3,34 +3,47 @@
|
|
|
3
3
|
* setting — into the listen entry a node needs before it can hold a circuit-relay
|
|
4
4
|
* slot, and validate the operator's entries while doing it.
|
|
5
5
|
*
|
|
6
|
-
* `@libp2p/circuit-relay-v2`'s listener branches on the SHAPE of the listen addr
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* |
|
|
11
|
-
* |
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* reservation
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
6
|
+
* `@libp2p/circuit-relay-v2`'s listener branches on the SHAPE of the listen addr:
|
|
7
|
+
*
|
|
8
|
+
* | listen entry | libp2p behaviour |
|
|
9
|
+
* | ----------------------------------- | --------------------------------------------------------------- |
|
|
10
|
+
* | `<relayAddr>/p2p-circuit` | CONFIGURED: dial that relay from inside `listen()` and reserve, or throw |
|
|
11
|
+
* | bare `/p2p-circuit` | SEARCH: register a pending reservation; open no connection |
|
|
12
|
+
*
|
|
13
|
+
* Every cadre-built node takes the SEARCH shape, and something explicit drives the
|
|
14
|
+
* reservation afterwards (`relay-reservation.ts`):
|
|
15
|
+
*
|
|
16
|
+
* - The CONTROL node resolves `relayAddrs` here to ONE bare entry (whichever relay
|
|
17
|
+
* answers first fills it) and `CadreNode.start()` drives the reservation once the
|
|
18
|
+
* control database is up. That ordering is the point: a configured circuit
|
|
19
|
+
* listener dials the relay from inside `libp2p.start()`, so the control database
|
|
20
|
+
* was being built while a sibling relay was already in this node's cohort — and a
|
|
21
|
+
* sibling that has not yet replicated this node's `CadrePeer` row correctly refuses
|
|
22
|
+
* its control-DB streams, which killed `start()` outright. See `cadre-node.ts` →
|
|
23
|
+
* `start()`.
|
|
24
|
+
* - Each STRAND node resolves the same `relayAddrs` to one bare entry PER relay and
|
|
25
|
+
* runs one reservation supervisor per relay (`strand-network-config.ts`,
|
|
26
|
+
* `strand-instance-manager.ts`).
|
|
27
|
+
*
|
|
28
|
+
* The CONFIGURED shape is deliberately not producible from a `NetworkConfig` any
|
|
29
|
+
* more: it loses its address and never recovers when the relay restarts, when either
|
|
30
|
+
* side hangs up, and — with no network event at all — on libp2p's own reservation
|
|
31
|
+
* refresh, which removes the reservation before re-creating it while a configured
|
|
32
|
+
* listener republishes only from inside its own `listen()`. A hand-written
|
|
33
|
+
* `<relay>/p2p-circuit` entry in `network.listenAddrs` is rejected on the control
|
|
34
|
+
* node ({@link resolveListenAddrs}) and folded into the supervised relay set on a
|
|
35
|
+
* strand node.
|
|
36
|
+
*
|
|
37
|
+
* `network.relayAddrs` is FAIL-FAST for the operator by default: a malformed entry
|
|
38
|
+
* throws here at config resolution regardless of posture, and on the control node a
|
|
39
|
+
* first reservation attempt that does not land throws out of `start()`
|
|
40
|
+
* (`RelayReservationFailedError`). Naming a relay that is down still means the control
|
|
41
|
+
* node does not come up. `network.requireRelay: false` softens the second half only —
|
|
42
|
+
* for a node that must still boot with no network (a phone, a browser tab) — logging
|
|
43
|
+
* the failed attempt instead of throwing and leaving the retry supervisor running in
|
|
44
|
+
* the background; the malformed-entry check is unaffected. A strand node is
|
|
45
|
+
* fail-SOFT regardless of `requireRelay` (its supervisor keeps trying while the
|
|
46
|
+
* strand serves).
|
|
34
47
|
*
|
|
35
48
|
* The bare `/p2p-circuit` search listener cannot open a connection on its own:
|
|
36
49
|
* libp2p fills a pending reservation from `RelayDiscovery`, which nominates a peer
|
|
@@ -42,18 +55,25 @@
|
|
|
42
55
|
import type { NetworkConfig } from './types.js';
|
|
43
56
|
import type { RelayReservationState } from './relay-reservation.js';
|
|
44
57
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
58
|
+
* The direct listener a node keeps when it configures `relayAddrs` but no
|
|
59
|
+
* `listenAddrs`. Without this, naming a relay would silently REPLACE the node's
|
|
60
|
+
* direct TCP listener with a circuit-only one. Shared with the strand-node
|
|
61
|
+
* derivation (`strand-network-config.ts`), which applies the same rule.
|
|
62
|
+
*
|
|
63
|
+
* NOTE: mirrors `createLibp2pNode`'s own default listen addr in
|
|
64
|
+
* `../optimystic/packages/db-p2p/src/libp2p-node.ts` (`/ip4/0.0.0.0/tcp/<port>`,
|
|
65
|
+
* and `CadreNode` passes `port: 0`) — that file is the source of truth. If it
|
|
66
|
+
* ever changes its default, this constant has to follow.
|
|
49
67
|
*/
|
|
50
|
-
export declare const
|
|
68
|
+
export declare const DEFAULT_DIRECT_LISTEN_ADDR = "/ip4/0.0.0.0/tcp/0";
|
|
51
69
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
70
|
+
* libp2p's relay SEARCH listener: registers one pending reservation and dials
|
|
71
|
+
* nothing. The pending reservation is per-LISTENER, not per-relay: the control
|
|
72
|
+
* node binds one entry for its whole relay list (`relay-reservation.ts` fills it
|
|
73
|
+
* with whichever relay answers first), a strand node binds one entry per relay so
|
|
74
|
+
* each relay's supervisor has a slot of its own to fill.
|
|
55
75
|
*/
|
|
56
|
-
export
|
|
76
|
+
export declare const RELAY_SEARCH_LISTEN_ADDR = "/p2p-circuit";
|
|
57
77
|
/**
|
|
58
78
|
* The circuit-listen multiaddr for each configured relay: `<relayAddr>/p2p-circuit`.
|
|
59
79
|
* An entry that already carries `/p2p-circuit` is passed through unchanged.
|
|
@@ -61,22 +81,31 @@ export type RelayListenRoute = 'configured' | 'search';
|
|
|
61
81
|
* operator configuration, so a typo must fail loudly at start rather than
|
|
62
82
|
* silently costing the node its reachability.
|
|
63
83
|
*
|
|
84
|
+
* No node LISTENS on these any more (see the module doc); they are the validated
|
|
85
|
+
* form of `relayAddrs`, read by `CadreNode.circuitRelayTargets` for the delegate
|
|
86
|
+
* announce and by `strand-network-config.ts` for the per-relay supervisor list.
|
|
87
|
+
*
|
|
64
88
|
* Deliberately unlike `extractCircuitRelayTargets` (`delegate-admission.ts`), which
|
|
65
89
|
* SKIPS a bad entry: that one reads addresses discovered at runtime from peers, where
|
|
66
90
|
* one bad entry must not be fatal.
|
|
67
91
|
*/
|
|
68
92
|
export declare function relayCircuitAddrs(relayAddrs: readonly string[]): string[];
|
|
69
93
|
/**
|
|
70
|
-
* The listen multiaddrs
|
|
71
|
-
*
|
|
72
|
-
* entries first). Returns `undefined` when neither field
|
|
73
|
-
* omitting `listenAddrs` and inherit db-p2p's default.
|
|
74
|
-
*
|
|
75
|
-
* Every `relayAddrs` entry is validated
|
|
76
|
-
*
|
|
77
|
-
*
|
|
94
|
+
* The listen multiaddrs the CONTROL node binds: configured `listenAddrs` plus ONE
|
|
95
|
+
* bare `/p2p-circuit` search entry when `relayAddrs` names any relay, deduplicated,
|
|
96
|
+
* order-stable (configured entries first). Returns `undefined` when neither field
|
|
97
|
+
* is set, so callers keep omitting `listenAddrs` and inherit db-p2p's default.
|
|
98
|
+
*
|
|
99
|
+
* Every `relayAddrs` entry is validated even though the search entry discards the
|
|
100
|
+
* resolved circuit addrs — an operator typo must fail at config resolution. A
|
|
101
|
+
* hand-written `<relay>/p2p-circuit` entry in `listenAddrs` is rejected outright
|
|
102
|
+
* (see {@link rejectConfiguredCircuitListenAddrs}).
|
|
103
|
+
*
|
|
104
|
+
* Strand nodes derive their own listen set from the same config in
|
|
105
|
+
* `strand-network-config.ts` (`strandNodeAddrs`) — one search entry per relay,
|
|
106
|
+
* fixed direct ports made ephemeral.
|
|
78
107
|
*/
|
|
79
|
-
export declare function resolveListenAddrs(network: NetworkConfig | undefined
|
|
108
|
+
export declare function resolveListenAddrs(network: NetworkConfig | undefined): string[] | undefined;
|
|
80
109
|
/**
|
|
81
110
|
* The transport-derived `createLibp2pNode` options a resolved listen set implies —
|
|
82
111
|
* see {@link resolveTransportOptions}.
|
|
@@ -100,7 +129,8 @@ export interface ListenTransportOptions {
|
|
|
100
129
|
* missing WebSocket listener is never reported. Two halves of `NetworkConfig` that have
|
|
101
130
|
* to agree (`listenAddrs`, and the transports the node will actually have) were never
|
|
102
131
|
* checked against each other; this is that check, and it covers both node kinds because
|
|
103
|
-
* both
|
|
132
|
+
* both derive their listen set from the same config here ({@link resolveListenAddrs}) or
|
|
133
|
+
* in `strand-network-config.ts`, and both call this on the result.
|
|
104
134
|
*
|
|
105
135
|
* The two transport classes are handled differently, on purpose:
|
|
106
136
|
*
|
|
@@ -134,8 +164,8 @@ export interface ListenTransportOptions {
|
|
|
134
164
|
* rather than merely unlikely.
|
|
135
165
|
*
|
|
136
166
|
* @param listenAddrs the ALREADY-resolved listen set — the output of
|
|
137
|
-
* {@link resolveListenAddrs} after any per-node rewriting —
|
|
138
|
-
* this node will actually bind.
|
|
167
|
+
* {@link resolveListenAddrs}, or the strand derivation, after any per-node rewriting —
|
|
168
|
+
* so the check reads what this node will actually bind.
|
|
139
169
|
* @throws {UnbindableListenAddressError} when a listen entry names a transport outside
|
|
140
170
|
* {tcp, ws/wss, p2p-circuit}.
|
|
141
171
|
*/
|
|
@@ -152,10 +182,12 @@ export declare class UnbindableListenAddressError extends Error {
|
|
|
152
182
|
}
|
|
153
183
|
/**
|
|
154
184
|
* Thrown out of `CadreNode.start()` when the boot-path reservation drive for
|
|
155
|
-
* `network.relayAddrs` produces no `/p2p-circuit` address on its FIRST attempt
|
|
185
|
+
* `network.relayAddrs` produces no `/p2p-circuit` address on its FIRST attempt,
|
|
186
|
+
* UNLESS `network.requireRelay` is explicitly `false` (see `driveControlRelayReservation`,
|
|
187
|
+
* which logs and returns instead on that posture).
|
|
156
188
|
*
|
|
157
|
-
* This is what keeps `network.relayAddrs` fail-fast now that the control node
|
|
158
|
-
*
|
|
189
|
+
* This is what keeps `network.relayAddrs` fail-fast by default now that the control node
|
|
190
|
+
* listens on the bare search entry: libp2p's own `UnsupportedListenAddressesError` used
|
|
159
191
|
* to abort start from inside `listen()`, and an operator who names a relay is
|
|
160
192
|
* telling the node it has no other reachability — coming up undialable is worse
|
|
161
193
|
* than not coming up. Names the relays and the reservation's own reason, both of
|
|
@@ -166,3 +198,9 @@ export declare class RelayReservationFailedError extends Error {
|
|
|
166
198
|
readonly state: RelayReservationState;
|
|
167
199
|
constructor(relayAddrs: readonly string[], state: RelayReservationState);
|
|
168
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* `<something>/p2p-circuit` — the configured shape, as opposed to the bare search
|
|
203
|
+
* addr. An unparsable entry is `false`: libp2p reports a bad listen addr itself,
|
|
204
|
+
* and this helper only ever ADDS a denial (or, on the strand path, a supervisor).
|
|
205
|
+
*/
|
|
206
|
+
export declare function isConfiguredCircuitListenAddr(listenAddr: string): boolean;
|
package/dist/relay-addrs.js
CHANGED
|
@@ -3,34 +3,47 @@
|
|
|
3
3
|
* setting — into the listen entry a node needs before it can hold a circuit-relay
|
|
4
4
|
* slot, and validate the operator's entries while doing it.
|
|
5
5
|
*
|
|
6
|
-
* `@libp2p/circuit-relay-v2`'s listener branches on the SHAPE of the listen addr
|
|
7
|
-
* and the same `relayAddrs` list resolves to either shape depending on WHO is
|
|
8
|
-
* listening ({@link RelayListenRoute}):
|
|
6
|
+
* `@libp2p/circuit-relay-v2`'s listener branches on the SHAPE of the listen addr:
|
|
9
7
|
*
|
|
10
|
-
* |
|
|
11
|
-
* |
|
|
12
|
-
* |
|
|
13
|
-
* |
|
|
8
|
+
* | listen entry | libp2p behaviour |
|
|
9
|
+
* | ----------------------------------- | --------------------------------------------------------------- |
|
|
10
|
+
* | `<relayAddr>/p2p-circuit` | CONFIGURED: dial that relay from inside `listen()` and reserve, or throw |
|
|
11
|
+
* | bare `/p2p-circuit` | SEARCH: register a pending reservation; open no connection |
|
|
14
12
|
*
|
|
15
|
-
*
|
|
16
|
-
* reservation
|
|
17
|
-
* That ordering is the point: a configured circuit listener dials the relay from
|
|
18
|
-
* inside `libp2p.start()`, so the control database was being built while a sibling
|
|
19
|
-
* relay was already in this node's cohort — and a sibling that has not yet
|
|
20
|
-
* replicated this node's `CadrePeer` row correctly refuses its control-DB streams,
|
|
21
|
-
* which killed `start()` outright. See `cadre-node.ts` → `start()`.
|
|
13
|
+
* Every cadre-built node takes the SEARCH shape, and something explicit drives the
|
|
14
|
+
* reservation afterwards (`relay-reservation.ts`):
|
|
22
15
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
16
|
+
* - The CONTROL node resolves `relayAddrs` here to ONE bare entry (whichever relay
|
|
17
|
+
* answers first fills it) and `CadreNode.start()` drives the reservation once the
|
|
18
|
+
* control database is up. That ordering is the point: a configured circuit
|
|
19
|
+
* listener dials the relay from inside `libp2p.start()`, so the control database
|
|
20
|
+
* was being built while a sibling relay was already in this node's cohort — and a
|
|
21
|
+
* sibling that has not yet replicated this node's `CadrePeer` row correctly refuses
|
|
22
|
+
* its control-DB streams, which killed `start()` outright. See `cadre-node.ts` →
|
|
23
|
+
* `start()`.
|
|
24
|
+
* - Each STRAND node resolves the same `relayAddrs` to one bare entry PER relay and
|
|
25
|
+
* runs one reservation supervisor per relay (`strand-network-config.ts`,
|
|
26
|
+
* `strand-instance-manager.ts`).
|
|
28
27
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
28
|
+
* The CONFIGURED shape is deliberately not producible from a `NetworkConfig` any
|
|
29
|
+
* more: it loses its address and never recovers when the relay restarts, when either
|
|
30
|
+
* side hangs up, and — with no network event at all — on libp2p's own reservation
|
|
31
|
+
* refresh, which removes the reservation before re-creating it while a configured
|
|
32
|
+
* listener republishes only from inside its own `listen()`. A hand-written
|
|
33
|
+
* `<relay>/p2p-circuit` entry in `network.listenAddrs` is rejected on the control
|
|
34
|
+
* node ({@link resolveListenAddrs}) and folded into the supervised relay set on a
|
|
35
|
+
* strand node.
|
|
36
|
+
*
|
|
37
|
+
* `network.relayAddrs` is FAIL-FAST for the operator by default: a malformed entry
|
|
38
|
+
* throws here at config resolution regardless of posture, and on the control node a
|
|
39
|
+
* first reservation attempt that does not land throws out of `start()`
|
|
40
|
+
* (`RelayReservationFailedError`). Naming a relay that is down still means the control
|
|
41
|
+
* node does not come up. `network.requireRelay: false` softens the second half only —
|
|
42
|
+
* for a node that must still boot with no network (a phone, a browser tab) — logging
|
|
43
|
+
* the failed attempt instead of throwing and leaving the retry supervisor running in
|
|
44
|
+
* the background; the malformed-entry check is unaffected. A strand node is
|
|
45
|
+
* fail-SOFT regardless of `requireRelay` (its supervisor keeps trying while the
|
|
46
|
+
* strand serves).
|
|
34
47
|
*
|
|
35
48
|
* The bare `/p2p-circuit` search listener cannot open a connection on its own:
|
|
36
49
|
* libp2p fills a pending reservation from `RelayDiscovery`, which nominates a peer
|
|
@@ -44,19 +57,21 @@ import { circuitRelayTargetOrThrow } from './delegate-admission.js';
|
|
|
44
57
|
/**
|
|
45
58
|
* The direct listener a node keeps when it configures `relayAddrs` but no
|
|
46
59
|
* `listenAddrs`. Without this, naming a relay would silently REPLACE the node's
|
|
47
|
-
* direct TCP listener with a circuit-only one.
|
|
60
|
+
* direct TCP listener with a circuit-only one. Shared with the strand-node
|
|
61
|
+
* derivation (`strand-network-config.ts`), which applies the same rule.
|
|
48
62
|
*
|
|
49
63
|
* NOTE: mirrors `createLibp2pNode`'s own default listen addr in
|
|
50
64
|
* `../optimystic/packages/db-p2p/src/libp2p-node.ts` (`/ip4/0.0.0.0/tcp/<port>`,
|
|
51
65
|
* and `CadreNode` passes `port: 0`) — that file is the source of truth. If it
|
|
52
66
|
* ever changes its default, this constant has to follow.
|
|
53
67
|
*/
|
|
54
|
-
const DEFAULT_DIRECT_LISTEN_ADDR = '/ip4/0.0.0.0/tcp/0';
|
|
68
|
+
export const DEFAULT_DIRECT_LISTEN_ADDR = '/ip4/0.0.0.0/tcp/0';
|
|
55
69
|
/**
|
|
56
70
|
* libp2p's relay SEARCH listener: registers one pending reservation and dials
|
|
57
|
-
* nothing.
|
|
58
|
-
*
|
|
59
|
-
* relay answers first
|
|
71
|
+
* nothing. The pending reservation is per-LISTENER, not per-relay: the control
|
|
72
|
+
* node binds one entry for its whole relay list (`relay-reservation.ts` fills it
|
|
73
|
+
* with whichever relay answers first), a strand node binds one entry per relay so
|
|
74
|
+
* each relay's supervisor has a slot of its own to fill.
|
|
60
75
|
*/
|
|
61
76
|
export const RELAY_SEARCH_LISTEN_ADDR = '/p2p-circuit';
|
|
62
77
|
/**
|
|
@@ -66,6 +81,10 @@ export const RELAY_SEARCH_LISTEN_ADDR = '/p2p-circuit';
|
|
|
66
81
|
* operator configuration, so a typo must fail loudly at start rather than
|
|
67
82
|
* silently costing the node its reachability.
|
|
68
83
|
*
|
|
84
|
+
* No node LISTENS on these any more (see the module doc); they are the validated
|
|
85
|
+
* form of `relayAddrs`, read by `CadreNode.circuitRelayTargets` for the delegate
|
|
86
|
+
* announce and by `strand-network-config.ts` for the per-relay supervisor list.
|
|
87
|
+
*
|
|
69
88
|
* Deliberately unlike `extractCircuitRelayTargets` (`delegate-admission.ts`), which
|
|
70
89
|
* SKIPS a bad entry: that one reads addresses discovered at runtime from peers, where
|
|
71
90
|
* one bad entry must not be fatal.
|
|
@@ -74,24 +93,26 @@ export function relayCircuitAddrs(relayAddrs) {
|
|
|
74
93
|
return dedupe(relayAddrs.map(circuitListenAddr));
|
|
75
94
|
}
|
|
76
95
|
/**
|
|
77
|
-
* The listen multiaddrs
|
|
78
|
-
*
|
|
79
|
-
* entries first). Returns `undefined` when neither field
|
|
80
|
-
* omitting `listenAddrs` and inherit db-p2p's default.
|
|
96
|
+
* The listen multiaddrs the CONTROL node binds: configured `listenAddrs` plus ONE
|
|
97
|
+
* bare `/p2p-circuit` search entry when `relayAddrs` names any relay, deduplicated,
|
|
98
|
+
* order-stable (configured entries first). Returns `undefined` when neither field
|
|
99
|
+
* is set, so callers keep omitting `listenAddrs` and inherit db-p2p's default.
|
|
100
|
+
*
|
|
101
|
+
* Every `relayAddrs` entry is validated even though the search entry discards the
|
|
102
|
+
* resolved circuit addrs — an operator typo must fail at config resolution. A
|
|
103
|
+
* hand-written `<relay>/p2p-circuit` entry in `listenAddrs` is rejected outright
|
|
104
|
+
* (see {@link rejectConfiguredCircuitListenAddrs}).
|
|
81
105
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
106
|
+
* Strand nodes derive their own listen set from the same config in
|
|
107
|
+
* `strand-network-config.ts` (`strandNodeAddrs`) — one search entry per relay,
|
|
108
|
+
* fixed direct ports made ephemeral.
|
|
85
109
|
*/
|
|
86
|
-
export function resolveListenAddrs(network
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
: configured;
|
|
110
|
+
export function resolveListenAddrs(network) {
|
|
111
|
+
const relayEntries = relayCircuitAddrs(network?.relayAddrs ?? []).length > 0
|
|
112
|
+
? [RELAY_SEARCH_LISTEN_ADDR]
|
|
113
|
+
: [];
|
|
91
114
|
const listenAddrs = network?.listenAddrs;
|
|
92
|
-
|
|
93
|
-
rejectConfiguredCircuitListenAddrs(listenAddrs ?? []);
|
|
94
|
-
}
|
|
115
|
+
rejectConfiguredCircuitListenAddrs(listenAddrs ?? []);
|
|
95
116
|
if (!listenAddrs && relayEntries.length === 0) {
|
|
96
117
|
return undefined;
|
|
97
118
|
}
|
|
@@ -141,7 +162,8 @@ const WS_TRANSPORT_SWITCH_PORT = 0;
|
|
|
141
162
|
* missing WebSocket listener is never reported. Two halves of `NetworkConfig` that have
|
|
142
163
|
* to agree (`listenAddrs`, and the transports the node will actually have) were never
|
|
143
164
|
* checked against each other; this is that check, and it covers both node kinds because
|
|
144
|
-
* both
|
|
165
|
+
* both derive their listen set from the same config here ({@link resolveListenAddrs}) or
|
|
166
|
+
* in `strand-network-config.ts`, and both call this on the result.
|
|
145
167
|
*
|
|
146
168
|
* The two transport classes are handled differently, on purpose:
|
|
147
169
|
*
|
|
@@ -175,8 +197,8 @@ const WS_TRANSPORT_SWITCH_PORT = 0;
|
|
|
175
197
|
* rather than merely unlikely.
|
|
176
198
|
*
|
|
177
199
|
* @param listenAddrs the ALREADY-resolved listen set — the output of
|
|
178
|
-
* {@link resolveListenAddrs} after any per-node rewriting —
|
|
179
|
-
* this node will actually bind.
|
|
200
|
+
* {@link resolveListenAddrs}, or the strand derivation, after any per-node rewriting —
|
|
201
|
+
* so the check reads what this node will actually bind.
|
|
180
202
|
* @throws {UnbindableListenAddressError} when a listen entry names a transport outside
|
|
181
203
|
* {tcp, ws/wss, p2p-circuit}.
|
|
182
204
|
*/
|
|
@@ -297,10 +319,12 @@ const TRANSPORT_PACKAGES = {
|
|
|
297
319
|
};
|
|
298
320
|
/**
|
|
299
321
|
* Thrown out of `CadreNode.start()` when the boot-path reservation drive for
|
|
300
|
-
* `network.relayAddrs` produces no `/p2p-circuit` address on its FIRST attempt
|
|
322
|
+
* `network.relayAddrs` produces no `/p2p-circuit` address on its FIRST attempt,
|
|
323
|
+
* UNLESS `network.requireRelay` is explicitly `false` (see `driveControlRelayReservation`,
|
|
324
|
+
* which logs and returns instead on that posture).
|
|
301
325
|
*
|
|
302
|
-
* This is what keeps `network.relayAddrs` fail-fast now that the control node
|
|
303
|
-
*
|
|
326
|
+
* This is what keeps `network.relayAddrs` fail-fast by default now that the control node
|
|
327
|
+
* listens on the bare search entry: libp2p's own `UnsupportedListenAddressesError` used
|
|
304
328
|
* to abort start from inside `listen()`, and an operator who names a relay is
|
|
305
329
|
* telling the node it has no other reachability — coming up undialable is worse
|
|
306
330
|
* than not coming up. Names the relays and the reservation's own reason, both of
|
|
@@ -317,7 +341,7 @@ export class RelayReservationFailedError extends Error {
|
|
|
317
341
|
}
|
|
318
342
|
/**
|
|
319
343
|
* A hand-written `<relay>/p2p-circuit` entry in `network.listenAddrs` is the
|
|
320
|
-
* CONFIGURED listener shape, and it is unusable on the
|
|
344
|
+
* CONFIGURED listener shape, and it is unusable on the control node: libp2p
|
|
321
345
|
* dials that relay from inside `listen()`, the bring-up quiet period
|
|
322
346
|
* (`membership-connection-gater.ts`) denies exactly that dial, `listen()` fails, and
|
|
323
347
|
* the transport manager's default `FATAL_ALL` aborts `libp2p.start()`. The operator
|
|
@@ -325,7 +349,10 @@ export class RelayReservationFailedError extends Error {
|
|
|
325
349
|
* naming the cause, so name it here instead.
|
|
326
350
|
*
|
|
327
351
|
* `network.relayAddrs` is the exact replacement — same relay, and the reservation is
|
|
328
|
-
* driven after bring-up, which is the whole point of the
|
|
352
|
+
* driven after bring-up, which is the whole point of the search shape. (A strand node
|
|
353
|
+
* built directly from such a config folds the entry into its supervised relay set
|
|
354
|
+
* instead — `strand-network-config.ts` — but the control node's resolution runs first
|
|
355
|
+
* on every machine, so in production the entry never reaches a strand node.)
|
|
329
356
|
*/
|
|
330
357
|
function rejectConfiguredCircuitListenAddrs(listenAddrs) {
|
|
331
358
|
const configured = listenAddrs.filter(isConfiguredCircuitListenAddr);
|
|
@@ -336,8 +363,12 @@ function rejectConfiguredCircuitListenAddrs(listenAddrs) {
|
|
|
336
363
|
'connections. Move the relay to network.relayAddrs, which reserves after bring-up.');
|
|
337
364
|
}
|
|
338
365
|
}
|
|
339
|
-
/**
|
|
340
|
-
|
|
366
|
+
/**
|
|
367
|
+
* `<something>/p2p-circuit` — the configured shape, as opposed to the bare search
|
|
368
|
+
* addr. An unparsable entry is `false`: libp2p reports a bad listen addr itself,
|
|
369
|
+
* and this helper only ever ADDS a denial (or, on the strand path, a supervisor).
|
|
370
|
+
*/
|
|
371
|
+
export function isConfiguredCircuitListenAddr(listenAddr) {
|
|
341
372
|
if (listenAddr === RELAY_SEARCH_LISTEN_ADDR) {
|
|
342
373
|
return false;
|
|
343
374
|
}
|
|
@@ -351,13 +382,14 @@ function isConfiguredCircuitListenAddr(listenAddr) {
|
|
|
351
382
|
}
|
|
352
383
|
}
|
|
353
384
|
/**
|
|
354
|
-
* One entry of {@link relayCircuitAddrs}:
|
|
385
|
+
* One entry of {@link relayCircuitAddrs}: this relay's addr in the configured shape.
|
|
355
386
|
*
|
|
356
387
|
* NOTE: an entry carrying components AFTER `/p2p-circuit` — a full relayed DIAL addr,
|
|
357
|
-
* `…/p2p/<relay>/p2p-circuit/p2p/<someone else>` — is passed through as written
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
* out to be
|
|
388
|
+
* `…/p2p/<relay>/p2p-circuit/p2p/<someone else>` — is passed through as written. It
|
|
389
|
+
* validates (the relay it names is real), and every consumer reads only the relay
|
|
390
|
+
* peer id and the dial prefix before `/p2p-circuit`, so the tail is ignored rather
|
|
391
|
+
* than harmful; if that paste turns out to be common, truncate at the `/p2p-circuit`
|
|
392
|
+
* component here so the resolved list is canonical.
|
|
361
393
|
*/
|
|
362
394
|
function circuitListenAddr(relayAddr) {
|
|
363
395
|
const alreadyCircuit = parseComponents(relayAddr).some((c) => c.name === 'p2p-circuit');
|
package/dist/relay-addrs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-addrs.js","sourceRoot":"","sources":["../src/relay-addrs.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"relay-addrs.js","sourceRoot":"","sources":["../src/relay-addrs.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* module. It is **fail-soft by construction**: nothing here throws.
|
|
17
17
|
*
|
|
18
18
|
* EVERY cadre CONTROL node now takes that route — `network.relayAddrs` resolves
|
|
19
|
-
* to the bare search entry (`relay-addrs.ts` → `
|
|
19
|
+
* to the bare search entry (`relay-addrs.ts` → `resolveListenAddrs`) and
|
|
20
20
|
* `CadreNode.start()` drives this module once the control database is up. The
|
|
21
21
|
* configured shape would dial the relay from inside `libp2p.start()`, putting a
|
|
22
22
|
* sibling in this node's cohort before its own control database existed, and a
|
|
@@ -28,8 +28,30 @@
|
|
|
28
28
|
* a `RelayReservationFailedError` out of `start()` when the first attempt lands
|
|
29
29
|
* nothing (fail-fast, the operator asked for this relay), while a node that calls
|
|
30
30
|
* `CadreNode.reserveRelays()` itself gets a non-`reserved` status and stays up
|
|
31
|
-
* (fail-soft, the browser-tab posture).
|
|
32
|
-
*
|
|
31
|
+
* (fail-soft, the browser-tab posture). `network.requireRelay: false` gives a
|
|
32
|
+
* CONFIGURED relay the fail-soft posture too, for a node that must boot with no
|
|
33
|
+
* network at all — same drive, same supervisor, only the throw is dropped.
|
|
34
|
+
* Configuring both is now redundant, not fatal.
|
|
35
|
+
*
|
|
36
|
+
* STRAND nodes take the same route, ONE supervisor PER RELAY
|
|
37
|
+
* (`strand-instance-manager.ts` → `buildStrandRuntime`): each configured relay
|
|
38
|
+
* gets its own bare `/p2p-circuit` listener (`strand-network-config.ts`) and its
|
|
39
|
+
* own {@link superviseRelayReservation} over just that relay's addr, fail-soft
|
|
40
|
+
* like the browser tab. The configured shape they used to take
|
|
41
|
+
* (`<relay>/p2p-circuit`, reserved from inside `libp2p.start()`) is gone from
|
|
42
|
+
* cadre-built nodes entirely, because it loses its address and never recovers in
|
|
43
|
+
* three situations — the relay restarts, either side hangs up the relay
|
|
44
|
+
* connection, and libp2p's OWN reservation refresh (which removes the reservation
|
|
45
|
+
* before re-creating it, and a configured listener republishes only from inside
|
|
46
|
+
* its own `listen()`). The last one needs no network event at all: at the relay
|
|
47
|
+
* default TTL of two hours every configured-shape node went undialable ~1 h 55 min
|
|
48
|
+
* after reserving. A search listener republishes on `relay:created-reservation`
|
|
49
|
+
* for its own pending id, which the refresh re-queues, and the supervisor covers
|
|
50
|
+
* the other two.
|
|
51
|
+
*
|
|
52
|
+
* Because a strand node runs one supervisor per relay, "is the reservation held"
|
|
53
|
+
* is asked PER RELAY ({@link circuitMultiaddrsVia}): relay X's supervisor must not
|
|
54
|
+
* be satisfied by relay Y's circuit addr, or it would never re-drive X.
|
|
33
55
|
*
|
|
34
56
|
* ⚠️ WHY THE RESERVATION IS REQUESTED EXPLICITLY RATHER THAN LEFT TO DISCOVERY.
|
|
35
57
|
* libp2p fills a search listener's pending reservation from `RelayDiscovery`,
|
|
@@ -60,6 +82,19 @@
|
|
|
60
82
|
* so {@link driveRelayReservation} stays a single-shot primitive and
|
|
61
83
|
* {@link superviseRelayReservation} owns the retry cadence on top of it. That
|
|
62
84
|
* loop is what makes a lost reservation recover without a page reload.
|
|
85
|
+
*
|
|
86
|
+
* One measured refinement to "out of reach": libp2p 3.1.3's `connection.newStream`
|
|
87
|
+
* records every protocol OUR OWN outbound stream negotiated into the peer store, so
|
|
88
|
+
* the explicit reservation request writes the relay-hop protocol id against the
|
|
89
|
+
* relay itself. From then on, discovery CAN refill a freed slot on its own — but
|
|
90
|
+
* only when the relay answers at that moment (a hangup while the relay stays up;
|
|
91
|
+
* measured at ~25 ms). A relay that is down when the slot frees fails that one
|
|
92
|
+
* discovery attempt, which poisons the store's `relayFilter` against it, and
|
|
93
|
+
* nothing in libp2p tries again: a relay RESTART still needs the supervisor, which
|
|
94
|
+
* un-poisons the filter before every attempt ({@link clearRelayFilterEntry}). Two
|
|
95
|
+
* mechanisms in production, one guarantee; the specs disable discovery's half
|
|
96
|
+
* (`forgetRelayProtocol` in `relay-reservation.spec.ts`) so they prove the
|
|
97
|
+
* supervisor's.
|
|
63
98
|
*/
|
|
64
99
|
import type { Libp2p, PeerId } from '@libp2p/interface';
|
|
65
100
|
/**
|
|
@@ -133,6 +168,20 @@ export interface CircuitRelayTransportLike {
|
|
|
133
168
|
* live rather than a stale snapshot.
|
|
134
169
|
*/
|
|
135
170
|
export declare function circuitMultiaddrs(node: Libp2p): string[];
|
|
171
|
+
/**
|
|
172
|
+
* The node's `/p2p-circuit` multiaddrs THROUGH the relays `addrs` name — the ones
|
|
173
|
+
* whose `/p2p/<relayPeerId>/p2p-circuit` component names one of them. This is the
|
|
174
|
+
* "held" question a supervisor over a SUBSET of the node's relays has to ask: a
|
|
175
|
+
* strand node runs one supervisor per relay, and relay X's supervisor must not be
|
|
176
|
+
* satisfied by relay Y's addr, or a lost X is never re-driven.
|
|
177
|
+
*
|
|
178
|
+
* Falls back to EVERY circuit addr when any entry names no relay peer id (no
|
|
179
|
+
* `/p2p/` component, or unparsable): such an addr can still be dialed and reserved
|
|
180
|
+
* through, but the addr it earns cannot be attributed to it, so "any circuit addr"
|
|
181
|
+
* is the only answer that does not mark a live reservation as lost. The control
|
|
182
|
+
* node passes its whole relay list, so its answer is the same either way.
|
|
183
|
+
*/
|
|
184
|
+
export declare function circuitMultiaddrsVia(node: Libp2p, addrs: readonly string[]): string[];
|
|
136
185
|
/**
|
|
137
186
|
* The running node's circuit-relay transport, or `null` when it has none.
|
|
138
187
|
*
|
|
@@ -174,6 +223,19 @@ export interface RelayReservationSupervisorOptions extends RelayReserveOptions {
|
|
|
174
223
|
minBackoffMs?: number;
|
|
175
224
|
/** Backoff ceiling. Default 60_000. */
|
|
176
225
|
maxBackoffMs?: number;
|
|
226
|
+
/**
|
|
227
|
+
* Awaited before every drive AFTER the first — never before the first attempt,
|
|
228
|
+
* which the caller has typically prepared for already. A hook that throws or
|
|
229
|
+
* rejects is logged and the drive still runs: the hook is preparation, and a
|
|
230
|
+
* failed preparation must not cost the attempt itself.
|
|
231
|
+
*
|
|
232
|
+
* What a strand node uses it for: re-announcing its delegate peer id to the relay
|
|
233
|
+
* about to be re-dialed. A party control node running the relay server admits a
|
|
234
|
+
* strand node on an in-memory delegate grant, and a relay restart drops every
|
|
235
|
+
* grant it held — so a re-drive that did not re-announce first would be denied
|
|
236
|
+
* at the relay's connection gate.
|
|
237
|
+
*/
|
|
238
|
+
beforeRedrive?: () => Promise<void> | void;
|
|
177
239
|
}
|
|
178
240
|
/**
|
|
179
241
|
* A running retry loop for one node + relay list. Obtained from
|
|
@@ -217,6 +279,12 @@ export interface RelayReservationSupervisor {
|
|
|
217
279
|
* If that lands a reservation, rejoin the healthy path above; if not, sleep
|
|
218
280
|
* the current backoff and double it up to `maxBackoffMs`.
|
|
219
281
|
*
|
|
282
|
+
* "Held" is judged PER RELAY — a circuit addr through one of `addrs`' relays
|
|
283
|
+
* ({@link circuitMultiaddrsVia}) — so several loops over one node, one relay each
|
|
284
|
+
* (a strand node), each re-drive exactly the relay they own. Every drive after the
|
|
285
|
+
* first is preceded by the optional `beforeRedrive` hook
|
|
286
|
+
* ({@link RelayReservationSupervisorOptions}).
|
|
287
|
+
*
|
|
220
288
|
* Starts immediately; the first tick runs before this returns.
|
|
221
289
|
*/
|
|
222
290
|
export declare function superviseRelayReservation(node: Libp2p, addrs: readonly string[], opts?: RelayReservationSupervisorOptions): RelayReservationSupervisor;
|