@serfab/cadre-core 0.13.0 → 1.0.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 +20 -6
- package/dist/bootstrap-peer-store-file.js +2 -2
- package/dist/bootstrap-peer-store.d.ts +32 -12
- package/dist/bootstrap-peer-store.js +41 -15
- package/dist/bootstrap-peer-store.js.map +1 -1
- package/dist/cadre-node.d.ts +558 -80
- package/dist/cadre-node.js +1060 -170
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-authorization.d.ts +2 -2
- package/dist/control-authorization.js +1 -0
- package/dist/control-authorization.js.map +1 -1
- package/dist/control-cohort.d.ts +13 -26
- package/dist/control-cohort.js +0 -26
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +299 -44
- package/dist/control-database.js +469 -69
- package/dist/control-database.js.map +1 -1
- package/dist/control-read-retry.d.ts +40 -11
- package/dist/control-read-retry.js +48 -15
- package/dist/control-read-retry.js.map +1 -1
- package/dist/control-retry.d.ts +58 -3
- package/dist/control-retry.js +58 -3
- package/dist/control-retry.js.map +1 -1
- package/dist/control-schema.d.ts +1 -1
- package/dist/control-schema.js +103 -19
- package/dist/control-schema.js.map +1 -1
- package/dist/control-write-retry.d.ts +80 -11
- package/dist/control-write-retry.js +168 -29
- package/dist/control-write-retry.js.map +1 -1
- package/dist/enrolled-machine-store.js +2 -2
- package/dist/index.d.ts +14 -7
- package/dist/index.js +33 -6
- package/dist/index.js.map +1 -1
- package/dist/node-local-snapshot.d.ts +15 -7
- package/dist/node-local-snapshot.js +23 -8
- package/dist/node-local-snapshot.js.map +1 -1
- package/dist/peer-dial.d.ts +143 -0
- package/dist/peer-dial.js +176 -0
- package/dist/peer-dial.js.map +1 -0
- package/dist/relay-addrs.d.ts +90 -52
- package/dist/relay-addrs.js +91 -59
- package/dist/relay-addrs.js.map +1 -1
- package/dist/relay-reservation.d.ts +71 -3
- package/dist/relay-reservation.js +116 -8
- package/dist/relay-reservation.js.map +1 -1
- package/dist/seed-bootstrap.d.ts +30 -1
- package/dist/seed-bootstrap.js +62 -17
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/storage-scope.d.ts +62 -0
- package/dist/storage-scope.js +75 -0
- package/dist/storage-scope.js.map +1 -0
- package/dist/strand-database.d.ts +34 -16
- package/dist/strand-database.js +38 -24
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-first-sync-gate.d.ts +152 -0
- package/dist/strand-first-sync-gate.js +208 -0
- package/dist/strand-first-sync-gate.js.map +1 -0
- package/dist/strand-formation-manager.d.ts +87 -5
- package/dist/strand-formation-manager.js +109 -5
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-formation-protocol.d.ts +21 -1
- package/dist/strand-formation-protocol.js +30 -0
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +243 -6
- package/dist/strand-instance-manager.js +470 -37
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-member-key.d.ts +20 -12
- package/dist/strand-member-key.js +20 -12
- package/dist/strand-member-key.js.map +1 -1
- package/dist/strand-member-registry.js +3 -0
- package/dist/strand-member-registry.js.map +1 -1
- package/dist/strand-membership-reconciler.d.ts +361 -0
- package/dist/strand-membership-reconciler.js +548 -0
- package/dist/strand-membership-reconciler.js.map +1 -0
- package/dist/strand-membership-writer.d.ts +131 -11
- package/dist/strand-membership-writer.js +396 -134
- package/dist/strand-membership-writer.js.map +1 -1
- package/dist/strand-network-config.d.ts +46 -14
- package/dist/strand-network-config.js +79 -23
- package/dist/strand-network-config.js.map +1 -1
- package/dist/strand-solicitation.d.ts +10 -1
- package/dist/strand-solicitation.js +5 -0
- package/dist/strand-solicitation.js.map +1 -1
- package/dist/strand-wake-protocol.d.ts +11 -7
- package/dist/strand-wake-protocol.js +20 -52
- package/dist/strand-wake-protocol.js.map +1 -1
- package/dist/strand-watcher.js +6 -0
- package/dist/strand-watcher.js.map +1 -1
- package/dist/timed-step.d.ts +12 -0
- package/dist/timed-step.js +28 -0
- package/dist/timed-step.js.map +1 -0
- package/dist/timeout-scheduler.d.ts +19 -0
- package/dist/timeout-scheduler.js +22 -0
- package/dist/timeout-scheduler.js.map +1 -0
- package/dist/types.d.ts +262 -30
- package/dist/types.js.map +1 -1
- package/package.json +8 -8
- package/src/bootstrap-peer-store-file.ts +2 -2
- package/src/bootstrap-peer-store.ts +69 -27
- package/src/cadre-node.ts +7301 -6353
- package/src/control-authorization.ts +2 -1
- package/src/control-cohort.ts +122 -130
- package/src/control-database.ts +537 -69
- package/src/control-read-retry.ts +202 -167
- package/src/control-retry.ts +297 -190
- package/src/control-schema.ts +103 -19
- package/src/control-write-retry.ts +525 -383
- package/src/enrolled-machine-store.ts +2 -2
- package/src/index.ts +83 -6
- package/src/node-local-snapshot.ts +25 -8
- package/src/peer-dial.ts +215 -0
- package/src/relay-addrs.ts +458 -436
- package/src/relay-reservation.ts +951 -828
- package/src/seed-bootstrap.ts +76 -18
- package/src/storage-scope.ts +78 -0
- package/src/strand-database.ts +50 -27
- package/src/strand-first-sync-gate.ts +258 -0
- package/src/strand-formation-manager.ts +140 -5
- package/src/strand-formation-protocol.ts +43 -1
- package/src/strand-instance-manager.ts +577 -37
- package/src/strand-member-key.ts +20 -12
- package/src/strand-member-registry.ts +3 -0
- package/src/strand-membership-reconciler.ts +680 -0
- package/src/strand-membership-writer.ts +527 -189
- package/src/strand-network-config.ts +102 -24
- package/src/strand-solicitation.ts +16 -1
- package/src/strand-wake-protocol.ts +21 -66
- package/src/strand-watcher.ts +6 -0
- package/src/timed-step.ts +28 -0
- package/src/timeout-scheduler.ts +28 -0
- package/src/types.ts +1789 -1554
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The first-sync write gate for a JOINING machine.
|
|
3
|
+
*
|
|
4
|
+
* A machine that has never received anything from another member of a strand must not
|
|
5
|
+
* commit to it. With no strand peer connected, Optimystic's cohort for every block is the
|
|
6
|
+
* machine itself, so the first write to a table finds no collection locally and INVENTS
|
|
7
|
+
* one — exactly what a founder writing alone legitimately does. When the connection comes
|
|
8
|
+
* up, two independently created histories share one collection id: Optimystic keeps one and
|
|
9
|
+
* drops the other's commits, or never reconciles them at all. The joiner's own rows silently
|
|
10
|
+
* vanish (`joining-machine-writes-before-first-sync-fork-tables`). Optimystic cannot tell a
|
|
11
|
+
* founder from a joiner; sereus can, because the launch knows whether it is founding.
|
|
12
|
+
*
|
|
13
|
+
* The invariant this module enforces: **a machine that has never held this strand's
|
|
14
|
+
* `Strand.Header` row, and has not read every `App` table once, must not commit to it.**
|
|
15
|
+
* The founder writes the Header in its bootstrap; every other machine can only receive it
|
|
16
|
+
* from a peer, so "no local Header" means "never synced". The gate therefore holds the
|
|
17
|
+
* freshly initialized `StrandDatabase` back from the app — `StrandInstance.database` stays
|
|
18
|
+
* unset and the instance reports `'syncing'` — and probes on a cadence until the Header is
|
|
19
|
+
* readable AND a read of each app table settles, at which point the database is published
|
|
20
|
+
* and the instance goes `'active'`. A machine that already holds the Header (a restart, a
|
|
21
|
+
* hibernation resume, a founder) passes the probe on the first try and is never gated, so
|
|
22
|
+
* offline-first writes on a machine that has synced before keep working.
|
|
23
|
+
*
|
|
24
|
+
* Why the app tables too: the Header is ONE collection. It reaches a joiner ahead of the
|
|
25
|
+
* founder's app-table collections (peer-join backfill and pull-on-read deliver those
|
|
26
|
+
* separately), and a write to a table whose collection this machine has not yet fetched
|
|
27
|
+
* invents one exactly as a lone joiner did. Measured on the reference chat schema
|
|
28
|
+
* (2026-09-16, direct connections, Header-only probe): the joiner's participant + message
|
|
29
|
+
* written the instant `addStrand` resolved diverged in 3 of 4 runs. Reading each table once
|
|
30
|
+
* pulls its collection while the founder is reachable, so the write that follows appends.
|
|
31
|
+
*
|
|
32
|
+
* Every probe is a read (`Strand.Header`, then `select count(1)` on each `App` table). Reads
|
|
33
|
+
* never invent a collection (Optimystic's `Collection.open` resolves undefined on an
|
|
34
|
+
* authoritatively absent header, and throws on an unreachable one), so probing is safe to
|
|
35
|
+
* repeat and a throw is just "not yet". A table nobody has written yet reads as absent on
|
|
36
|
+
* every machine; the first write to it still creates its collection — see `docs/strands.md`
|
|
37
|
+
* ("Joining") for that residual.
|
|
38
|
+
*
|
|
39
|
+
* Owned by `StrandInstanceManager`: created in `buildStrandRuntime` for a non-founder launch
|
|
40
|
+
* whose first probe finds no Header, stopped and dropped in `releaseRuntime` (so a quiesce →
|
|
41
|
+
* resume rebuild re-probes over the same store), and force-opened when a founder request
|
|
42
|
+
* runs the bootstrap against the still-gated database (`foundExistingStrand`).
|
|
43
|
+
*
|
|
44
|
+
* NOTE: the gate covers app and membership tables, not the schema catalog. Every launch —
|
|
45
|
+
* joiner or founder — writes the catalog collection (`optimystic/schema` plus two
|
|
46
|
+
* hash-named blocks, measured 2026-09-16) alone, during `connectToStrand`'s schema apply,
|
|
47
|
+
* before any peer contact. That is the same "two histories under one id" shape this gate
|
|
48
|
+
* exists to prevent, and it is only safe because both sides derive byte-identical content
|
|
49
|
+
* from the same schema text. If the catalog ever carries per-machine or ordering-dependent
|
|
50
|
+
* content (optimystic's own `0.5-same-named-tables-in-two-schemas-share-storage` re-keys
|
|
51
|
+
* it), a joiner's catalog will fork against the founder's — gate the schema apply on the
|
|
52
|
+
* Header too, or have optimystic open the catalog read-only when a peer already holds it.
|
|
53
|
+
*/
|
|
54
|
+
import debug from 'debug';
|
|
55
|
+
import { defaultTimeoutScheduler } from './timeout-scheduler.js';
|
|
56
|
+
const log = debug('sereus:cadre:strand-first-sync');
|
|
57
|
+
/** How often a gated launch re-probes (`Strand.Header`, then every `App` table) while waiting for its first sync. */
|
|
58
|
+
export const DEFAULT_STRAND_FIRST_SYNC_POLL_MS = 500;
|
|
59
|
+
/**
|
|
60
|
+
* How long `CadreNode.addStrand` waits for a joining machine's first sync before rejecting
|
|
61
|
+
* with {@link StrandAwaitingFirstSyncError}. Measured: over a direct connection the
|
|
62
|
+
* joiner reads the founder's rows about 1.3 s after `addStrand` would previously have
|
|
63
|
+
* resolved; a relayed path is slower, and a phone that just redeemed an invitation has the
|
|
64
|
+
* host reachable moments ago, so 30 s is generous without leaving an app hanging.
|
|
65
|
+
*/
|
|
66
|
+
export const DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS = 30000;
|
|
67
|
+
/**
|
|
68
|
+
* Thrown by `CadreNode.addStrand` / `whenStrandWritable` when a joining machine has not
|
|
69
|
+
* received the strand's data from any other member within the wait budget. RETRYABLE: the
|
|
70
|
+
* strand stays launched and keeps probing, so a later `addStrand` (or `whenStrandWritable`)
|
|
71
|
+
* for the same strand completes the attach once another member is reachable; the
|
|
72
|
+
* `strand:writable` event fires at that moment too.
|
|
73
|
+
*/
|
|
74
|
+
export class StrandAwaitingFirstSyncError extends Error {
|
|
75
|
+
constructor(strandId, waitedMs) {
|
|
76
|
+
super(`Strand ${strandId} is not writable yet: no member of this strand has been reachable ` +
|
|
77
|
+
`since this machine joined, so it has not received the strand's data (waited ${waitedMs} ms). ` +
|
|
78
|
+
'The strand stays launched and keeps trying — call addStrand again, or wait for the ' +
|
|
79
|
+
"'strand:writable' event, once another member is reachable.");
|
|
80
|
+
this.name = 'StrandAwaitingFirstSyncError';
|
|
81
|
+
this.strandId = strandId;
|
|
82
|
+
this.waitedMs = waitedMs;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Whether this machine holds the strand's `Strand.Header` row — the "has synced at least
|
|
87
|
+
* once (or founded)" signal. A read, never a write: it cannot invent the collection. A
|
|
88
|
+
* throwing read (an unreachable cohort mid-sync, a store still hydrating) reports `false`
|
|
89
|
+
* with a log, since the caller's only response to either answer is "probe again".
|
|
90
|
+
*/
|
|
91
|
+
export async function strandHeaderHeld(db, label) {
|
|
92
|
+
try {
|
|
93
|
+
for await (const row of db.eval('select count(1) as Count from Strand.Header')) {
|
|
94
|
+
return (row.Count ?? 0) > 0;
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
log('[%s] Header probe failed (treated as not yet held): %s', label, error instanceof Error ? error.message : String(error));
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** The tables the sApp declared in the `App` schema, by name; views excluded. */
|
|
104
|
+
function appTableNames(db) {
|
|
105
|
+
const app = db.schemaManager.getSchema('App');
|
|
106
|
+
return app ? Array.from(app.getAllTables()).filter((table) => !table.isView).map((table) => table.name) : [];
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Whether a read of every `App` table settles on this machine — the "each app collection
|
|
110
|
+
* has been fetched from the cohort, or is authoritatively absent" signal. Only a throwing
|
|
111
|
+
* read (an unreachable cohort) reports `false`; an empty table is a settled read.
|
|
112
|
+
*/
|
|
113
|
+
export async function appTablesReadable(db, label) {
|
|
114
|
+
for (const table of appTableNames(db)) {
|
|
115
|
+
try {
|
|
116
|
+
for await (const _row of db.eval(`select count(1) as Count from App."${table}"`)) {
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
log('[%s] App.%s probe failed (treated as not yet synced): %s', label, table, error instanceof Error ? error.message : String(error));
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The gate's whole probe: the Header is held AND every app table has been read once. The
|
|
129
|
+
* app tables are read only once the Header is — a joiner with no peer would otherwise pay
|
|
130
|
+
* one failing network read per table per probe for nothing.
|
|
131
|
+
*/
|
|
132
|
+
export async function strandFirstSyncComplete(db, label) {
|
|
133
|
+
return await strandHeaderHeld(db, label) && await appTablesReadable(db, label);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Holds one launch's `StrandDatabase` until {@link strandFirstSyncComplete}. Probes are
|
|
137
|
+
* sequential (the next is scheduled only after the previous read settles), so a slow
|
|
138
|
+
* network read never stacks probes.
|
|
139
|
+
*/
|
|
140
|
+
export class StrandFirstSyncGate {
|
|
141
|
+
constructor(deps, config) {
|
|
142
|
+
this.deps = deps;
|
|
143
|
+
this.stopped = false;
|
|
144
|
+
this.opened = false;
|
|
145
|
+
this.probing = false;
|
|
146
|
+
this.scheduler = deps.scheduler ?? defaultTimeoutScheduler;
|
|
147
|
+
this.pollIntervalMs = config?.pollIntervalMs ?? DEFAULT_STRAND_FIRST_SYNC_POLL_MS;
|
|
148
|
+
}
|
|
149
|
+
/** The database this gate is holding back. */
|
|
150
|
+
get database() {
|
|
151
|
+
return this.deps.database;
|
|
152
|
+
}
|
|
153
|
+
/** True once the Header was seen (or the gate was force-opened); the loop is then done. */
|
|
154
|
+
get isOpen() {
|
|
155
|
+
return this.opened;
|
|
156
|
+
}
|
|
157
|
+
/** Arm the probe loop. The first probe runs after one interval — the caller already probed once. */
|
|
158
|
+
start() {
|
|
159
|
+
if (this.stopped || this.opened || this.timer !== undefined)
|
|
160
|
+
return;
|
|
161
|
+
this.schedule();
|
|
162
|
+
log('[%s] first-sync gate armed: no Strand.Header held yet; probing every %dms', this.deps.label, this.pollIntervalMs);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Force-open: the caller has just written the Header itself (a founder bootstrap run
|
|
166
|
+
* against the gated database). Stops the loop; `onHeaderHeld` is NOT invoked.
|
|
167
|
+
*/
|
|
168
|
+
open() {
|
|
169
|
+
if (this.opened)
|
|
170
|
+
return;
|
|
171
|
+
this.opened = true;
|
|
172
|
+
this.stop();
|
|
173
|
+
log('[%s] first-sync gate opened by the caller (Header written locally)', this.deps.label);
|
|
174
|
+
}
|
|
175
|
+
/** Disarm the loop; a probe already in flight completes and is then ignored. */
|
|
176
|
+
stop() {
|
|
177
|
+
this.stopped = true;
|
|
178
|
+
if (this.timer !== undefined) {
|
|
179
|
+
this.scheduler.clearTimeout(this.timer);
|
|
180
|
+
this.timer = undefined;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
schedule() {
|
|
184
|
+
this.timer = this.scheduler.setTimeout(() => {
|
|
185
|
+
this.timer = undefined;
|
|
186
|
+
void this.probe();
|
|
187
|
+
}, this.pollIntervalMs);
|
|
188
|
+
}
|
|
189
|
+
async probe() {
|
|
190
|
+
if (this.stopped || this.opened || this.probing)
|
|
191
|
+
return;
|
|
192
|
+
this.probing = true;
|
|
193
|
+
const held = await strandFirstSyncComplete(this.deps.database.getDatabase(), this.deps.label)
|
|
194
|
+
.finally(() => { this.probing = false; });
|
|
195
|
+
// Re-check after the await: a release or a force-open may have landed mid-read.
|
|
196
|
+
if (this.stopped || this.opened)
|
|
197
|
+
return;
|
|
198
|
+
if (!held) {
|
|
199
|
+
this.schedule();
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
this.opened = true;
|
|
203
|
+
this.stop();
|
|
204
|
+
log('[%s] first-sync gate opened: Strand.Header and every App table read from a peer', this.deps.label);
|
|
205
|
+
this.deps.onHeaderHeld();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=strand-first-sync-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strand-first-sync-gate.js","sourceRoot":"","sources":["../src/strand-first-sync-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,36 @@
|
|
|
1
1
|
import type { Libp2p } from '@libp2p/interface';
|
|
2
|
-
import type { OpenInvitation, FormStrandResult, StrandFormationDisclosure } from './types.js';
|
|
2
|
+
import type { OpenInvitation, FormStrandResult, StrandFormationDisclosure, StrandMembershipInvite } from './types.js';
|
|
3
3
|
import type { DisclosureValidator, FormationUsageRecorder, StrandProvisioner, FormationResponseValidator } from './strand-solicitation.js';
|
|
4
|
+
/**
|
|
5
|
+
* How long a formation-issued strand membership invitation stays redeemable (7 days).
|
|
6
|
+
* The responder's {@link StrandFormationManagerOptions.issueMembershipInvite} hook stamps
|
|
7
|
+
* `now + this` as the `Strand.Invite.Expiration`. Long enough for a slow joiner to bring
|
|
8
|
+
* its strand node up and redeem; short enough that a lost formation result does not leave
|
|
9
|
+
* a live bearer credential in the strand forever. An expired invitation rolls the
|
|
10
|
+
* redemption back cleanly (the schema's `NotExpired` gate) — the joiner's recovery is a
|
|
11
|
+
* fresh formation, which issues a fresh invitation.
|
|
12
|
+
*/
|
|
13
|
+
export declare const MEMBERSHIP_INVITE_TTL_MS: number;
|
|
14
|
+
/**
|
|
15
|
+
* Rejection reason for a bound CLOSED-strand redemption whose responder could not issue
|
|
16
|
+
* the joiner's membership invitation — the host strand runtime not live on this
|
|
17
|
+
* responder, no party identity key yet, or the strand DB refusing the `Strand.Invite`
|
|
18
|
+
* write. Retryable on purpose, and rejected BEFORE the consent row is recorded, so the
|
|
19
|
+
* formation token stays unspent: approving without an invitation would admit a joiner
|
|
20
|
+
* that looks joined but can never become a member, and a responder not running the
|
|
21
|
+
* strand cannot serve the joiner's sync anyway. The one permanent failure,
|
|
22
|
+
* a pre-split host strand, gets {@link HOST_STRAND_MUST_BE_RECREATED_REASON} instead.
|
|
23
|
+
*/
|
|
24
|
+
export declare const MEMBERSHIP_INVITE_UNAVAILABLE_REASON = "Strand membership invitation unavailable, retry";
|
|
25
|
+
/**
|
|
26
|
+
* Rejection reason for a bound CLOSED-strand redemption whose host strand was founded
|
|
27
|
+
* before the per-party identity split: the responder's founder launch refused it
|
|
28
|
+
* (`PreSplitStrandIdentityError`), so no invitation can ever be issued into it. Not
|
|
29
|
+
* retryable — the host must recreate the strand and issue a fresh invitation. Like
|
|
30
|
+
* {@link MEMBERSHIP_INVITE_UNAVAILABLE_REASON}, rejected before consent is recorded, so
|
|
31
|
+
* the formation token stays unspent.
|
|
32
|
+
*/
|
|
33
|
+
export declare const HOST_STRAND_MUST_BE_RECREATED_REASON = "Host strand must be recreated";
|
|
4
34
|
/**
|
|
5
35
|
* Configuration for StrandFormationManager
|
|
6
36
|
*/
|
|
@@ -51,6 +81,27 @@ export interface StrandFormationManagerOptions {
|
|
|
51
81
|
* addresses and cross-party joiners fall back to an empty seed.
|
|
52
82
|
*/
|
|
53
83
|
resolveStrandAddrs?: (strandId: string) => string[];
|
|
84
|
+
/**
|
|
85
|
+
* Issue a single-use `Strand.Invite` against the live host strand a BOUND redemption
|
|
86
|
+
* resolved to (responder side) — the seam that gets the JOINER its own membership.
|
|
87
|
+
* Like {@link resolveStrandAddrs}, the formation layer cannot reach the strand
|
|
88
|
+
* runtime itself, so `CadreNode` wires this to its own running-instance +
|
|
89
|
+
* `StrandPartyKey` lookup.
|
|
90
|
+
*
|
|
91
|
+
* Contract: return the issued invitation for a CLOSED host strand; return `null`
|
|
92
|
+
* for an open one (no members, nothing to invite into); THROW when a closed strand's
|
|
93
|
+
* invitation cannot be issued (runtime not live, no party key, strand DB write
|
|
94
|
+
* rejected) — the manager then rejects the whole redemption with
|
|
95
|
+
* {@link MEMBERSHIP_INVITE_UNAVAILABLE_REASON} BEFORE recording consent, so the
|
|
96
|
+
* formation token stays unspent and the joiner can retry. Throw a
|
|
97
|
+
* `PreSplitStrandIdentityError` for a host strand that can never issue one; that maps
|
|
98
|
+
* to the non-retryable {@link HOST_STRAND_MUST_BE_RECREATED_REASON}.
|
|
99
|
+
*
|
|
100
|
+
* Left unwired — mock/transport tests — the bound path approves with no invitation,
|
|
101
|
+
* mirroring the unwired {@link resolveStrandAddrs} posture. Production
|
|
102
|
+
* (`CadreNode.initializeStrandSolicitation`) always wires it.
|
|
103
|
+
*/
|
|
104
|
+
issueMembershipInvite?: (strandId: string) => Promise<StrandMembershipInvite | null>;
|
|
54
105
|
/** Configuration options */
|
|
55
106
|
config?: StrandFormationManagerConfig;
|
|
56
107
|
}
|
|
@@ -75,6 +126,7 @@ export declare class StrandFormationManager {
|
|
|
75
126
|
private readonly partyId;
|
|
76
127
|
private readonly cadrePeerAddrs;
|
|
77
128
|
private readonly resolveStrandAddrs?;
|
|
129
|
+
private readonly issueMembershipInvite?;
|
|
78
130
|
private readonly config;
|
|
79
131
|
private readonly listener;
|
|
80
132
|
private readonly registeredNodes;
|
|
@@ -110,6 +162,13 @@ export declare class StrandFormationManager {
|
|
|
110
162
|
listeners: number;
|
|
111
163
|
dialers: number;
|
|
112
164
|
};
|
|
165
|
+
/**
|
|
166
|
+
* The provision result's membership invitation, iff well-formed — a malformed one
|
|
167
|
+
* (possible only past a custom {@link FormationResponseValidator} that skipped the
|
|
168
|
+
* structural check) is dropped with a log rather than cached: it could never redeem
|
|
169
|
+
* anyway, and carrying an unbounded hostile object outlives the session.
|
|
170
|
+
*/
|
|
171
|
+
private membershipInviteFrom;
|
|
113
172
|
/**
|
|
114
173
|
* Derive the initiator's await-response budget from the configured RESPONDER budget —
|
|
115
174
|
* never the same number (see `StrandFormationManagerConfig.provisionTimeoutMs`). Mirrors
|
|
@@ -128,10 +187,14 @@ export declare class StrandFormationManager {
|
|
|
128
187
|
* {@link ResponderProvisionOutcome} with `approved: false` — `runSession` turns that
|
|
129
188
|
* into a clean, non-disclosing reply instead of a dropped result frame.
|
|
130
189
|
*
|
|
131
|
-
* - **bound** (host strand present): provision-then-record —
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
190
|
+
* - **bound** (host strand present): provision-then-record — first issue the joiner's
|
|
191
|
+
* single-use strand membership invitation via the wired `issueMembershipInvite` seam
|
|
192
|
+
* ({@link issueBoundMembershipInvite}; a closed strand whose invitation cannot be
|
|
193
|
+
* issued rejects retryably HERE, before any consent row spends the token), then write
|
|
194
|
+
* the single `FormationUsage` consent row against the pre-existing strand
|
|
195
|
+
* (record-only) and return it + its membership key + the invitation (all read-gating
|
|
196
|
+
* secrets disclosed only here, behind the token + disclosure validation `runSession`
|
|
197
|
+
* already enforced).
|
|
135
198
|
* - **missing** (invite names a host strand absent on this responder, e.g. unconverged):
|
|
136
199
|
* reject cleanly + retryably, writing NO usage row — recording usage here would fail the
|
|
137
200
|
* deferred `StrandExists` CHECK at commit and drop the frame.
|
|
@@ -150,6 +213,25 @@ export declare class StrandFormationManager {
|
|
|
150
213
|
* not control-flow-by-exception.
|
|
151
214
|
*/
|
|
152
215
|
private provisionAsResponder;
|
|
216
|
+
/**
|
|
217
|
+
* Issue the joiner's membership invitation for a BOUND redemption via the wired
|
|
218
|
+
* {@link StrandFormationManagerOptions.issueMembershipInvite} seam.
|
|
219
|
+
*
|
|
220
|
+
* - Hook unwired (mock/transport tests): approve with no invitation, the same posture
|
|
221
|
+
* as an unwired `resolveStrandAddrs`.
|
|
222
|
+
* - Hook returns an invitation (closed host strand): carry it on the approval.
|
|
223
|
+
* - Hook returns `null` (open host strand): approve with no invitation.
|
|
224
|
+
* - Hook throws (runtime not live, no party key, strand-DB write rejected): report
|
|
225
|
+
* `ok: false` with {@link MEMBERSHIP_INVITE_UNAVAILABLE_REASON} — the caller rejects
|
|
226
|
+
* BEFORE any consent row is written, so the formation token stays unspent.
|
|
227
|
+
* - Hook throws `PreSplitStrandIdentityError` (the host strand was founded before the
|
|
228
|
+
* per-party identity split and can never issue one): same, but with the
|
|
229
|
+
* non-retryable {@link HOST_STRAND_MUST_BE_RECREATED_REASON}.
|
|
230
|
+
*
|
|
231
|
+
* The LOG-before-reject keeps both a deliberate internal-error→protocol-rejection
|
|
232
|
+
* conversion, same as the catch-all in {@link provisionAsResponder}.
|
|
233
|
+
*/
|
|
234
|
+
private issueBoundMembershipInvite;
|
|
153
235
|
/**
|
|
154
236
|
* Responder-provisions fallback (invite binds no host strand). Precedence:
|
|
155
237
|
*
|
|
@@ -3,8 +3,9 @@ import { fromString as uint8ArrayFromString } from 'uint8arrays';
|
|
|
3
3
|
import { FormationApprovalError } from './formation-approval.js';
|
|
4
4
|
// control-database does not import this manager, so this import introduces no cycle.
|
|
5
5
|
import { FormationAbortedError, InvitationExhaustedError } from './control-database.js';
|
|
6
|
+
import { PreSplitStrandIdentityError } from './strand-membership-writer.js';
|
|
6
7
|
import { canonicalJson } from './canonical-json.js';
|
|
7
|
-
import { FormationListener, INVALID_TOKEN_REASON, PROVISION_RESPONSE_TRAVEL_MARGIN_MS, dialFormation, isValidResponderCreatesResult } from './strand-formation-protocol.js';
|
|
8
|
+
import { FormationListener, INVALID_TOKEN_REASON, PROVISION_RESPONSE_TRAVEL_MARGIN_MS, dialFormation, isValidResponderCreatesResult, isWellFormedMembershipInvite } from './strand-formation-protocol.js';
|
|
8
9
|
const log = debug('sereus:cadre:formation-manager');
|
|
9
10
|
/**
|
|
10
11
|
* Largest serialized disclosure a responder will accept (8 KiB). The disclosure is
|
|
@@ -13,6 +14,36 @@ const log = debug('sereus:cadre:formation-manager');
|
|
|
13
14
|
* any database read or hook contact so an oversized blob costs nothing downstream.
|
|
14
15
|
*/
|
|
15
16
|
const MAX_DISCLOSURE_BYTES = 8 * 1024;
|
|
17
|
+
/**
|
|
18
|
+
* How long a formation-issued strand membership invitation stays redeemable (7 days).
|
|
19
|
+
* The responder's {@link StrandFormationManagerOptions.issueMembershipInvite} hook stamps
|
|
20
|
+
* `now + this` as the `Strand.Invite.Expiration`. Long enough for a slow joiner to bring
|
|
21
|
+
* its strand node up and redeem; short enough that a lost formation result does not leave
|
|
22
|
+
* a live bearer credential in the strand forever. An expired invitation rolls the
|
|
23
|
+
* redemption back cleanly (the schema's `NotExpired` gate) — the joiner's recovery is a
|
|
24
|
+
* fresh formation, which issues a fresh invitation.
|
|
25
|
+
*/
|
|
26
|
+
export const MEMBERSHIP_INVITE_TTL_MS = 7 * 24 * 3600000;
|
|
27
|
+
/**
|
|
28
|
+
* Rejection reason for a bound CLOSED-strand redemption whose responder could not issue
|
|
29
|
+
* the joiner's membership invitation — the host strand runtime not live on this
|
|
30
|
+
* responder, no party identity key yet, or the strand DB refusing the `Strand.Invite`
|
|
31
|
+
* write. Retryable on purpose, and rejected BEFORE the consent row is recorded, so the
|
|
32
|
+
* formation token stays unspent: approving without an invitation would admit a joiner
|
|
33
|
+
* that looks joined but can never become a member, and a responder not running the
|
|
34
|
+
* strand cannot serve the joiner's sync anyway. The one permanent failure,
|
|
35
|
+
* a pre-split host strand, gets {@link HOST_STRAND_MUST_BE_RECREATED_REASON} instead.
|
|
36
|
+
*/
|
|
37
|
+
export const MEMBERSHIP_INVITE_UNAVAILABLE_REASON = 'Strand membership invitation unavailable, retry';
|
|
38
|
+
/**
|
|
39
|
+
* Rejection reason for a bound CLOSED-strand redemption whose host strand was founded
|
|
40
|
+
* before the per-party identity split: the responder's founder launch refused it
|
|
41
|
+
* (`PreSplitStrandIdentityError`), so no invitation can ever be issued into it. Not
|
|
42
|
+
* retryable — the host must recreate the strand and issue a fresh invitation. Like
|
|
43
|
+
* {@link MEMBERSHIP_INVITE_UNAVAILABLE_REASON}, rejected before consent is recorded, so
|
|
44
|
+
* the formation token stays unspent.
|
|
45
|
+
*/
|
|
46
|
+
export const HOST_STRAND_MUST_BE_RECREATED_REASON = 'Host strand must be recreated';
|
|
16
47
|
/**
|
|
17
48
|
* Rejection reason a would-be joiner is told for each approval-failure category
|
|
18
49
|
* (see {@link FormationApprovalFailure}). Distinct per category on purpose: mapping any
|
|
@@ -50,6 +81,7 @@ export class StrandFormationManager {
|
|
|
50
81
|
this.partyId = options.partyId;
|
|
51
82
|
this.cadrePeerAddrs = options.cadrePeerAddrs ?? [];
|
|
52
83
|
this.resolveStrandAddrs = options.resolveStrandAddrs;
|
|
84
|
+
this.issueMembershipInvite = options.issueMembershipInvite;
|
|
53
85
|
this.config = options.config ?? {};
|
|
54
86
|
this.listener = new FormationListener({
|
|
55
87
|
validateToken: (token) => this.validateToken(token),
|
|
@@ -131,6 +163,10 @@ export class StrandFormationManager {
|
|
|
131
163
|
// Undefined for an open strand. Kept separate from invitePrivateKey (the initiator's
|
|
132
164
|
// generated signing key), which is set by the StrandSolicitationService layer.
|
|
133
165
|
memberPrivateKey: provision.memberPrivateKey,
|
|
166
|
+
// Carried only when well-formed. The DEFAULT response validator already rejects
|
|
167
|
+
// a malformed one; this re-check keeps a custom validator that skips the shape
|
|
168
|
+
// check from feeding a hostile responder's object into the joiner's invite cache.
|
|
169
|
+
membershipInvite: this.membershipInviteFrom(provision),
|
|
134
170
|
// Possibly empty — the responder may hold no live strand node yet.
|
|
135
171
|
strandAddrs: dialed.strandAddrs
|
|
136
172
|
};
|
|
@@ -145,6 +181,22 @@ export class StrandFormationManager {
|
|
|
145
181
|
getActiveSessionCounts() {
|
|
146
182
|
return { listeners: this.listener.activeCount, dialers: this.dialerSessions };
|
|
147
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* The provision result's membership invitation, iff well-formed — a malformed one
|
|
186
|
+
* (possible only past a custom {@link FormationResponseValidator} that skipped the
|
|
187
|
+
* structural check) is dropped with a log rather than cached: it could never redeem
|
|
188
|
+
* anyway, and carrying an unbounded hostile object outlives the session.
|
|
189
|
+
*/
|
|
190
|
+
membershipInviteFrom(provision) {
|
|
191
|
+
if (provision.membershipInvite === undefined) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
if (!isWellFormedMembershipInvite(provision.membershipInvite)) {
|
|
195
|
+
log('formStrand: dropping malformed membershipInvite from responder for strand %s', provision.strand.strandId);
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
return { inviteKey: provision.membershipInvite.inviteKey, invitePrivateKey: provision.membershipInvite.invitePrivateKey };
|
|
199
|
+
}
|
|
148
200
|
/**
|
|
149
201
|
* Derive the initiator's await-response budget from the configured RESPONDER budget —
|
|
150
202
|
* never the same number (see `StrandFormationManagerConfig.provisionTimeoutMs`). Mirrors
|
|
@@ -188,10 +240,14 @@ export class StrandFormationManager {
|
|
|
188
240
|
* {@link ResponderProvisionOutcome} with `approved: false` — `runSession` turns that
|
|
189
241
|
* into a clean, non-disclosing reply instead of a dropped result frame.
|
|
190
242
|
*
|
|
191
|
-
* - **bound** (host strand present): provision-then-record —
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
243
|
+
* - **bound** (host strand present): provision-then-record — first issue the joiner's
|
|
244
|
+
* single-use strand membership invitation via the wired `issueMembershipInvite` seam
|
|
245
|
+
* ({@link issueBoundMembershipInvite}; a closed strand whose invitation cannot be
|
|
246
|
+
* issued rejects retryably HERE, before any consent row spends the token), then write
|
|
247
|
+
* the single `FormationUsage` consent row against the pre-existing strand
|
|
248
|
+
* (record-only) and return it + its membership key + the invitation (all read-gating
|
|
249
|
+
* secrets disclosed only here, behind the token + disclosure validation `runSession`
|
|
250
|
+
* already enforced).
|
|
195
251
|
* - **missing** (invite names a host strand absent on this responder, e.g. unconverged):
|
|
196
252
|
* reject cleanly + retryably, writing NO usage row — recording usage here would fail the
|
|
197
253
|
* deferred `StrandExists` CHECK at commit and drop the frame.
|
|
@@ -227,6 +283,16 @@ export class StrandFormationManager {
|
|
|
227
283
|
try {
|
|
228
284
|
switch (resolved.kind) {
|
|
229
285
|
case 'bound': {
|
|
286
|
+
// Issued BEFORE the consent row is recorded, so an issue failure rejects with
|
|
287
|
+
// the formation token still unspent (retryable). The inverse window — invite
|
|
288
|
+
// issued, then recordUsage fails/aborts — orphans a live `Strand.Invite` no
|
|
289
|
+
// joiner ever received; bounded deliberately by its expiry
|
|
290
|
+
// ({@link MEMBERSHIP_INVITE_TTL_MS}) rather than compensated, since nothing
|
|
291
|
+
// here can atomically un-issue a strand-DB row.
|
|
292
|
+
const issued = await this.issueBoundMembershipInvite(token, resolved.strandId);
|
|
293
|
+
if (!issued.ok) {
|
|
294
|
+
return { approved: false, reason: issued.reason };
|
|
295
|
+
}
|
|
230
296
|
// recorder is guaranteed non-null here: only resolveStrand can yield 'bound'.
|
|
231
297
|
await recorder.recordUsage({
|
|
232
298
|
token,
|
|
@@ -240,6 +306,9 @@ export class StrandFormationManager {
|
|
|
240
306
|
return this.approve({
|
|
241
307
|
strand: { strandId: resolved.strandId, createdBy: 'responder' },
|
|
242
308
|
memberPrivateKey: resolved.memberPrivateKey ?? undefined,
|
|
309
|
+
// Omitted (not sent undefined-valued) so rejection-parity and open-strand
|
|
310
|
+
// assertions stay plain `toBeUndefined()` on the parsed frame.
|
|
311
|
+
...(issued.invite ? { membershipInvite: issued.invite } : {}),
|
|
243
312
|
dbConnectionInfo: { endpoint: 'local', credentialsRef: '' }
|
|
244
313
|
});
|
|
245
314
|
}
|
|
@@ -279,6 +348,41 @@ export class StrandFormationManager {
|
|
|
279
348
|
return { approved: false, reason: 'Formation conflict, retry' };
|
|
280
349
|
}
|
|
281
350
|
}
|
|
351
|
+
/**
|
|
352
|
+
* Issue the joiner's membership invitation for a BOUND redemption via the wired
|
|
353
|
+
* {@link StrandFormationManagerOptions.issueMembershipInvite} seam.
|
|
354
|
+
*
|
|
355
|
+
* - Hook unwired (mock/transport tests): approve with no invitation, the same posture
|
|
356
|
+
* as an unwired `resolveStrandAddrs`.
|
|
357
|
+
* - Hook returns an invitation (closed host strand): carry it on the approval.
|
|
358
|
+
* - Hook returns `null` (open host strand): approve with no invitation.
|
|
359
|
+
* - Hook throws (runtime not live, no party key, strand-DB write rejected): report
|
|
360
|
+
* `ok: false` with {@link MEMBERSHIP_INVITE_UNAVAILABLE_REASON} — the caller rejects
|
|
361
|
+
* BEFORE any consent row is written, so the formation token stays unspent.
|
|
362
|
+
* - Hook throws `PreSplitStrandIdentityError` (the host strand was founded before the
|
|
363
|
+
* per-party identity split and can never issue one): same, but with the
|
|
364
|
+
* non-retryable {@link HOST_STRAND_MUST_BE_RECREATED_REASON}.
|
|
365
|
+
*
|
|
366
|
+
* The LOG-before-reject keeps both a deliberate internal-error→protocol-rejection
|
|
367
|
+
* conversion, same as the catch-all in {@link provisionAsResponder}.
|
|
368
|
+
*/
|
|
369
|
+
async issueBoundMembershipInvite(token, strandId) {
|
|
370
|
+
if (!this.issueMembershipInvite) {
|
|
371
|
+
return { ok: true };
|
|
372
|
+
}
|
|
373
|
+
try {
|
|
374
|
+
const invite = await this.issueMembershipInvite(strandId);
|
|
375
|
+
return { ok: true, invite: invite ?? undefined };
|
|
376
|
+
}
|
|
377
|
+
catch (err) {
|
|
378
|
+
if (err instanceof PreSplitStrandIdentityError) {
|
|
379
|
+
log('host strand %s is pre-split (token %s); rejecting — it must be recreated: %o', strandId, token, err);
|
|
380
|
+
return { ok: false, reason: HOST_STRAND_MUST_BE_RECREATED_REASON };
|
|
381
|
+
}
|
|
382
|
+
log('membership-invite issue for strand %s failed (token %s); rejecting retryably: %o', strandId, token, err);
|
|
383
|
+
return { ok: false, reason: MEMBERSHIP_INVITE_UNAVAILABLE_REASON };
|
|
384
|
+
}
|
|
385
|
+
}
|
|
282
386
|
/**
|
|
283
387
|
* Responder-provisions fallback (invite binds no host strand). Precedence:
|
|
284
388
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strand-formation-manager.js","sourceRoot":"","sources":["../src/strand-formation-manager.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"strand-formation-manager.js","sourceRoot":"","sources":["../src/strand-formation-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* discloses none of them.
|
|
19
19
|
*/
|
|
20
20
|
import type { Libp2p } from '@libp2p/interface';
|
|
21
|
-
import type { StrandFormationDisclosure } from './types.js';
|
|
21
|
+
import type { StrandFormationDisclosure, StrandMembershipInvite } from './types.js';
|
|
22
22
|
/** Protocol id for the native formation transport (parallel to `/sereus/seed/1.0.0`). */
|
|
23
23
|
export declare const FORMATION_PROTOCOL = "/sereus/formation/1.0.0";
|
|
24
24
|
/**
|
|
@@ -74,6 +74,16 @@ export interface FormationProvisionResult {
|
|
|
74
74
|
* already-used token discloses neither identity nor key. Absent for open strands.
|
|
75
75
|
*/
|
|
76
76
|
memberPrivateKey?: string;
|
|
77
|
+
/**
|
|
78
|
+
* The joiner's single-use membership invitation into the (closed, bound) host
|
|
79
|
+
* strand — issued by the responder's live strand runtime against `Strand.Invite`
|
|
80
|
+
* just before the consent row was recorded. Same disclosure timing as
|
|
81
|
+
* {@link memberPrivateKey}: present only on an approved result, absent for open
|
|
82
|
+
* strands and the responder-provisions path. Bounded and shape-checked on the
|
|
83
|
+
* initiator side ({@link isWellFormedMembershipInvite}) — a hostile responder's
|
|
84
|
+
* malformed field fails validation rather than crashing the initiator.
|
|
85
|
+
*/
|
|
86
|
+
membershipInvite?: StrandMembershipInvite;
|
|
77
87
|
dbConnectionInfo: FormationDbConnectionInfo;
|
|
78
88
|
}
|
|
79
89
|
/**
|
|
@@ -154,6 +164,16 @@ export interface FormationResultMessage {
|
|
|
154
164
|
* cost a formation that has otherwise succeeded.
|
|
155
165
|
*/
|
|
156
166
|
export declare function sanitizeStrandAddrs(addrs: unknown): string[];
|
|
167
|
+
/**
|
|
168
|
+
* Is `value` a well-formed {@link StrandMembershipInvite} — exactly two bounded
|
|
169
|
+
* base64url strings? The initiator-side shape check for the one attacker-influenced
|
|
170
|
+
* OBJECT the formation result carries (`strandAddrs` gets the equivalent treatment in
|
|
171
|
+
* {@link sanitizeStrandAddrs}): a malformed field from a hostile responder must fail
|
|
172
|
+
* validation, never crash the initiator or be carried into the joiner's invite cache.
|
|
173
|
+
* `undefined` is NOT well-formed — absence is legal on the result and callers gate on
|
|
174
|
+
* presence first.
|
|
175
|
+
*/
|
|
176
|
+
export declare function isWellFormedMembershipInvite(value: unknown): value is StrandMembershipInvite;
|
|
157
177
|
/**
|
|
158
178
|
* Structural check on a `responderCreates` result: the responder must have approved,
|
|
159
179
|
* disclosed a non-empty real identity + cadre (not the legacy `cadre-*.local`
|