@serfab/quereus-plugin-sereus 1.7.0 → 1.8.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
@@ -280,7 +280,7 @@ on shutdown.
280
280
  |---|---|---|---|
281
281
  | `strandId` | string | *required* | UUID of the strand to connect to |
282
282
  | `bootstrapNodes` | string[] | `[]` | Bootstrap multiaddrs for peer discovery |
283
- | `schema` | string | — | sApp schema DDL to apply, as the body of `declare schema App { ... }`. Its tables may share names with the strand's own tables; they are stored separately ([Same-Named Tables](../../docs/strands.md#same-named-tables-in-strand-and-app)) |
283
+ | `schema` | string | — | sApp schema DDL to apply, as the body of `declare schema App { ... }`. An item the Quereus parser would skip (`create unique index …`, a misspelled item keyword) is refused rather than ignored. Its tables may share names with the strand's own tables; they are stored separately ([Same-Named Tables](../../docs/strands.md#same-named-tables-in-strand-and-app)) |
284
284
  | `sAppId` | string | `'unknown'` | sApp author public key |
285
285
  | `sAppVersion` | string | `'1.0.0'` | sApp version |
286
286
  | `port` | number | `0` | libp2p listening port (0 = random) |
@@ -301,10 +301,20 @@ on shutdown.
301
301
  | `schema` | string | — | sApp schema DDL |
302
302
  | `sapp_id` | string | `'unknown'` | sApp author public key |
303
303
  | `sapp_version` | string | `'1.0.0'` | sApp version |
304
- | `port` | number | `0` | libp2p listening port |
305
- | `enable_cache` | boolean | `true` | Enable caching |
306
- | `fret_profile` | string | `'edge'` | FRET profile (`'edge'` or `'core'`) |
307
- | `transactor` | string | `'network'` | Storage engine (`'local'`, `'network'` or `'test'`) — see the `transactor` row above. An unrecognised value is rejected at load |
304
+ | `port` | number | `0` | libp2p listening port: an integer from 0 to 65535 (0 = random) |
305
+ | `enable_cache` | boolean | `true` | Enable caching: `true`, `false`, `1` or `0` |
306
+ | `fret_profile` | select | `'edge'` | FRET profile (`'edge'` or `'core'`) |
307
+ | `transactor` | select | `'network'` | Storage engine (`'local'`, `'network'` or `'test'`) — see the `transactor` row above |
308
+ | `storage_path` | string | — | Node entry only: directory for a persistent `FileRawStorage`. The browser entry rejects it (it stores in IndexedDB) |
309
+
310
+ Settings are checked strictly at load. An unknown key (including the retired
311
+ `mode`) or a value of the wrong type fails the load with one error that names
312
+ every offending setting; nothing falls back to a default the user did not
313
+ choose. Leaving a setting out, NULL, or `''` means "use the default" (`''`
314
+ only for the string and select settings). A host that adds keys of its own to
315
+ the plugin's settings map therefore fails to load the plugin. `port` and
316
+ `enable_cache` must be a JSON number and boolean: a `${VAR}` reference in
317
+ `quoomb.config.json` expands to a string, which they reject.
308
318
 
309
319
  **Bootstrap multiaddrs.** Browsers can only dial transports reachable from an
310
320
  `https://` page. Use `/wss` (or `/dns/.../wss`, or a relay-fronted multiaddr).
@@ -14,18 +14,35 @@ export declare const MIN_CLUSTER_SIZE = 2;
14
14
  * 1000 ms, a LAN-shaped budget, and it deliberately left that default alone — choosing the
15
15
  * value for a deployment of phones is the embedder's job.
16
16
  *
