@serfab/cadre-core 1.11.0 → 1.13.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.
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Which strand nodes are built with Optimystic's change-notification substrate
3
+ * (`NodeOptions.cohortTopic` in `@optimystic/db-p2p`): a node built with it announces every
4
+ * commit whose collection log tail it applied, and gives the strand's tables tagged
5
+ * `"optimystic.network_watch" = true` a network watch, so an app on another machine is woken
6
+ * by a commit instead of polling. Off unless the operator turns it on; `docs/strands.md` →
7
+ * "Change notifications (reactivity)" says what turning it on costs and exposes.
8
+ *
9
+ * "Cohort topic" is Optimystic's name for the substrate and stays inside this module; cadre
10
+ * calls the setting `strandReactivity`.
11
+ */
12
+
13
+ /** Node-local, operator-supplied; never read from the control database, a strand row, or a peer. */
14
+ export interface StrandReactivityConfig {
15
+ /** Master switch. Anything but `true` leaves every strand node exactly as before. */
16
+ enabled: boolean;
17
+ /** Strands to enable it for, by exact strand id. Absent or empty: every strand this node runs. */
18
+ strandIds?: string[];
19
+ }
20
+
21
+ /**
22
+ * The `createLibp2pNode` options slice for one strand: `{ cohortTopic: { enabled: true } }` when
23
+ * `config.enabled === true` and (`strandIds` is absent, an empty array, or contains `strandId`
24
+ * exactly); otherwise `{}` — no `cohortTopic` key at all, never `{ enabled: false }`, so db-p2p
25
+ * takes its "absent" path unchanged.
26
+ *
27
+ * Fails closed on a JS embedder's mistake: an `enabled` that is not the boolean `true`, or a
28
+ * `strandIds` that is present but not an array, enables nothing.
29
+ *
30
+ * Deliberately no `wantK` or `host` tuning: a reactivity root is the tail block's storage group
31
+ * (`clusterSize` wide), verified at the consensus super-majority, and neither `wantK` nor
32
+ * `cohortTopic.host.minSigs` governs it; the tiers below the root keep db-p2p's defaults.
33
+ *
34
+ * NOTE: with no `host.profile`, db-p2p builds every host as core (forwarder-eligible, 30 s
35
+ * renewal), a `'transaction'` phone included — it does not follow `fretProfile`. If phone-side
36
+ * forwarding load shows up, pass `host: { profile: edgeProfile() }` for transaction nodes, after
37
+ * confirming an edge host still serves its part of a tail block's root group.
38
+ */
39
+ export function strandCohortTopicOption(
40
+ config: StrandReactivityConfig | undefined,
41
+ strandId: string
42
+ ): { cohortTopic?: { enabled: true } } {
43
+ return config?.enabled === true && strandSelected(config.strandIds, strandId)
44
+ ? { cohortTopic: { enabled: true } }
45
+ : {};
46
+ }
47
+
48
+ function strandSelected(strandIds: unknown, strandId: string): boolean {
49
+ if (strandIds === undefined) {
50
+ return true;
51
+ }
52
+ return Array.isArray(strandIds) && (strandIds.length === 0 || strandIds.includes(strandId));
53
+ }
package/src/types.ts CHANGED
@@ -3,6 +3,7 @@ import type { CircuitRelayServerInit } from '@libp2p/circuit-relay-v2';
3
3
  import type { IRawStorage, Libp2pConnectionMonitorInit, Libp2pTransports, NoiseCryptoInterface } from '@optimystic/db-p2p';
4
4
  import type { IPeerNetwork, IRepo } from '@optimystic/db-core';
5
5
  import type { PeerJoinBackfillConfig } from './peer-join-backfill.js';
6
+ import type { StrandReactivityConfig } from './strand-reactivity.js';
6
7
  import type { StrandRevocationEnforcementConfig } from './strand-revocation-enforcer.js';
7
8
  import type { StrandMembershipReconciliationConfig } from './strand-membership-reconciler.js';
8
9
  import type { StrandFirstSyncConfig } from './strand-first-sync-gate.js';
@@ -826,6 +827,25 @@ export interface CadreNodeConfig {
826
827
  */
827
828
  strandBackfill?: PeerJoinBackfillConfig;
828
829
 
830
+ /**
831
+ * Optimystic change notifications on this node's strand nodes (`strand-reactivity.ts`):
832
+ * a strand node built with them announces the commits whose collection log tail it applied,
833
+ * and an sApp table tagged `"optimystic.network_watch" = true` wakes its `Database.watch`
834
+ * subscribers on another machine's commit instead of only on local ones. Default off; with
835
+ * it off every strand node is built exactly as before. `strandIds` narrows it to chosen
836
+ * strands, independently of `strandFilter` (which decides whether a strand runs at all).
837
+ *
838
+ * Frozen when the strand's libp2p node is built: a change takes effect at the next build —
839
+ * a restart, or the strand's next wake from hibernation. Every machine serving the strand
840
+ * should enable it, because a node without it neither announces nor serves as part of the
841
+ * group a notification is rooted at. Enabling registers Optimystic's change-notification
842
+ * protocol handlers on the strand node, which the closed-strand per-stream gate does not
843
+ * cover, and each watched table pays a cold-start proof of work on every node build. Both
844
+ * are spelled out in `docs/strands.md` → "Change notifications (reactivity)". The control
845
+ * node never gets it.
846
+ */
847
+ strandReactivity?: StrandReactivityConfig;
848
+
829
849
  /**
830
850
  * Tuning for the CONTROL network's peer-join block catch-up (same module,
831
851
  * `peer-join-backfill.ts`, wired in `CadreNode.start`): when the control