@serfab/cadre-core 0.10.0 → 0.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 (105) hide show
  1. package/README.md +255 -255
  2. package/dist/cadre-node.d.ts +203 -28
  3. package/dist/cadre-node.js +445 -84
  4. package/dist/cadre-node.js.map +1 -1
  5. package/dist/control-cohort.d.ts +26 -0
  6. package/dist/control-cohort.js +26 -0
  7. package/dist/control-cohort.js.map +1 -1
  8. package/dist/control-database.d.ts +82 -117
  9. package/dist/control-database.js +217 -295
  10. package/dist/control-database.js.map +1 -1
  11. package/dist/control-formation-recorder.d.ts +8 -7
  12. package/dist/control-formation-recorder.js +8 -7
  13. package/dist/control-formation-recorder.js.map +1 -1
  14. package/dist/control-schema.d.ts +1 -1
  15. package/dist/control-schema.js +789 -774
  16. package/dist/control-schema.js.map +1 -1
  17. package/dist/control-write-retry.d.ts +15 -5
  18. package/dist/control-write-retry.js +19 -18
  19. package/dist/control-write-retry.js.map +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.js +1 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/peer-addr-book.d.ts +92 -0
  24. package/dist/peer-addr-book.js +220 -0
  25. package/dist/peer-addr-book.js.map +1 -0
  26. package/dist/seed-bootstrap.d.ts +7 -0
  27. package/dist/seed-bootstrap.js +15 -13
  28. package/dist/seed-bootstrap.js.map +1 -1
  29. package/dist/strand-cohort.d.ts +4 -37
  30. package/dist/strand-cohort.js +4 -15
  31. package/dist/strand-cohort.js.map +1 -1
  32. package/dist/strand-database.d.ts +11 -16
  33. package/dist/strand-database.js +19 -7
  34. package/dist/strand-database.js.map +1 -1
  35. package/dist/strand-formation-manager.d.ts +8 -8
  36. package/dist/strand-formation-manager.js +15 -18
  37. package/dist/strand-formation-manager.js.map +1 -1
  38. package/dist/strand-instance-manager.d.ts +52 -21
  39. package/dist/strand-instance-manager.js +100 -34
  40. package/dist/strand-instance-manager.js.map +1 -1
  41. package/dist/strand-membership-writer.d.ts +6 -4
  42. package/dist/strand-membership-writer.js +38 -36
  43. package/dist/strand-membership-writer.js.map +1 -1
  44. package/dist/types.d.ts +39 -31
  45. package/dist/types.js.map +1 -1
  46. package/package.json +8 -7
  47. package/src/arachnode-stub.ts +159 -159
  48. package/src/bootstrap-peer-store-file.ts +44 -44
  49. package/src/bootstrap-peer-store.ts +172 -172
  50. package/src/cadre-node.ts +5158 -4771
  51. package/src/canonical-json.ts +30 -30
  52. package/src/control-authorization.ts +114 -114
  53. package/src/control-cohort.ts +130 -103
  54. package/src/control-database.ts +2406 -2506
  55. package/src/control-formation-recorder.ts +305 -304
  56. package/src/control-schema.ts +801 -786
  57. package/src/control-stream.ts +205 -205
  58. package/src/control-write-retry.ts +483 -473
  59. package/src/delegate-admission.ts +302 -302
  60. package/src/device-token.ts +94 -94
  61. package/src/diagnostics/connection-path.ts +277 -277
  62. package/src/diagnostics/webrtc-turn-tracker.ts +222 -222
  63. package/src/ed25519-key.ts +134 -134
  64. package/src/enrollment.ts +199 -199
  65. package/src/file-durable-slot.ts +61 -61
  66. package/src/formation-approval.ts +604 -604
  67. package/src/fs-atomic.ts +107 -107
  68. package/src/hibernation-manager.ts +437 -437
  69. package/src/identity-key.ts +109 -109
  70. package/src/index.ts +1 -0
  71. package/src/key-store-file.ts +132 -132
  72. package/src/key-store.ts +99 -99
  73. package/src/membership-connection-gater.ts +204 -204
  74. package/src/node-local-snapshot.ts +260 -260
  75. package/src/peer-addr-book.ts +250 -0
  76. package/src/peer-authorization.ts +259 -259
  77. package/src/peer-record.ts +164 -164
  78. package/src/push-credentials.ts +84 -84
  79. package/src/push-node.ts +64 -64
  80. package/src/push-notifier-apns.ts +247 -247
  81. package/src/push-notifier-fcm.ts +220 -220
  82. package/src/push-notifier-shared.ts +28 -28
  83. package/src/push-notifier.ts +52 -52
  84. package/src/relay-addrs.ts +111 -111
  85. package/src/relay-reservation.ts +823 -823
  86. package/src/seed-bootstrap.ts +15 -15
  87. package/src/seed-trust-policy.ts +140 -140
  88. package/src/strand-addr-protocol.ts +441 -441
  89. package/src/strand-backfill.ts +366 -366
  90. package/src/strand-cohort.ts +38 -77
  91. package/src/strand-database.ts +202 -204
  92. package/src/strand-formation-manager.ts +491 -494
  93. package/src/strand-formation-protocol.ts +692 -692
  94. package/src/strand-instance-manager.ts +108 -50
  95. package/src/strand-member-key.ts +39 -39
  96. package/src/strand-member-registry.ts +182 -182
  97. package/src/strand-membership-writer.ts +1432 -1430
  98. package/src/strand-solicitation.ts +472 -472
  99. package/src/strand-transport-key.ts +58 -58
  100. package/src/strand-wake-payload.ts +32 -32
  101. package/src/strand-wake-protocol.ts +314 -314
  102. package/src/strand-watcher.ts +314 -314
  103. package/src/trusted-owner-store-file.ts +41 -41
  104. package/src/trusted-owner-store.ts +171 -171
  105. package/src/types.ts +1309 -1303
