@serfab/cadre-core 1.17.0 → 1.18.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 (144) hide show
  1. package/README.md +3 -2
  2. package/dist/cadre-invite-protocol.d.ts +35 -13
  3. package/dist/cadre-invite-protocol.js +90 -25
  4. package/dist/cadre-invite-protocol.js.map +1 -1
  5. package/dist/cadre-node.d.ts +586 -255
  6. package/dist/cadre-node.js +1176 -564
  7. package/dist/cadre-node.js.map +1 -1
  8. package/dist/control-cohort.d.ts +66 -9
  9. package/dist/control-cohort.js +67 -9
  10. package/dist/control-cohort.js.map +1 -1
  11. package/dist/control-database.d.ts +451 -193
  12. package/dist/control-database.js +701 -359
  13. package/dist/control-database.js.map +1 -1
  14. package/dist/control-formation-recorder.d.ts +46 -43
  15. package/dist/control-formation-recorder.js +77 -78
  16. package/dist/control-formation-recorder.js.map +1 -1
  17. package/dist/control-protocol-guard.d.ts +6 -0
  18. package/dist/control-protocol-guard.js +14 -2
  19. package/dist/control-protocol-guard.js.map +1 -1
  20. package/dist/control-schema-version.d.ts +48 -31
  21. package/dist/control-schema-version.js +67 -45
  22. package/dist/control-schema-version.js.map +1 -1
  23. package/dist/control-schema.d.ts +8 -8
  24. package/dist/control-schema.js +16 -16
  25. package/dist/control-schema.js.map +1 -1
  26. package/dist/control-write-retry.d.ts +42 -7
  27. package/dist/control-write-retry.js +87 -19
  28. package/dist/control-write-retry.js.map +1 -1
  29. package/dist/enrolled-machine-store.d.ts +3 -3
  30. package/dist/enrolled-machine-store.js +3 -3
  31. package/dist/formation-token.d.ts +48 -0
  32. package/dist/formation-token.js +26 -0
  33. package/dist/formation-token.js.map +1 -0
  34. package/dist/index.d.ts +14 -13
  35. package/dist/index.js +16 -13
  36. package/dist/index.js.map +1 -1
  37. package/dist/joined-strand-store.d.ts +26 -9
  38. package/dist/joined-strand-store.js +63 -24
  39. package/dist/joined-strand-store.js.map +1 -1
  40. package/dist/link-budget.d.ts +9 -7
  41. package/dist/link-budget.js +9 -7
  42. package/dist/link-budget.js.map +1 -1
  43. package/dist/member-redemption.d.ts +15 -9
  44. package/dist/member-redemption.js +13 -8
  45. package/dist/member-redemption.js.map +1 -1
  46. package/dist/membership-view.d.ts +91 -0
  47. package/dist/membership-view.js +110 -0
  48. package/dist/membership-view.js.map +1 -0
  49. package/dist/owner-grant-protocol.d.ts +11 -7
  50. package/dist/owner-grant-protocol.js +31 -17
  51. package/dist/owner-grant-protocol.js.map +1 -1
  52. package/dist/peer-addr-book.d.ts +10 -1
  53. package/dist/peer-addr-book.js +4 -3
  54. package/dist/peer-addr-book.js.map +1 -1
  55. package/dist/peer-join-backfill.d.ts +89 -20
  56. package/dist/peer-join-backfill.js +265 -108
  57. package/dist/peer-join-backfill.js.map +1 -1
  58. package/dist/pending-join-runner.d.ts +48 -18
  59. package/dist/pending-join-runner.js +74 -56
  60. package/dist/pending-join-runner.js.map +1 -1
  61. package/dist/seed-bootstrap.d.ts +47 -40
  62. package/dist/seed-bootstrap.js +66 -61
  63. package/dist/seed-bootstrap.js.map +1 -1
  64. package/dist/strand-database.d.ts +23 -2
  65. package/dist/strand-database.js +26 -6
  66. package/dist/strand-database.js.map +1 -1
  67. package/dist/strand-first-sync-gate.d.ts +176 -89
  68. package/dist/strand-first-sync-gate.js +288 -122
  69. package/dist/strand-first-sync-gate.js.map +1 -1
  70. package/dist/strand-formation-deadlines.d.ts +9 -9
  71. package/dist/strand-formation-deadlines.js +9 -9
  72. package/dist/strand-formation-manager.d.ts +23 -17
  73. package/dist/strand-formation-manager.js +46 -44
  74. package/dist/strand-formation-manager.js.map +1 -1
  75. package/dist/strand-formation-protocol.d.ts +5 -8
  76. package/dist/strand-formation-protocol.js +101 -16
  77. package/dist/strand-formation-protocol.js.map +1 -1
  78. package/dist/strand-instance-manager.d.ts +26 -17
  79. package/dist/strand-instance-manager.js +119 -50
  80. package/dist/strand-instance-manager.js.map +1 -1
  81. package/dist/strand-membership-reconciler.d.ts +16 -13
  82. package/dist/strand-membership-reconciler.js +30 -22
  83. package/dist/strand-membership-reconciler.js.map +1 -1
  84. package/dist/strand-membership-writer.d.ts +40 -28
  85. package/dist/strand-membership-writer.js +212 -153
  86. package/dist/strand-membership-writer.js.map +1 -1
  87. package/dist/strand-move.js +5 -2
  88. package/dist/strand-move.js.map +1 -1
  89. package/dist/strand-revocation-enforcer.d.ts +134 -60
  90. package/dist/strand-revocation-enforcer.js +144 -71
  91. package/dist/strand-revocation-enforcer.js.map +1 -1
  92. package/dist/strand-solicitation.d.ts +50 -39
  93. package/dist/strand-solicitation.js +10 -3
  94. package/dist/strand-solicitation.js.map +1 -1
  95. package/dist/strand-watcher.d.ts +80 -13
  96. package/dist/strand-watcher.js +152 -48
  97. package/dist/strand-watcher.js.map +1 -1
  98. package/dist/table-change-watch.d.ts +63 -0
  99. package/dist/table-change-watch.js +209 -0
  100. package/dist/table-change-watch.js.map +1 -0
  101. package/dist/timeout-scheduler.d.ts +4 -3
  102. package/dist/timeout-scheduler.js +4 -3
  103. package/dist/timeout-scheduler.js.map +1 -1
  104. package/dist/types.d.ts +90 -49
  105. package/dist/types.js +4 -4
  106. package/dist/types.js.map +1 -1
  107. package/package.json +8 -8
  108. package/src/cadre-invite-protocol.ts +119 -31
  109. package/src/cadre-node.ts +1282 -584
  110. package/src/control-cohort.ts +86 -9
  111. package/src/control-database.ts +848 -403
  112. package/src/control-formation-recorder.ts +359 -353
  113. package/src/control-protocol-guard.ts +16 -2
  114. package/src/control-schema-version.ts +101 -51
  115. package/src/control-schema.ts +16 -16
  116. package/src/control-write-retry.ts +91 -19
  117. package/src/delegate-admission.ts +351 -351
  118. package/src/enrolled-machine-store.ts +3 -3
  119. package/src/formation-token.ts +63 -0
  120. package/src/index.ts +30 -17
  121. package/src/joined-strand-store.ts +69 -24
  122. package/src/link-budget.ts +9 -7
  123. package/src/member-redemption.ts +20 -13
  124. package/src/membership-view.ts +144 -0
  125. package/src/owner-grant-protocol.ts +33 -18
  126. package/src/peer-addr-book.ts +4 -3
  127. package/src/peer-join-backfill.ts +971 -761
  128. package/src/pending-join-runner.ts +76 -53
  129. package/src/seed-bootstrap.ts +72 -67
  130. package/src/strand-database.ts +352 -322
  131. package/src/strand-first-sync-gate.ts +334 -134
  132. package/src/strand-formation-deadlines.ts +227 -227
  133. package/src/strand-formation-manager.ts +731 -725
  134. package/src/strand-formation-protocol.ts +122 -24
  135. package/src/strand-instance-manager.ts +2092 -2026
  136. package/src/strand-membership-reconciler.ts +33 -25
  137. package/src/strand-membership-writer.ts +2093 -2019
  138. package/src/strand-move.ts +5 -2
  139. package/src/strand-revocation-enforcer.ts +740 -654
  140. package/src/strand-solicitation.ts +61 -34
  141. package/src/strand-watcher.ts +175 -56
  142. package/src/table-change-watch.ts +285 -0
  143. package/src/timeout-scheduler.ts +29 -28
  144. package/src/types.ts +89 -51