17
- * **Why 5000.** Optimystic GitHub #22, reported by a sereus-based chat app: two parties that
18
- * reach each other only through a public circuit relay have a round trip near 1.8 s, and a
19
- * fresh stream (dial or reuse, protocol select, send, receive) cannot finish inside a second.
20
- * At a cohort of two there is exactly one peer to ask, so one late answer leaves the read with
21
- * nothing to corroborate against and it is declined. 5000 covers 1.8 s with room for the Noise
22
- * handshake, the relay hops and a phone's pure-JS crypto (cadre-core's
23
- * `NetworkConfig.noiseCrypto`), and it is the value Optimystic's own note advises for that
24
- * link. The whole-pass reconcile bound Optimystic derives from it (`max(5000, 5 x this)`)
25
- * becomes 25 s.
26
- *
27
- * **Measured** 2026-09-26, one Windows developer machine, two relay-only `CadreNode`s on one
28
- * shared loopback dedicated relay, the one-way outbound frame delay raised to 900 ms
17
+ * **Why 7000: it is a derivation, written out.** One read-path request opens a fresh protocol
18
+ * stream over a connection that is already open, which costs the protocol negotiation plus the
19
+ * request and its answer: two link round trips, `CIRCUIT_REQUEST_ROUND_TRIPS` in cadre-core's
20
+ * `link-budget.ts`. At the link round trip cadre declares by default,
21
+ * `DECLARED_LINK_ROUND_TRIP_MS` (3 500 ms, covering the 3-second relayed round trip sereus
22
+ * supports), that is 2 x 3 500 = 7 000 ms — `cohortReadDeadlineMs()` in that module. This
23
+ * constant must equal it, and `cadre-core/test/link-budget.spec.ts` pins the two equal; it is
24
+ * spelled as a number here only because this package cannot import cadre-core (the dependency
25
+ * runs the other way). A host that declares its own link (`NetworkConfig.linkRoundTripMs`)
26
+ * gets the derivation at that link instead of this constant, and a host that sets
27
+ * `NetworkConfig.cohortQueryTimeoutMs` gets exactly what it set. No admission allowance is
28
+ * added: a read runs over an open connection and opens none. The whole-pass reconcile bound
29
+ * Optimystic derives from it (`max(5000, 5 x this)`) becomes 35 s.
30
+ *
31
+ * **History: why 5000 before, and why it was not enough.** Optimystic GitHub #22, reported by
32
+ * a sereus-based chat app: two parties that reach each other only through a public circuit
33
+ * relay have a round trip near 1.8 s, and a fresh stream (dial or reuse, protocol select, send,
34
+ * receive) cannot finish inside a second. At a cohort of two there is exactly one peer to ask,
35
+ * so one late answer leaves the read with nothing to corroborate against and it is declined.
36
+ * 5000 was chosen to cover 1.8 s with room for the Noise handshake, the relay hops and a
37
+ * phone's pure-JS crypto (cadre-core's `NetworkConfig.noiseCrypto`), and it is the value
38
+ * Optimystic's own note advises for that link. But sereus supports a 3 s round trip, and two
39
+ * round trips of pure delay at that link is 6 s: above 5 000, so at the supported link every
40
+ * consult timed out and every read had nothing to corroborate against — the #22 failure again,
41
+ * one band up. The derivation ends that class: the next person to widen the link changes one
42
+ * declaration.
43
+ *
44
+ * **Measured at 5000**, 2026-09-26, one Windows developer machine, two relay-only `CadreNode`s
45
+ * on one shared loopback dedicated relay, the one-way outbound frame delay raised to 900 ms
29
46
  * (`pipelined` mode — see `docs/testing.md` → "Where measurements live") AFTER the strand had
30
47
  * formed and first synced, for a round trip near 1.8 s. A machine that had joined, gone away
31
48
  * while the other wrote, then re-attached over its own stale store and read the row written
@@ -34,53 +51,55 @@ export declare const MIN_CLUSTER_SIZE = 2;
34
51
  * `cluster-fetch:peers-silent { silent: 1, consulted: 2 }` followed by
35
52
  * `cluster-fetch:no-quorum`, retried about a second later until one lands; at 5000 the
36
53
  * corroboration succeeded instead (`cluster-fetch:local-current`). Both journeys COMPLETED at
37
- * either value, which is why no committed scenario gates this — 900 ms is not where it breaks,
38
- * and above it a run breaks on redialling instead. The recipe for re-measuring is in
39
- * `docs/testing.md` → "Where measurements live".
54
+ * either value, which is why no committed scenario gates this — 900 ms is not where it breaks.
55
+ * The recipe for re-measuring is in `docs/testing.md` → "Where measurements live".
56
+ *
57
+ * **Measured at 7000 against 5000**, 2026-09-29, same machine and shape, at 1 500 ms one-way
58
+ * (the supported 3 s round trip), fresh-join arm, one run each under
59
+ * `DEBUG=optimystic:db-p2p:coordinator-repo*`, on `@optimystic/*` 1.7.0: **39 declined reads at
60
+ * 5000, 32 at 7000**, both runs completing (writable at 70.2 s and 63.6 s). That difference
61
+ * was noise, not the deadline at work: in both runs every consult on the joiner ended
62
+ * 3.00-3.02 s after the one before it, the signature of Optimystic's then-fixed 3 000 ms
63
+ * request dial deadline, which also bounded the protocol negotiation on an ALREADY-OPEN
64
+ * connection, where the negotiation alone costs one link round trip — 3 s at this delay.
65
+ *
66
+ * **Measured at 7000 on 1.8.0**, the same day and shape, once a consult ran under this deadline
67
+ * alone and cadre-core stated its declared link to Optimystic: **no declined read** during the
68
+ * measured attach, over two fresh joins and three re-attaches, and every consult on the joiner
69
+ * completed 6.03-6.05 s after the one before it — the two round trips this deadline is derived
70
+ * from, with about 0.95 s to spare. The first-sync figures from those runs are on
71
+ * `DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS`.
40
72
  *
41
73
  * **What it costs**, in the terms Optimystic's own field doc uses: a peer that is truly gone