@@ -1 +1 @@
1
- {"version":3,"file":"control-schema.js","sourceRoot":"","sources":["../src/control-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAswBH,CAAC"}
1
+ {"version":3,"file":"control-schema.js","sourceRoot":"","sources":["../src/control-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAqxBH,CAAC"}
@@ -67,8 +67,7 @@ export declare const CONTROL_WRITE_RETRY_BUDGET_MS = 10000;
67
67
  * re-presenting the SAME signed write a moment later the right response?
68
68
  *
69
69
  * Classifies by MESSAGE, walking the `cause` chain with Quereus' own {@link unwrapError} —
70
- * the exact shape of `isLostUseNumberRace`, for the same reason: neither surface is
71
- * recognisable by type. The transactor and the coordinator both throw bare `Error`s, and by
70
+ * the failure surface is not recognisable by type. The transactor and the coordinator both throw bare `Error`s, and by
72
71
  * the time one reaches `ControlDatabase` it is wrapped in a `QuereusError` (the real chain
73
72
  * is `QuereusError` → `Error` → `Error`), so the match must work at any depth. Anything
74
73
  * that is not an `Error` is never retried.
@@ -78,10 +77,12 @@ export declare const CONTROL_WRITE_RETRY_BUDGET_MS = 10000;
78
77
  * other level looking transient.
79
78
  *
80
79
  * NOTE: this depends on engine/transactor error TEXT. The messages producible without a
81
- * network are driven from the REAL engine in `control-formation-use-number-retry.spec.ts`, so
80
+ * network are driven from the REAL engine in `control-formation-seat-budget.spec.ts`, so
82
81
  * a rewording reddens a spec rather than silently disabling the retry. The two RETRIABLE
83
- * messages need a real multi-node cluster to produce and are still literals in
84
- * `control-write-retry.spec.ts`; `control-write-retry-scenario-coverage` closes that gap.
82
+ * messages need a real multi-node cluster to produce; the degraded-cohort scenario
83
+ * (`control-write-degraded-cohort-member.integration.ts`) asserts this classifier against
84
+ * both LIVE — the super-majority shortfall from a real silent member, and the transactor
85
+ * aggregate from a real stream reset, which the retry there absorbs end to end.
85
86
  */
86
87
  export declare function isRetriableControlWriteFailure(error: unknown): boolean;
87
88
  /**
@@ -114,6 +115,15 @@ export interface ControlWriteRetryOptions {
114
115
  * {@link isRetriableSchemaInitFailure}, via {@link SCHEMA_INIT_RETRY_POLICY}.
115
116
  */
116
117
  isRetriable?: (error: unknown) => boolean;
118
+ /**
119
+ * Operation label stamped into every log line this loop emits (rendered as
120
+ * `Control write [<label>] …`), and NOTHING else — no behavioural effect. The debug
121
+ * log is the only surface where the loop's decisions are observable (the rethrown
122
+ * error is unchanged and no attempt counter is exposed), and several writes retry
123
+ * CONCURRENTLY in a real party, so an unlabelled line cannot be attributed to a
124
+ * write. The degraded-cohort scenario asserts on these lines per-operation.
125
+ */
126
+ label?: string;
117
127
  /** The sleep primitive. Default: `setTimeout`. */
118
128
  sleep?: (ms: number) => Promise<void>;
119
129
  /** Clock for the elapsed-budget check. Default: `Date.now`. */
@@ -146,9 +146,10 @@ const SUPER_MAJORITY_SHORTFALL_UNANSWERED = /Failed to get super-majority: \d+\/
146
146
  * NOTE: the discriminator is a formatting detail of another repo. If Optimystic ever reformats
147
147
  * those per-batch details, this fails CLOSED — the aggregate stops matching and control writes
148
148
  * simply stop being retried, silently losing the absorption rather than doing anything unsafe.
149
- * The guard against that is a scenario asserting the classifier against a live cluster failure
150
- * (`control-write-retry-scenario-coverage`); if that lands and later reddens here, an upstream
151
- * reformat is the first thing to check.
149
+ * The guard against that is live: `control-write-degraded-cohort-member.integration.ts` asserts
150
+ * this classifier against the real degraded-cohort failure object and asserts the `[block:`
151
+ * token on the live aggregate; if it reddens there, an upstream reformat is the first thing to
152
+ * check.
152
153
  *
153
154
  * An aggregate whose details came out EMPTY (possible when `formatBatchStatuses` has no
154
155
  * batches to format) carries neither token, matches nothing here, and is not retried — an
@@ -226,9 +227,8 @@ function chainMessages(error) {
226
227
  *
227
228
  * Deliberately NOT matched: every constraint / authorization failure
228
229
  * (`CHECK constraint failed:`, `UNIQUE constraint failed:`) — those reach the same funnel
229
- * and must be retried zero times. In particular the lost-use-number messages
230
- * (`isLostUseNumberRace` in `control-database.ts`) are constraint failures, so this
231
- * classifier and that one are DISJOINT and the two retry loops can never multiply.
230
+ * and must be retried zero times: the cohort REFUSED the write, so re-presenting it
231
+ * re-presents a spent signature and can never commit.
232
232
  */
233
233
  const RETRIABLE_CONTROL_WRITE_MATCHERS = [
234
234
  isUncommittedTransactorAggregate,
@@ -261,8 +261,7 @@ const RETRIABLE_SCHEMA_INIT_MATCHERS = [
261
261
  * re-presenting the SAME signed write a moment later the right response?
262
262
  *
263
263
  * Classifies by MESSAGE, walking the `cause` chain with Quereus' own {@link unwrapError} —
264
- * the exact shape of `isLostUseNumberRace`, for the same reason: neither surface is
265
- * recognisable by type. The transactor and the coordinator both throw bare `Error`s, and by
264
+ * the failure surface is not recognisable by type. The transactor and the coordinator both throw bare `Error`s, and by
266
265
  * the time one reaches `ControlDatabase` it is wrapped in a `QuereusError` (the real chain
267
266
  * is `QuereusError` → `Error` → `Error`), so the match must work at any depth. Anything
268
267
  * that is not an `Error` is never retried.
@@ -272,10 +271,12 @@ const RETRIABLE_SCHEMA_INIT_MATCHERS = [
272
271
  * other level looking transient.
273
272
  *
274
273
  * NOTE: this depends on engine/transactor error TEXT. The messages producible without a
275
- * network are driven from the REAL engine in `control-formation-use-number-retry.spec.ts`, so
274
+ * network are driven from the REAL engine in `control-formation-seat-budget.spec.ts`, so
276
275
  * a rewording reddens a spec rather than silently disabling the retry. The two RETRIABLE
277
- * messages need a real multi-node cluster to produce and are still literals in
278
- * `control-write-retry.spec.ts`; `control-write-retry-scenario-coverage` closes that gap.
276
+ * messages need a real multi-node cluster to produce; the degraded-cohort scenario
277
+ * (`control-write-degraded-cohort-member.integration.ts`) asserts this classifier against
278
+ * both LIVE — the super-majority shortfall from a real silent member, and the transactor
279
+ * aggregate from a real stream reset, which the retry there absorbs end to end.
279
280
  */
280
281
  export function isRetriableControlWriteFailure(error) {
281
282
  return matchesRetriableMessage(error, RETRIABLE_CONTROL_WRITE_MATCHERS);
@@ -345,6 +346,9 @@ export async function retryControlWrite(attempt, options = {}) {
345
346
  const isRetriable = options.isRetriable ?? isRetriableControlWriteFailure;
346
347
  const sleep = options.sleep ?? defaultSleep;
347
348
  const now = options.now ?? Date.now;
349
+ // Empty when unlabelled, so an unlabelled line is byte-identical to what this loop
350
+ // logged before labels existed.
351
+ const tag = options.label ? ` [${options.label}]` : '';
348
352
  const start = now();
349
353
  let lastError;
350
354
  let attemptsMade = 0;
@@ -353,7 +357,7 @@ export async function retryControlWrite(attempt, options = {}) {
353
357
  try {
354
358
  const result = await attempt();
355
359
  if (attemptNumber > 1) {
356
- log('Control write committed on attempt %d/%d', attemptNumber, attempts);
360
+ log('Control write%s committed on attempt %d/%d', tag, attemptNumber, attempts);
357
361
  }
358
362
  return result;
359
363
  }
@@ -362,10 +366,7 @@ export async function retryControlWrite(attempt, options = {}) {
362
366
  // The ONLY trace that this funnel saw a failure and declined it. Without it the
363
367
  // log is silent either way, so "the classifier vetoed this one" is
364
368
  // indistinguishable from "the retry is not wired into this path at all".
365
- // "here" is load-bearing: this fires for every non-transient failure, and some
366
- // of them — the lost-use-number constraint failures — ARE retried by the OUTER
367
- // loop (`ControlDatabase.withUseNumberRetry`) that this one nests inside.
368
- log('Control write failed non-transiently on attempt %d/%d, not retried here: %s', attemptNumber, attempts, error);
369
+ log('Control write%s failed non-transiently on attempt %d/%d, not retried here: %s', tag, attemptNumber, attempts, error);
369
370
  throw error;
370
371
  }
371
372
  lastError = error;
@@ -377,11 +378,11 @@ export async function retryControlWrite(attempt, options = {}) {
377
378
  break;
378
379
  }
379
380
  const delay = jitteredDelay(delays, attemptNumber);
380
- log('Control write failed transiently (attempt %d/%d), retrying in %d ms: %s', attemptNumber, attempts, delay, error);
381
+ log('Control write%s failed transiently (attempt %d/%d), retrying in %d ms: %s', tag, attemptNumber, attempts, delay, error);
381
382
  await sleep(delay);
382
383
  }
383
384
  }
384
- log('Control write failed after %d/%d attempt(s): %s', attemptsMade, attempts, lastError);
385
+ log('Control write%s failed after %d/%d attempt(s): %s', tag, attemptsMade, attempts, lastError);
385
386
  throw lastError;
386
387
  }
387
388
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"control-write-retry.js","sourceRoot":"","sources":["../src/control-write-retry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"control-write-retry.js","sourceRoot":"","sources":["../src/control-write-retry.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -34,7 +34,7 @@ export { STRAND_WAKE_TYPE, type StrandWakePayload } from './strand-wake-payload.
34
34
  export type { PushNotifier, PushMessage, PushSendResult } from './push-notifier.js';
35
35
  export { validatePushCredentials, redactPushCredentials, REDACTED } from './push-credentials.js';
36
36
  export { PushFanoutService, DEFAULT_PUSH_COOLDOWN_MS, DEFAULT_PUSH_DEBOUNCE_MS, type PushFanoutOptions, type FanoutMember } from './push-fanout.js';
37
- export { selectControlCohortDials, DEFAULT_CONTROL_COHORT_RECONCILE_MS, DEFAULT_CONTROL_COHORT_TARGET_DEGREE, type ControlCohortSelection } from './control-cohort.js';
37
+ export { selectControlCohortDials, DEFAULT_CONTROL_COHORT_DIAL_TIMEOUT_MS, DEFAULT_CONTROL_COHORT_RECONCILE_MS, DEFAULT_CONTROL_COHORT_TARGET_DEGREE, type ControlCohortSelection } from './control-cohort.js';
38
38
  export { anchoredTrustPolicy, pinnedKeyTrustPolicy, tofuTrustPolicy, type SeedTrustPolicy, type SeedTrustContext, type SeedTrustDecision } from './seed-trust-policy.js';
39
39
  export { signSchema, verifySchema, assertSchemaSignature, SchemaVerificationError } from './schema-verification.js';
40
40
  export { FormationListener, dialFormation, isValidResponderCreatesResult, FORMATION_PROTOCOL, type FormationParty, type FormationContactMessage, type FormationResultMessage, type FormationProvisionResult, type FormationStrandInfo, type FormationDbConnectionInfo, type FormationListenerOptions, type FormationDialOptions, type ResponderProvisionOutcome } from './strand-formation-protocol.js';
package/dist/index.js CHANGED
@@ -96,7 +96,7 @@ export { validatePushCredentials, redactPushCredentials, REDACTED } from './push
96
96
  export { PushFanoutService, DEFAULT_PUSH_COOLDOWN_MS, DEFAULT_PUSH_DEBOUNCE_MS } from './push-fanout.js';
97
97
  // Control-cohort dial selection (backbone-preferential, bounded out-degree) +
98
98
  // the cadence/degree defaults the proactive reconcile routine reads.
99
- export { selectControlCohortDials, DEFAULT_CONTROL_COHORT_RECONCILE_MS, DEFAULT_CONTROL_COHORT_TARGET_DEGREE } from './control-cohort.js';
99
+ export { selectControlCohortDials, DEFAULT_CONTROL_COHORT_DIAL_TIMEOUT_MS, DEFAULT_CONTROL_COHORT_RECONCILE_MS, DEFAULT_CONTROL_COHORT_TARGET_DEGREE } from './control-cohort.js';
100
100
  // Seed trust policy (trust anchor for incoming seeds)
101
101
  export { anchoredTrustPolicy, pinnedKeyTrustPolicy, tofuTrustPolicy } from './seed-trust-policy.js';
102
102
  // Schema Verification
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Merging verified cadre addresses into a libp2p node's **address book** (its
3
+ * peerStore), so that every layer below cadre-core which dials by bare peer id
4
+ * — Optimystic's cluster client, repo client, FRET ping/announce, all funnelling
5
+ * into `libp2p.dialProtocol(peerId, …)` — has an address to use for a sibling
6
+ * whose connection has dropped, instead of failing with `NoValidAddressesError`
7
+ * until the next reconcile pass happens to re-dial it.
8
+ *
9
+ * Shared by the control-cohort reconcile pass and the strand one (both in
10
+ * `CadreNode`): each resolves a peer's addresses its own way and then needs
11
+ * exactly this write, with exactly this workaround.
12
+ *
13
+ * Two exports, in the order a caller uses them: {@link groupAddrsByPeerId} turns
14
+ * a flat address list into per-peer groups (the strand path starts from the
15
+ * peer-agnostic union the strand-addr RPC returns), and {@link mergePeerAddrs}
16
+ * writes one peer's group.
17
+ */
18
+ import type { PeerId, PeerStore } from '@libp2p/interface';
19
+ import { type Multiaddr } from '@multiformats/multiaddr';
20
+ /** Outcome of one address-book merge, for logging and tests. */
21
+ export type MergeAddrsResult = 'merged' | 'restamped' | 'skipped' | 'failed';
22
+ /**
23
+ * The slice of a libp2p node {@link mergePeerAddrs} touches. Structurally
24
+ * satisfied by `Libp2p`; narrow so a caller (or a test) can pass a bare
25
+ * peerStore holder.
26
+ */
27
+ export interface PeerAddrBookHost {
28
+ peerStore: PeerStore;
29
+ }
30
+ /**
31
+ * Write `addrs` into `host`'s address book for `peerId`, best-effort. `peerId`
32
+ * may be the string form callers hold (a `CadrePeer.PeerId` column, a strand-addr
33
+ * RPC result); parsing it is part of the best-effort contract below, so no caller
34
+ * has to wrap this in a second try/catch of its own.
35
+ *
36
+ * - **Empty `addrs` writes nothing** and reports `'skipped'`. That is how a
37
+ * revoked / stale / untrusted peer's existing entry is allowed to age out on
38
+ * its own: the caller resolves `[]` for it, and nothing here refreshes it.
39
+ * - Otherwise the addresses are merged, and — see the timestamp trap below —
40
+ * re-saved when the merge alone leaves them invisible (`'restamped'`).
41
+ * - Any throw (unparsable peer id, datastore failure, a peerStore double without
42
+ * `merge`) is logged and folded to `'failed'`. Callers are best-effort loops:
43
+ * an address-book failure must never abort a reconcile pass.
44
+ *
45
+ * NOTE: `peerStore.merge` cannot refresh an address's `observed` timestamp in
46
+ * `@libp2p/peer-store` 12.0.10 — `to-peer-pb.js` shadows its own loop variable
47
+ * (`…addresses?.find(addr => uint8ArrayEquals(addr.multiaddr, addr.multiaddr))`,
48
+ * always true, so it returns the FIRST stored address's timestamp), and every
49
+ * read filters out addresses older than `MAX_ADDRESS_AGE` (1 hour). So an entry
50
+ * silently dies at the one-hour mark and no amount of re-merging revives it —
51
+ * not even merging a brand-new address. The `save` below is the workaround:
52
+ * `save` is the one write path that omits `existingPeer` and therefore stamps
53
+ * `Date.now()`. Drop it once upstream stamps `Date.now()` on merge.
54
+ *
55
+ * NOTE: raising the store's own `maxAddressAge` (libp2p forwards `init.peerStore`
56
+ * straight into `persistentPeerStore`) would sidestep the bug in one line, and is
57
+ * deliberately NOT done: expiry is load-bearing here. Only *verified* addresses
58
+ * come through this helper, and the design relies on everything else — the
59
+ * cold-start seed entries, identify-learned addresses — still ageing out on the
60
+ * stock schedule. A global age bump would keep those alive too.
61
+ *
62
+ * NOTE: if the store ever REJECTS an address we hand it, the restamp repeats
63
+ * every pass forever — `allVisible` stays false, and `save` re-submits the same
64
+ * rejected address. libp2p wires the store's `addressFilter` to
65
+ * `connectionGater.filterMultiaddrForPeer`, which nothing in this repo implements
66
+ * today (`createMembershipConnectionGater` gates dials, not addresses), so the
67
+ * loop is unreachable. If an app ever supplies that gater hook via
68
+ * `NetworkConfig.connectionGater`, bound the retry — the cost is one redundant
69
+ * datastore write per gated sibling per reconcile pass.
70
+ */
71
+ export declare function mergePeerAddrs(host: PeerAddrBookHost, peerId: PeerId | string, addrs: Multiaddr[]): Promise<MergeAddrsResult>;
72
+ /**
73
+ * Group multiaddr strings by the peer id in their final `/p2p/` component — the
74
+ * attribution step between a flat, peer-agnostic address list and the per-peer
75
+ * writes {@link mergePeerAddrs} takes.
76
+ *
77
+ * The **last** `/p2p/` component is the addressed peer, not the first: a relayed
78
+ * address (`…/p2p/<relay>/p2p-circuit/p2p/<dst>`) names the relay first and the
79
+ * destination last, while a direct address (`…/tcp/…/p2p/<dst>`) names only the
80
+ * destination. Taking the last therefore attributes both shapes to the peer the
81
+ * address actually reaches.
82
+ *
83
+ * Three shapes are dropped rather than attributed, because none of them names a
84
+ * peer the address reaches: one that does not parse, one carrying no `/p2p/`
85
+ * component at all (a peer advertising a bare listen addr), and one whose
86
+ * `p2p-circuit` comes AFTER its last `/p2p/` (`…/p2p/<relay>/p2p-circuit`) — that
87
+ * last one is a relay hop with the destination missing, so its trailing peer id
88
+ * is the RELAY's and filing it under the relay would claim the relay is reachable
89
+ * at a circuit leading nowhere. Duplicates collapse within a group. Insertion
90
+ * order is preserved, both between groups and inside one.
91
+ */
92
+ export declare function groupAddrsByPeerId(addrs: string[]): Map<string, Multiaddr[]>;
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Merging verified cadre addresses into a libp2p node's **address book** (its
3
+ * peerStore), so that every layer below cadre-core which dials by bare peer id
4
+ * — Optimystic's cluster client, repo client, FRET ping/announce, all funnelling
5
+ * into `libp2p.dialProtocol(peerId, …)` — has an address to use for a sibling
6
+ * whose connection has dropped, instead of failing with `NoValidAddressesError`
7
+ * until the next reconcile pass happens to re-dial it.
8
+ *
9
+ * Shared by the control-cohort reconcile pass and the strand one (both in
10
+ * `CadreNode`): each resolves a peer's addresses its own way and then needs
11
+ * exactly this write, with exactly this workaround.
12
+ *
13
+ * Two exports, in the order a caller uses them: {@link groupAddrsByPeerId} turns
14
+ * a flat address list into per-peer groups (the strand path starts from the
15
+ * peer-agnostic union the strand-addr RPC returns), and {@link mergePeerAddrs}
16
+ * writes one peer's group.
17
+ */
18
+ import debug from 'debug';
19
+ import { peerIdFromString } from '@libp2p/peer-id';
20
+ import { multiaddr, CODE_P2P, CODE_P2P_CIRCUIT } from '@multiformats/multiaddr';
21
+ const log = debug('sereus:cadre:peer-addr-book');
22
+ /**
23
+ * Write `addrs` into `host`'s address book for `peerId`, best-effort. `peerId`
24
+ * may be the string form callers hold (a `CadrePeer.PeerId` column, a strand-addr
25
+ * RPC result); parsing it is part of the best-effort contract below, so no caller
26
+ * has to wrap this in a second try/catch of its own.
27
+ *
28
+ * - **Empty `addrs` writes nothing** and reports `'skipped'`. That is how a
29
+ * revoked / stale / untrusted peer's existing entry is allowed to age out on
30
+ * its own: the caller resolves `[]` for it, and nothing here refreshes it.
31
+ * - Otherwise the addresses are merged, and — see the timestamp trap below —
32
+ * re-saved when the merge alone leaves them invisible (`'restamped'`).
33
+ * - Any throw (unparsable peer id, datastore failure, a peerStore double without
34
+ * `merge`) is logged and folded to `'failed'`. Callers are best-effort loops:
35
+ * an address-book failure must never abort a reconcile pass.
36
+ *
37
+ * NOTE: `peerStore.merge` cannot refresh an address's `observed` timestamp in
38
+ * `@libp2p/peer-store` 12.0.10 — `to-peer-pb.js` shadows its own loop variable
39
+ * (`…addresses?.find(addr => uint8ArrayEquals(addr.multiaddr, addr.multiaddr))`,
40
+ * always true, so it returns the FIRST stored address's timestamp), and every
41
+ * read filters out addresses older than `MAX_ADDRESS_AGE` (1 hour). So an entry
42
+ * silently dies at the one-hour mark and no amount of re-merging revives it —
43
+ * not even merging a brand-new address. The `save` below is the workaround:
44
+ * `save` is the one write path that omits `existingPeer` and therefore stamps
45
+ * `Date.now()`. Drop it once upstream stamps `Date.now()` on merge.
46
+ *
47
+ * NOTE: raising the store's own `maxAddressAge` (libp2p forwards `init.peerStore`
48
+ * straight into `persistentPeerStore`) would sidestep the bug in one line, and is
49
+ * deliberately NOT done: expiry is load-bearing here. Only *verified* addresses
50
+ * come through this helper, and the design relies on everything else — the
51
+ * cold-start seed entries, identify-learned addresses — still ageing out on the
52
+ * stock schedule. A global age bump would keep those alive too.
53
+ *
54
+ * NOTE: if the store ever REJECTS an address we hand it, the restamp repeats
55
+ * every pass forever — `allVisible` stays false, and `save` re-submits the same
56
+ * rejected address. libp2p wires the store's `addressFilter` to
57
+ * `connectionGater.filterMultiaddrForPeer`, which nothing in this repo implements
58
+ * today (`createMembershipConnectionGater` gates dials, not addresses), so the
59
+ * loop is unreachable. If an app ever supplies that gater hook via
60
+ * `NetworkConfig.connectionGater`, bound the retry — the cost is one redundant
61
+ * datastore write per gated sibling per reconcile pass.
62
+ */
63
+ export async function mergePeerAddrs(host, peerId, addrs) {
64
+ if (addrs.length === 0) {
65
+ return 'skipped';
66
+ }
67
+ try {
68
+ const id = typeof peerId === 'string' ? peerIdFromString(peerId) : peerId;
69
+ const peer = await host.peerStore.merge(id, { multiaddrs: addrs });
70
+ // The returned Peer is already expiry-filtered, so it says whether the merge
71
+ // is actually VISIBLE — which is the only question that matters downstream.
72
+ if (allVisible(peer.addresses, addrs, id)) {
73
+ return 'merged';
74
+ }
75
+ await host.peerStore.save(id, restampData(peer, addrs, id));
76
+ return 'restamped';
77
+ }
78
+ catch (error) {
79
+ log('mergePeerAddrs: address-book write for %s failed: %o', peerId.toString(), error);
80
+ return 'failed';
81
+ }
82
+ }
83
+ /**
84
+ * Group multiaddr strings by the peer id in their final `/p2p/` component — the
85
+ * attribution step between a flat, peer-agnostic address list and the per-peer
86
+ * writes {@link mergePeerAddrs} takes.
87
+ *
88
+ * The **last** `/p2p/` component is the addressed peer, not the first: a relayed
89
+ * address (`…/p2p/<relay>/p2p-circuit/p2p/<dst>`) names the relay first and the
90
+ * destination last, while a direct address (`…/tcp/…/p2p/<dst>`) names only the
91
+ * destination. Taking the last therefore attributes both shapes to the peer the
92
+ * address actually reaches.
93
+ *
94
+ * Three shapes are dropped rather than attributed, because none of them names a
95
+ * peer the address reaches: one that does not parse, one carrying no `/p2p/`
96
+ * component at all (a peer advertising a bare listen addr), and one whose
97
+ * `p2p-circuit` comes AFTER its last `/p2p/` (`…/p2p/<relay>/p2p-circuit`) — that
98
+ * last one is a relay hop with the destination missing, so its trailing peer id
99
+ * is the RELAY's and filing it under the relay would claim the relay is reachable
100
+ * at a circuit leading nowhere. Duplicates collapse within a group. Insertion
101
+ * order is preserved, both between groups and inside one.
102
+ */
103
+ export function groupAddrsByPeerId(addrs) {
104
+ const groups = new Map();
105
+ const seen = new Set();
106
+ for (const addr of addrs) {
107
+ const attributed = attributeAddr(addr);
108
+ if (!attributed) {
109
+ continue;
110
+ }
111
+ const key = `${attributed.peerId}\n${addr}`;
112
+ if (seen.has(key)) {
113
+ continue;
114
+ }
115
+ seen.add(key);
116
+ const group = groups.get(attributed.peerId);
117
+ if (group) {
118
+ group.push(attributed.multiaddr);
119
+ }
120
+ else {
121
+ groups.set(attributed.peerId, [attributed.multiaddr]);
122
+ }
123
+ }
124
+ return groups;
125
+ }
126
+ /**
127
+ * Parse one address and pull out the peer id it addresses (its last `/p2p/`
128
+ * value); null when the address is unparsable or names no destination. Every case
129
+ * is logged at the same level — an unattributable address is a sibling telling us
130
+ * something we cannot use, which is worth seeing but never worth throwing over.
131
+ */
132
+ function attributeAddr(addr) {
133
+ let parsed;
134
+ try {
135
+ parsed = multiaddr(addr);
136
+ }
137
+ catch (error) {
138
+ log('groupAddrsByPeerId: skipping unparsable addr %s: %o', addr, error);
139
+ return null;
140
+ }
141
+ const peerId = lastAddressedPeerId(parsed);
142
+ if (peerId === null) {
143
+ log('groupAddrsByPeerId: skipping addr naming no destination peer: %s', addr);
144
+ return null;
145
+ }
146
+ return { peerId, multiaddr: parsed };
147
+ }
148
+ /**
149
+ * The peer id a parsed address reaches: its last `/p2p/` value, or null when it
150
+ * has none — or when a `p2p-circuit` sits after it, which makes that value the
151
+ * relay's rather than a destination's (see {@link groupAddrsByPeerId}). Walking
152
+ * backwards, meeting the circuit first is exactly that case.
153
+ */
154
+ function lastAddressedPeerId(parsed) {
155
+ const components = parsed.getComponents();
156
+ for (let i = components.length - 1; i >= 0; i--) {
157
+ const component = components[i];
158
+ if (component.code === CODE_P2P_CIRCUIT) {
159
+ return null;
160
+ }
161
+ if (component.code === CODE_P2P && component.value) {
162
+ return component.value;
163
+ }
164
+ }
165
+ return null;
166
+ }
167
+ /**
168
+ * The peerStore strips a trailing `/p2p/<peerId>` from an address it stores when
169
+ * that id is the address's FIRST `/p2p/` component (`dedupe-addresses.js`), so a
170
+ * plain address round-trips without its suffix while a relayed one — whose first
171
+ * `/p2p/` names the relay — keeps it. Comparing and de-duplicating on this
172
+ * suffix-stripped form matches either shape.
173
+ */
174
+ function addrKey(addr, peerId) {
175
+ const suffix = `/p2p/${peerId.toString()}`;
176
+ return addr.endsWith(suffix) ? addr.slice(0, -suffix.length) : addr;
177
+ }
178
+ /** Is every address in `addrs` readable back from the stored, expiry-filtered set? */
179
+ function allVisible(stored, addrs, peerId) {
180
+ const keys = new Set(stored.map((a) => addrKey(a.multiaddr.toString(), peerId)));
181
+ return addrs.every((addr) => keys.has(addrKey(addr.toString(), peerId)));
182
+ }
183
+ /**
184
+ * The full `save` payload for a restamp: `save` re-uses the `patch` strategy with
185
+ * no existing peer, so every field it is not handed is DROPPED — and Optimystic
186
+ * classifies peers by their peerStore protocol list (`libp2p-key-network.ts`
187
+ * `membershipOf`), so losing protocols here would break cluster/repo routing.
188
+ * Everything therefore carries forward from the just-merged `peer`.
189
+ *
190
+ * The read-modify-write is NOT atomic against a concurrent `identify` write to
191
+ * the same entry. The loss window is milliseconds and costs at most one identify
192
+ * update, recovered by the next identify push — not worth a lock.
193
+ *
194
+ * A tag's remaining TTL is not carried: the public `Tag` type exposes only
195
+ * `value`, so the stored expiry is unreadable from here and the tag comes back
196
+ * non-expiring. `publicKey` is likewise not passed, which is lossless for the
197
+ * Ed25519 peer ids a cadre uses (the key is embedded in the id).
198
+ */
199
+ function restampData(peer, addrs, peerId) {
200
+ const addresses = new Map();
201
+ for (const address of peer.addresses) {
202
+ addresses.set(addrKey(address.multiaddr.toString(), peerId), address);
203
+ }
204
+ for (const addr of addrs) {
205
+ const key = addrKey(addr.toString(), peerId);
206
+ if (!addresses.has(key)) {
207
+ // Uncertified, matching what `merge`'s own `multiaddrs` path would have
208
+ // stored; an address already present keeps whatever flag it carried.
209
+ addresses.set(key, { multiaddr: addr, isCertified: false });
210
+ }
211
+ }
212
+ return {
213
+ addresses: [...addresses.values()],
214
+ protocols: peer.protocols,
215
+ metadata: new Map(peer.metadata),
216
+ tags: new Map([...peer.tags].map(([name, tag]) => [name, { value: tag.value }])),
217
+ peerRecordEnvelope: peer.peerRecordEnvelope
218
+ };
219
+ }
220
+ //# sourceMappingURL=peer-addr-book.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer-addr-book.js","sourceRoot":"","sources":["../src/peer-addr-book.ts"],"names":[],"mappings":"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"}
@@ -454,6 +454,13 @@ export declare class SeedBootstrapService {
454
454
  * This makes any owner node markable — not just the local one — and ties
455
455
  * `isOwner` to the control table rather than to `peerId === self`.
456
456
  *
457
+ * Read through {@link ControlDatabase.queryCadrePeers}, not a raw `CadrePeer`
458
+ * select: that reader drops any row whose `StampId` is retired in
459
+ * `CadreControl.Revocation`, so a removed member's addresses are never packed
460
+ * into a seed and pushed into a joiner's peerstore (`applySeed` adds every
461
+ * seed peer's addrs and dials the owner-flagged ones). A revoked peer is off
462
+ * the addressable surface everywhere, and this is one of its exits.
463
+ *
457
464
  * NOTE: this is the one owner lookup deliberately left on the REPLICATED
458
465
  * table rather than the node-local anchor. `SeedPeer.isOwner` is a dial hint
459
466
  * — the receiver dials owner-flagged peers first — not a trust decision, and
@@ -251,7 +251,7 @@ export class SeedBootstrapService {
251
251
  insert into CadreControl.DeviceToken (PeerId, Platform, Token, UpdatedAt, Sig, StampId)
252
252
  with context OwnerKey = ?, Signature = ?
253
253
  values (?, ?, ?, ?, ?, ?)
254
- `, [this.ownerPublicKey, signature, record.peerId, record.platform, record.token, record.updatedAt, record.sig, stampId]);
254
+ `, [this.ownerPublicKey, signature, record.peerId, record.platform, record.token, record.updatedAt, record.sig, stampId], 'device-token-insert');
255
255
  log('Device token inserted (owner-signed): %s', record.peerId);
256
256
  }
257
257
  /**
@@ -546,8 +546,8 @@ export class SeedBootstrapService {
546
546
  // `ApplySeedResult.ownerDialsFailed`). Recovery is not this loop's job —
547
547
  // `CadreNode.dialColdStartBootstrap` retries these same addresses on every
548
548
  // control-cohort reconcile pass until the control database has siblings.
549
- // `createSeed` projects EVERY CadrePeer row, so an owner applying a seed
550
- // minted after it joined finds ITSELF in the owner list. Dialing self always
549
+ // `createSeed` projects every non-revoked CadrePeer row, so an owner applying
550
+ // a seed minted after it joined finds ITSELF in the owner list. Dialing self always
551
551
  // throws, which would report a healthy owner as "seeded but stranded".
552
552
  // Optional-chained: partial libp2p handles (unit-test doubles) omit `peerId`,
553
553
  // and an undefined self simply matches nothing.
@@ -720,6 +720,13 @@ export class SeedBootstrapService {
720
720
  * This makes any owner node markable — not just the local one — and ties
721
721
  * `isOwner` to the control table rather than to `peerId === self`.
722
722
  *
723
+ * Read through {@link ControlDatabase.queryCadrePeers}, not a raw `CadrePeer`
724
+ * select: that reader drops any row whose `StampId` is retired in
725
+ * `CadreControl.Revocation`, so a removed member's addresses are never packed
726
+ * into a seed and pushed into a joiner's peerstore (`applySeed` adds every
727
+ * seed peer's addrs and dials the owner-flagged ones). A revoked peer is off
728
+ * the addressable surface everywhere, and this is one of its exits.
729
+ *
723
730
  * NOTE: this is the one owner lookup deliberately left on the REPLICATED
724
731
  * table rather than the node-local anchor. `SeedPeer.isOwner` is a dial hint
725
732
  * — the receiver dials owner-flagged peers first — not a trust decision, and
@@ -733,25 +740,20 @@ export class SeedBootstrapService {
733
740
  return [];
734
741
  }
735
742
  const ownerKeys = await this.controlDatabase.getOwnerKeys();
736
- const db = this.controlDatabase.getDatabase();
737
- const peers = [];
738
- // Query CadrePeer table
739
- for await (const row of db.eval('select PeerId, Multiaddr from CadreControl.CadrePeer')) {
740
- const peerId = row.PeerId;
741
- const multiaddr = row.Multiaddr;
743
+ const rows = await this.controlDatabase.queryCadrePeers();
744
+ return rows.map(({ peerId, multiaddr }) => {
742
745
  // Derive the peer's ed25519 key from its PeerId; a non-Ed25519 peer or an
743
746
  // unparsable id yields null and is treated as a non-owner rather than
744
747
  // failing the whole seed creation.
745
748
  const pubKeyB64 = ed25519PublicKeyB64FromPeerId(peerId);
746
749
  const isOwner = pubKeyB64 !== null && ownerKeys.has(pubKeyB64);
747
- peers.push({
750
+ return {
748
751
  peerId,
749
752
  multiaddrs: multiaddr ? multiaddr.split(',') : [],
750
753
  isOwner,
751
754
  ...(isOwner ? { publicKey: pubKeyB64 } : {}),
752
- });
753
- }
754
- return peers;
755
+ };
756
+ });
755
757
  }
756
758
  /**
757
759
  * Register the seed protocol handler. The inbound closure just delegates to