@@ -2,19 +2,19 @@ import { Parser } from '@quereus/quereus';
2
2
  /**
3
3
  * The version of the declared control schema, carried in the schema text as the
4
4
  * {@link CONTROL_SCHEMA_VERSION_TAG} tag on the `OwnerKey` table. A warm start compares
5
- * it with the version the store records before applying anything
6
- * (`control-schema-version.ts`, which also says why that is the version the store was
7
- * created under).
5
+ * it with the version the store records before applying anything, and the apply then
6
+ * migrates the store to this version and records it (`control-schema-version.ts`).
8
7
  *
9
8
  * When it changes, what a new version may change, and the fixture each version keeps:
10
- * docs/architecture.md → "Control schema changes and incompatible stores", enforced by
9
+ * docs/architecture.md → "Control schema versions and migrations", enforced by
11
10
  * `control-schema-versions.spec.ts`.
12
11
  */
13
12
  export const CONTROL_SCHEMA_VERSION = 3;
14
13
  /**
15
14
  * The first control schema version whose fixture is kept, and the oldest a store can be
16
- * migrated from. Stores that record no version (written by 1.16.0 and earlier) are not
17
- * migrated; docs/architecture.md says why.
15
+ * migrated from. A store that records a version below it (written by 1.16.0 and earlier,
16
+ * which recorded none) is refused with reason `before-migrations`; docs/architecture.md
17
+ * says why it is not migrated.
18
18
  */
19
19
  export const CONTROL_SCHEMA_BASELINE_VERSION = 3;
20
20
  /** The `OwnerKey` table tag that records {@link CONTROL_SCHEMA_VERSION} in the schema and in every store. */
@@ -663,7 +663,7 @@ declare schema CadreControl {
663
663
  ) with context (Now datetime, ValidationKey text null, ValidationSignature text null);
664
664
 
665
665
  -- Serves the per-token reads (the cap count above; cadre-core's countFormationUsage,
666
- -- isTokenUsed and hasOutstandingFormationInvite) on an append-only table. Its cross-machine
666
+ -- readToken and hasOutstandingFormationInvite) on an append-only table. Its cross-machine
667
667
  -- convergence is guarded by the integration scenario strand-formation-concurrent-redemption.
668
668
  index FormationUsageByToken on FormationUsage (Token);
669
669
 