42
- * now holds a read of a block missing locally for up to 5 s before the read is declined
43
- * instead of 1 s. A joining machine's first sync runs several such consults, so it is the cost
44
- * that binds — and the first-sync band measured at BOTH deadlines, against the budget it has
45
- * to fit, is recorded once, on `DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS` in cadre-core's
46
- * `strand-first-sync-gate.ts`. At 5000 against the 30 s budget that preceded it, this change
47
- * would have turned a slow-but-working join into the very `StrandAwaitingFirstSyncError` the
48
- * report named. 3000 is the fallback if that margin ever goes — still 1.7x the reported round
49
- * trip, and a doomed consult costs 40% less.
50
- *
51
- * **What else it lengthens, which the first-sync budget is only the largest case of.** Every
52
- * cadre-core deadline that wraps an Optimystic read or commit was sized when one silent peer
53
- * cost 1 s and a whole reconcile pass cost 5 s; those are now 5 s and 25 s. Two consequences
54
- * are already true rather than hypothetical:
55
- *
56
- * - The inbound membership admission gate reads the control database uncached and FAILS OPEN
57
- * after 2 s (`ADMISSION_DECISION_TIMEOUT_MS`, cadre-core's
58
- * `membership-connection-gater.ts`), so a decision whose read consults a silent peer can no
59
- * longer settle inside that deadline at all: an unplaced peer is admitted at the connection
60
- * layer for the whole 5 s instead of roughly 1 s. It is admitted to nothing more than a
61
- * connection — the per-protocol stream gates still decide, and unplaced relay reservations
62
- * stay capped (`MAX_UNAUTHORIZED_RELAY_RESERVATIONS`) — which is why this is a widened cost
63
- * and not a hole. `backlog/debt-cadre-deadlines-sized-against-old-optimystic-bounds` owns
64
- * the audit of the rest.
65
- * - `CONTROL_READ_RETRY_BUDGET_MS` (1500 ms) now terminates the control-read retry loop after
66
- * the FIRST attempt whenever that attempt burned this deadline, so a read failing that way
67
- * gets no second presentation. Its own doc carries the coupling.
74
+ * now holds a read of a block missing locally for up to 7 s before the read is declined
75
+ * instead of 5 s (1 s at Optimystic's default). A joining machine's first sync runs several
76
+ * such consults, so it is the cost that binds — and the first-sync band measured at each
77
+ * deadline, against the budget it has to fit, is recorded once, on
78
+ * `DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS` in cadre-core's `strand-first-sync-gate.ts`. At 5000
79
+ * against the 30 s budget that once preceded it, that change alone would have turned a
80
+ * slow-but-working join into the very `StrandAwaitingFirstSyncError` the report named.
81
+ *
82
+ * **What else it lengthens.** Every cadre-core deadline that waits on an Optimystic read or
83
+ * commit waits on this one too; which of them cut the read off on purpose and which must
84
+ * outlast it is listed in `docs/cadre-consistency.md` → "Deadlines Over Optimystic's Reads and
85
+ * Commits".
68
86
  *
69
87
  * **One value for both networks, not two.** The reason to widen is the link, and a phone's
70
88
  * control node and its strand nodes share it; nothing about control traffic or strand traffic
71
89
  * argues for different numbers. A host that needs another value moves both at once —
72
- * cadre-core's `NetworkConfig.cohortQueryTimeoutMs`, threaded to {@link controlClusterPolicy}
73
- * and {@link strandClusterPolicy}.
90
+ * cadre-core's `NetworkConfig.cohortQueryTimeoutMs` or `NetworkConfig.linkRoundTripMs`,
91
+ * threaded to {@link controlClusterPolicy} and {@link strandClusterPolicy} through one helper
92
+ * (`declaredCohortReadDeadlineMs` in cadre-core's `link-budget.ts`).
74
93
  *
75
94
  * NOTE: this number and the first-sync budget are coupled; the margin between that budget and
76
- * the slowest first sync measured is stated on it. Re-measure the first-sync band before raising
77
- * this again, or when a deployment's first sync grows more collections than the two-table
78
- * scenario measured above — `DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS` carries the result and
79
- * `docs/testing.md` the recipe.
95
+ * the slowest first sync measured is stated on it. Re-measure the first-sync band before
96
+ * raising the declared link round trip, or when a deployment's first sync grows more
97
+ * collections than the two-table scenario measured above — `DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS`
98
+ * carries the result and `docs/testing.md` the recipe.
80
99
  * Nothing warns when the margin goes; the symptom is `StrandAwaitingFirstSyncError` on a join
81
100
  * that was progressing normally, which is the failure this pair of numbers exists to end.
82
101
  */
83
- export declare const COHORT_READ_DEADLINE_MS = 5000;
102
+ export declare const COHORT_READ_DEADLINE_MS = 7000;
84
103
  /**
85
104
  * How many nodes each block of the **control** (cadre membership) database is replicated
86
105
  * to. Chosen to exceed any party's node count, so in practice every control block lands
@@ -248,7 +267,9 @@ export declare const CONTROL_CLUSTER_POLICY: Readonly<{
248
267
  * The control network derives one from the party's enrolled machines
249
268
  * ({@link controlClusterPolicy}), which is exact there because every enrolled machine runs the
250
269
  * control node. A strand has no equivalent count: it launches only on machines whose embedder
251
- * registered its sApp config, and reusing the party count over-declares — which is worse than
270
+ * registered its sApp config, plus storage-profile machines that host unclaimed strands as
271
+ * storage replicas (cadre-core's `CadreNodeConfig.hostUnclaimedStrands`) and whose strand filter
272
+ * admits it. Phones serve only what their app claims, so reusing the party count over-declares, which is worse than
252
273
  * declaring nothing, because at a yardstick of three or more the corroboration floor is pinned
253
274
  * at two peers and a strand that can field only one could then never repair at all. So every
254
275
  * strand runs {@link STRAND_CLUSTER_POLICY} unchanged and takes the single-voter exposure
@@ -441,8 +462,10 @@ export declare function resolveStrandClusterSize(configured?: number): number;
441
462
  * machine runs the control node by construction: there, enrolled IS serving
442
463
  * ({@link controlClusterPolicy}, fed by cadre-core's `enrolled-machine-store.ts`). A STRAND does
443
464
  * not — it launches only on machines whose embedder registered its sApp config
444
- * (`CadreNode.addStrand`), so a closed strand shared by two machines of a three-machine party is
445
- * served by two, and declaring three would be exactly the unsafe over-declaration above. No
465
+ * (`CadreNode.addStrand`), plus storage-profile machines hosting it as a storage replica
466
+ * (`CadreNodeConfig.hostUnclaimedStrands`) whose strand filter admits it. A closed strand
467
+ * claimed by two phones of a three-phone party is served by two, and declaring three would be
468
+ * exactly the unsafe over-declaration above. No
446
469
  * authenticated per-strand serving count exists yet, so strand nodes declare NOTHING and run the
447
470
  * frozen {@link STRAND_CLUSTER_POLICY} — keeping the known, upstream-tracked single-voter
448
471
  * exposure (`backlog/debt-read-repair-single-voter-corroboration`) rather than risking the
@@ -13,18 +13,35 @@ export const MIN_CLUSTER_SIZE = 2;
13
13
  * 1000 ms, a LAN-shaped budget, and it deliberately left that default alone — choosing the
14
14
  * value for a deployment of phones is the embedder's job.
15
15
  *
16
- * **Why 5000.** Optimystic GitHub #22, reported by a sereus-based chat app: two parties that
17
- * reach each other only through a public circuit relay have a round trip near 1.8 s, and a
18
- * fresh stream (dial or reuse, protocol select, send, receive) cannot finish inside a second.
19
- * At a cohort of two there is exactly one peer to ask, so one late answer leaves the read with
20
- * nothing to corroborate against and it is declined. 5000 covers 1.8 s with room for the Noise
21
- * handshake, the relay hops and a phone's pure-JS crypto (cadre-core's
22
- * `NetworkConfig.noiseCrypto`), and it is the value Optimystic's own note advises for that
23
- * link. The whole-pass reconcile bound Optimystic derives from it (`max(5000, 5 x this)`)
24
- * becomes 25 s.
25
- *
26
- * **Measured** 2026-09-26, one Windows developer machine, two relay-only `CadreNode`s on one
27
- * shared loopback dedicated relay, the one-way outbound frame delay raised to 900 ms
16
+ * **Why 7000: it is a derivation, written out.** One read-path request opens a fresh protocol
17
+ * stream over a connection that is already open, which costs the protocol negotiation plus the
18
+ * request and its answer: two link round trips, `CIRCUIT_REQUEST_ROUND_TRIPS` in cadre-core's
19
+ * `link-budget.ts`. At the link round trip cadre declares by default,
20
+ * `DECLARED_LINK_ROUND_TRIP_MS` (3 500 ms, covering the 3-second relayed round trip sereus
21
+ * supports), that is 2 x 3 500 = 7 000 ms — `cohortReadDeadlineMs()` in that module. This
22
+ * constant must equal it, and `cadre-core/test/link-budget.spec.ts` pins the two equal; it is
23
+ * spelled as a number here only because this package cannot import cadre-core (the dependency
24
+ * runs the other way). A host that declares its own link (`NetworkConfig.linkRoundTripMs`)
25
+ * gets the derivation at that link instead of this constant, and a host that sets
26
+ * `NetworkConfig.cohortQueryTimeoutMs` gets exactly what it set. No admission allowance is
27
+ * added: a read runs over an open connection and opens none. The whole-pass reconcile bound
28
+ * Optimystic derives from it (`max(5000, 5 x this)`) becomes 35 s.
29
+ *
30
+ * **History: why 5000 before, and why it was not enough.** Optimystic GitHub #22, reported by
31
+ * a sereus-based chat app: two parties that reach each other only through a public circuit
32
+ * relay have a round trip near 1.8 s, and a fresh stream (dial or reuse, protocol select, send,
33
+ * receive) cannot finish inside a second. At a cohort of two there is exactly one peer to ask,
34
+ * so one late answer leaves the read with nothing to corroborate against and it is declined.
35
+ * 5000 was chosen to cover 1.8 s with room for the Noise handshake, the relay hops and a
36
+ * phone's pure-JS crypto (cadre-core's `NetworkConfig.noiseCrypto`), and it is the value
37
+ * Optimystic's own note advises for that link. But sereus supports a 3 s round trip, and two
38
+ * round trips of pure delay at that link is 6 s: above 5 000, so at the supported link every
39
+ * consult timed out and every read had nothing to corroborate against — the #22 failure again,
40
+ * one band up. The derivation ends that class: the next person to widen the link changes one
41
+ * declaration.
42
+ *
43
+ * **Measured at 5000**, 2026-09-26, one Windows developer machine, two relay-only `CadreNode`s
44
+ * on one shared loopback dedicated relay, the one-way outbound frame delay raised to 900 ms
28
45
  * (`pipelined` mode — see `docs/testing.md` → "Where measurements live") AFTER the strand had
29
46
  * formed and first synced, for a round trip near 1.8 s. A machine that had joined, gone away
30
47
  * while the other wrote, then re-attached over its own stale store and read the row written
@@ -33,53 +50,55 @@ export const MIN_CLUSTER_SIZE = 2;
33
50
  * `cluster-fetch:peers-silent { silent: 1, consulted: 2 }` followed by
34
51
  * `cluster-fetch:no-quorum`, retried about a second later until one lands; at 5000 the
35
52
  * corroboration succeeded instead (`cluster-fetch:local-current`). Both journeys COMPLETED at
36
- * either value, which is why no committed scenario gates this — 900 ms is not where it breaks,
37
- * and above it a run breaks on redialling instead. The recipe for re-measuring is in
38
- * `docs/testing.md` → "Where measurements live".
53
+ * either value, which is why no committed scenario gates this — 900 ms is not where it breaks.
54
+ * The recipe for re-measuring is in `docs/testing.md` → "Where measurements live".
55
+ *
56
+ * **Measured at 7000 against 5000**, 2026-09-29, same machine and shape, at 1 500 ms one-way
57
+ * (the supported 3 s round trip), fresh-join arm, one run each under
58
+ * `DEBUG=optimystic:db-p2p:coordinator-repo*`, on `@optimystic/*` 1.7.0: **39 declined reads at
59
+ * 5000, 32 at 7000**, both runs completing (writable at 70.2 s and 63.6 s). That difference
60
+ * was noise, not the deadline at work: in both runs every consult on the joiner ended
61
+ * 3.00-3.02 s after the one before it, the signature of Optimystic's then-fixed 3 000 ms
62
+ * request dial deadline, which also bounded the protocol negotiation on an ALREADY-OPEN
63
+ * connection, where the negotiation alone costs one link round trip — 3 s at this delay.
64
+ *
65
+ * **Measured at 7000 on 1.8.0**, the same day and shape, once a consult ran under this deadline
66
+ * alone and cadre-core stated its declared link to Optimystic: **no declined read** during the
67
+ * measured attach, over two fresh joins and three re-attaches, and every consult on the joiner
68
+ * completed 6.03-6.05 s after the one before it — the two round trips this deadline is derived
69
+ * from, with about 0.95 s to spare. The first-sync figures from those runs are on
70
+ * `DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS`.
39
71
  *
40
72
  * **What it costs**, in the terms Optimystic's own field doc uses: a peer that is truly gone
41
- * now holds a read of a block missing locally for up to 5 s before the read is declined
42
- * instead of 1 s. A joining machine's first sync runs several such consults, so it is the cost
43
- * that binds — and the first-sync band measured at BOTH deadlines, against the budget it has
44
- * to fit, is recorded once, on `DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS` in cadre-core's
45
- * `strand-first-sync-gate.ts`. At 5000 against the 30 s budget that preceded it, this change
46
- * would have turned a slow-but-working join into the very `StrandAwaitingFirstSyncError` the
47
- * report named. 3000 is the fallback if that margin ever goes — still 1.7x the reported round
48
- * trip, and a doomed consult costs 40% less.
49
- *
50
- * **What else it lengthens, which the first-sync budget is only the largest case of.** Every
51
- * cadre-core deadline that wraps an Optimystic read or commit was sized when one silent peer
52
- * cost 1 s and a whole reconcile pass cost 5 s; those are now 5 s and 25 s. Two consequences
53
- * are already true rather than hypothetical:
54
- *
55
- * - The inbound membership admission gate reads the control database uncached and FAILS OPEN
56
- * after 2 s (`ADMISSION_DECISION_TIMEOUT_MS`, cadre-core's
57
- * `membership-connection-gater.ts`), so a decision whose read consults a silent peer can no
58
- * longer settle inside that deadline at all: an unplaced peer is admitted at the connection
59
- * layer for the whole 5 s instead of roughly 1 s. It is admitted to nothing more than a
60
- * connection — the per-protocol stream gates still decide, and unplaced relay reservations
61
- * stay capped (`MAX_UNAUTHORIZED_RELAY_RESERVATIONS`) — which is why this is a widened cost
62
- * and not a hole. `backlog/debt-cadre-deadlines-sized-against-old-optimystic-bounds` owns
63
- * the audit of the rest.
64
- * - `CONTROL_READ_RETRY_BUDGET_MS` (1500 ms) now terminates the control-read retry loop after
65
- * the FIRST attempt whenever that attempt burned this deadline, so a read failing that way
66
- * gets no second presentation. Its own doc carries the coupling.
73
+ * now holds a read of a block missing locally for up to 7 s before the read is declined
74
+ * instead of 5 s (1 s at Optimystic's default). A joining machine's first sync runs several
75
+ * such consults, so it is the cost that binds — and the first-sync band measured at each
76
+ * deadline, against the budget it has to fit, is recorded once, on
77
+ * `DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS` in cadre-core's `strand-first-sync-gate.ts`. At 5000
78
+ * against the 30 s budget that once preceded it, that change alone would have turned a
79
+ * slow-but-working join into the very `StrandAwaitingFirstSyncError` the report named.
80
+ *
81
+ * **What else it lengthens.** Every cadre-core deadline that waits on an Optimystic read or
82
+ * commit waits on this one too; which of them cut the read off on purpose and which must
83
+ * outlast it is listed in `docs/cadre-consistency.md` → "Deadlines Over Optimystic's Reads and
84
+ * Commits".
67
85
  *
68
86
  * **One value for both networks, not two.** The reason to widen is the link, and a phone's
69
87
  * control node and its strand nodes share it; nothing about control traffic or strand traffic
70
88
  * argues for different numbers. A host that needs another value moves both at once —
71
- * cadre-core's `NetworkConfig.cohortQueryTimeoutMs`, threaded to {@link controlClusterPolicy}
72
- * and {@link strandClusterPolicy}.
89
+ * cadre-core's `NetworkConfig.cohortQueryTimeoutMs` or `NetworkConfig.linkRoundTripMs`,
90
+ * threaded to {@link controlClusterPolicy} and {@link strandClusterPolicy} through one helper
91
+ * (`declaredCohortReadDeadlineMs` in cadre-core's `link-budget.ts`).
73
92
  *
74
93
  * NOTE: this number and the first-sync budget are coupled; the margin between that budget and
75
- * the slowest first sync measured is stated on it. Re-measure the first-sync band before raising
76
- * this again, or when a deployment's first sync grows more collections than the two-table
77
- * scenario measured above — `DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS` carries the result and
78
- * `docs/testing.md` the recipe.
94
+ * the slowest first sync measured is stated on it. Re-measure the first-sync band before
95
+ * raising the declared link round trip, or when a deployment's first sync grows more
96
+ * collections than the two-table scenario measured above — `DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS`
97
+ * carries the result and `docs/testing.md` the recipe.
79
98
  * Nothing warns when the margin goes; the symptom is `StrandAwaitingFirstSyncError` on a join
80
99
  * that was progressing normally, which is the failure this pair of numbers exists to end.
81
100
  */
82
- export const COHORT_READ_DEADLINE_MS = 5000;
101
+ export const COHORT_READ_DEADLINE_MS = 7000;
83
102
  /**
84
103
  * How many nodes each block of the **control** (cadre membership) database is replicated
85
104
  * to. Chosen to exceed any party's node count, so in practice every control block lands
@@ -247,7 +266,9 @@ export const CONTROL_CLUSTER_POLICY = Object.freeze({
247
266
  * The control network derives one from the party's enrolled machines
248
267
  * ({@link controlClusterPolicy}), which is exact there because every enrolled machine runs the
249
268
  * control node. A strand has no equivalent count: it launches only on machines whose embedder
250
- * registered its sApp config, and reusing the party count over-declares — which is worse than
269
+ * registered its sApp config, plus storage-profile machines that host unclaimed strands as
270
+ * storage replicas (cadre-core's `CadreNodeConfig.hostUnclaimedStrands`) and whose strand filter
271
+ * admits it. Phones serve only what their app claims, so reusing the party count over-declares, which is worse than
251
272
  * declaring nothing, because at a yardstick of three or more the corroboration floor is pinned
252
273
  * at two peers and a strand that can field only one could then never repair at all. So every
253
274
  * strand runs {@link STRAND_CLUSTER_POLICY} unchanged and takes the single-voter exposure
@@ -448,8 +469,10 @@ export function resolveStrandClusterSize(configured) {
448
469
  * machine runs the control node by construction: there, enrolled IS serving
449
470
  * ({@link controlClusterPolicy}, fed by cadre-core's `enrolled-machine-store.ts`). A STRAND does
450
471
  * not — it launches only on machines whose embedder registered its sApp config
451
- * (`CadreNode.addStrand`), so a closed strand shared by two machines of a three-machine party is
452
- * served by two, and declaring three would be exactly the unsafe over-declaration above. No
472
+ * (`CadreNode.addStrand`), plus storage-profile machines hosting it as a storage replica
473
+ * (`CadreNodeConfig.hostUnclaimedStrands`) whose strand filter admits it. A closed strand
474
+ * claimed by two phones of a three-phone party is served by two, and declaring three would be
475
+ * exactly the unsafe over-declaration above. No
453
476
  * authenticated per-strand serving count exists yet, so strand nodes declare NOTHING and run the
454
477
  * frozen {@link STRAND_CLUSTER_POLICY} — keeping the known, upstream-tracked single-voter
455
478
  * exposure (`backlog/debt-read-repair-single-voter-corroboration`) rather than risking the
@@ -1 +1 @@
1
- {"version":3,"file":"cluster-size.js","sourceRoot":"","sources":["../src/cluster-size.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cluster-size.js","sourceRoot":"","sources":["../src/cluster-size.ts"],"names":[],"mappings":"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"}
@@ -90,4 +90,17 @@ export interface StrandPlatform {
90
90
  * vtab, hydrate the catalog, then apply the sApp schema.
91
91
  */
92
92
  export declare function composeStrand(db: Database, options: StrandConnectionOptions, platform: StrandPlatform): Promise<SereusPluginResult>;
93
+ /**
94
+ * Step 7 of {@link composeStrand}: declare the sApp's schema as `App` and apply it — a
95
+ * declarative diff against the catalog, so re-applying a schema that is already in place
96
+ * emits nothing. The one site that applies an `App` schema: `composeStrand` calls it at
97
+ * bring-up, and cadre-core's `StrandDatabase.attachAppSchema` calls it on a live storage
98
+ * replica when an app claims the strand. The database must already be composed (optimystic
99
+ * set as the default vtab), or the tables land in memory instead of the strand.
100
+ *
101
+ * Refuses a schema holding an item the Quereus parser skipped: it keeps any item whose leading
102
+ * keyword it does not model (`create unique index …`, a misspelled `tabel`, `domain`) as an
103
+ * opaque placeholder that apply ignores, so the app would otherwise run without that item.
104
+ */
105
+ export declare function applyAppSchema(db: Database, schema: string): Promise<void>;
93
106
  export {};
@@ -115,10 +115,11 @@ export async function composeStrand(db, options, platform) {
115
115
  else {
116
116
  // NOTE: this path takes the frozen policy whole, so it takes COHORT_READ_DEADLINE_MS
117
117
  // with no way to override it — `StrandConnectionOptions` has no counterpart to
118
- // cadre-core's `NetworkConfig.cohortQueryTimeoutMs`. Fine today: every production
119
- // strand comes up through cadre-core, which does thread the field, and this path is
120
- // the plugin's own connect/e2e route where 5000 ms is the right answer anyway. If a
121
- // plugin embedder ever needs a different deadline,
118
+ // cadre-core's `NetworkConfig.cohortQueryTimeoutMs` or `linkRoundTripMs`. Fine
119
+ // today: every production strand comes up through cadre-core, which does thread
120
+ // both, and this path is the plugin's own connect/e2e route, where the constant —
121
+ // two link round trips at cadre's default declared link — is the right answer
122
+ // anyway. If a plugin embedder ever needs a different deadline,
122
123
  // `strandClusterPolicy(clusterSize, { cohortQueryTimeoutMs: <ms> })` is the call to
123
124
  // reach for here.
124
125
  const created = await platform.createNode({ networkName, bootstrapNodes, fretProfile, port, clusterSize, clusterPolicy: STRAND_CLUSTER_POLICY, storage });
@@ -189,12 +190,7 @@ export async function composeStrand(db, options, platform) {
189
190
  // is a note for whoever first ships a migration that does.
190
191
  if (schema) {
191
192
  log('Applying sApp schema for strand %s', strandId);
192
- await db.exec(`
193
- declare schema App {
194
- ${schema}
195
- }
196
- apply schema App;
197
- `);
193
+ await applyAppSchema(db, schema);
198
194
  log('sApp schema applied');
199
195
  }
200
196
  }
@@ -232,6 +228,35 @@ export async function composeStrand(db, options, platform) {
232
228
  },
233
229
  };
234
230
  }
231
+ /**
232
+ * Step 7 of {@link composeStrand}: declare the sApp's schema as `App` and apply it — a
233
+ * declarative diff against the catalog, so re-applying a schema that is already in place
234
+ * emits nothing. The one site that applies an `App` schema: `composeStrand` calls it at
235
+ * bring-up, and cadre-core's `StrandDatabase.attachAppSchema` calls it on a live storage
236
+ * replica when an app claims the strand. The database must already be composed (optimystic
237
+ * set as the default vtab), or the tables land in memory instead of the strand.
238
+ *
239
+ * Refuses a schema holding an item the Quereus parser skipped: it keeps any item whose leading
240
+ * keyword it does not model (`create unique index …`, a misspelled `tabel`, `domain`) as an
241
+ * opaque placeholder that apply ignores, so the app would otherwise run without that item.
242
+ */
243
+ export async function applyAppSchema(db, schema) {
244
+ await db.exec(`
245
+ declare schema App {
246
+ ${schema}
247
+ }
248
+ `);
249
+ assertNoIgnoredItems(db);
250
+ await db.exec('apply schema App;');
251
+ }
252
+ function assertNoIgnoredItems(db) {
253
+ const items = db.declaredSchemaManager.getDeclaredSchema('App')?.items ?? [];
254
+ const ignored = items.filter(item => item.type === 'declareIgnored').length;
255
+ if (ignored > 0) {
256
+ // Only a count: Quereus leaves an ignored item's source text empty.
257
+ throw new Error(`sApp schema has ${ignored} item(s) the parser does not recognize (a \`create …\` prefix or a misspelled item keyword such as \`tabel\`); items are \`table\`, \`index\`, \`unique index\`, \`view\`, \`materialized view\`, \`seed\` and \`assertion\``);
258
+ }
259
+ }
235
260
  /**
236
261
  * Steps 1-2 of {@link composeStrand}: register the crypto plugin, then the optimystic
237
262
  * plugin carrying this strand's transactor defaults. Split out so the composition can
@@ -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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"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { connectToStrand } from './connect.js';
2
+ export { applyAppSchema } from './compose-strand.js';
2
3
  export { MIN_CLUSTER_SIZE, COHORT_READ_DEADLINE_MS, CONTROL_REPLICATION_BREADTH, CONTROL_CLUSTER_POLICY, DEFAULT_STRAND_CLUSTER_SIZE, STRAND_CLUSTER_POLICY, resolveStrandClusterSize, resolveRepairYardstick, controlClusterPolicy, strandClusterPolicy } from './cluster-size.js';
3
4
  export { wrapStorageWithCache, disposeStorageCache } from './cached-storage.js';
4
5
  export type { StrandConnectionOptions, SereusPluginResult, StrandTransactor } from './types.js';
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { connectToStrand } from './connect.js';
2
+ export { applyAppSchema } from './compose-strand.js';
2
3
  export { MIN_CLUSTER_SIZE, COHORT_READ_DEADLINE_MS, CONTROL_REPLICATION_BREADTH, CONTROL_CLUSTER_POLICY, DEFAULT_STRAND_CLUSTER_SIZE, STRAND_CLUSTER_POLICY, resolveStrandClusterSize, resolveRepairYardstick, controlClusterPolicy, strandClusterPolicy } from './cluster-size.js';
3
4
  export { wrapStorageWithCache, disposeStorageCache } from './cached-storage.js';
4
5
  //# 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;AAC/C,OAAO,EACN,gBAAgB,EAChB,uBAAuB,EACvB,2BAA2B,EAC3B,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,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,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EACN,gBAAgB,EAChB,uBAAuB,EACvB,2BAA2B,EAC3B,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -1,7 +1,38 @@
1
1
  import type { SqlValue } from '@quereus/quereus';
2
- import type { StrandConnectionOptions } from './types.js';
2
+ import type { StrandConnectionOptions, StrandTransactor } from './types.js';
3
+ type FretProfile = NonNullable<StrandConnectionOptions['fretProfile']>;
4
+ export interface ParsedPluginConfig {
5
+ options: StrandConnectionOptions;
6
+ /**
7
+ * Node-only: resolved to a `FileRawStorage` by `plugin.ts`, rejected by
8
+ * `plugin-browser.ts`. It travels beside the options rather than in them
9
+ * because `StrandConnectionOptions.storage` is an `IRawStorage`, which no
10
+ * `SqlValue` can carry.
11
+ */
12
+ storagePath?: string;
13
+ }
14
+ /** What each setting reads to once it is supplied and accepted. */
15
+ interface SettingTypes {
16
+ strand_id: string;
17
+ bootstrap_nodes: string[];
18
+ schema: string;
19
+ sapp_id: string;
20
+ sapp_version: string;
21
+ port: number;
22
+ enable_cache: boolean;
23
+ fret_profile: FretProfile;
24
+ transactor: StrandTransactor;
25
+ storage_path: string;
26
+ }
27
+ type SettingKey = keyof SettingTypes;
28
+ /** Every key the plugin accepts; the package manifest's `quereus.settings` must list the same. */
29
+ export declare const PLUGIN_SETTING_KEYS: readonly SettingKey[];
3
30
  /**
4
31
  * Parse the plugin-loader SqlValue config into typed StrandConnectionOptions.
5
32
  * Shared by the Node (`plugin.ts`) and browser (`plugin-browser.ts`) entries.
33
+ *
34
+ * Strict: an unknown key or a value of the wrong type throws one error listing
35
+ * every problem, rather than falling back to a default the user never chose.
6
36
  */
7
- export declare function parseConfig(config: Record<string, SqlValue>): StrandConnectionOptions;
37
+ export declare function parseConfig(config: Record<string, SqlValue>): ParsedPluginConfig;
38
+ export {};