@serfab/quereus-plugin-sereus 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -279,6 +279,7 @@ on shutdown.
279
279
  | `port` | number | `0` | libp2p listening port (0 = random) |
280
280
  | `enableCache` | boolean | `true` | Enable optimystic caching |
281
281
  | `fretProfile` | `'edge' \| 'core'` | `'edge'` | FRET profile |
282
+ | `clusterSize` | number | `4` | Nodes the **strand's** replication cluster should have (the control network uses its own fixed `CONTROL_REPLICATION_BREADTH` and never reads this). 4 is the smallest breadth whose 0.75 super-majority (`ceil(4 × 0.75) = 3`) still commits with one holder offline; at 2 or 3 every holder must be awake for every write, and at 2 read repair cannot converge at all. Every peer on the strand should use the same value: a peer configured much higher derives a larger expected cohort and can reject a coordinator's smaller declared peer set as a downsize. Frozen when the libp2p node is created, so a change takes effect on the next restart. Must be an integer ≥ 2 (`MIN_CLUSTER_SIZE`, Optimystic's own minimum). Leaving it unset is *not* the same as passing 4 elsewhere — Optimystic's own fallback is 10, so this default is applied here. Ignored when `libp2pNode` is injected |
282
283
  | `libp2pNode` | Libp2p | — | Inject an existing libp2p node |
283
284
  | `coordinatedRepo` | IRepo | — | Required when `libp2pNode` is provided |
284
285
  | `mode` | `'bootstrap' \| 'networked'` | `'networked'` | `'bootstrap'` routes through the local transactor (no peer round trips); `'networked'` uses the network transactor |
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Smallest cluster size Optimystic will honour — its own `minAbsoluteClusterSize`,
3
+ * and the smallest value that reaches the cluster path at all (a lone node writes to
4
+ * local storage without forming a cluster). A validation floor, not a default.
5
+ */
6
+ export declare const MIN_CLUSTER_SIZE = 2;
7
+ /**
8
+ * How many nodes each block of the **control** (cadre membership) database is replicated
9
+ * to. Chosen to exceed any party's node count, so in practice every control block lands
10
+ * on every member of the party.
11
+ *
12
+ * **Why full replication here.** Every control node reads the whole control database —
13
+ * membership, peer addresses, the strand list — so a member left out of a block's cohort is
14
+ * a member that may never learn the fact, and at a cohort of two the *read repair* that is
15
+ * supposed to make that safe cannot converge (it can ask exactly one peer, and that peer may
16
+ * be the member that also missed the write). Measured: 4 failures in 10 runs of the
17
+ * control-DB replication scenario at size 2, 0 in 20 at size 3 and 0 in 10 at size 8.
18
+ *
19
+ * **Why this number, and why a constant.** Optimystic caps a cohort at the peers that
20
+ * actually serve the network and downsizes a cohort it cannot fill (`allowDownsize: true`,
21
+ * which Cadre passes), so any value at or above the party's node count yields the same
22
+ * behaviour: cohort = whole party. 16 is roughly twice the largest deployment the product
23
+ * documents (`docs/architecture.md` → "Enterprise (Multi-Node Mixed)", 7 nodes) — headroom
24
+ * without pretending to support arbitrarily large parties; a party genuinely running more
25
+ * than 16 nodes is back to partial replication for its control database and should raise
26
+ * this. It is a constant rather than the live member count because Optimystic freezes the
27
+ * value at libp2p-node construction, before the `ControlDatabase` holding the `CadrePeer`
28
+ * rows exists, and because a per-node derivation lets two members disagree.
29
+ *
30
+ * A cadre is one party by construction, so the owner-blind cohort-selection question that
31
+ * applies to strand networks (see {@link DEFAULT_STRAND_CLUSTER_SIZE} below) is not a question
32
+ * here: "replicate to the whole party" already is the whole intent, and there is no party
33
+ * diversity for this breadth to buy or fail to buy.
34
+ *
35
+ * Canonical explanation of all of the above — the read-repair mechanics, what whole-party
36
+ * breadth does and does not remove, and what the wider cohort costs in write availability —
37
+ * is `docs/architecture.md` → "Replication cluster size". Keep it there, not here.
38
+ *
39
+ * **Deliberately not `clusterPolicy.assumedClusterSize`.** That option means "the smallest
40
+ * cohort this deployment can genuinely field", and it feeds both the membership admission
41
+ * gate's low-confidence path *and* the read-repair/reconcile corroboration floor. A Cadre
42
+ * party legitimately runs one or two nodes, so Cadre leaves it at Optimystic's default of 2;
43
+ * asserting 16 there would make the admission gate demand `ceil(0.75 x 16) = 12` declared
44
+ * peers and refuse every real party's writes.
45
+ *
46
+ * NOTE: cohort selection overfetches proportionally to this number —
47
+ * `Libp2pKeyPeerNetwork.membershipOverfetch` asks FRET for
48
+ * `max(clusterSize * 4, clusterSize + 16)` candidates and does a peerStore protocol lookup
49
+ * per candidate, so 16 requests a 64-peer proximity band. Free today, because the result is
50
+ * bounded by the peers FRET actually knows (2 in a 3-node party). If control-network cohort
51
+ * selection ever shows up as slow on a large mesh, look here first.
52
+ */
53
+ export declare const CONTROL_REPLICATION_BREADTH = 16;
54
+ /**
55
+ * Cluster consensus policy every CONTROL-network libp2p node runs — production
56
+ * (`CadreNode.buildControlNodeOptions`) and the integration harness alike. One definition so
57
+ * the two cannot drift: the object literal used to be hand-copied at both sites, and the copy
58
+ * at the harness had picked up a `superMajorityThreshold` override production never had.
59
+ *
60
+ * `superMajorityThreshold` is deliberately ABSENT. Omitting it makes both the cluster member
61
+ * and the coordinator fall back to Optimystic's `DEFAULT_SUPER_MAJORITY_THRESHOLD` (0.75) —
62
+ * the bar a real party commits control writes against. Setting it here, or at any one
63
+ * consumer, reintroduces exactly the divergence this constant exists to prevent. The absence
64
+ * is a researched decision, not an inherited default: no threshold both relaxes the
65
+ * three-node unanimity bar and satisfies Optimystic's partition-safety condition at the
66
+ * shipped admission fraction — see `docs/architecture.md` → "Replication cluster size".
67
+ *
68
+ * `sizeTolerance` only makes sense alongside `allowDownsize`: a fixed
69
+ * {@link CONTROL_REPLICATION_BREADTH}-wide target is unsatisfiable by any real (2-7 node)
70
+ * party, so the cohort must be allowed to shrink to the party that actually exists.
71
+ *
72
+ * NOT for strand networks: {@link STRAND_CLUSTER_POLICY} is a structurally identical object
73
+ * for a different network with different reasoning. The shape match is a coincidence; keep
74
+ * them separate.
75
+ *
76
+ * `assumedClusterSize` is REQUIRED here, and 2 is not a guess — it is the smallest party we
77
+ * support. The field feeds two Optimystic consumers with opposite failure modes, and only one
78
+ * of them defaults to something workable for us:
79
+ *
80
+ * - The membership admission gate already defaults to 2 (`minAbsoluteClusterSize`), so naming
81
+ * it changes nothing there.
82
+ * - The read-repair / reconcile corroboration floor does NOT. Absent this field it falls back
83
+ * to `clusterSize` — which for the control network is {@link CONTROL_REPLICATION_BREADTH},
84
+ * 16. `corroboratorCapacity(peers, 16)` is then `max(peers, 15)`, so the floor of two
85
+ * distinct non-self corroborators binds. A two-node party fields exactly one, can never
86
+ * reach two, and so can never repair a block: `cluster-fetch:no-quorum` forever.
87
+ *
88
+ * Declaring 2 does NOT lower the replication factor — that stays at
89
+ * {@link CONTROL_REPLICATION_BREADTH}. It states the cohort size we can genuinely field, which
90
+ * is what the corroboration floor is asking. See Optimystic's `cluster/cluster-policy.ts`
91
+ * ("Why two size yardsticks, not one") and `corroboratorCapacity` in `cluster/quorum-restore.ts`.
92
+ *
93
+ * The `satisfies` is load-bearing: without it a mistyped or obsolete key would compile at both
94
+ * this definition and every consumer, because TypeScript only excess-property-checks fresh
95
+ * object literals, not a shared constant handed to `clusterPolicy`.
96
+ */
97
+ export declare const CONTROL_CLUSTER_POLICY: Readonly<{
98
+ allowDownsize: true;
99
+ sizeTolerance: number;
100
+ assumedClusterSize: number;
101
+ }>;
102
+ /**
103
+ * Default number of nodes a **strand** network is told its replication cluster should have.
104
+ *
105
+ * Strand data is application data, where partial replication is a legitimate choice: only
106
+ * the control database's every-member-reads-all-of-it character forces full replication
107
+ * ({@link CONTROL_REPLICATION_BREADTH}). So the question here is not "everyone" but "how
108
+ * few is too few", and the answer is four.
109
+ *
110
+ * **Why four.** A write commits when a super-majority of the cohort approves, and that bar is
111
+ * Optimystic's `DEFAULT_SUPER_MAJORITY_THRESHOLD` of 0.75 — which Cadre selects by naming no
112
+ * threshold at all (see {@link CONTROL_CLUSTER_POLICY}). Approvals needed is
113
+ * `ceil(cohort x 0.75)`, so:
114
+ *
115
+ * | copies | approvals needed | holders that may be offline |
116
+ * |--------|------------------|-----------------------------|
117
+ * | 2 | 2 | 0 |
118
+ * | 3 | 3 | 0 |
119
+ * | 4 | 3 | 1 |
120
+ * | 5 | 4 | 1 |
121
+ * | 6 | 5 | 1 |
122
+ *
123
+ * Four is the smallest breadth that lets a write commit while one holder is away. At two or
124
+ * three every holder must be awake for every write, which for a workspace shared between
125
+ * phones and laptops is the ordinary case, not the rare one. Six buys no more fault tolerance
126
+ * than four and costs more overfetch (see the NOTE below).
127
+ *
128
+ * **It is also a correctness floor, not only a durability one — but the floor is measured in
129
+ * machines, not in this number.** A node that has fallen behind and can ask exactly one peer
130
+ * whether it is current has Optimystic accept that single answer as the cluster's truth
131
+ * (`corroboratorCapacity` in `db-p2p/src/cluster/quorum-restore.ts` lowers the corroboration
132
+ * floor to one when the cohort cannot hold a second voter). If that peer is also behind it
133
+ * honestly reports the stale revision, the reader concludes it is current, and re-arms its
134
+ * repair window forever. Measured on the control-DB replication scenario: 4 failures in 10 runs
135
+ * at breadth 2, 0 in 20 at breadth 3, 0 in 10 at breadth 8. What lifts the floor off a single
136
+ * voter is a *third machine*, not a wider target: `corroboratorCapacity` takes the cohort peers
137
+ * actually present against {@link STRAND_CLUSTER_POLICY}'s declared `assumedClusterSize`, so a
138
+ * three-machine strand needs two corroborators whatever this number says, and a two-machine one
139
+ * cannot supply them however wide the target is. The underlying Optimystic behaviour is unfixed
140
+ * — `backlog/debt-read-repair-single-voter-corroboration` — so a two-machine strand still takes
141
+ * the exposure. Raising this number does not buy the way out; adding a machine does.
142
+ *
143
+ * **Why not derived from the party or member count.** The strand's `Member` rows live *in* the
144
+ * strand database, which runs on the strand libp2p node, whose cluster size is frozen at
145
+ * construction — reading the list to configure the node needs the node to exist. Open strands
146
+ * have no member list at all. The count also changes in the unsafe direction: a node that
147
+ * restarted after someone joined derives a wider expected cohort than one that did not, and the
148
+ * membership admission gate's confident path is what rejects the *wider* view. And a `Member` is
149
+ * a party, not a machine; cohort width consumes machines. Full reasoning in
150
+ * `docs/architecture.md` → "Replication cluster size".
151
+ *
152
+ * **Four machines, not four parties.** This number counts machines, and today that is not the
153
+ * same as counting independent holders. Cohort selection upstream is owner-blind — it
154
+ * picks nodes by hash proximity with no concept of which peer belongs to which party
155
+ * (`optimystic/tickets/backlog/feat-cohort-selection-owner-aware-placement`) — and on the
156
+ * cadre-driven path, the one that resolves a seed for itself, a strand's libp2p mesh is seeded
157
+ * from exactly one party's own control-database peer rows (`CadreNode.resolveCohortSeed`), so
158
+ * the cohort actually formed is that one party's machines. Four copies are, on that path, four
159
+ * machines belonging to one party — one failure domain, one witness. A cross-party mesh is not
160
+ * forbidden, only undiscoverable: it takes hand-supplied addresses
161
+ * (`StrandConnectionOptions.bootstrapNodes`) or a hand-written dial (see the doc). Tracked:
162
+ * `backlog/feat-strand-party-identity`. Full reasoning: `docs/architecture.md` →
163
+ * "Replication cluster size".
164
+ *
165
+ * Raising or lowering it per strand is still the embedder's call
166
+ * ({@link resolveStrandClusterSize}, `CadreNodeConfig.strandClusterSize`,
167
+ * `StrandConnectionOptions.clusterSize`) — but every node on one strand must agree, so a change
168
+ * means restarting all of them. There is no smaller-than-{@link MIN_CLUSTER_SIZE} option.
169
+ *
170
+ * NOTE: cohort selection overfetches proportionally — `Libp2pKeyPeerNetwork.membershipOverfetch`
171
+ * asks FRET for `max(clusterSize * 4, clusterSize + 16)` candidates and does one peerStore
172
+ * protocol lookup per candidate, so 4 requests a 20-peer band (was 18 at breadth 2). Negligible,
173
+ * and bounded by the peers FRET actually knows. If strand cohort selection ever shows up as slow,
174
+ * look here first.
175
+ */
176
+ export declare const DEFAULT_STRAND_CLUSTER_SIZE = 4;
177
+ /**
178
+ * Cluster consensus policy every STRAND-network libp2p node runs — production
179
+ * (`StrandInstanceManager.buildStrandRuntime`) and the plugin's own networked e2e mesh alike.
180
+ * One definition so the two cannot drift; the literal used to be hand-copied at both sites.
181
+ *
182
+ * Structurally identical to {@link CONTROL_CLUSTER_POLICY} and deliberately separate from it:
183
+ * that one is the control database's, whose breadth is fixed at
184
+ * {@link CONTROL_REPLICATION_BREADTH} for reasons that do not apply to application data.
185
+ *
186
+ * `superMajorityThreshold` is ABSENT for the same reason it is absent there: omitting it is how
187
+ * both the coordinator and the cluster member select Optimystic's
188
+ * `DEFAULT_SUPER_MAJORITY_THRESHOLD` (0.75), the bar {@link DEFAULT_STRAND_CLUSTER_SIZE}'s
189
+ * copies/approvals table is computed against.
190
+ *
191
+ * `sizeTolerance` only makes sense alongside `allowDownsize`: a {@link
192
+ * DEFAULT_STRAND_CLUSTER_SIZE}-wide target is unsatisfiable by a strand of one, two or three
193
+ * machines, so the cohort must be allowed to shrink to the mesh that actually exists.
194
+ *
195
+ * `assumedClusterSize` is REQUIRED, and {@link MIN_CLUSTER_SIZE} is not a guess — it is the
196
+ * smallest strand that reaches the cluster path at all. Absent this field the read-repair /
197
+ * reconcile corroboration floor falls back to `clusterSize`, so
198
+ * `corroboratorCapacity(peers, 4)` is `max(peers, 3)` and two distinct non-self corroborators
199
+ * become mandatory. A two-machine strand fields exactly one peer, can never reach two, and so
200
+ * can never repair a block — `cluster-fetch:no-quorum` forever, which is a read that never
201
+ * catches up and a `create table` that dies with `Missing block` on the joining node. Declaring
202
+ * the honest floor does NOT lower the replication factor (that stays at
203
+ * {@link DEFAULT_STRAND_CLUSTER_SIZE}) and does not relax anything a strand with three or more
204
+ * machines does: `corroboratorCapacity` takes the MAX of the cohort actually present and this
205
+ * number, so the relaxed branch is reachable only by a cohort that is genuinely that small. See
206
+ * Optimystic's `cluster/cluster-policy.ts` ("Why two size yardsticks, not one").
207
+ *
208
+ * The `satisfies` is load-bearing for the same reason as {@link CONTROL_CLUSTER_POLICY}'s.
209
+ */
210
+ export declare const STRAND_CLUSTER_POLICY: Readonly<{
211
+ allowDownsize: true;
212
+ sizeTolerance: number;
213
+ assumedClusterSize: number;
214
+ }>;
215
+ /**
216
+ * Resolve the cluster size to hand `createLibp2pNode` for a **strand** network, applying
217
+ * {@link DEFAULT_STRAND_CLUSTER_SIZE} and rejecting values Optimystic cannot honour. The
218
+ * control network does not route through here — it uses the fixed
219
+ * {@link CONTROL_REPLICATION_BREADTH}.
220
+ *
221
+ * Leaving the value unset is NOT the same as passing the default: Optimystic's own fallback
222
+ * is 10, so every strand node-creating path must route through here.
223
+ *
224
+ * Every node on the same strand should resolve to the same value: the membership admission
225
+ * gate's *confident* path compares a coordinator's declared set against the member's own
226
+ * cohort view, and that view is bounded by this number, so a member configured much higher
227
+ * can reject a smaller declared set as a downsize. Divergence is a live hazard whenever FRET
228
+ * has a confident network-size estimate — not the unconditional refusal earlier revisions of
229
+ * this comment described. See `docs/architecture.md` → "Replication cluster size".
230
+ */
231
+ export declare function resolveStrandClusterSize(configured?: number): number;
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Smallest cluster size Optimystic will honour — its own `minAbsoluteClusterSize`,
3
+ * and the smallest value that reaches the cluster path at all (a lone node writes to
4
+ * local storage without forming a cluster). A validation floor, not a default.
5
+ */
6
+ export const MIN_CLUSTER_SIZE = 2;
7
+ /**
8
+ * How many nodes each block of the **control** (cadre membership) database is replicated
9
+ * to. Chosen to exceed any party's node count, so in practice every control block lands
10
+ * on every member of the party.
11
+ *
12
+ * **Why full replication here.** Every control node reads the whole control database —
13
+ * membership, peer addresses, the strand list — so a member left out of a block's cohort is
14
+ * a member that may never learn the fact, and at a cohort of two the *read repair* that is
15
+ * supposed to make that safe cannot converge (it can ask exactly one peer, and that peer may
16
+ * be the member that also missed the write). Measured: 4 failures in 10 runs of the
17
+ * control-DB replication scenario at size 2, 0 in 20 at size 3 and 0 in 10 at size 8.
18
+ *
19
+ * **Why this number, and why a constant.** Optimystic caps a cohort at the peers that
20
+ * actually serve the network and downsizes a cohort it cannot fill (`allowDownsize: true`,
21
+ * which Cadre passes), so any value at or above the party's node count yields the same
22
+ * behaviour: cohort = whole party. 16 is roughly twice the largest deployment the product
23
+ * documents (`docs/architecture.md` → "Enterprise (Multi-Node Mixed)", 7 nodes) — headroom
24
+ * without pretending to support arbitrarily large parties; a party genuinely running more
25
+ * than 16 nodes is back to partial replication for its control database and should raise
26
+ * this. It is a constant rather than the live member count because Optimystic freezes the
27
+ * value at libp2p-node construction, before the `ControlDatabase` holding the `CadrePeer`
28
+ * rows exists, and because a per-node derivation lets two members disagree.
29
+ *
30
+ * A cadre is one party by construction, so the owner-blind cohort-selection question that
31
+ * applies to strand networks (see {@link DEFAULT_STRAND_CLUSTER_SIZE} below) is not a question
32
+ * here: "replicate to the whole party" already is the whole intent, and there is no party
33
+ * diversity for this breadth to buy or fail to buy.
34
+ *
35
+ * Canonical explanation of all of the above — the read-repair mechanics, what whole-party
36
+ * breadth does and does not remove, and what the wider cohort costs in write availability —
37
+ * is `docs/architecture.md` → "Replication cluster size". Keep it there, not here.
38
+ *
39
+ * **Deliberately not `clusterPolicy.assumedClusterSize`.** That option means "the smallest
40
+ * cohort this deployment can genuinely field", and it feeds both the membership admission
41
+ * gate's low-confidence path *and* the read-repair/reconcile corroboration floor. A Cadre
42
+ * party legitimately runs one or two nodes, so Cadre leaves it at Optimystic's default of 2;
43
+ * asserting 16 there would make the admission gate demand `ceil(0.75 x 16) = 12` declared
44
+ * peers and refuse every real party's writes.
45
+ *
46
+ * NOTE: cohort selection overfetches proportionally to this number —
47
+ * `Libp2pKeyPeerNetwork.membershipOverfetch` asks FRET for
48
+ * `max(clusterSize * 4, clusterSize + 16)` candidates and does a peerStore protocol lookup
49
+ * per candidate, so 16 requests a 64-peer proximity band. Free today, because the result is
50
+ * bounded by the peers FRET actually knows (2 in a 3-node party). If control-network cohort
51
+ * selection ever shows up as slow on a large mesh, look here first.
52
+ */
53
+ export const CONTROL_REPLICATION_BREADTH = 16;
54
+ /**
55
+ * Cluster consensus policy every CONTROL-network libp2p node runs — production
56
+ * (`CadreNode.buildControlNodeOptions`) and the integration harness alike. One definition so
57
+ * the two cannot drift: the object literal used to be hand-copied at both sites, and the copy
58
+ * at the harness had picked up a `superMajorityThreshold` override production never had.
59
+ *
60
+ * `superMajorityThreshold` is deliberately ABSENT. Omitting it makes both the cluster member
61
+ * and the coordinator fall back to Optimystic's `DEFAULT_SUPER_MAJORITY_THRESHOLD` (0.75) —
62
+ * the bar a real party commits control writes against. Setting it here, or at any one
63
+ * consumer, reintroduces exactly the divergence this constant exists to prevent. The absence
64
+ * is a researched decision, not an inherited default: no threshold both relaxes the
65
+ * three-node unanimity bar and satisfies Optimystic's partition-safety condition at the
66
+ * shipped admission fraction — see `docs/architecture.md` → "Replication cluster size".
67
+ *
68
+ * `sizeTolerance` only makes sense alongside `allowDownsize`: a fixed
69
+ * {@link CONTROL_REPLICATION_BREADTH}-wide target is unsatisfiable by any real (2-7 node)
70
+ * party, so the cohort must be allowed to shrink to the party that actually exists.
71
+ *
72
+ * NOT for strand networks: {@link STRAND_CLUSTER_POLICY} is a structurally identical object
73
+ * for a different network with different reasoning. The shape match is a coincidence; keep
74
+ * them separate.
75
+ *
76
+ * `assumedClusterSize` is REQUIRED here, and 2 is not a guess — it is the smallest party we
77
+ * support. The field feeds two Optimystic consumers with opposite failure modes, and only one
78
+ * of them defaults to something workable for us:
79
+ *
80
+ * - The membership admission gate already defaults to 2 (`minAbsoluteClusterSize`), so naming
81
+ * it changes nothing there.
82
+ * - The read-repair / reconcile corroboration floor does NOT. Absent this field it falls back
83
+ * to `clusterSize` — which for the control network is {@link CONTROL_REPLICATION_BREADTH},
84
+ * 16. `corroboratorCapacity(peers, 16)` is then `max(peers, 15)`, so the floor of two
85
+ * distinct non-self corroborators binds. A two-node party fields exactly one, can never
86
+ * reach two, and so can never repair a block: `cluster-fetch:no-quorum` forever.
87
+ *
88
+ * Declaring 2 does NOT lower the replication factor — that stays at
89
+ * {@link CONTROL_REPLICATION_BREADTH}. It states the cohort size we can genuinely field, which
90
+ * is what the corroboration floor is asking. See Optimystic's `cluster/cluster-policy.ts`
91
+ * ("Why two size yardsticks, not one") and `corroboratorCapacity` in `cluster/quorum-restore.ts`.
92
+ *
93
+ * The `satisfies` is load-bearing: without it a mistyped or obsolete key would compile at both
94
+ * this definition and every consumer, because TypeScript only excess-property-checks fresh
95
+ * object literals, not a shared constant handed to `clusterPolicy`.
96
+ */
97
+ export const CONTROL_CLUSTER_POLICY = Object.freeze({
98
+ allowDownsize: true,
99
+ sizeTolerance: 0.5,
100
+ assumedClusterSize: 2,
101
+ });
102
+ /**
103
+ * Default number of nodes a **strand** network is told its replication cluster should have.
104
+ *
105
+ * Strand data is application data, where partial replication is a legitimate choice: only
106
+ * the control database's every-member-reads-all-of-it character forces full replication
107
+ * ({@link CONTROL_REPLICATION_BREADTH}). So the question here is not "everyone" but "how
108
+ * few is too few", and the answer is four.
109
+ *
110
+ * **Why four.** A write commits when a super-majority of the cohort approves, and that bar is
111
+ * Optimystic's `DEFAULT_SUPER_MAJORITY_THRESHOLD` of 0.75 — which Cadre selects by naming no
112
+ * threshold at all (see {@link CONTROL_CLUSTER_POLICY}). Approvals needed is
113
+ * `ceil(cohort x 0.75)`, so:
114
+ *
115
+ * | copies | approvals needed | holders that may be offline |
116
+ * |--------|------------------|-----------------------------|
117
+ * | 2 | 2 | 0 |
118
+ * | 3 | 3 | 0 |
119
+ * | 4 | 3 | 1 |
120
+ * | 5 | 4 | 1 |
121
+ * | 6 | 5 | 1 |
122
+ *
123
+ * Four is the smallest breadth that lets a write commit while one holder is away. At two or
124
+ * three every holder must be awake for every write, which for a workspace shared between
125
+ * phones and laptops is the ordinary case, not the rare one. Six buys no more fault tolerance
126
+ * than four and costs more overfetch (see the NOTE below).
127
+ *
128
+ * **It is also a correctness floor, not only a durability one — but the floor is measured in
129
+ * machines, not in this number.** A node that has fallen behind and can ask exactly one peer
130
+ * whether it is current has Optimystic accept that single answer as the cluster's truth
131
+ * (`corroboratorCapacity` in `db-p2p/src/cluster/quorum-restore.ts` lowers the corroboration
132
+ * floor to one when the cohort cannot hold a second voter). If that peer is also behind it
133
+ * honestly reports the stale revision, the reader concludes it is current, and re-arms its
134
+ * repair window forever. Measured on the control-DB replication scenario: 4 failures in 10 runs
135
+ * at breadth 2, 0 in 20 at breadth 3, 0 in 10 at breadth 8. What lifts the floor off a single
136
+ * voter is a *third machine*, not a wider target: `corroboratorCapacity` takes the cohort peers
137
+ * actually present against {@link STRAND_CLUSTER_POLICY}'s declared `assumedClusterSize`, so a
138
+ * three-machine strand needs two corroborators whatever this number says, and a two-machine one
139
+ * cannot supply them however wide the target is. The underlying Optimystic behaviour is unfixed
140
+ * — `backlog/debt-read-repair-single-voter-corroboration` — so a two-machine strand still takes
141
+ * the exposure. Raising this number does not buy the way out; adding a machine does.
142
+ *
143
+ * **Why not derived from the party or member count.** The strand's `Member` rows live *in* the
144
+ * strand database, which runs on the strand libp2p node, whose cluster size is frozen at
145
+ * construction — reading the list to configure the node needs the node to exist. Open strands
146
+ * have no member list at all. The count also changes in the unsafe direction: a node that
147
+ * restarted after someone joined derives a wider expected cohort than one that did not, and the
148
+ * membership admission gate's confident path is what rejects the *wider* view. And a `Member` is
149
+ * a party, not a machine; cohort width consumes machines. Full reasoning in
150
+ * `docs/architecture.md` → "Replication cluster size".
151
+ *
152
+ * **Four machines, not four parties.** This number counts machines, and today that is not the
153
+ * same as counting independent holders. Cohort selection upstream is owner-blind — it
154
+ * picks nodes by hash proximity with no concept of which peer belongs to which party
155
+ * (`optimystic/tickets/backlog/feat-cohort-selection-owner-aware-placement`) — and on the
156
+ * cadre-driven path, the one that resolves a seed for itself, a strand's libp2p mesh is seeded
157
+ * from exactly one party's own control-database peer rows (`CadreNode.resolveCohortSeed`), so
158
+ * the cohort actually formed is that one party's machines. Four copies are, on that path, four
159
+ * machines belonging to one party — one failure domain, one witness. A cross-party mesh is not
160
+ * forbidden, only undiscoverable: it takes hand-supplied addresses
161
+ * (`StrandConnectionOptions.bootstrapNodes`) or a hand-written dial (see the doc). Tracked:
162
+ * `backlog/feat-strand-party-identity`. Full reasoning: `docs/architecture.md` →
163
+ * "Replication cluster size".
164
+ *
165
+ * Raising or lowering it per strand is still the embedder's call
166
+ * ({@link resolveStrandClusterSize}, `CadreNodeConfig.strandClusterSize`,
167
+ * `StrandConnectionOptions.clusterSize`) — but every node on one strand must agree, so a change
168
+ * means restarting all of them. There is no smaller-than-{@link MIN_CLUSTER_SIZE} option.
169
+ *
170
+ * NOTE: cohort selection overfetches proportionally — `Libp2pKeyPeerNetwork.membershipOverfetch`
171
+ * asks FRET for `max(clusterSize * 4, clusterSize + 16)` candidates and does one peerStore
172
+ * protocol lookup per candidate, so 4 requests a 20-peer band (was 18 at breadth 2). Negligible,
173
+ * and bounded by the peers FRET actually knows. If strand cohort selection ever shows up as slow,
174
+ * look here first.
175
+ */
176
+ export const DEFAULT_STRAND_CLUSTER_SIZE = 4;
177
+ /**
178
+ * Cluster consensus policy every STRAND-network libp2p node runs — production
179
+ * (`StrandInstanceManager.buildStrandRuntime`) and the plugin's own networked e2e mesh alike.
180
+ * One definition so the two cannot drift; the literal used to be hand-copied at both sites.
181
+ *
182
+ * Structurally identical to {@link CONTROL_CLUSTER_POLICY} and deliberately separate from it:
183
+ * that one is the control database's, whose breadth is fixed at
184
+ * {@link CONTROL_REPLICATION_BREADTH} for reasons that do not apply to application data.
185
+ *
186
+ * `superMajorityThreshold` is ABSENT for the same reason it is absent there: omitting it is how
187
+ * both the coordinator and the cluster member select Optimystic's
188
+ * `DEFAULT_SUPER_MAJORITY_THRESHOLD` (0.75), the bar {@link DEFAULT_STRAND_CLUSTER_SIZE}'s
189
+ * copies/approvals table is computed against.
190
+ *
191
+ * `sizeTolerance` only makes sense alongside `allowDownsize`: a {@link
192
+ * DEFAULT_STRAND_CLUSTER_SIZE}-wide target is unsatisfiable by a strand of one, two or three
193
+ * machines, so the cohort must be allowed to shrink to the mesh that actually exists.
194
+ *
195
+ * `assumedClusterSize` is REQUIRED, and {@link MIN_CLUSTER_SIZE} is not a guess — it is the
196
+ * smallest strand that reaches the cluster path at all. Absent this field the read-repair /
197
+ * reconcile corroboration floor falls back to `clusterSize`, so
198
+ * `corroboratorCapacity(peers, 4)` is `max(peers, 3)` and two distinct non-self corroborators
199
+ * become mandatory. A two-machine strand fields exactly one peer, can never reach two, and so
200
+ * can never repair a block — `cluster-fetch:no-quorum` forever, which is a read that never
201
+ * catches up and a `create table` that dies with `Missing block` on the joining node. Declaring
202
+ * the honest floor does NOT lower the replication factor (that stays at
203
+ * {@link DEFAULT_STRAND_CLUSTER_SIZE}) and does not relax anything a strand with three or more
204
+ * machines does: `corroboratorCapacity` takes the MAX of the cohort actually present and this
205
+ * number, so the relaxed branch is reachable only by a cohort that is genuinely that small. See
206
+ * Optimystic's `cluster/cluster-policy.ts` ("Why two size yardsticks, not one").
207
+ *
208
+ * The `satisfies` is load-bearing for the same reason as {@link CONTROL_CLUSTER_POLICY}'s.
209
+ */
210
+ export const STRAND_CLUSTER_POLICY = Object.freeze({
211
+ allowDownsize: true,
212
+ sizeTolerance: 0.5,
213
+ assumedClusterSize: MIN_CLUSTER_SIZE,
214
+ });
215
+ /**
216
+ * Resolve the cluster size to hand `createLibp2pNode` for a **strand** network, applying
217
+ * {@link DEFAULT_STRAND_CLUSTER_SIZE} and rejecting values Optimystic cannot honour. The
218
+ * control network does not route through here — it uses the fixed
219
+ * {@link CONTROL_REPLICATION_BREADTH}.
220
+ *
221
+ * Leaving the value unset is NOT the same as passing the default: Optimystic's own fallback
222
+ * is 10, so every strand node-creating path must route through here.
223
+ *
224
+ * Every node on the same strand should resolve to the same value: the membership admission
225
+ * gate's *confident* path compares a coordinator's declared set against the member's own
226
+ * cohort view, and that view is bounded by this number, so a member configured much higher
227
+ * can reject a smaller declared set as a downsize. Divergence is a live hazard whenever FRET
228
+ * has a confident network-size estimate — not the unconditional refusal earlier revisions of
229
+ * this comment described. See `docs/architecture.md` → "Replication cluster size".
230
+ */
231
+ export function resolveStrandClusterSize(configured) {
232
+ if (configured === undefined) {
233
+ return DEFAULT_STRAND_CLUSTER_SIZE;
234
+ }
235
+ if (!Number.isInteger(configured) || configured < MIN_CLUSTER_SIZE) {
236
+ throw new Error(`clusterSize must be an integer >= ${MIN_CLUSTER_SIZE} (Optimystic's minimum cluster size); got ${configured}`);
237
+ }
238
+ return configured;
239
+ }
240
+ //# sourceMappingURL=cluster-size.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cluster-size.js","sourceRoot":"","sources":["../src/cluster-size.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAElC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC,MAAM,CAAC;IACnD,aAAa,EAAE,IAAI;IACnB,aAAa,EAAE,GAAG;IAClB,kBAAkB,EAAE,CAAC;CAC+B,CAAC,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyEG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,CAAC;IAClD,aAAa,EAAE,IAAI;IACnB,aAAa,EAAE,GAAG;IAClB,kBAAkB,EAAE,gBAAgB;CACgB,CAAC,CAAC;AAEvD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,wBAAwB,CAAC,UAAmB;IAC3D,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO,2BAA2B,CAAC;IACpC,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,gBAAgB,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CACd,qCAAqC,gBAAgB,6CAA6C,UAAU,EAAE,CAC9G,CAAC;IACH,CAAC;IACD,OAAO,UAAU,CAAC;AACnB,CAAC"}
@@ -23,6 +23,12 @@ export interface CreateNodeContext {
23
23
  fretProfile: 'edge' | 'core';
24
24
  /** libp2p listening port (Node TCP). Browser transports ignore it. */
25
25
  port: number;
26
+ /**
27
+ * Resolved replication cluster size. Already defaulted — pass it to
28
+ * `createLibp2pNode` verbatim; omitting it falls back to Optimystic's own
29
+ * default of 10, which gates writes on any smaller party.
30
+ */
31
+ clusterSize: number;
26
32
  /** Resolved persistent storage to back the node, if any. */
27
33
  storage?: IRawStorage;
28
34
  }
@@ -1,6 +1,7 @@
1
1
  import debug from 'debug';
2
2
  import optimysticPlugin from '@optimystic/quereus-plugin-optimystic/plugin';
3
3
  import { STRAND_SCHEMA } from './strand-schema.js';
4
+ import { resolveStrandClusterSize } from './cluster-size.js';
4
5
  const log = debug('sereus:plugin:strand');
5
6
  const timing = debug('sereus:plugin:strand:timing');
6
7
  /**
@@ -33,6 +34,9 @@ export function applyRegistrations(db, result) {
33
34
  */
34
35
  export async function composeStrand(db, options, platform) {
35
36
  const { strandId, bootstrapNodes = [], schema, port = 0, enableCache = true, fretProfile = 'edge', mode, } = options;
37
+ // Resolve (and validate) up front so a nonsense value fails before any plugin
38
+ // registration or node creation has happened.
39
+ const clusterSize = resolveStrandClusterSize(options.clusterSize);
36
40
  // Resolve the transactor. `mode` is the public knob: bootstrap -> local,
37
41
  // networked -> network. The legacy `transactor` override (used by unit
38
42
  // tests with `'test'`) only applies when `mode` is unspecified.
@@ -94,7 +98,7 @@ export async function composeStrand(db, options, platform) {
94
98
  log('Using injected libp2p node');
95
99
  }
96
100
  else {
97
- const created = await platform.createNode({ networkName, bootstrapNodes, fretProfile, port, storage });
101
+ const created = await platform.createNode({ networkName, bootstrapNodes, fretProfile, port, clusterSize, storage });
98
102
  createdNode = created;
99
103
  node = created;
100
104
  const repo = created.coordinatedRepo;
@@ -1 +1 @@
1
- {"version":3,"file":"compose-strand.js","sourceRoot":"","sources":["../src/compose-strand.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compose-strand.js","sourceRoot":"","sources":["../src/compose-strand.ts"],"names":[],"mappings":"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"}
@@ -30,13 +30,14 @@ export async function connectToStrandBrowser(db, options) {
30
30
  const dbHandle = await openOptimysticWebDb(`sereus-strand-${strandId}`);
31
31
  return new IndexedDBRawStorage(dbHandle);
32
32
  },
33
- async createNode({ networkName, bootstrapNodes, fretProfile, storage }) {
33
+ async createNode({ networkName, bootstrapNodes, fretProfile, clusterSize, storage }) {
34
34
  return createLibp2pNode({
35
35
  transports: [webSockets(), circuitRelayTransport()],
36
36
  listenAddrs: [],
37
37
  bootstrapNodes,
38
38
  networkName,
39
39
  fretProfile,
40
+ clusterSize,
40
41
  ...(storage && { storage }),
41
42
  });
42
43
  },
@@ -1 +1 @@
1
- {"version":3,"file":"connect-browser.js","sourceRoot":"","sources":["../src/connect-browser.ts"],"names":[],"mappings":"AACA,OAAO,YAAY,MAAM,0CAA0C,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAG1F,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AASxE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC3C,EAAY,EACZ,OAAgC;IAEhC,OAAO,aAAa,CAAC,EAAE,EAAE,OAAO,EAAE;QACjC,cAAc,CAAC,QAAQ;YACtB,kBAAkB,CAAC,QAAQ,EAAE,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAkC,CAAC,CAAC;QAC3F,CAAC;QACD,KAAK,CAAC,cAAc,CAAC,EAAE,QAAQ,EAAE,kBAAkB,EAAE,gBAAgB,EAAE;YACtE,IAAI,gBAAgB;gBAAE,OAAO,gBAAgB,CAAC;YAC9C,iEAAiE;YACjE,8CAA8C;YAC9C,IAAI,kBAAkB,KAAK,MAAM;gBAAE,OAAO,SAAS,CAAC;YACpD,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,iBAAiB,QAAQ,EAAE,CAAC,CAAC;YACxE,OAAO,IAAI,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,OAAO,EAAE;YACrE,OAAO,gBAAgB,CAAC;gBACvB,UAAU,EAAE,CAAC,UAAU,EAAE,EAAE,qBAAqB,EAAE,CAAC;gBACnD,WAAW,EAAE,EAAE;gBACf,cAAc;gBACd,WAAW;gBACX,WAAW;gBACX,GAAG,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,CAAC;aAC3B,CAAoB,CAAC;QACvB,CAAC;KACD,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"connect-browser.js","sourceRoot":"","sources":["../src/connect-browser.ts"],"names":[],"mappings":"AACA,OAAO,YAAY,MAAM,0CAA0C,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAG1F,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AASxE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC3C,EAAY,EACZ,OAAgC;IAEhC,OAAO,aAAa,CAAC,EAAE,EAAE,OAAO,EAAE;QACjC,cAAc,CAAC,QAAQ;YACtB,kBAAkB,CAAC,QAAQ,EAAE,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAkC,CAAC,CAAC;QAC3F,CAAC;QACD,KAAK,CAAC,cAAc,CAAC,EAAE,QAAQ,EAAE,kBAAkB,EAAE,gBAAgB,EAAE;YACtE,IAAI,gBAAgB;gBAAE,OAAO,gBAAgB,CAAC;YAC9C,iEAAiE;YACjE,8CAA8C;YAC9C,IAAI,kBAAkB,KAAK,MAAM;gBAAE,OAAO,SAAS,CAAC;YACpD,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,iBAAiB,QAAQ,EAAE,CAAC,CAAC;YACxE,OAAO,IAAI,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE;YAClF,OAAO,gBAAgB,CAAC;gBACvB,UAAU,EAAE,CAAC,UAAU,EAAE,EAAE,qBAAqB,EAAE,CAAC;gBACnD,WAAW,EAAE,EAAE;gBACf,cAAc;gBACd,WAAW;gBACX,WAAW;gBACX,WAAW;gBACX,GAAG,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,CAAC;aAC3B,CAAoB,CAAC;QACvB,CAAC;KACD,CAAC,CAAC;AACJ,CAAC"}
package/dist/connect.js CHANGED
@@ -13,7 +13,7 @@ export async function connectToStrand(db, options) {
13
13
  async registerCrypto(database) {
14
14
  await registerPlugin(database, cryptoPlugin);
15
15
  },
16
- async createNode({ networkName, bootstrapNodes, fretProfile, port, storage }) {
16
+ async createNode({ networkName, bootstrapNodes, fretProfile, port, clusterSize, storage }) {
17
17
  // Dynamically import to keep the module cross-platform friendly: this
18
18
  // pulls the Node-only `@optimystic/db-p2p` (TCP) entry, and is only
19
19
  // reached when actually creating a node (never for injected nodes).
@@ -23,6 +23,7 @@ export async function connectToStrand(db, options) {
23
23
  bootstrapNodes,
24
24
  networkName,
25
25
  fretProfile,
26
+ clusterSize,
26
27
  ...(storage && { storage }),
27
28
  });
28
29
  },
@@ -1 +1 @@
1
- {"version":3,"file":"connect.js","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,YAAY,MAAM,0CAA0C,CAAC;AAGpE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,EAAY,EACZ,OAAgC;IAEhC,OAAO,aAAa,CAAC,EAAE,EAAE,OAAO,EAAE;QACjC,KAAK,CAAC,cAAc,CAAC,QAAQ;YAC5B,MAAM,cAAc,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QAC9C,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE;YAC3E,sEAAsE;YACtE,oEAAoE;YACpE,oEAAoE;YACpE,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;YAChE,OAAO,gBAAgB,CAAC;gBACvB,IAAI;gBACJ,cAAc;gBACd,WAAW;gBACX,WAAW;gBACX,GAAG,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,CAAC;aAC3B,CAAoB,CAAC;QACvB,CAAC;KACD,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"connect.js","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,YAAY,MAAM,0CAA0C,CAAC;AAGpE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,EAAY,EACZ,OAAgC;IAEhC,OAAO,aAAa,CAAC,EAAE,EAAE,OAAO,EAAE;QACjC,KAAK,CAAC,cAAc,CAAC,QAAQ;YAC5B,MAAM,cAAc,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QAC9C,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE;YACxF,sEAAsE;YACtE,oEAAoE;YACpE,oEAAoE;YACpE,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;YAChE,OAAO,gBAAgB,CAAC;gBACvB,IAAI;gBACJ,cAAc;gBACd,WAAW;gBACX,WAAW;gBACX,WAAW;gBACX,GAAG,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,CAAC;aAC3B,CAAoB,CAAC;QACvB,CAAC;KACD,CAAC,CAAC;AACJ,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { connectToStrand } from './connect.js';
2
+ export { MIN_CLUSTER_SIZE, CONTROL_REPLICATION_BREADTH, CONTROL_CLUSTER_POLICY, DEFAULT_STRAND_CLUSTER_SIZE, STRAND_CLUSTER_POLICY, resolveStrandClusterSize } from './cluster-size.js';
2
3
  export type { StrandConnectionOptions, SereusPluginResult } from './types.js';
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export { connectToStrand } from './connect.js';
2
+ export { MIN_CLUSTER_SIZE, CONTROL_REPLICATION_BREADTH, CONTROL_CLUSTER_POLICY, DEFAULT_STRAND_CLUSTER_SIZE, STRAND_CLUSTER_POLICY, resolveStrandClusterSize } from './cluster-size.js';
2
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EACN,gBAAgB,EAChB,2BAA2B,EAC3B,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,wBAAwB,EACxB,MAAM,mBAAmB,CAAC"}