@@ -899,16 +899,15 @@ declare schema CadreControl {
899
899
 
900
900
  -- Append-only retirement record for the stamps of removed rows of every guarded table, with
901
901
  -- the removed row's key. Every append is owner-signed, so the growth surface is the owner
902
- -- keys. One singleton marker row, ('Revocation', 'ledger', 'opened'), retires nothing: an
903
- -- owner's connected reconcile pass files it (ControlDatabase.openRevocationLedger) so this
904
- -- table is never a never-written block, which the storage layer would consult the cohort
905
- -- for on every guarded insert and membership lookup. Readers filter on their own guarded
902
+ -- keys. A party founded before genesis created every control collection may hold one marker
903
+ -- row, ('Revocation', 'ledger', 'opened'), which retires nothing: an owner filed it so this
904
+ -- table had a collection. Nothing files it now. Readers filter on their own guarded
906
905
  -- TableName, and queryRevocations skips it. A 'CadreInvite' or 'OwnerGrant' row withdraws
907
906
  -- an invitation or a grant whose row stays (the table comments).
908
907
  table Revocation (
909
908
  TableName text, -- 'OwnerKey' | 'ValidationKey' | 'Strand' | 'StrandPartyKey' | 'JoinedStrand' |
910
909
  -- 'JoinRequest' | 'CadrePeer' | 'DeviceToken' | 'CadreInvite' | 'OwnerGrant', or
911
- -- 'Revocation' for the marker
910
+ -- 'Revocation' for an older party's marker
912
911
  RowKey text, -- primary key of the removed row
913
912
  StampId text, -- the retired stamp
914
913
  ReissuedAt integer default 0, -- bumped by an owner-signed re-issue so a tombstone committed while the
@@ -938,7 +937,9 @@ declare schema CadreControl {
938
937
  -- never existed is harmless: stamps carry 128 random bits (generateStampId). For
939
938
  -- 'CadreInvite' and 'OwnerGrant' there is no row-gone test: a tombstone there is a
940
939
  -- withdrawal, and the row stays (the table comments). The last branch admits the marker
941
- -- row and nothing else under 'Revocation'.
940
+ -- row an older party may hold, and nothing else under 'Revocation'; nothing files it now.
941
+ -- NOTE: drop this branch, REVOCATION_LEDGER_MARKER and the readers' skip together with the
942
+ -- next CONTROL_SCHEMA_VERSION change that already needs a migration.
942
943
  constraint RowIsGone check on insert (
943
944
  (new.TableName = 'OwnerKey' and not exists (select 1 from OwnerKey K where K.StampId = new.StampId))
944
945
  or (new.TableName = 'CadrePeer' and not exists (select 1 from CadrePeer P where P.StampId = new.StampId))
@@ -957,9 +958,8 @@ declare schema CadreControl {
957
958
  -- writer could evict a peer party-wide and block its re-admission. RowKey is not checked
958
959
  -- against the retired row here (that would reject a tombstone re-issued later, or filed on
959
960
  -- a node that never held the row); each guarded table's RevocationRecorded binds it
960
- -- instead. Reads live OwnerKey like the sibling tables. The marker is signed under this
961
- -- rule too, over ('Revocation', 'ledger', 'opened'). The stored signer pair must be the
962
- -- pair verified here.
961
+ -- instead. Reads live OwnerKey like the sibling tables. An older party's marker was signed
962
+ -- under this rule too. The stored signer pair must be the pair verified here.
963
963
  constraint Authorized check on insert (
964
964
  exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.Revocation', 'remove', party_id(), new.TableName, new.RowKey, new.StampId), context.Signature, A.Key, 'ed25519'))
965
965
  and coalesce(new.SignerKey, '') = context.OwnerKey
@@ -1 +1 @@
1
- {"version":3,"file":"control-schema.js","sourceRoot":"","sources":["../src/control-schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"control-schema.js","sourceRoot":"","sources":["../src/control-schema.ts"],"names":[],"mappings":"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"}
@@ -28,12 +28,13 @@ import type { ControlRetryOptions } from './control-retry.js';
28
28
  * before any matcher runs. The one half-landed write that IS re-presented is a torn write the
29
29
  * library marks final — not saved and unable to land ({@link isFinalTornWrite}).
30
30
  *
31
- * TWO policies ship from here, and the split is deliberate: the default
31
+ * THREE policies ship from here, and the split is deliberate: the default
32
32
  * ({@link isRetriableControlWriteFailure}, {@link CONTROL_WRITE_ATTEMPTS}) sits under every
33
- * control write, and {@link SCHEMA_INIT_RETRY_POLICY} is opted into by
34
- * `ControlDatabase.applyControlSchema` alone. The widened one absorbs one extra failure class whose
35
- * re-run safety holds for schema DDL and NOT for writes in general; anything added to the default
36
- * list must be safe for all ~19 callers.
33
+ * control write; {@link SCHEMA_INIT_RETRY_POLICY} is opted into by
34
+ * `ControlDatabase.applyControlSchema` alone; {@link LOST_UPDATE_RETRY_POLICY} is opted into by
35
+ * the `CadrePeer` writers whose bodies re-read the row and decide again. Each widened one absorbs
36
+ * one extra failure class whose re-run safety holds for its opted-in bodies and NOT for writes in
37
+ * general; anything added to the default list must be safe for all ~19 callers.
37
38
  *
38
39
  * Lives outside `control-database.ts` so a spec can drive the classifier against real engine
39
40
  * errors without importing the whole database class. The loop itself lives in
@@ -218,6 +219,14 @@ export declare function isRetriableControlWriteFailure(error: unknown): boolean;
218
219
  * default classifier untouched.
219
220
  */
220
221
  export declare function isRetriableSchemaInitFailure(error: unknown): boolean;
222
+ /**
223
+ * {@link isRetriableControlWriteFailure} widened by exactly one class — a lost update
224
+ * ({@link isLostUpdate}), claimed by type — for the `CadrePeer` writers that re-read the row inside
225
+ * their locked body. Opt-in per call site, via {@link LOST_UPDATE_RETRY_POLICY}; both vetoes still
226
+ * run first, so a lost update carried inside a partial commit, a spent sync budget or a non-final
227
+ * torn write stays declined.
228
+ */
229
+ export declare function isRetriableLostUpdateFailure(error: unknown): boolean;
221
230
  /**
222
231
  * Policy and pacing overrides for {@link retryControlWrite}. Production callers pass nothing
223
232
  * (or a named policy such as {@link SCHEMA_INIT_RETRY_POLICY}); specs inject a recorded
@@ -230,7 +239,7 @@ export declare function isRetriableSchemaInitFailure(error: unknown): boolean;
230
239
  */
231
240
  export type ControlWriteRetryOptions = ControlRetryOptions;
232
241
  /**
233
- * The one non-default retry policy: `ControlDatabase.applyControlSchema`'s distributed DDL, which
242
+ * The schema-init policy: `ControlDatabase.applyControlSchema`'s distributed DDL, which
234
243
  * additionally absorbs optimystic's self-coordination grace refusal and gets more attempts over a
235
244
  * longer backoff to do it (see {@link SCHEMA_INIT_ATTEMPTS} and
236
245
  * {@link SCHEMA_INIT_RETRY_DELAYS_MS}). Still bounded by the shared
@@ -241,9 +250,35 @@ export type ControlWriteRetryOptions = ControlRetryOptions;
241
250
  * and verified against the pre-apply catalog (full argument at the `applyControlSchema` call site
242
251
  * in `control-database.ts`). A second opt-in must re-derive that argument for its own write body
243
252
  * first; if this policy ever grows a third consumer, rename it for what the callers share rather
244
- * than widening it by default.
253
+ * than widening it by default. {@link LOST_UPDATE_RETRY_POLICY} is a separate policy for a
254
+ * separate class, not a second consumer of this one.
245
255
  */
246
256
  export declare const SCHEMA_INIT_RETRY_POLICY: Readonly<ControlWriteRetryOptions>;
257
+ /**
258
+ * The lost-update policy: the default classifier plus a lost update claimed by type
259
+ * ({@link isRetriableLostUpdateFailure}), over the default attempts, backoff and budget. A re-run
260
+ * here is a RE-DECISION, not a re-presentation: the body reads the row again under the lock and
261
+ * signs over what it finds, so the write that lost to a rival is replaced by one made over the
262
+ * rival's result. The budget bounds it as it bounds every write — a losing attempt whose read
263
+ * phase waited out one per-peer read deadline (~7.5 s measured in the degraded-cohort scenario's
264
+ * trio boot) gets exactly one retry, which is the ceiling to expect; one that waited out two
265
+ * (14.7 s, measured once in 18 of that scenario's boots, on the member that cannot reach one
266
+ * sibling) gets none and is abandoned on the budget, and the lost publish is then the reconcile
267
+ * pass's or the heartbeat's to redo, as every abandoned self-publish is.
268
+ *
269
+ * NOTE: the re-run safety this policy assumes is each opted-in body's, not a general property. A
270
+ * body may opt in only when its read of the row, its decision and its signature ALL run inside
271
+ * the locked body, so that the re-run presents a write made over the row as it now stands rather
272
+ * than the first attempt's signed record (which the rival may have stamped past, or which may
273
+ * overwrite what the rival wrote). Three `CadrePeer` writers opt in, through
274
+ * `ControlDatabase.mutateCadrePeer`'s policy parameter or `lockedWithRetry` directly:
275
+ * `updateSelfPeerRecord` (re-reads and re-signs the node's own record), `reauthorizeCadrePeer`
276
+ * (re-reads the stamp and decides again whether the row is still the owner's to re-touch) and
277
+ * the two removals `deleteCadrePeer` / `removeMachine` (stamp read and signature already inside
278
+ * the body, and a self-publish never changes the stamp the `'remove'` digest binds). A new opt-in
279
+ * must re-derive that argument for its own body first.
280
+ */
281
+ export declare const LOST_UPDATE_RETRY_POLICY: Readonly<ControlWriteRetryOptions>;
247
282
  /**
248
283
  * Run `attempt` up to {@link CONTROL_WRITE_ATTEMPTS} times (or `options.attempts`), retrying only
249
284
  * failures the policy's classifier — {@link isRetriableControlWriteFailure} by default —
@@ -1,5 +1,5 @@
1
1
  import { CoordinatorPartialCommitError, SyncRetryExhaustedError, TornActionError } from '@optimystic/db-core';
2
- import { PartialCommitError } from '@optimystic/quereus-plugin-optimystic';
2
+ import { ConcurrentModificationError, PartialCommitError } from '@optimystic/quereus-plugin-optimystic';
3
3
  import { causeChain, chainMessages, retryControlOperation } from './control-retry.js';
4
4
  /**
5
5
  * Bounded retry for TRANSIENT control-write failures — the classifier and loop behind
@@ -30,12 +30,13 @@ import { causeChain, chainMessages, retryControlOperation } from './control-retr
30
30
  * before any matcher runs. The one half-landed write that IS re-presented is a torn write the
31
31
  * library marks final — not saved and unable to land ({@link isFinalTornWrite}).
32
32
  *
33
- * TWO policies ship from here, and the split is deliberate: the default
33
+ * THREE policies ship from here, and the split is deliberate: the default
34
34
  * ({@link isRetriableControlWriteFailure}, {@link CONTROL_WRITE_ATTEMPTS}) sits under every
35
- * control write, and {@link SCHEMA_INIT_RETRY_POLICY} is opted into by
36
- * `ControlDatabase.applyControlSchema` alone. The widened one absorbs one extra failure class whose
37
- * re-run safety holds for schema DDL and NOT for writes in general; anything added to the default
38
- * list must be safe for all ~19 callers.
35
+ * control write; {@link SCHEMA_INIT_RETRY_POLICY} is opted into by
36
+ * `ControlDatabase.applyControlSchema` alone; {@link LOST_UPDATE_RETRY_POLICY} is opted into by
37
+ * the `CadrePeer` writers whose bodies re-read the row and decide again. Each widened one absorbs
38
+ * one extra failure class whose re-run safety holds for its opted-in bodies and NOT for writes in
39
+ * general; anything added to the default list must be safe for all ~19 callers.
39
40
  *
40
41
  * Lives outside `control-database.ts` so a spec can drive the classifier against real engine
41
42
  * errors without importing the whole database class. The loop itself lives in
@@ -324,6 +325,33 @@ function isFinalTornWrite(link) {
324
325
  function isUnansweredSuperMajorityShortfall(message) {
325
326
  return SUPER_MAJORITY_SHORTFALL_UNANSWERED.test(message);
326
327
  }
328
+ /**
329
+ * A LOST UPDATE: the optimystic bridge refusing a commit because another writer changed or
330
+ * removed a row this statement read (`TransactionBridge.mapCommitRefusal` over a
331
+ * `TreeEntryChangedError`, `quereus-plugin-optimystic/src/optimystic-adapter/txn-bridge.ts`).
332
+ * Claimed by TYPE for {@link LOST_UPDATE_RETRY_POLICY} only.
333
+ *
334
+ * Why it is a proven non-commit: the refusal is raised inside the action's atomic handler, so
335
+ * none of that action's entries land. A refusal after a sibling tree or collection of the same
336
+ * transaction already synced is not delivered bare — the per-tree sweep wraps it in
337
+ * `PartialCommitError`, the coordinator path in `CoordinatorPartialCommitError` — and
338
+ * {@link reportsPossiblyStoredWrite} vetoes both by type before this predicate runs. A bare chain
339
+ * carrying this class therefore means nothing of the write is durable.
340
+ *
341
+ * Why it is not in the default list: the rejection is of the write's READ SET, not of its
342
+ * signature, so re-presenting the same statement is only right when the body re-reads the row and
343
+ * decides again over what it finds. That is a property of each opted-in body (see the policy's
344
+ * `NOTE:`), not of control writes in general.
345
+ *
346
+ * Not matched by text: `ConcurrentModificationError` is exported from both the plugin's root and
347
+ * `/plugin` entries over one shared chunk, as `PartialCommitError` is, so the one `instanceof`
348
+ * covers both (the `NOTE:` on {@link reportsPossiblyStoredWrite} applies to it too). A second
349
+ * loaded copy of the plugin would fail the `instanceof` and the lost update would then simply not
350
+ * be retried — the safe side.
351
+ */
352
+ function isLostUpdate(link) {
353
+ return link instanceof ConcurrentModificationError;
354
+ }
327
355
  /**
328
356
  * Optimystic refusing to let a node elect ITSELF coordinator because its last connection dropped
329
357
  * less than the guard's grace period ago (`Libp2pKeyPeerNetwork.shouldAllowSelfCoordination` →
@@ -388,9 +416,9 @@ const RETRIABLE_CONTROL_WRITE_MATCHERS = [
388
416
  * does not complete — an undo statement threw, the post-unwind catalog did not match the pre-apply
389
417
  * fingerprint, that catalog could not be re-collected to check, or a step the differ marks
390
418
  * irreversible because it discards data (dropping a table or a column, narrowing a column's type —
391
- * none of which reaches a `CadreControl` apply: a cold store's diff is all creates, and
392
- * `refuseIncompatibleControlStore` in `control-schema-version.ts` refuses a warm store whose diff
393
- * holds anything but creates and tag changes before the apply runs) poisoned the journal before it
419
+ * of which only dropping a column reaches a `CadreControl` apply, as the catalog-only migration
420
+ * step `refuseIncompatibleControlStore` in `control-schema-version.ts` lets through; it discards
421
+ * nothing there, since rows keep the field) poisoned the journal before it
394
422
  * ran, leaving nothing unwound — Quereus keeps the failing step's own message, APPENDS the reason the
395
423
  * schema could not be restored, and carries the original as `cause`. So a transient cause
396
424
  * underneath still matches here, and neither veto ({@link reportsPossiblyStoredWrite},
@@ -443,14 +471,26 @@ export function isRetriableSchemaInitFailure(error) {
443
471
  return matchesRetriableFailure(error, RETRIABLE_SCHEMA_INIT_MATCHERS);
444
472
  }
445
473
  /**
446
- * Shared body of both classifiers: walk the `cause` chain, let {@link reportsPossiblyStoredWrite}
447
- * and then {@link reportsIndeterminateCommit} veto it, then claim a final torn write
448
- * ({@link isFinalTornWrite}), then ask `matchers`. The vetoes run BEFORE any matcher for every
449
- * policy — a chain reporting a possibly-stored write or a commit-phase batch is never retried,
450
- * however transient some other level looks, and a final torn write carried by a partial commit is
451
- * still declined because a sibling collection landed.
474
+ * {@link isRetriableControlWriteFailure} widened by exactly one class — a lost update
475
+ * ({@link isLostUpdate}), claimed by type — for the `CadrePeer` writers that re-read the row inside
476
+ * their locked body. Opt-in per call site, via {@link LOST_UPDATE_RETRY_POLICY}; both vetoes still
477
+ * run first, so a lost update carried inside a partial commit, a spent sync budget or a non-final
478
+ * torn write stays declined.
452
479
  */
453
- function matchesRetriableFailure(error, matchers) {
480
+ export function isRetriableLostUpdateFailure(error) {
481
+ return matchesRetriableFailure(error, RETRIABLE_CONTROL_WRITE_MATCHERS, isLostUpdate);
482
+ }
483
+ /**
484
+ * Shared body of the three classifiers: walk the `cause` chain, let
485
+ * {@link reportsPossiblyStoredWrite} and then {@link reportsIndeterminateCommit} veto it, then claim
486
+ * a final torn write ({@link isFinalTornWrite}) or any link `claimsLink` names, then ask `matchers`.
487
+ * The vetoes run BEFORE any claim for every policy — a chain reporting a possibly-stored write or a
488
+ * commit-phase batch is never retried, however transient some other level looks, and a final torn
489
+ * write or a lost update carried by a partial commit is still declined because a sibling collection
490
+ * landed. `claimsLink` is the one typed claim a policy may add; it sits here beside the torn-write
491
+ * claim so the vetoes stay in one place above both.
492
+ */
493
+ function matchesRetriableFailure(error, matchers, claimsLink) {
454
494
  if (!(error instanceof Error)) {
455
495
  return false;
456
496
  }
@@ -462,13 +502,13 @@ function matchesRetriableFailure(error, matchers) {
462
502
  if (reportsIndeterminateCommit(messages)) {
463
503
  return false;
464
504
  }
465
- if (links.some(isFinalTornWrite)) {
505
+ if (links.some(link => isFinalTornWrite(link) || claimsLink?.(link) === true)) {
466
506
  return true;
467
507
  }
468
508
  return messages.some(message => matchers.some(matches => matches(message)));
469
509
  }
470
510
  /**
471
- * The one non-default retry policy: `ControlDatabase.applyControlSchema`'s distributed DDL, which
511
+ * The schema-init policy: `ControlDatabase.applyControlSchema`'s distributed DDL, which
472
512
  * additionally absorbs optimystic's self-coordination grace refusal and gets more attempts over a
473
513
  * longer backoff to do it (see {@link SCHEMA_INIT_ATTEMPTS} and
474
514
  * {@link SCHEMA_INIT_RETRY_DELAYS_MS}). Still bounded by the shared
@@ -479,13 +519,41 @@ function matchesRetriableFailure(error, matchers) {
479
519
  * and verified against the pre-apply catalog (full argument at the `applyControlSchema` call site
480
520
  * in `control-database.ts`). A second opt-in must re-derive that argument for its own write body
481
521
  * first; if this policy ever grows a third consumer, rename it for what the callers share rather
482
- * than widening it by default.
522
+ * than widening it by default. {@link LOST_UPDATE_RETRY_POLICY} is a separate policy for a
523
+ * separate class, not a second consumer of this one.
483
524
  */
484
525
  export const SCHEMA_INIT_RETRY_POLICY = {
485
526
  attempts: SCHEMA_INIT_ATTEMPTS,
486
527
  delaysMs: SCHEMA_INIT_RETRY_DELAYS_MS,
487
528
  isRetriable: isRetriableSchemaInitFailure,
488
529
  };
530
+ /**
531
+ * The lost-update policy: the default classifier plus a lost update claimed by type
532
+ * ({@link isRetriableLostUpdateFailure}), over the default attempts, backoff and budget. A re-run
533
+ * here is a RE-DECISION, not a re-presentation: the body reads the row again under the lock and
534
+ * signs over what it finds, so the write that lost to a rival is replaced by one made over the
535
+ * rival's result. The budget bounds it as it bounds every write — a losing attempt whose read
536
+ * phase waited out one per-peer read deadline (~7.5 s measured in the degraded-cohort scenario's
537
+ * trio boot) gets exactly one retry, which is the ceiling to expect; one that waited out two
538
+ * (14.7 s, measured once in 18 of that scenario's boots, on the member that cannot reach one
539
+ * sibling) gets none and is abandoned on the budget, and the lost publish is then the reconcile
540
+ * pass's or the heartbeat's to redo, as every abandoned self-publish is.
541
+ *
542
+ * NOTE: the re-run safety this policy assumes is each opted-in body's, not a general property. A
543
+ * body may opt in only when its read of the row, its decision and its signature ALL run inside
544
+ * the locked body, so that the re-run presents a write made over the row as it now stands rather
545
+ * than the first attempt's signed record (which the rival may have stamped past, or which may
546
+ * overwrite what the rival wrote). Three `CadrePeer` writers opt in, through
547
+ * `ControlDatabase.mutateCadrePeer`'s policy parameter or `lockedWithRetry` directly:
548
+ * `updateSelfPeerRecord` (re-reads and re-signs the node's own record), `reauthorizeCadrePeer`
549
+ * (re-reads the stamp and decides again whether the row is still the owner's to re-touch) and
550
+ * the two removals `deleteCadrePeer` / `removeMachine` (stamp read and signature already inside
551
+ * the body, and a self-publish never changes the stamp the `'remove'` digest binds). A new opt-in
552
+ * must re-derive that argument for its own body first.
553
+ */
554
+ export const LOST_UPDATE_RETRY_POLICY = {
555
+ isRetriable: isRetriableLostUpdateFailure,
556
+ };
489
557
  /**
490
558
  * Run `attempt` up to {@link CONTROL_WRITE_ATTEMPTS} times (or `options.attempts`), retrying only
491
559
  * failures the policy's classifier — {@link isRetriableControlWriteFailure} by default —
@@ -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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"}
@@ -65,9 +65,9 @@ export declare class PersistentEnrolledMachineStore implements EnrolledMachineSt
65
65
  * Record the count: visible via {@link count} synchronously, then persisted.
66
66
  *
67
67
  * Writes are SKIPPED when the slot already holds this number, because the sole
68
- * caller runs on every committed membership write *and* every timed reconcile
69
- * pass — a party whose membership is stable would otherwise rewrite the same
70
- * integer forever. The skip is keyed off {@link persisted} rather than
68
+ * caller runs on every membership refresh — each committed membership write, each
69
+ * replicated membership change, and each reconcile pass down to its backstop — so a
70
+ * party whose membership is stable would otherwise rewrite the same integer forever. The skip is keyed off {@link persisted} rather than
71
71
  * {@link current}, so a write that FAILED is retried by the next refresh instead
72
72
  * of being suppressed as "unchanged".
73
73
  */
@@ -123,9 +123,9 @@ export class PersistentEnrolledMachineStore {
123
123
  * Record the count: visible via {@link count} synchronously, then persisted.
124
124
  *
125
125
  * Writes are SKIPPED when the slot already holds this number, because the sole
126
- * caller runs on every committed membership write *and* every timed reconcile
127
- * pass — a party whose membership is stable would otherwise rewrite the same
128
- * integer forever. The skip is keyed off {@link persisted} rather than
126
+ * caller runs on every membership refresh — each committed membership write, each
127
+ * replicated membership change, and each reconcile pass down to its backstop — so a
128
+ * party whose membership is stable would otherwise rewrite the same integer forever. The skip is keyed off {@link persisted} rather than
129
129
  * {@link current}, so a write that FAILED is retried by the next refresh instead
130
130
  * of being suppressed as "unchanged".
131
131
  */
@@ -0,0 +1,48 @@
1
+ import type { FormationRejectionCode } from './strand-formation-rejection.js';
2
+ /** The `FormationInvite` columns a formation session acts on. */
3
+ export interface FormationInviteFacts {
4
+ sAppId: string;
5
+ /** Epoch ms of the stored `ExpiresAt`; null when the invitation never expires. */
6
+ expiresAtMs: number | null;
7
+ /** Null is unlimited. */
8
+ totalUses: number | null;
9
+ /** A bound invitation's host strand; null on the responder-provisions path. */
10
+ strandId: string | null;
11
+ /** The outside approval hook; null when no approval is required. */
12
+ validationUrl: string | null;
13
+ }
14
+ /**
15
+ * One formation token as read once at the start of a responder session. Every later step of
16
+ * the session takes this value instead of reading the invite again.
17
+ */
18
+ export interface FormationTokenRead {
19
+ token: string;
20
+ /** Null: no invite row here (it may not have replicated to this machine yet). */
21
+ invite: FormationInviteFacts | null;
22
+ /** Uses recorded so far; counted only for a metered, unexpired invite, else null. */
23
+ usesRecorded: number | null;
24
+ }
25
+ /**
26
+ * What the responder's token check yields; an invalid token names which way it failed. The
27
+ * valid arm carries the session's {@link FormationTokenRead}, absent only when the manager has
28
+ * no recorder and so accepts every token with nothing read.
29
+ */
30
+ export type FormationTokenCheck = {
31
+ valid: true;
32
+ read?: FormationTokenRead;
33
+ } | {
34
+ valid: false;
35
+ code: Extract<FormationRejectionCode, 'token-unknown' | 'token-spent'>;
36
+ };
37
+ /**
38
+ * `expiresAtMs <= nowMs` is expired: the same boundary `hasOutstandingFormationInvite` applies,
39
+ * so an invite the formation handler refuses can never hold the connection gate open.
40
+ */
41
+ export declare function isInviteExpired(invite: FormationInviteFacts, nowMs: number): boolean;
42
+ /** Used up: a metered invite whose recorded uses reached `totalUses`. An unknown token is not used up. */
43
+ export declare function isTokenReadSpent(read: FormationTokenRead): boolean;
44
+ /**
45
+ * No invite row is `'token-unknown'` (retryable: the row may not have replicated to this
46
+ * machine yet); an expired or used-up invite is `'token-spent'` (final).
47
+ */
48
+ export declare function classifyTokenRead(read: FormationTokenRead, nowMs: number): FormationTokenCheck;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * `expiresAtMs <= nowMs` is expired: the same boundary `hasOutstandingFormationInvite` applies,
3
+ * so an invite the formation handler refuses can never hold the connection gate open.
4
+ */
5
+ export function isInviteExpired(invite, nowMs) {
6
+ return invite.expiresAtMs !== null && invite.expiresAtMs <= nowMs;
7
+ }
8
+ /** Used up: a metered invite whose recorded uses reached `totalUses`. An unknown token is not used up. */
9
+ export function isTokenReadSpent(read) {
10
+ const totalUses = read.invite?.totalUses ?? null;
11
+ return totalUses !== null && read.usesRecorded !== null && read.usesRecorded >= totalUses;
12
+ }
13
+ /**
14
+ * No invite row is `'token-unknown'` (retryable: the row may not have replicated to this
15
+ * machine yet); an expired or used-up invite is `'token-spent'` (final).
16
+ */
17
+ export function classifyTokenRead(read, nowMs) {
18
+ if (read.invite === null) {
19
+ return { valid: false, code: 'token-unknown' };
20
+ }
21
+ if (isInviteExpired(read.invite, nowMs) || isTokenReadSpent(read)) {
22
+ return { valid: false, code: 'token-spent' };
23
+ }
24
+ return { valid: true, read };
25
+ }
26
+ //# sourceMappingURL=formation-token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formation-token.js","sourceRoot":"","sources":["../src/formation-token.ts"],"names":[],"mappings":"AAoCA;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,MAA4B,EAAE,KAAa;IACzE,OAAO,MAAM,CAAC,WAAW,KAAK,IAAI,IAAI,MAAM,CAAC,WAAW,IAAI,KAAK,CAAC;AACpE,CAAC;AAED,0GAA0G;AAC1G,MAAM,UAAU,gBAAgB,CAAC,IAAwB;IACvD,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,SAAS,IAAI,IAAI,CAAC;IACjD,OAAO,SAAS,KAAK,IAAI,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,IAAI,IAAI,CAAC,YAAY,IAAI,SAAS,CAAC;AAC5F,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAwB,EAAE,KAAa;IACvE,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;QACzB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;IACjD,CAAC;IACD,IAAI,eAAe,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;QAClE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IAC/C,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAC/B,CAAC"}
package/dist/index.d.ts CHANGED
@@ -3,10 +3,10 @@ export { canonicalJson } from './canonical-json.js';
3
3
  export { CadreNode, CADRE_INVITATION_DEFAULT_TTL_MS, OWNER_GRANT_DEFAULT_TTL_MS } from './cadre-node.js';
4
4
  export { MachineRemovalRefusedError } from './machine-removal.js';
5
5
  export { OwnerKeyAddRefusedError, OwnerKeyRemovalRefusedError } from './owner-keys.js';
6
- export { ControlDatabase, parseNullableStoredDatetimeMs, MissingHostStrandError, InvitationExhaustedError, CadreInviteIssuerUnknownError, OwnerGrantIssuerUnknownError, IssuedRowNotHereYetError, buildAuthorizationMessage, registerPartyIdFunction, formationVouchMessage, formationConsentMessage, cadreInviteAddMessage, cadreInviteRedeemMessage, cadreInviteConsentMessage, ownerGrantAddMessage, ownerGrantRedeemMessage, ownerGrantConsentMessage, projectSeedPeers, REAPABLE_TABLES, type ReapableTable, type ControlDatabaseConfig, type ControlTable, type RevocableTable, type FormationUsageResult, type FormationInviteRow, type MembershipChangeListener, type GuardedDeleteListener, type ControlWriteAbandonedListener, type RevokedRowRef, type ControlSign, type OwnerGrantRedemptionFields } from './control-database.js';
7
- export { CONTROL_SCHEMA_VERSION, CONTROL_SCHEMA_BASELINE_VERSION } from './control-schema.js';
8
- export { ControlStoreIncompatibleError } from './control-schema-version.js';
9
- export { CONTROL_WRITE_ATTEMPTS, CONTROL_WRITE_RETRY_BUDGET_MS, SCHEMA_INIT_ATTEMPTS, SCHEMA_INIT_RETRY_POLICY, isRetriableControlWriteFailure, isRetriableSchemaInitFailure, retryControlWrite, type ControlWriteRetryOptions } from './control-write-retry.js';
6
+ export { ControlDatabase, parseNullableStoredDatetimeMs, MissingHostStrandError, InvitationExhaustedError, CadreInviteIssuerUnknownError, OwnerGrantIssuerUnknownError, IssuedRowNotHereYetError, buildAuthorizationMessage, registerPartyIdFunction, formationVouchMessage, formationConsentMessage, cadreInviteAddMessage, cadreInviteRedeemMessage, cadreInviteConsentMessage, ownerGrantAddMessage, ownerGrantRedeemMessage, ownerGrantConsentMessage, projectSeedPeers, REAPABLE_TABLES, type ReapableTable, type ControlDatabaseConfig, type ControlTable, type RevocableTable, type FormationUsageResult, type FormationInviteRow, type FoundingRows, type FoundingInvite, type MembershipChangeListener, type GuardedDeleteListener, type ControlWriteAbandonedListener, type RevokedRowRef, type ControlSign, type SelfRecordSigner, type CadrePeerRetouchMode, type CadrePeerRetouchOutcome, type OwnerGrantRedemptionFields } from './control-database.js';
7
+ export { CONTROL_SCHEMA_VERSION, CONTROL_SCHEMA_BASELINE_VERSION, CONTROL_SCHEMA_VERSION_TAG } from './control-schema.js';
8
+ export { ControlStoreIncompatibleError, type ControlStoreIncompatibility } from './control-schema-version.js';
9
+ export { CONTROL_WRITE_ATTEMPTS, CONTROL_WRITE_RETRY_BUDGET_MS, LOST_UPDATE_RETRY_POLICY, SCHEMA_INIT_ATTEMPTS, SCHEMA_INIT_RETRY_POLICY, isRetriableControlWriteFailure, isRetriableLostUpdateFailure, isRetriableSchemaInitFailure, retryControlWrite, type ControlWriteRetryOptions } from './control-write-retry.js';
10
10
  export { CONTROL_READ_ATTEMPTS, CONTROL_READ_RETRY_DELAYS_MS, CONTROL_READ_RETRY_BUDGET_MS, isRetriableControlReadFailure, retryControlRead, type ControlReadRetryOptions } from './control-read-retry.js';
11
11
  export type { ControlRetryAbandonment, ControlRetryAbandonReason, ControlRetryAbandonListener } from './control-retry.js';
12
12
  export { controlAuthorizationFields, unboundAuthorizationFields, cadreInviteRowFields, ownerGrantRowFields, type ControlDomain, type BoundControlDomain, type UnboundControlDomain, type ControlAction, type CadreInviteSignedFields, type OwnerGrantSignedFields } from './control-authorization.js';
@@ -30,23 +30,23 @@ export { StrandDatabase, type StrandDatabaseConfig } from './strand-database.js'
30
30
  export type { StrandCommitDurability, StrandCommitHeld } from './strand-commit-durability.js';
31
31
  export { StrandWatcher, type StrandWatcherCallbacks, type StrandQueryable, type SAppIdLookup, type StrandRowWithApp } from './strand-watcher.js';
32
32
  export { StrandInstanceManager, type StartStrandConfig, type ResumeStrandOverrides, getStrandStoragePath, isAwaitingFirstSync, liveStrandStatus } from './strand-instance-manager.js';
33
- export { StrandAwaitingFirstSyncError, DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS, DEFAULT_STRAND_FIRST_SYNC_POLL_MS, strandHeaderHeld, appTablesReadable, strandFirstSyncComplete, type StrandFirstSyncConfig } from './strand-first-sync-gate.js';
33
+ export { StrandAwaitingFirstSyncError, DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS, DEFAULT_STRAND_FIRST_SYNC_BACKSTOP_MS, type StrandFirstSyncConfig } from './strand-first-sync-gate.js';
34
34
  export { PeerJoinBackfill, DEFAULT_PEER_JOIN_BACKFILL, MAX_BLOCK_MESSAGE_BYTES, PEER_JOIN_BACKFILL_WARN_AFTER_FAILURES, type PeerJoinBackfillConfig, type PeerJoinBackfillDeps, type PeerJoinBackfillResult, type PeerJoinBackfillPushClient } from './peer-join-backfill.js';
35
35
  export { strandCohortTopicOption, type StrandReactivityConfig } from './strand-reactivity.js';
36
- export { StrandRevocationEnforcer, createRevocationConnectionGater, readStrandRevocationRows, DEFAULT_REVOCATION_POLL_INTERVAL_MS, type StrandRevocationEnforcementConfig, type StrandRevocationEnforcerDeps, type StrandRevocationRows, type StrandMemberPeerBinding, type StrandRevocationJudge, type StrandRevocationNetwork, type RevocationRefreshScheduler } from './strand-revocation-enforcer.js';
37
- export { StrandMembershipReconciler, IDLE_PASSES_BEFORE_ESCALATION, INITIAL_JOIN_RETRY_INTERVAL_MS, type StrandMembershipReconcilerDeps, type StrandMembershipReconciliationConfig, type PendingMembershipInviteSource } from './strand-membership-reconciler.js';
36
+ export { StrandRevocationEnforcer, createRevocationConnectionGater, readStrandRevocationRows, DEFAULT_REVOCATION_BACKSTOP_MS, REVOCATION_REFRESH_RETRY_MS, REVOCATION_WIDE_STRAND_REFRESH_MS, type StrandRevocationEnforcementConfig, type StrandRevocationEnforcerDeps, type StrandRevocationRows, type StrandMemberPeerBinding, type StrandRevocationJudge, type StrandRevocationNetwork } from './strand-revocation-enforcer.js';
37
+ export { StrandMembershipReconciler, DEFAULT_MEMBERSHIP_RECONCILE_INTERVAL_MS, IDLE_PASSES_BEFORE_ESCALATION, INITIAL_JOIN_RETRY_INTERVAL_MS, type StrandMembershipReconcilerDeps, type StrandMembershipReconciliationConfig, type PendingMembershipInviteSource } from './strand-membership-reconciler.js';
38
38
  export { defaultTimeoutScheduler, type TimeoutScheduler } from './timeout-scheduler.js';
39
39
  export { HibernationManager, type HibernationCallbacks } from './hibernation-manager.js';
40
40
  export { ArachnodeStub, createArachnodeStub, type RingConfig } from './arachnode-stub.js';
41
41
  export { EnrollmentService, type MemberVerifier, type MemberRegistry } from './enrollment.js';
42
- export { StrandSolicitationService, createDefaultFormationResponseValidator, type DisclosureValidator, type FormationUsageRecorder, type FormationUsageParams, type AuthorizedFormationUsage, type ResolvedHostStrand, type StrandProvisioner, type FormationResponseValidator, type StrandSolicitationServiceOptions } from './strand-solicitation.js';
42
+ export { StrandSolicitationService, createDefaultFormationResponseValidator, type DisclosureValidator, type FormationUsageRecorder, type FormationUsageParams, type AuthorizedFormationUsage, type BoundHostStrand, type ResolvedHostStrand, type StrandProvisioner, type FormationResponseValidator, type StrandSolicitationServiceOptions } from './strand-solicitation.js';
43
43
  export { createHttpFormationApprover, signFormationApproval, verifyFormationApproval, FormationApprovalError, type FormationApprovalRequest, type FormationVouchFields, type FormationApproval, type FormationApprover, type FormationApprovalFailure } from './formation-approval.js';
44
44
  export { ControlFormationUsageRecorder } from './control-formation-recorder.js';
45
45
  export { generateStrandMemberKey, strandMemberKeyPair } from './strand-member-key.js';
46
46
  export { signStrandPayload, verifyStrandPayload, signStrandApproval, generateStrandStampId, bootstrapFounderMembership, PreSplitStrandIdentityError, issueInvite, consumeInvite, burnInvite, isStrandMember, cancelInvite, listOutstandingInvites, addMemberByManager, revokeMember, leaveStrand, registerMemberPeer, listMemberPeers, removeMemberPeer, addManager, admitManager, removeManager, sealStrand, isStrandSealed, StrandTransactionBusyError, STRAND_ENGINE, STRAND_ENGINE_VERSION, type FounderBootstrapParams, type IssueInviteParams, type IssuedInvite, type ConsumeInviteParams, type CancelInviteParams, type OutstandingInvite, type AddMemberByManagerParams, type RevokeMemberParams, type LeaveStrandParams, type RegisterMemberPeerParams, type RemoveOwnPeerParams, type RemoveMemberPeerByManagerParams, type RemoveMemberPeerParams, type AddManagerParams, type AdmitManagerParams, type RemoveManagerParams, type SealStrandParams, type StrandWriteOptions } from './strand-membership-writer.js';
47
47
  export { canonicalDatetime } from './canonical-datetime.js';
48
48
  export { StrandMemberRegistry, StrandMemberVerifier, memberRegistrationPayload, type StrandAdmission } from './strand-member-registry.js';
49
- export { SeedBootstrapService, SEED_PROTOCOL, mergeSeedPeers, seedDigest, type SeedBootstrapConfig, type SeedEventCallbacks } from './seed-bootstrap.js';
49
+ export { SeedBootstrapService, SEED_PROTOCOL, mergeSeedPeers, seedDialTargets, seedDigest, type SeedBootstrapConfig, type SeedEventCallbacks } from './seed-bootstrap.js';
50
50
  export { peerAuthorizationDigest, cadrePeerVoucherDigest, cadrePeerRemoveDigest, deviceTokenAddDigest, deviceTokenRemoveDigest, formationConsentDigest, cadreInviteAddDigest, cadreInviteRedeemDigest, cadreInviteConsentDigest, ownerKeyAddDigest, verifyPeerAuthorization, verifyCadrePeerVoucher, verifyOwnerKeyVoucher, verifyRevocationSigner, verifyFormationConsent, verifyInvitationAdmission, verifyCadreInviteRow, verifyCadreInviteRedemption, ownerGrantAddDigest, ownerGrantRedeemDigest, ownerGrantConsentDigest, verifyOwnerGrantRow, verifyOwnerGrantRedemption, verifyOwnerGrantAdmission, type DeviceTokenAuthorizedRow } from './peer-authorization.js';
51
51
  export { deriveOwnerAnchor, type OwnerAnchorInputs, type OwnerAnchorDerivation } from './owner-anchor-sync.js';
52
52
  export { StrandWakeService, dialWake, WAKE_PROTOCOL, DEFAULT_WAKE_DIAL_BUDGET_MS, type StrandWakeServiceOptions, type DialWakeOptions } from './strand-wake-protocol.js';
@@ -57,7 +57,7 @@ export { STRAND_WAKE_TYPE, type StrandWakePayload } from './strand-wake-payload.
57
57
  export type { PushNotifier, PushMessage, PushSendResult } from './push-notifier.js';
58
58
  export { validatePushCredentials, redactPushCredentials, REDACTED } from './push-credentials.js';
59
59
  export { PushFanoutService, DEFAULT_PUSH_COOLDOWN_MS, DEFAULT_PUSH_DEBOUNCE_MS, type PushFanoutOptions, type FanoutMember } from './push-fanout.js';
60
- export { selectControlCohortDials, DEFAULT_CONTROL_COHORT_RECONCILE_MS, DEFAULT_CONTROL_COHORT_TARGET_DEGREE, type ControlCohortSelection, type ControlCohortReconcileResult } from './control-cohort.js';
60
+ export { selectControlCohortDials, DEFAULT_CONTROL_COHORT_RECONCILE_MS, DEFAULT_CONTROL_COHORT_TARGET_DEGREE, CONTROL_COHORT_REDIAL_MS, type ControlCohortSelection, type ControlCohortReconcileResult } from './control-cohort.js';
61
61
  export { dialAddr, withTrailingPeerId, dialPeerAddrs, SelfRelayOnlyError, PeerUnreachableError, tryAddrsInTurn, directBeforeRelayed, DEFAULT_CONTROL_COHORT_DIAL_TIMEOUT_MS, DEFAULT_CONTROL_COHORT_PER_ADDRESS_DIAL_TIMEOUT_MS, CONTROL_COHORT_DIAL_ADDRESS_ATTEMPTS, DEFAULT_PEER_DIAL_BUDGET, type PeerDialBudget, type AddrDialer } from './peer-dial.js';
62
62
  export { DECLARED_LINK_ROUND_TRIP_MS, CIRCUIT_DIAL_ROUND_TRIPS, RELAY_DIAL_ROUND_TRIPS, RELAYED_DIAL_ROUND_TRIPS, RELAY_RESERVATION_ROUND_TRIPS, CIRCUIT_REQUEST_ROUND_TRIPS, RELAYED_REQUEST_ROUND_TRIPS, PUSH_TRANSFER_ALLOWANCE_MS, ADMISSION_DECISION_TIMEOUT_MS, resolveLinkRoundTripMs, relayedDialBudgetMs, relayReservationBudgetMs, circuitRequestBudgetMs, relayedRequestBudgetMs, peerJoinPushBudget, type PeerJoinPushBudget, DIAL_ADMISSION_DECISIONS, optimysticDialLimits, type OptimysticDialLimits } from './link-budget.js';
63
63
  export { anchoredTrustPolicy, pinnedKeyTrustPolicy, tofuTrustPolicy, claimSecretTrustPolicy, type SeedTrustPolicy, type SeedTrustContext, type SeedTrustDecision, type ClaimSecretTrustPolicyOptions } from './seed-trust-policy.js';
@@ -67,14 +67,15 @@ export { exportStrandBundle, adoptStrandBundle, readStrandHoldings, unheldBundle
67
67
  export { encodeCadreJoinRequest, decodeCadreJoinRequest, requireCadreJoinReply, CADRE_JOIN_REQUEST_PREFIX, CADRE_INVITATION_PREFIX, OWNER_KEY_EXPORT_STEM, OWNER_GRANT_PREFIX, CADRE_JOIN_REQUEST_MAX_APP_NAME, type CadreJoinReply, type CadreJoinRequest } from './cadre-join-request.js';
68
68
  export * from './provider/index.js';
69
69
  export { signSchema, verifySchema, assertSchemaSignature, SchemaVerificationError } from './schema-verification.js';
70
- export { FormationListener, dialFormation, dialFormationByMachine, isValidResponderCreatesResult, isWellFormedMembershipInvite, sanitizeStrandAddrs, FORMATION_PROTOCOL, type FormationParty, type FormationContactMessage, type FormationResultMessage, type FormationProvisionResult, type FormationStrandInfo, type FormationDbConnectionInfo, type FormationListenerOptions, type FormationDialOptions, type FormationDialResult, type FormationRejection, type FormationTokenCheck, type ResponderProvisionOutcome } from './strand-formation-protocol.js';
70
+ export { FormationListener, dialFormation, dialFormationByMachine, isValidResponderCreatesResult, isWellFormedMembershipInvite, sanitizeStrandAddrs, FORMATION_PROTOCOL, type FormationParty, type FormationContactMessage, type FormationResultMessage, type FormationProvisionResult, type FormationStrandInfo, type FormationDbConnectionInfo, type FormationListenerOptions, type FormationDialOptions, type FormationDialResult, type FormationRejection, type ResponderProvisionOutcome } from './strand-formation-protocol.js';
71
+ export { classifyTokenRead, isTokenReadSpent, type FormationInviteFacts, type FormationTokenRead, type FormationTokenCheck } from './formation-token.js';
71
72
  export { FormationRejectedError, FormationUnreachableError, FormationPostApprovalError, FORMATION_REJECTION_RETRYABLE, isFormationRejectionCode, type FormationRejectionCode } from './strand-formation-rejection.js';
72
73
  export { formationDeadlines, type FormationDeadlines } from './strand-formation-deadlines.js';
73
- export { CadreInviteHandler, redeemAtMembers, redeemInPasses, signRedeemRequest, verifyRedeemReply, encodeCadreInvitation, decodeCadreInvitation, isWellFormedCadreInviteRow, sanitizeAddrs, CadreInviteRejectedError, CadreInviteUnreachableError, CadreInviteReplyInvalidError, CADRE_INVITE_PROTOCOL, CADRE_INVITE_REJECTION_RETRYABLE, isCadreInviteRejectionCode, type CadreInvitation, type CadreInviteRedeemRequest, type CadreInviteRedeemReply, type CadreInviteAccepted, type CadreInviteRejectionCode, type CadreInviteAddressOutcome, type CadreInviteHandlerOptions, type CadreInviteStore, type RedeemAtMembersOptions, type RedeemInPassesOptions, type RedeemSigner, type CadreInviteRedemption } from './cadre-invite-protocol.js';
74
+ export { CadreInviteHandler, redeemAtMembers, redeemInPasses, signRedeemRequest, verifyRedeemReply, encodeCadreInvitation, decodeCadreInvitation, isWellFormedCadreInviteRow, sanitizeAddrs, CadreInviteRejectedError, CadreInviteUnreachableError, CadreInviteReplyInvalidError, CADRE_INVITE_PROTOCOL, CADRE_INVITE_REJECTION_RETRYABLE, isCadreInviteRejectionCode, type CadreInvitation, type CadreInviteRedeemRequest, type CadreInviteRedeemReply, type CadreInviteAccepted, type CadreInviteRejectionCode, type CadreInviteAddressOutcome, type CadreInviteHandlerOptions, type CadreInviteStore, type RedeemAtMembersOptions, type RedeemInPassesOptions, type RedeemSigner, type RedeemSelfRecord, type CadreInviteRedemption } from './cadre-invite-protocol.js';
74
75
  export { OwnerGrantHandler, redeemOwnerGrantAtMembers, redeemOwnerGrantInPasses, signOwnerGrantRequest, verifyOwnerGrantReply, encodeOwnerGrant, decodeOwnerGrant, isWellFormedOwnerGrantRow, OwnerGrantRejectedError, OwnerGrantUnreachableError, OwnerGrantReplyInvalidError, OWNER_GRANT_PROTOCOL, OWNER_GRANT_REJECTION_RETRYABLE, isOwnerGrantRejectionCode, type OwnerGrantBundle, type OwnerGrantRedeemRequest, type OwnerGrantRedeemReply, type OwnerGrantAccepted, type OwnerGrantRejectionCode, type OwnerGrantHandlerOptions, type OwnerGrantStore, type RedeemOwnerGrantAtMembersOptions, type RedeemOwnerGrantInPassesOptions, type OwnerGrantRedemption } from './owner-grant-protocol.js';
75
76
  export { MemberRedemptionHandler, dialRedemption, dialRedemptionInPasses, MAX_REDEMPTION_FRAME_BYTES, type MemberRedemptionHandlerOptions, type MemberRedemptionOptions, type MemberRedemptionPassOptions, type MemberRedemption, type RedemptionAddressOutcome, type RedemptionRejection, type RedemptionRefusalFrame } from './member-redemption.js';
76
77
  export { selectCadreInvitationMembers, cadreInvitationReach, CADRE_INVITATION_MAX_ADDRS, CADRE_INVITATION_ADDRS_PER_SIBLING } from './invitation-bootstrap.js';
77
- export { PENDING_JOIN_POLL_MS, PENDING_JOIN_MAX_BACKOFF_MS, MAX_PENDING_JOIN_MS } from './pending-join-runner.js';
78
+ export { PENDING_JOIN_BACKSTOP_MS, PENDING_JOIN_MAX_BACKOFF_MS, MAX_PENDING_JOIN_MS } from './pending-join-runner.js';
78
79
  export { StrandFormationManager, createStrandFormationManager, MEMBERSHIP_INVITE_TTL_MS, MEMBERSHIP_INVITE_UNAVAILABLE_REASON, HOST_STRAND_MUST_BE_RECREATED_REASON, type CadrePeerAddrsSource, type MembershipInviteIssuer, type StrandFormationManagerConfig, type StrandFormationManagerOptions } from './strand-formation-manager.js';
79
80
  export { controlProtocolClasses, type ControlProtocolClass } from './control-protocol-guard.js';
80
81
  export { createMembershipConnectionGater, PROVISIONAL_ADMISSION_DEADLINE_MS, PROVISIONAL_ADMISSION_CLOSE_TIMEOUT_MS, MAX_UNAUTHORIZED_RELAY_RESERVATIONS, UnauthorizedReservationBudget, type InboundAdmissionPolicy, type InboundConnectionVerdict } from './membership-connection-gater.js';