@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
package/README.md
CHANGED
|
@@ -65,8 +65,9 @@ const node = new CadreNode({
|
|
|
65
65
|
},
|
|
66
66
|
profile: 'storage', // 'storage' for servers, 'transaction' for mobile
|
|
67
67
|
storage: {
|
|
68
|
-
// Storage provider factory - called
|
|
69
|
-
|
|
68
|
+
// Storage provider factory - called once per scope (each strand id, plus this
|
|
69
|
+
// party's control database) for per-scope isolation
|
|
70
|
+
provider: (scope) => new FileRawStorage(`/data/sereus/${scope}`),
|
|
70
71
|
quotaBytes: 10 * 1024 * 1024 * 1024 // 10 GB
|
|
71
72
|
}
|
|
72
73
|
});
|
|
@@ -154,6 +155,10 @@ const { seed, encodedSeed } = await node.addDrone({
|
|
|
154
155
|
|
|
155
156
|
// Send to provider for drone initialization
|
|
156
157
|
await provider.initializeNode(droneInfo.containerId, encodedSeed);
|
|
158
|
+
|
|
159
|
+
// Dial the drone now from the addresses addDrone kept (a drone cannot dial a
|
|
160
|
+
// phone); otherwise the next reconcile pass, about every 15 s, does it
|
|
161
|
+
await node.reconcileControlCohort();
|
|
157
162
|
```
|
|
158
163
|
|
|
159
164
|
## API Reference
|
|
@@ -209,8 +214,8 @@ const node = new CadreNode({
|
|
|
209
214
|
profile: 'transaction', // Mobile devices typically use transaction profile
|
|
210
215
|
strandFilter: { mode: 'sAppId', sAppId: 'com.example.myapp' },
|
|
211
216
|
storage: {
|
|
212
|
-
// RN storage provider creates
|
|
213
|
-
provider: (
|
|
217
|
+
// RN storage provider creates per-scope AsyncStorage
|
|
218
|
+
provider: (scope) => new RNRawStorage(scope)
|
|
214
219
|
},
|
|
215
220
|
network: {
|
|
216
221
|
// TCP doesn't work in React Native — use WebSocket transport instead
|
|
@@ -223,8 +228,17 @@ const node = new CadreNode({
|
|
|
223
228
|
### Storage Provider Pattern
|
|
224
229
|
|
|
225
230
|
The `storage.provider` option accepts either:
|
|
226
|
-
- **An `IRawStorage` instance** -
|
|
227
|
-
- **A factory function** `(
|
|
231
|
+
- **An `IRawStorage` instance** - One store shared by every scope, so it can serve only a single party
|
|
232
|
+
- **A factory function** `(scope: string) => IRawStorage` - Creates isolated storage per scope (recommended)
|
|
233
|
+
|
|
234
|
+
A **scope key** is what the factory receives. It is the strand id for each strand, and
|
|
235
|
+
`controlStorageScope(partyId)` — `control-<base64url party id>` — for the control database,
|
|
236
|
+
which holds one party's own records and must not be shared between parties. Every key is
|
|
237
|
+
opaque and already safe as a file, directory or database name (always within
|
|
238
|
+
`[A-Za-z0-9._-]`): use it verbatim, do not parse it. `isControlStorageScope(scope)` tells
|
|
239
|
+
the two kinds apart. Caveat, until `bug-strand-scope-key-charset-unenforced` lands: the
|
|
240
|
+
charset is enforced for the control key but only *conventional* for a strand id, which
|
|
241
|
+
reaches the factory verbatim even when the strand row replicated in from another node.
|
|
228
242
|
|
|
229
243
|
Available storage implementations:
|
|
230
244
|
| Package | Environment | Description |
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Node-only file-backed {@link BootstrapPeerStore}: one JSON file per party under
|
|
3
3
|
* a configured directory (the node's state directory — `cadre-cli` passes
|
|
4
|
-
* `ResolvedConfig.nodeStateDir`), holding the node-local, non-replicated
|
|
5
|
-
*
|
|
4
|
+
* `ResolvedConfig.nodeStateDir`), holding the node-local, non-replicated dial
|
|
5
|
+
* targets learned out of band (a seed's owner peers, and nodes this node added).
|
|
6
6
|
*
|
|
7
7
|
* Nothing but the file itself lives here: the envelope, load policy, per-entry
|
|
8
8
|
* validation (shape only — never signatures; see the `bootstrap-peer-store.ts`
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { type DurableSlot } from './node-local-snapshot.js';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* One retained dial target learned out of band: an owner peer a seed nominated,
|
|
4
|
+
* or a node this node added.
|
|
5
|
+
*/
|
|
3
6
|
export interface BootstrapPeerEntry {
|
|
4
|
-
/** Multiaddr strings exactly as
|
|
7
|
+
/** Multiaddr strings exactly as they were handed over (parsed at dial time). */
|
|
5
8
|
addrs: string[];
|
|
6
9
|
/** Wall-clock ms the entry was last recorded (diagnostics / future eviction). */
|
|
7
10
|
recordedAt: number;
|
|
@@ -11,9 +14,9 @@ export interface BootstrapPeerStore {
|
|
|
11
14
|
readonly partyId: string;
|
|
12
15
|
/**
|
|
13
16
|
* Every retained target, peerId -> entry. Every backend copies the map per call,
|
|
14
|
-
* so the result is a snapshot decoupled from later {@link record}
|
|
15
|
-
* safe to iterate while recording. The entry objects
|
|
16
|
-
* REPLACES an entry rather than mutating it in place.
|
|
17
|
+
* so the result is a snapshot decoupled from later {@link record} and
|
|
18
|
+
* {@link forget} calls and is safe to iterate while recording. The entry objects
|
|
19
|
+
* need no copy: `record` REPLACES an entry rather than mutating it in place.
|
|
17
20
|
*/
|
|
18
21
|
all(): ReadonlyMap<string, BootstrapPeerEntry>;
|
|
19
22
|
/**
|
|
@@ -26,15 +29,25 @@ export interface BootstrapPeerStore {
|
|
|
26
29
|
* `CadreNode.recordSeedBootstrapPeers` keep its signature while a file
|
|
27
30
|
* backend persists in the background.
|
|
28
31
|
*
|
|
29
|
-
* NOTE: entries are never evicted
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
32
|
+
* NOTE: entries are never evicted (only a local `removePeer` {@link forget}s
|
|
33
|
+
* one), and a persistent backend's file therefore grows across the node's whole
|
|
34
|
+
* lifetime rather than one process. Fine while a seed nominates one or a few
|
|
35
|
+
* owners, a node adds a handful of machines, and entries are keyed by peer id;
|
|
36
|
+
* if a node ever applies seeds naming many distinct owners or adds many
|
|
37
|
+
* machines, add eviction (oldest {@link BootstrapPeerEntry.recordedAt} first, or
|
|
38
|
+
* a cap) rather than letting the file grow unbounded — `recordedAt` exists so
|
|
39
|
+
* eviction has something to sort by.
|
|
36
40
|
*/
|
|
37
41
|
record(peerId: string, addrs: readonly string[]): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Drop a peer's retained dial addresses — for a peer this node removed from the
|
|
44
|
+
* party, which must not stay a dial target. Forgetting a peer with no entry is a
|
|
45
|
+
* no-op.
|
|
46
|
+
*
|
|
47
|
+
* Same contract as {@link record}: the removal is reflected in {@link all}
|
|
48
|
+
* SYNCHRONOUSLY, and the returned promise tracks durability only.
|
|
49
|
+
*/
|
|
50
|
+
forget(peerId: string): Promise<void>;
|
|
38
51
|
}
|
|
39
52
|
/**
|
|
40
53
|
* Ephemeral in-memory store for nodes without durable storage (tests, browser
|
|
@@ -47,6 +60,7 @@ export declare class MemoryBootstrapPeerStore implements BootstrapPeerStore {
|
|
|
47
60
|
constructor(partyId: string);
|
|
48
61
|
all(): ReadonlyMap<string, BootstrapPeerEntry>;
|
|
49
62
|
record(peerId: string, addrs: readonly string[]): Promise<void>;
|
|
63
|
+
forget(peerId: string): Promise<void>;
|
|
50
64
|
}
|
|
51
65
|
/**
|
|
52
66
|
* Durable {@link BootstrapPeerStore} over an app-supplied {@link DurableSlot} —
|
|
@@ -70,4 +84,10 @@ export declare class PersistentBootstrapPeerStore implements BootstrapPeerStore
|
|
|
70
84
|
* the full snapshot is persisted (see `NodeLocalSnapshot.put`).
|
|
71
85
|
*/
|
|
72
86
|
record(peerId: string, addrs: readonly string[]): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Drop a peer's dial addresses: gone from {@link all} synchronously, then the
|
|
89
|
+
* full snapshot is persisted — unless there was no entry, which writes nothing
|
|
90
|
+
* (see `NodeLocalSnapshot.remove`).
|
|
91
|
+
*/
|
|
92
|
+
forget(peerId: string): Promise<void>;
|
|
73
93
|
}
|
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Node-local, NON-replicated
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Node-local, NON-replicated bootstrap-peer store: dial targets a node learned
|
|
3
|
+
* out of band, kept so it can dial those peers when nothing better is known.
|
|
4
|
+
* Two sources feed it:
|
|
5
5
|
*
|
|
6
|
-
* A newcomer applies a signed seed nominating the
|
|
7
|
-
* addresses they answer on.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* only
|
|
13
|
-
*
|
|
6
|
+
* - **A seed's owner peers.** A newcomer applies a signed seed nominating the
|
|
7
|
+
* party's owner machines and the addresses they answer on.
|
|
8
|
+
* `SeedBootstrapService.applySeed` dials those owners exactly once,
|
|
9
|
+
* best-effort; when that dial fails (owner briefly down, relay reservation not
|
|
10
|
+
* up, not yet vouched) the node has an empty `CadrePeer` table and no
|
|
11
|
+
* connection, and `CadreNode.reconcileControlCohort`'s cold-start branch is
|
|
12
|
+
* the only way back — it re-dials these retained addresses every pass.
|
|
13
|
+
* - **Nodes this node added.** `CadreNode.addDrone` is handed the added node's
|
|
14
|
+
* addresses, but that node's `CadrePeer` row stays unsigned, and therefore
|
|
15
|
+
* unresolvable, until the node self-publishes — which needs a connection, and
|
|
16
|
+
* a node that cannot listen (a phone) is never dialed by it. The steady-state
|
|
17
|
+
* reconcile pass falls back to these addresses for a sibling whose signed
|
|
18
|
+
* record and address-book entry both yield nothing, including after a
|
|
19
|
+
* relaunch that outlived the record's freshness window.
|
|
20
|
+
*
|
|
21
|
+
* Held only in memory, either kind dies with the process and the node is left
|
|
22
|
+
* with no address to dial, which is what this store exists to prevent.
|
|
14
23
|
*
|
|
15
24
|
* Three implementations, mirroring `trusted-owner-store.ts` (read that first —
|
|
16
25
|
* it solves the same "must outlive the process, storage differs per platform"
|
|
@@ -28,10 +37,11 @@
|
|
|
28
37
|
*
|
|
29
38
|
* **Nothing here is trust-bearing, and a loader must not try to re-verify it.**
|
|
30
39
|
* Only *dial targets* are retained — never a seed, a signature, or an authority
|
|
31
|
-
* claim.
|
|
32
|
-
* anchor before its addresses were retained, and
|
|
33
|
-
*
|
|
34
|
-
*
|
|
40
|
+
* claim. A seed was signature-checked against the node-local trusted-owner
|
|
41
|
+
* anchor before its addresses were retained, and an added node's addresses were
|
|
42
|
+
* supplied by the owner that chose to add it. Either way a dial grants no
|
|
43
|
+
* authority: `CadreNode.bootstrapDialAddrs` binds every address to the peer id it
|
|
44
|
+
* was retained under, so a dial cannot be redirected to whoever answers. What a
|
|
35
45
|
* persistent backend's loader DOES owe is dropping structurally junk entries
|
|
36
46
|
* (unparseable peer id, empty address list, non-string address) rather than
|
|
37
47
|
* carrying them into the dial loop.
|
|
@@ -57,6 +67,11 @@ export class MemoryBootstrapPeerStore {
|
|
|
57
67
|
this.peers.set(peerId, { addrs: [...addrs], recordedAt: Date.now() });
|
|
58
68
|
log('bootstrap peer retained (party=%s, peer=%s, addrs=%d)', this.partyId, peerId, addrs.length);
|
|
59
69
|
}
|
|
70
|
+
async forget(peerId) {
|
|
71
|
+
if (this.peers.delete(peerId)) {
|
|
72
|
+
log('bootstrap peer forgotten (party=%s, peer=%s)', this.partyId, peerId);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
60
75
|
}
|
|
61
76
|
/**
|
|
62
77
|
* What the store persists: `peers` maps peerId -> {@link BootstrapPeerEntry}.
|
|
@@ -124,5 +139,16 @@ export class PersistentBootstrapPeerStore {
|
|
|
124
139
|
log('bootstrap peer retained (party=%s, peer=%s, addrs=%d); persisting', this.partyId, peerId, addrs.length);
|
|
125
140
|
return this.snapshot.put(peerId, { addrs: [...addrs], recordedAt: Date.now() });
|
|
126
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Drop a peer's dial addresses: gone from {@link all} synchronously, then the
|
|
144
|
+
* full snapshot is persisted — unless there was no entry, which writes nothing
|
|
145
|
+
* (see `NodeLocalSnapshot.remove`).
|
|
146
|
+
*/
|
|
147
|
+
forget(peerId) {
|
|
148
|
+
if (this.snapshot.has(peerId)) {
|
|
149
|
+
log('bootstrap peer forgotten (party=%s, peer=%s); persisting', this.partyId, peerId);
|
|
150
|
+
}
|
|
151
|
+
return this.snapshot.remove(peerId);
|
|
152
|
+
}
|
|
127
153
|
}
|
|
128
154
|
//# sourceMappingURL=bootstrap-peer-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap-peer-store.js","sourceRoot":"","sources":["../src/bootstrap-peer-store.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"bootstrap-peer-store.js","sourceRoot":"","sources":["../src/bootstrap-peer-store.ts"],"names":[],"mappings":"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"}
|