@serfab/cadre-core 0.10.0 → 0.12.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 +255 -255
- package/dist/announce-addrs.d.ts +49 -0
- package/dist/announce-addrs.js +78 -0
- package/dist/announce-addrs.js.map +1 -0
- package/dist/cadre-node.d.ts +484 -58
- package/dist/cadre-node.js +953 -182
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-cohort.d.ts +26 -0
- package/dist/control-cohort.js +26 -0
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +222 -125
- package/dist/control-database.js +356 -266
- package/dist/control-database.js.map +1 -1
- package/dist/control-formation-recorder.d.ts +8 -7
- package/dist/control-formation-recorder.js +8 -7
- package/dist/control-formation-recorder.js.map +1 -1
- package/dist/control-read-retry.d.ts +103 -0
- package/dist/control-read-retry.js +145 -0
- package/dist/control-read-retry.js.map +1 -0
- package/dist/control-retry.d.ts +101 -0
- package/dist/control-retry.js +108 -0
- package/dist/control-retry.js.map +1 -0
- package/dist/control-schema.d.ts +1 -1
- package/dist/control-schema.js +805 -774
- package/dist/control-schema.js.map +1 -1
- package/dist/control-write-retry.d.ts +65 -31
- package/dist/control-write-retry.js +33 -103
- package/dist/control-write-retry.js.map +1 -1
- package/dist/delegate-admission.d.ts +17 -15
- package/dist/delegate-admission.js +17 -15
- package/dist/delegate-admission.js.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.js +18 -9
- package/dist/index.js.map +1 -1
- package/dist/membership-connection-gater.d.ts +211 -25
- package/dist/membership-connection-gater.js +309 -29
- package/dist/membership-connection-gater.js.map +1 -1
- package/dist/peer-addr-book.d.ts +92 -0
- package/dist/peer-addr-book.js +220 -0
- package/dist/peer-addr-book.js.map +1 -0
- package/dist/peer-record.d.ts +26 -0
- package/dist/peer-record.js +32 -0
- package/dist/peer-record.js.map +1 -1
- package/dist/relay-addrs.d.ts +76 -26
- package/dist/relay-addrs.js +115 -27
- package/dist/relay-addrs.js.map +1 -1
- package/dist/relay-reservation.d.ts +20 -15
- package/dist/relay-reservation.js +18 -14
- package/dist/relay-reservation.js.map +1 -1
- package/dist/seed-bootstrap.d.ts +7 -0
- package/dist/seed-bootstrap.js +15 -13
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/strand-addr-protocol.js +16 -0
- package/dist/strand-addr-protocol.js.map +1 -1
- package/dist/strand-backfill.js +13 -4
- package/dist/strand-backfill.js.map +1 -1
- package/dist/strand-cohort.d.ts +4 -37
- package/dist/strand-cohort.js +4 -15
- package/dist/strand-cohort.js.map +1 -1
- package/dist/strand-database.d.ts +11 -16
- package/dist/strand-database.js +19 -7
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-formation-manager.d.ts +18 -17
- package/dist/strand-formation-manager.js +25 -27
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-formation-protocol.d.ts +7 -7
- package/dist/strand-formation-protocol.js +8 -8
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +53 -21
- package/dist/strand-instance-manager.js +128 -37
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-membership-writer.d.ts +113 -19
- package/dist/strand-membership-writer.js +219 -51
- package/dist/strand-membership-writer.js.map +1 -1
- package/dist/strand-solicitation.d.ts +3 -3
- package/dist/strand-solicitation.js +4 -5
- package/dist/strand-solicitation.js.map +1 -1
- package/dist/strand-wake-protocol.d.ts +47 -2
- package/dist/strand-wake-protocol.js +76 -6
- package/dist/strand-wake-protocol.js.map +1 -1
- package/dist/types.d.ts +107 -54
- package/dist/types.js.map +1 -1
- package/package.json +8 -7
- package/src/announce-addrs.ts +93 -0
- package/src/arachnode-stub.ts +159 -159
- package/src/bootstrap-peer-store-file.ts +44 -44
- package/src/bootstrap-peer-store.ts +172 -172
- package/src/cadre-node.ts +1026 -194
- package/src/canonical-json.ts +30 -30
- package/src/control-authorization.ts +114 -114
- package/src/control-cohort.ts +130 -103
- package/src/control-database.ts +376 -298
- package/src/control-formation-recorder.ts +305 -304
- package/src/control-read-retry.ts +167 -0
- package/src/control-retry.ts +190 -0
- package/src/control-schema.ts +817 -786
- package/src/control-stream.ts +205 -205
- package/src/control-write-retry.ts +383 -473
- package/src/delegate-admission.ts +304 -302
- package/src/device-token.ts +94 -94
- package/src/diagnostics/connection-path.ts +277 -277
- package/src/diagnostics/webrtc-turn-tracker.ts +222 -222
- package/src/ed25519-key.ts +134 -134
- package/src/enrollment.ts +199 -199
- package/src/file-durable-slot.ts +61 -61
- package/src/formation-approval.ts +604 -604
- package/src/fs-atomic.ts +107 -107
- package/src/hibernation-manager.ts +437 -437
- package/src/identity-key.ts +109 -109
- package/src/index.ts +435 -406
- package/src/key-store-file.ts +132 -132
- package/src/key-store.ts +99 -99
- package/src/membership-connection-gater.ts +368 -39
- package/src/node-local-snapshot.ts +260 -260
- package/src/peer-addr-book.ts +250 -0
- package/src/peer-authorization.ts +259 -259
- package/src/peer-record.ts +197 -164
- package/src/push-credentials.ts +84 -84
- package/src/push-node.ts +64 -64
- package/src/push-notifier-apns.ts +247 -247
- package/src/push-notifier-fcm.ts +220 -220
- package/src/push-notifier-shared.ts +28 -28
- package/src/push-notifier.ts +52 -52
- package/src/relay-addrs.ts +134 -27
- package/src/relay-reservation.ts +22 -17
- package/src/seed-bootstrap.ts +15 -15
- package/src/seed-trust-policy.ts +140 -140
- package/src/strand-addr-protocol.ts +458 -441
- package/src/strand-backfill.ts +28 -5
- package/src/strand-cohort.ts +38 -77
- package/src/strand-database.ts +202 -204
- package/src/strand-formation-manager.ts +26 -28
- package/src/strand-formation-protocol.ts +692 -692
- package/src/strand-instance-manager.ts +636 -553
- package/src/strand-member-key.ts +39 -39
- package/src/strand-member-registry.ts +182 -182
- package/src/strand-membership-writer.ts +1627 -1430
- package/src/strand-solicitation.ts +471 -472
- package/src/strand-transport-key.ts +58 -58
- package/src/strand-wake-payload.ts +32 -32
- package/src/strand-wake-protocol.ts +400 -314
- package/src/strand-watcher.ts +314 -314
- package/src/trusted-owner-store-file.ts +41 -41
- package/src/trusted-owner-store.ts +171 -171
- package/src/types.ts +1354 -1303
package/dist/types.d.ts
CHANGED
|
@@ -82,8 +82,28 @@ export interface HibernationTimeouts {
|
|
|
82
82
|
*/
|
|
83
83
|
export declare const HIBERNATION_TIMEOUTS: Record<LatencyHint, HibernationTimeouts>;
|
|
84
84
|
/**
|
|
85
|
-
* Storage provider
|
|
85
|
+
* Storage provider - either an `IRawStorage` instance or a factory function.
|
|
86
86
|
* Factory functions are useful for creating strand-specific storage instances.
|
|
87
|
+
*
|
|
88
|
+
* **Called once per scope per runtime lifetime.** The scopes are the literal string
|
|
89
|
+
* `'control'` (once per `CadreNode.start()`) and each strand id (once per
|
|
90
|
+
* `startStrand`). Hibernation — `quiesceStrand` then `resumeStrand` — reuses the
|
|
91
|
+
* store already resolved for that strand and does NOT re-enter this callback.
|
|
92
|
+
*
|
|
93
|
+
* It IS re-entered for a scope after that scope's runtime has stopped
|
|
94
|
+
* (`stopStrand`, or a `stop()` then `start()` cycle on one `CadreNode`), so it must
|
|
95
|
+
* be able to hand back a store over the same durable backend a second time. A
|
|
96
|
+
* factory may mint a fresh object per call; it must not *need* to.
|
|
97
|
+
*
|
|
98
|
+
* cadre-core disposes only its own cache wrapper — it never closes the store you
|
|
99
|
+
* returned. Closing the underlying handle stays the embedder's job.
|
|
100
|
+
*
|
|
101
|
+
* Either form is safe with a persistent backend. One instance handed to every scope
|
|
102
|
+
* also shares ONE cache wrapper (`wrapStorageWithCache` memoizes per inner instance),
|
|
103
|
+
* which counts its holders: a scope stopping releases only its own claim, and the
|
|
104
|
+
* wrapper is retired when the last scope releases it. The factory form is still the
|
|
105
|
+
* better default for a different reason — it partitions each strand's data, which
|
|
106
|
+
* simplifies cleanup.
|
|
87
107
|
*/
|
|
88
108
|
export type RawStorageProvider = IRawStorage | ((strandId: string) => IRawStorage);
|
|
89
109
|
/**
|
|
@@ -92,6 +112,7 @@ export type RawStorageProvider = IRawStorage | ((strandId: string) => IRawStorag
|
|
|
92
112
|
export interface StorageConfig {
|
|
93
113
|
/**
|
|
94
114
|
* Storage provider - either an IRawStorage instance or a factory function.
|
|
115
|
+
* See {@link RawStorageProvider} for how often cadre-core calls it.
|
|
95
116
|
*
|
|
96
117
|
* For Node.js environments, use FileRawStorage from @optimystic/db-p2p-storage-fs:
|
|
97
118
|
* ```typescript
|
|
@@ -121,26 +142,48 @@ export interface StorageConfig {
|
|
|
121
142
|
export interface NetworkConfig {
|
|
122
143
|
listenAddrs?: string[];
|
|
123
144
|
/**
|
|
124
|
-
* Addresses to advertise to peers instead of `listenAddrs` — for a node behind
|
|
145
|
+
* Addresses to advertise to peers **instead of** `listenAddrs` — for a node behind
|
|
125
146
|
* NAT or a reverse proxy that is reachable at a different address than it binds
|
|
126
|
-
* (e.g. `mynode.example.com:4001` in front of a `0.0.0.0:4001` listener).
|
|
147
|
+
* (e.g. `mynode.example.com:4001` in front of a `0.0.0.0:4001` listener). Reaches
|
|
148
|
+
* libp2p's `addresses.announce` via `@optimystic/db-p2p`'s `NodeOptions.announceAddrs`,
|
|
149
|
+
* on this node's control node and on every strand node it runs.
|
|
150
|
+
*
|
|
151
|
+
* **A non-empty value REPLACES the advertised set entirely.** Observed addresses,
|
|
152
|
+
* and the `/p2p-circuit` address earned from a {@link relayAddrs} reservation, are
|
|
153
|
+
* all dropped from what peers are told — so a node configured with both this and
|
|
154
|
+
* `relayAddrs` stops being reachable through its relay. `CadreNode.start()` warns
|
|
155
|
+
* when it sees that combination; it does not refuse it, since a node whose relay
|
|
156
|
+
* slot is decorative may legitimately want only the announced address.
|
|
127
157
|
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
* request filed against `@optimystic/db-p2p`; wiring lands here once this repo's
|
|
135
|
-
* dependency range moves to a version carrying it.
|
|
158
|
+
* Reach for {@link appendAnnounceAddrs} instead in the common case — one publicly
|
|
159
|
+
* reachable address ADDED to everything else the node advertises. Or for
|
|
160
|
+
* {@link relayAddrs} alone, when the node has no stable public address to name and
|
|
161
|
+
* a relay reservation is what makes it dialable. (`inviteAddressResolver` is a
|
|
162
|
+
* narrower tool again: it substitutes a reachable address into invite payloads
|
|
163
|
+
* only, and changes nothing about what this node advertises on the wire.)
|
|
136
164
|
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
165
|
+
* An empty array means "unset" — it is dropped rather than forwarded, so it cannot
|
|
166
|
+
* land as an explicit empty announce set. An empty *entry* is a different thing and
|
|
167
|
+
* is rejected: a list of one blank string is non-empty, so it would replace every
|
|
168
|
+
* advertised address with nothing. Malformed entries are rejected on the same terms,
|
|
169
|
+
* both at node start — libp2p itself would not notice either until its first address
|
|
170
|
+
* lookup (see `announce-addrs.ts`), so this repo checks each entry up front.
|
|
142
171
|
*/
|
|
143
172
|
announceAddrs?: string[];
|
|
173
|
+
/**
|
|
174
|
+
* Addresses to advertise **in addition to** `listenAddrs` — the field most
|
|
175
|
+
* deployments actually want, since it makes a node reachable at a public address
|
|
176
|
+
* without discarding the observed and `/p2p-circuit` addresses it would otherwise
|
|
177
|
+
* advertise. Reaches libp2p's `addresses.appendAnnounce` via
|
|
178
|
+
* `@optimystic/db-p2p`'s `NodeOptions.appendAnnounceAddrs`.
|
|
179
|
+
*
|
|
180
|
+
* **Ignored while {@link announceAddrs} is non-empty** — that is libp2p's own
|
|
181
|
+
* precedence, applied upstream; this repo does not merge the two locally. Set one
|
|
182
|
+
* or the other, not both.
|
|
183
|
+
*
|
|
184
|
+
* Empty-array, blank-entry and malformed-entry handling all match {@link announceAddrs}.
|
|
185
|
+
*/
|
|
186
|
+
appendAnnounceAddrs?: string[];
|
|
144
187
|
/**
|
|
145
188
|
* Circuit-relay servers this node reserves a slot on, so peers can reach it
|
|
146
189
|
* from behind NAT. Each entry is the relay's DIRECT dial multiaddr ending in
|
|
@@ -148,18 +191,26 @@ export interface NetworkConfig {
|
|
|
148
191
|
* `/p2p-circuit` suffix (one is appended for you; an entry that already has
|
|
149
192
|
* one is passed through unchanged).
|
|
150
193
|
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
194
|
+
* On the CONTROL node this adds libp2p's bare `/p2p-circuit` SEARCH listener to
|
|
195
|
+
* {@link listenAddrs} (`relay-addrs.ts`) and `CadreNode.start()` drives the
|
|
196
|
+
* reservation itself once the control database is up — the ordering matters,
|
|
197
|
+
* because a listener that dials its relay from inside `libp2p.start()` put a
|
|
198
|
+
* sibling in this node's cohort before its own database existed, and a sibling
|
|
199
|
+
* that had not yet replicated this node's membership row refused the bring-up.
|
|
200
|
+
* STRAND nodes inherit the older per-relay `<relay>/p2p-circuit` listener, which
|
|
201
|
+
* has no such ordering hazard. A relay named here also becomes a
|
|
202
|
+
* delegate-announce target, so this node's strand nodes may reserve on it too
|
|
203
|
+
* (see `delegate-admission.ts`).
|
|
156
204
|
*
|
|
157
205
|
* Setting this gives the node a listener even when `listenAddrs` is `[]` —
|
|
158
206
|
* that is the point of naming a relay, but note that React Native hosts
|
|
159
207
|
* (`reference-app-rn`'s `cadre-phone.ts`) deliberately do NOT listen, and
|
|
160
208
|
* must not acquire a listener by accident.
|
|
161
209
|
*
|
|
162
|
-
*
|
|
210
|
+
* FAIL-FAST both ways: a malformed entry throws at node start rather than being
|
|
211
|
+
* silently dropped, and a first reservation attempt that lands no
|
|
212
|
+
* `/p2p-circuit` address throws `RelayReservationFailedError` out of `start()`.
|
|
213
|
+
* The fail-soft posture over the same machinery is `CadreNode.reserveRelays()`.
|
|
163
214
|
*/
|
|
164
215
|
relayAddrs?: string[];
|
|
165
216
|
/**
|
|
@@ -168,6 +219,16 @@ export interface NetworkConfig {
|
|
|
168
219
|
* better connectivity and uptime), false for transaction profile nodes.
|
|
169
220
|
*/
|
|
170
221
|
enableRelay?: boolean;
|
|
222
|
+
/**
|
|
223
|
+
* Cap on concurrent circuit-relay reservations this node's relay server grants to
|
|
224
|
+
* peers it cannot (yet) recognize as authorized members — the boot-ordering window
|
|
225
|
+
* where a genuine member reserves its relay slot before its membership row has
|
|
226
|
+
* replicated here (see `membership-connection-gater.ts` → "The relay-reservation
|
|
227
|
+
* seam"). Authorized members and announced delegate peers are never counted against
|
|
228
|
+
* it. Default `MAX_UNAUTHORIZED_RELAY_RESERVATIONS` (8); 0 refuses every
|
|
229
|
+
* unauthorized reservation. Only meaningful while {@link enableRelay} is on.
|
|
230
|
+
*/
|
|
231
|
+
unauthorizedRelayReservationCap?: number;
|
|
171
232
|
/**
|
|
172
233
|
* Custom libp2p transports. When omitted, the default transports from
|
|
173
234
|
* `@optimystic/db-p2p` are used (TCP + circuit relay for Node.js).
|
|
@@ -190,6 +251,11 @@ export interface NetworkConfig {
|
|
|
190
251
|
* (and other owner-address contexts). When unset, `libp2pNode.getMultiaddrs()`
|
|
191
252
|
* is used. Hosts behind NAT supply this to substitute their DDNS hostname
|
|
192
253
|
* and externally-mapped port — see `@serfab/cadre-host`'s NatService.
|
|
254
|
+
*
|
|
255
|
+
* Returned addresses need NOT carry a `/p2p/<peerId>` suffix: `CadreNode`
|
|
256
|
+
* appends its own before publishing or dialing any of them, so a resolver
|
|
257
|
+
* cannot leave a sibling with the mixed suffixed/unsuffixed list that
|
|
258
|
+
* `libp2p.dial` rejects. Returning them already suffixed is equally fine.
|
|
193
259
|
*/
|
|
194
260
|
inviteAddressResolver?: () => Promise<string[]>;
|
|
195
261
|
/**
|
|
@@ -212,9 +278,10 @@ export interface NetworkConfig {
|
|
|
212
278
|
* Tuning for the proactive control-cohort dial routine
|
|
213
279
|
* ({@link CadreNode.reconcileControlCohort}), which keeps a party's control
|
|
214
280
|
* nodes connected so the `CadreControl` collections form a replicating cohort.
|
|
215
|
-
*
|
|
281
|
+
* Every field is optional; omit for the defaults
|
|
216
282
|
* ({@link DEFAULT_CONTROL_COHORT_TARGET_DEGREE} /
|
|
217
|
-
* {@link DEFAULT_CONTROL_COHORT_RECONCILE_MS}
|
|
283
|
+
* {@link DEFAULT_CONTROL_COHORT_RECONCILE_MS} /
|
|
284
|
+
* {@link DEFAULT_CONTROL_COHORT_DIAL_TIMEOUT_MS} / `STRAND_PEER_ADDR_REFRESH_MS`).
|
|
218
285
|
*/
|
|
219
286
|
controlCohort?: {
|
|
220
287
|
/**
|
|
@@ -228,6 +295,20 @@ export interface NetworkConfig {
|
|
|
228
295
|
* {@link DEFAULT_CONTROL_COHORT_RECONCILE_MS}.
|
|
229
296
|
*/
|
|
230
297
|
reconcileMs?: number;
|
|
298
|
+
/**
|
|
299
|
+
* Budget for ONE sibling's proactive dial, in ms. Bounds a pass at
|
|
300
|
+
* (dialed siblings) × this, instead of leaving it to each address's own
|
|
301
|
+
* libp2p attempt timeout multiplied by address fan-out and relay hops.
|
|
302
|
+
* Defaults to {@link DEFAULT_CONTROL_COHORT_DIAL_TIMEOUT_MS}.
|
|
303
|
+
*/
|
|
304
|
+
dialTimeoutMs?: number;
|
|
305
|
+
/**
|
|
306
|
+
* How often each running strand re-resolves its siblings' strand-network
|
|
307
|
+
* addresses into its own libp2p address book — a step of the reconcile pass,
|
|
308
|
+
* on its own much longer throttle. Defaults to `STRAND_PEER_ADDR_REFRESH_MS`
|
|
309
|
+
* (10 min); must stay well under the peerStore's one-hour address expiry.
|
|
310
|
+
*/
|
|
311
|
+
strandAddrRefreshMs?: number;
|
|
231
312
|
};
|
|
232
313
|
}
|
|
233
314
|
/**
|
|
@@ -287,8 +368,8 @@ export interface CadreNodeConfig {
|
|
|
287
368
|
* Pluggable secure store for node key material. When set, the node loads its
|
|
288
369
|
* identity from `keyStore` under {@link identityKeyId}, generating + persisting
|
|
289
370
|
* a fresh Ed25519 key on first run (protobuf bytes are the canonical stored
|
|
290
|
-
* form). Mutually exclusive with {@link privateKey}. Absent ⇒
|
|
291
|
-
*
|
|
371
|
+
* form). Mutually exclusive with {@link privateKey}. Absent ⇒ use `privateKey`
|
|
372
|
+
* when set, else libp2p generates an ephemeral key.
|
|
292
373
|
*
|
|
293
374
|
* In the single-key reference model the owner signing key is *derived from*
|
|
294
375
|
* the node identity (see {@link CadreNode.getIdentityOwnerKey}), so
|
|
@@ -475,13 +556,6 @@ export interface StrandInstance {
|
|
|
475
556
|
strandId: string;
|
|
476
557
|
status: StrandStatus;
|
|
477
558
|
sAppInfo?: SAppInfo;
|
|
478
|
-
/**
|
|
479
|
-
* The lifecycle mode this instance's runtime was built with — `bootstrap`
|
|
480
|
-
* (purely local transactor) or `networked` (cluster transactor). Refreshed on
|
|
481
|
-
* every runtime (re)build, so a resume that shifts `bootstrap → networked`
|
|
482
|
-
* updates it. Read-only reporting; drives no behavior.
|
|
483
|
-
*/
|
|
484
|
-
mode: StrandMode;
|
|
485
559
|
/** The libp2p node for this strand (only when active/idle) */
|
|
486
560
|
libp2pNode?: Libp2p;
|
|
487
561
|
/** The Quereus database for this strand (only when active/idle) */
|
|
@@ -549,20 +623,6 @@ export interface SAppConfig {
|
|
|
549
623
|
/** Latency hint for hibernation behavior (optional, defaults to config) */
|
|
550
624
|
latencyHint?: LatencyHint;
|
|
551
625
|
}
|
|
552
|
-
/**
|
|
553
|
-
* Lifecycle mode for a strand.
|
|
554
|
-
*
|
|
555
|
-
* - `bootstrap`: newly created locally; no remote peers exist for this strand yet.
|
|
556
|
-
* Schema apply and data operations route through a local (non-network) transactor
|
|
557
|
-
* so a solo node can fully initialize without any peer round trips.
|
|
558
|
-
* - `networked`: strand has (or is expected to have) remote peers. Operations route
|
|
559
|
-
* through the network transactor.
|
|
560
|
-
*
|
|
561
|
-
* Transition `bootstrap -> networked` happens at first remote-peer enrollment for
|
|
562
|
-
* the strand; because the transactor is fixed at plugin registration time, the
|
|
563
|
-
* transition requires stopping and restarting the strand.
|
|
564
|
-
*/
|
|
565
|
-
export type StrandMode = 'bootstrap' | 'networked';
|
|
566
626
|
/**
|
|
567
627
|
* Full strand configuration when adding a strand via the API
|
|
568
628
|
*/
|
|
@@ -571,13 +631,6 @@ export interface StrandConfig {
|
|
|
571
631
|
strandRow: StrandRow;
|
|
572
632
|
/** sApp configuration provided by the hosting application */
|
|
573
633
|
sAppConfig: SAppConfig;
|
|
574
|
-
/**
|
|
575
|
-
* Lifecycle mode for this strand instance. When omitted, CadreNode infers the
|
|
576
|
-
* mode from cohort membership (`bootstrap` for a solo/first node with no other
|
|
577
|
-
* `CadrePeer` rows, `networked` once the cohort has other members). An explicit
|
|
578
|
-
* value always wins.
|
|
579
|
-
*/
|
|
580
|
-
mode?: StrandMode;
|
|
581
634
|
/**
|
|
582
635
|
* Whether THIS node is the strand's founder — the party that provisioned and
|
|
583
636
|
* published the strand (the responder in formation, the creator in host/solo
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA2EA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAA6C;IAC5E,QAAQ,EAAE;QACR,WAAW,EAAE,QAAQ,EAAS,aAAa;QAC3C,gBAAgB,EAAE,QAAQ,EAAI,kBAAkB;QAChD,eAAe,EAAE,QAAQ,EAAK,MAAM;QACpC,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,QAAQ,CAAE,MAAM;KACrC;IACD,WAAW,EAAE;QACX,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,YAAY;QAC1C,gBAAgB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,wBAAwB;QAC1D,eAAe,EAAE,EAAE,GAAG,IAAI,EAAI,mBAAmB;QACjD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe;KACnD;IACD,UAAU,EAAE;QACV,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,WAAW;QACzC,gBAAgB,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAG,uBAAuB;QACzD,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,kBAAkB;QACpD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,gBAAgB;KACxD;IACD,OAAO,EAAE;QACP,WAAW,EAAE,EAAE,GAAG,IAAI,EAAQ,aAAa;QAC3C,gBAAgB,EAAE,EAAE,GAAG,IAAI,EAAG,wBAAwB;QACtD,eAAe,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAG,eAAe;QACjD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe;KAC5D;CACF,CAAC;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA2EA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAA6C;IAC5E,QAAQ,EAAE;QACR,WAAW,EAAE,QAAQ,EAAS,aAAa;QAC3C,gBAAgB,EAAE,QAAQ,EAAI,kBAAkB;QAChD,eAAe,EAAE,QAAQ,EAAK,MAAM;QACpC,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,QAAQ,CAAE,MAAM;KACrC;IACD,WAAW,EAAE;QACX,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,YAAY;QAC1C,gBAAgB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,wBAAwB;QAC1D,eAAe,EAAE,EAAE,GAAG,IAAI,EAAI,mBAAmB;QACjD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe;KACnD;IACD,UAAU,EAAE;QACV,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,WAAW;QACzC,gBAAgB,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAG,uBAAuB;QACzD,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,kBAAkB;QACpD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,gBAAgB;KACxD;IACD,OAAO,EAAE;QACP,WAAW,EAAE,EAAE,GAAG,IAAI,EAAQ,aAAa;QAC3C,gBAAgB,EAAE,EAAE,GAAG,IAAI,EAAG,wBAAwB;QACtD,eAAe,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAG,eAAe;QACjD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe;KAC5D;CACF,CAAC;AA4PF;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,GAAG,IAAI,CAAC;AAYnD;;;;;;;;;;;GAWG;AACH,OAAO,EACL,gBAAgB,EAChB,2BAA2B,EAC3B,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,+BAA+B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serfab/cadre-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Core library for Sereus cadre nodes - manages control networks and strand participation",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
"@libp2p/tcp": "^11.0.10",
|
|
68
68
|
"@libp2p/webrtc": "6.0.14",
|
|
69
69
|
"@libp2p/websockets": "^10.1.3",
|
|
70
|
+
"@optimystic/db-p2p-storage-fs": "^0.27.0",
|
|
70
71
|
"@types/debug": "^4.1.12",
|
|
71
72
|
"@types/node": "^25.0.10",
|
|
72
73
|
"@vitest/coverage-v8": "^4.0.17",
|
|
@@ -79,12 +80,12 @@
|
|
|
79
80
|
"@libp2p/interface": "^3.1.0",
|
|
80
81
|
"@libp2p/peer-id": "^6.0.4",
|
|
81
82
|
"@multiformats/multiaddr": "^12.5.1",
|
|
82
|
-
"@optimystic/db-core": "^0.
|
|
83
|
-
"@optimystic/db-p2p": "^0.
|
|
84
|
-
"@optimystic/quereus-plugin-crypto": "^0.
|
|
85
|
-
"@optimystic/quereus-plugin-optimystic": "^0.
|
|
86
|
-
"@quereus/quereus": "^4.
|
|
87
|
-
"@serfab/quereus-plugin-sereus": "^0.
|
|
83
|
+
"@optimystic/db-core": "^0.27.0",
|
|
84
|
+
"@optimystic/db-p2p": "^0.27.0",
|
|
85
|
+
"@optimystic/quereus-plugin-crypto": "^0.27.0",
|
|
86
|
+
"@optimystic/quereus-plugin-optimystic": "^0.27.0",
|
|
87
|
+
"@quereus/quereus": "^4.18.0",
|
|
88
|
+
"@serfab/quereus-plugin-sereus": "^0.12.0",
|
|
88
89
|
"debug": "^4.4.3",
|
|
89
90
|
"libp2p": "^3.1.3",
|
|
90
91
|
"uint8arrays": "^5.1.0"
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Translate `network.announceAddrs` / `network.appendAnnounceAddrs` — the operator-facing
|
|
3
|
+
* "advertise these addresses" settings — into the two `@optimystic/db-p2p` `NodeOptions`
|
|
4
|
+
* fields of the same names, which reach libp2p's `addresses.announce` /
|
|
5
|
+
* `addresses.appendAnnounce`.
|
|
6
|
+
*
|
|
7
|
+
* Two jobs, both of which the control node and every strand node need identically, hence
|
|
8
|
+
* one module rather than the same conditional spread written twice:
|
|
9
|
+
*
|
|
10
|
+
* - **Empty means unset.** libp2p treats `announce: []` as "no override", and forwarding
|
|
11
|
+
* `[]` would still be forwarding a field. An empty config array is dropped so it cannot
|
|
12
|
+
* land as an explicit empty announce set.
|
|
13
|
+
* - **Validate the multiaddrs here, at start.** libp2p does NOT validate announce addrs
|
|
14
|
+
* at construction: `AddressManager` stores them as raw strings and only parses them on
|
|
15
|
+
* the first `getAnnounceAddrs()`. A typo therefore produces a node that starts
|
|
16
|
+
* cleanly, then throws `InvalidMultiaddrError` out of every `getMultiaddrs()` call —
|
|
17
|
+
* including an UNHANDLED one from the debounced peer-store update libp2p runs on
|
|
18
|
+
* `transport:listening`. Verified against libp2p's `address-manager` (its constructor
|
|
19
|
+
* does `announce.map(ma => ma.toString())`; `getAnnounceAddrs()` does `multiaddr(a)`).
|
|
20
|
+
* Parsing each entry up front converts that into a loud failure at node start, which is
|
|
21
|
+
* what {@link relayCircuitAddrs} already does for `network.relayAddrs`.
|
|
22
|
+
*
|
|
23
|
+
* Parsing alone is not enough, though: `''` and `'/'` both parse into a component-less
|
|
24
|
+
* multiaddr, and a set holding only those is non-empty by the rule above — so it would
|
|
25
|
+
* replace every advertised address with nothing at all. An entry naming no address is
|
|
26
|
+
* therefore rejected alongside one that fails to parse.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { multiaddr, type Multiaddr } from '@multiformats/multiaddr';
|
|
30
|
+
import type { NetworkConfig } from './types.js';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The announce fields to spread into `createLibp2pNode` options. A field is present
|
|
34
|
+
* only when the operator configured a non-empty list for it, so a node that configures
|
|
35
|
+
* neither passes neither.
|
|
36
|
+
*/
|
|
37
|
+
export interface ResolvedAnnounceAddrs {
|
|
38
|
+
announceAddrs?: string[];
|
|
39
|
+
appendAnnounceAddrs?: string[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Resolve both announce fields off a `NetworkConfig`. Throws — naming the offending
|
|
44
|
+
* config field and entry — when any entry is not a parsable multiaddr.
|
|
45
|
+
*/
|
|
46
|
+
export function resolveAnnounceAddrs(network: NetworkConfig | undefined): ResolvedAnnounceAddrs {
|
|
47
|
+
const announceAddrs = configuredAddrs('network.announceAddrs', network?.announceAddrs);
|
|
48
|
+
const appendAnnounceAddrs = configuredAddrs('network.appendAnnounceAddrs', network?.appendAnnounceAddrs);
|
|
49
|
+
return {
|
|
50
|
+
...(announceAddrs && { announceAddrs }),
|
|
51
|
+
...(appendAnnounceAddrs && { appendAnnounceAddrs })
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Whether this node's advertised set will be REPLACED rather than extended — i.e.
|
|
57
|
+
* whether {@link ResolvedAnnounceAddrs.announceAddrs} is in play at all. The single
|
|
58
|
+
* predicate behind `CadreNode.start()`'s operator warning, kept here so the warning
|
|
59
|
+
* and the forwarding agree on what "set" means (non-empty, not merely present).
|
|
60
|
+
*/
|
|
61
|
+
export function replacesAdvertisedAddrs(network: NetworkConfig | undefined): boolean {
|
|
62
|
+
return (network?.announceAddrs?.length ?? 0) > 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** The entries to forward for one announce field, or `undefined` when it is unset or empty. */
|
|
66
|
+
function configuredAddrs(field: string, addrs: readonly string[] | undefined): string[] | undefined {
|
|
67
|
+
if (!addrs || addrs.length === 0) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
return addrs.map((addr) => validated(field, addr));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** `addr` unchanged, having proved it names a real address — config field named on failure. */
|
|
74
|
+
function validated(field: string, addr: string): string {
|
|
75
|
+
if (parsed(field, addr).getComponents().length === 0) {
|
|
76
|
+
// `''` and `'/'` PARSE: both yield a component-less multiaddr that stringifies to
|
|
77
|
+
// `/`. Forwarded, they make an announce set that is non-empty but names nothing —
|
|
78
|
+
// which by the replacement rule above costs the node every address it would
|
|
79
|
+
// otherwise advertise, silently. A templated `cadre.yaml` whose address variable went
|
|
80
|
+
// unsubstituted is the realistic way in, so it is rejected rather than forwarded.
|
|
81
|
+
throw new Error(`${field} entry names no address: ${JSON.stringify(addr)}`);
|
|
82
|
+
}
|
|
83
|
+
return addr;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** `addr` as a multiaddr, with the config field named on a parse failure. */
|
|
87
|
+
function parsed(field: string, addr: string): Multiaddr {
|
|
88
|
+
try {
|
|
89
|
+
return multiaddr(addr);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
throw new Error(`${field} entry is not a valid multiaddr: ${addr}`, { cause: err });
|
|
92
|
+
}
|
|
93
|
+
}
|