@serfab/cadre-core 0.10.0 → 0.11.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.
Files changed (105) hide show
  1. package/README.md +255 -255
  2. package/dist/cadre-node.d.ts +203 -28
  3. package/dist/cadre-node.js +445 -84
  4. package/dist/cadre-node.js.map +1 -1
  5. package/dist/control-cohort.d.ts +26 -0
  6. package/dist/control-cohort.js +26 -0
  7. package/dist/control-cohort.js.map +1 -1
  8. package/dist/control-database.d.ts +82 -117
  9. package/dist/control-database.js +217 -295
  10. package/dist/control-database.js.map +1 -1
  11. package/dist/control-formation-recorder.d.ts +8 -7
  12. package/dist/control-formation-recorder.js +8 -7
  13. package/dist/control-formation-recorder.js.map +1 -1
  14. package/dist/control-schema.d.ts +1 -1
  15. package/dist/control-schema.js +789 -774
  16. package/dist/control-schema.js.map +1 -1
  17. package/dist/control-write-retry.d.ts +15 -5
  18. package/dist/control-write-retry.js +19 -18
  19. package/dist/control-write-retry.js.map +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.js +1 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/peer-addr-book.d.ts +92 -0
  24. package/dist/peer-addr-book.js +220 -0
  25. package/dist/peer-addr-book.js.map +1 -0
  26. package/dist/seed-bootstrap.d.ts +7 -0
  27. package/dist/seed-bootstrap.js +15 -13
  28. package/dist/seed-bootstrap.js.map +1 -1
  29. package/dist/strand-cohort.d.ts +4 -37
  30. package/dist/strand-cohort.js +4 -15
  31. package/dist/strand-cohort.js.map +1 -1
  32. package/dist/strand-database.d.ts +11 -16
  33. package/dist/strand-database.js +19 -7
  34. package/dist/strand-database.js.map +1 -1
  35. package/dist/strand-formation-manager.d.ts +8 -8
  36. package/dist/strand-formation-manager.js +15 -18
  37. package/dist/strand-formation-manager.js.map +1 -1
  38. package/dist/strand-instance-manager.d.ts +52 -21
  39. package/dist/strand-instance-manager.js +100 -34
  40. package/dist/strand-instance-manager.js.map +1 -1
  41. package/dist/strand-membership-writer.d.ts +6 -4
  42. package/dist/strand-membership-writer.js +38 -36
  43. package/dist/strand-membership-writer.js.map +1 -1
  44. package/dist/types.d.ts +39 -31
  45. package/dist/types.js.map +1 -1
  46. package/package.json +8 -7
  47. package/src/arachnode-stub.ts +159 -159
  48. package/src/bootstrap-peer-store-file.ts +44 -44
  49. package/src/bootstrap-peer-store.ts +172 -172
  50. package/src/cadre-node.ts +5158 -4771
  51. package/src/canonical-json.ts +30 -30
  52. package/src/control-authorization.ts +114 -114
  53. package/src/control-cohort.ts +130 -103
  54. package/src/control-database.ts +2406 -2506
  55. package/src/control-formation-recorder.ts +305 -304
  56. package/src/control-schema.ts +801 -786
  57. package/src/control-stream.ts +205 -205
  58. package/src/control-write-retry.ts +483 -473
  59. package/src/delegate-admission.ts +302 -302
  60. package/src/device-token.ts +94 -94
  61. package/src/diagnostics/connection-path.ts +277 -277
  62. package/src/diagnostics/webrtc-turn-tracker.ts +222 -222
  63. package/src/ed25519-key.ts +134 -134
  64. package/src/enrollment.ts +199 -199
  65. package/src/file-durable-slot.ts +61 -61
  66. package/src/formation-approval.ts +604 -604
  67. package/src/fs-atomic.ts +107 -107
  68. package/src/hibernation-manager.ts +437 -437
  69. package/src/identity-key.ts +109 -109
  70. package/src/index.ts +1 -0
  71. package/src/key-store-file.ts +132 -132
  72. package/src/key-store.ts +99 -99
  73. package/src/membership-connection-gater.ts +204 -204
  74. package/src/node-local-snapshot.ts +260 -260
  75. package/src/peer-addr-book.ts +250 -0
  76. package/src/peer-authorization.ts +259 -259
  77. package/src/peer-record.ts +164 -164
  78. package/src/push-credentials.ts +84 -84
  79. package/src/push-node.ts +64 -64
  80. package/src/push-notifier-apns.ts +247 -247
  81. package/src/push-notifier-fcm.ts +220 -220
  82. package/src/push-notifier-shared.ts +28 -28
  83. package/src/push-notifier.ts +52 -52
  84. package/src/relay-addrs.ts +111 -111
  85. package/src/relay-reservation.ts +823 -823
  86. package/src/seed-bootstrap.ts +15 -15
  87. package/src/seed-trust-policy.ts +140 -140
  88. package/src/strand-addr-protocol.ts +441 -441
  89. package/src/strand-backfill.ts +366 -366
  90. package/src/strand-cohort.ts +38 -77
  91. package/src/strand-database.ts +202 -204
  92. package/src/strand-formation-manager.ts +491 -494
  93. package/src/strand-formation-protocol.ts +692 -692
  94. package/src/strand-instance-manager.ts +108 -50
  95. package/src/strand-member-key.ts +39 -39
  96. package/src/strand-member-registry.ts +182 -182
  97. package/src/strand-membership-writer.ts +1432 -1430
  98. package/src/strand-solicitation.ts +472 -472
  99. package/src/strand-transport-key.ts +58 -58
  100. package/src/strand-wake-payload.ts +32 -32
  101. package/src/strand-wake-protocol.ts +314 -314
  102. package/src/strand-watcher.ts +314 -314
  103. package/src/trusted-owner-store-file.ts +41 -41
  104. package/src/trusted-owner-store.ts +171 -171
  105. package/src/types.ts +1309 -1303
package/README.md CHANGED
@@ -1,255 +1,255 @@
1
- # @serfab/cadre-core
2
-
3
- Core library for Sereus cadre nodes—the infrastructure that enables parties to manage their personal cluster of nodes participating in distributed strand networks.
4
-
5
- ## Why Sereus Cadre Core?
6
-
7
- In traditional distributed systems, users depend on centralized services to manage their data and identities. **Sereus** inverts this model: each user (or "party") controls their own **cadre**—a personal cluster of nodes ranging from always-on cloud servers to intermittently-connected mobile devices.
8
-
9
- The cadre architecture provides:
10
-
11
- - **Sovereignty**: Your data lives on your nodes, under your control
12
- - **Resilience**: Multiple nodes means no single point of failure
13
- - **Flexibility**: Mix cloud servers, home NAS, laptops, and phones
14
- - **Privacy**: Cryptographic authorization without central servers
15
-
16
- A cadre doesn't exist in isolation. Cadres participate in **strands**—shared data spaces where multiple parties collaborate. When you join a messaging app, your cadre joins that strand. When you share a document, you're creating a strand between your cadre and theirs.
17
-
18
- ## Architecture Overview
19
-
20
- ```
21
- ┌─────────────────────────────────────────────────────────────────────────┐
22
- │ Your Cadre │
23
- │ │
24
- │ ┌────────────────────────────────────────────────────────────────────┐ │
25
- │ │ Control Network │ │
26
- │ │ (Private Optimystic DB for cadre management) │ │
27
- │ │ │ │
28
- │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
29
- │ │ │ Phone │──│ Laptop │──│ Cloud │──│ NAS │ │ │
30
- │ │ │ (edge) │ │ (edge) │ │ (core) │ │ (core) │ │ │
31
- │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
32
- │ └────────────────────────────────────────────────────────────────────┘ │
33
- │ │ │
34
- │ ┌─────────────────────┼─────────────────────┐ │
35
- │ ▼ ▼ ▼ │
36
- │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
37
- │ │ Strand A │ │ Strand B │ │ Strand C │ │
38
- │ │ (Chat App) │ │ (Shared Docs) │ │ (Photo Backup) │ │
39
- │ └──────────────────┘ └──────────────────┘ └──────────────────┘ │
40
- └─────────────────────────────────────────────────────────────────────────┘
41
- ```
42
-
43
- Each `CadreNode` instance:
44
- 1. **Connects to the control network** - A private Optimystic database shared only by your nodes
45
- 2. **Watches for strand changes** - Automatically detects when you join or leave strands
46
- 3. **Manages strand instances** - Spins up isolated libp2p networks for each strand
47
- 4. **Handles peer enrollment** - Cryptographically authorizes new devices to join your cadre
48
-
49
- ## Installation
50
-
51
- ```bash
52
- npm install @serfab/cadre-core
53
- ```
54
-
55
- ## Quick Start
56
-
57
- ```typescript
58
- import { CadreNode } from '@serfab/cadre-core';
59
- import { FileRawStorage } from '@optimystic/db-p2p-storage-fs';
60
-
61
- const node = new CadreNode({
62
- controlNetwork: {
63
- partyId: 'your-unique-party-id',
64
- bootstrapNodes: ['/ip4/192.168.1.100/tcp/4001/p2p/12D3KooW...']
65
- },
66
- profile: 'storage', // 'storage' for servers, 'transaction' for mobile
67
- storage: {
68
- // Storage provider factory - called with strandId for strand-isolated storage
69
- provider: (strandId) => new FileRawStorage(`/data/sereus/${strandId}`),
70
- quotaBytes: 10 * 1024 * 1024 * 1024 // 10 GB
71
- }
72
- });
73
-
74
- // Start the node
75
- await node.start();
76
- console.log('Node started with Peer ID:', node.peerId?.toString());
77
-
78
- // Listen for strand events
79
- node.on('strand:started', ({ strandId }) => {
80
- console.log('Joined strand:', strandId);
81
- });
82
-
83
- // Check active strands
84
- for (const [id, strand] of node.getStrands()) {
85
- console.log(`Strand ${id}: ${strand.status}, ${strand.connectedPeers} peers`);
86
- }
87
-
88
- // Graceful shutdown
89
- await node.stop();
90
- ```
91
-
92
- ## Node Profiles
93
-
94
- | Profile | Storage Role | Use Case | Ring Zulu |
95
- |---------|--------------|----------|-----------|
96
- | **transaction** | Arachnode disabled; transaction verification via FRET only | Mobile devices, intermittent connectivity | No |
97
- | **storage** | Ring Zulu + storage rings (storage rings not yet implemented — stub) | Servers, NAS, always-on nodes | Yes |
98
-
99
- Transaction-profile nodes verify transactions via FRET only, with Arachnode disabled. Storage-profile nodes additionally join Ring Zulu and (when implemented) the concentric storage rings.
100
-
101
- > **Note:** The storage-ring subsystem is currently a no-op stub (`arachnode-stub.ts`). See [Node Profiles in `docs/architecture.md`](../../docs/architecture.md#node-profiles) for the full design.
102
-
103
- ## Strand Filtering
104
-
105
- Mobile apps typically shouldn't participate in all strands. Use filters to control participation:
106
-
107
- ```typescript
108
- // Only participate in strands for a specific app
109
- strandFilter: { mode: 'sAppId', sAppId: 'com.example.chat' }
110
-
111
- // Only participate in one specific strand
112
- strandFilter: { mode: 'strandId', strandId: 'strand-abc123' }
113
-
114
- // Control network only, no strand participation
115
- strandFilter: { mode: 'none' }
116
-
117
- // Participate in all strands (default for servers)
118
- strandFilter: { mode: 'all' }
119
- ```
120
-
121
- ## Enrolling New Devices
122
-
123
- Adding a new device to your cadre uses the Seed Bootstrap API:
124
-
125
- ```typescript
126
- // On the new device: generate identity
127
- const enrollment = new EnrollmentService();
128
- const { peerId, privateKey } = await enrollment.createCadrePeer();
129
- // Store privateKey securely, send peerId + multiaddrs to owner
130
-
131
- // On owner device: authorize the new peer and create seed
132
- await node.authorizePeer(newDevicePeerId, newDeviceMultiaddrs);
133
- const seed = await node.createSeed();
134
-
135
- // Deliver seed to new device (via protocol, API, or out-of-band)
136
- await node.deliverSeed(newDeviceMultiaddr, seed);
137
- // Or encode for QR/link: const encoded = node.encodeSeed(seed);
138
-
139
- // On new device: apply seed to join cadre
140
- const result = await newNode.applySeed(seed);
141
- ```
142
-
143
- For provider-hosted drones, use the helper:
144
-
145
- ```typescript
146
- // Get drone info from provider API
147
- const droneInfo = await provider.createContainer(plan);
148
-
149
- // One call: authorize + create seed
150
- const { seed, encodedSeed } = await node.addDrone({
151
- dronePeerId: droneInfo.peerId,
152
- droneMultiaddrs: droneInfo.multiaddrs
153
- });
154
-
155
- // Send to provider for drone initialization
156
- await provider.initializeNode(droneInfo.containerId, encodedSeed);
157
- ```
158
-
159
- ## API Reference
160
-
161
- ### CadreNode
162
-
163
- The main entry point for cadre participation.
164
-
165
- | Method | Description |
166
- |--------|-------------|
167
- | `start()` | Connect to control network and begin strand participation |
168
- | `stop()` | Gracefully disconnect from all networks |
169
- | `getStrands()` | Get all active strand instances |
170
- | `getStrand(id)` | Get a specific strand instance |
171
- | `addStrand(row)` | Manually add a strand (testing/direct API) |
172
- | `stopStrand(id)` | Stop a strand on this node only (the shared `Strand` row stays; rediscovered on restart) |
173
- | `publishStrand(id, type?, memberKey?)` | Owner-signed `Strand` row insert — makes the strand visible cadre-wide |
174
- | `unpublishStrand(id)` | Owner-signed party-wide removal — deletes this party's `Strand` row; every node stops the strand. Destroys a closed strand's `MemberPrivateKey` |
175
- | `getEnrollmentService()` | Access peer enrollment API |
176
-
177
- ### Events
178
-
179
- | Event | Payload | Description |
180
- |-------|---------|-------------|
181
- | `control:connected` | - | Connected to control network |
182
- | `control:disconnected` | - | Disconnected from control network |
183
- | `strand:started` | `{ strandId }` | Strand instance started |
184
- | `strand:stopped` | `{ strandId }` | Strand instance stopped |
185
- | `strand:error` | `{ strandId, error }` | Error in strand instance. A failed launch re-emits this on every retry (backing off up to 5 min), so treat it as a repeating signal, not a one-shot |
186
-
187
- ## Related Documentation
188
-
189
- - **[Cadre Architecture](../../docs/architecture.md)** - Deep dive into the cadre system design
190
- - **[Strand Management](../../docs/strands.md)** - How strands connect multiple cadres
191
- - **[Schema Guide](../../docs/schema-guide.md)** - Optimystic schema definitions
192
-
193
- ## React Native Support
194
-
195
- This package is compatible with React Native. For mobile apps, use the RN-specific storage provider:
196
-
197
- ```typescript
198
- import { CadreNode } from '@serfab/cadre-core';
199
- import { RNRawStorage } from '@optimystic/db-p2p-storage-rn';
200
- import { webSockets } from '@libp2p/websockets';
201
- import { circuitRelayTransport } from '@libp2p/circuit-relay-v2';
202
-
203
- const node = new CadreNode({
204
- controlNetwork: {
205
- partyId: 'your-unique-party-id',
206
- bootstrapNodes: ['/dns4/relay.example.com/tcp/443/wss/p2p/12D3KooW...']
207
- },
208
- profile: 'transaction', // Mobile devices typically use transaction profile
209
- strandFilter: { mode: 'sAppId', sAppId: 'com.example.myapp' },
210
- storage: {
211
- // RN storage provider creates strand-isolated AsyncStorage
212
- provider: (strandId) => new RNRawStorage(strandId)
213
- },
214
- network: {
215
- // TCP doesn't work in React Native — use WebSocket transport instead
216
- transports: [webSockets(), circuitRelayTransport()],
217
- listenAddrs: [] // RN nodes typically cannot listen
218
- }
219
- });
220
- ```
221
-
222
- ### Storage Provider Pattern
223
-
224
- The `storage.provider` option accepts either:
225
- - **An `IRawStorage` instance** - Shared storage for all strands
226
- - **A factory function** `(strandId: string) => IRawStorage` - Creates isolated storage per strand (recommended)
227
-
228
- Available storage implementations:
229
- | Package | Environment | Description |
230
- |---------|-------------|-------------|
231
- | `@optimystic/db-p2p` | All | `MemoryRawStorage` - In-memory (testing only) |
232
- | `@optimystic/db-p2p-storage-fs` | Node.js | `FileRawStorage` - File system storage |
233
- | `@optimystic/db-p2p-storage-rn` | React Native | `RNRawStorage` - AsyncStorage-based |
234
-
235
- ### React Native Considerations
236
-
237
- 1. **Network Transport**: libp2p's TCP transport doesn't work in RN. Pass `transports: [webSockets(), circuitRelayTransport()]` in the `network` config (see example above).
238
-
239
- 2. **Schema Loading**: The control schema is embedded in the package. Do not set `schemaPath` in React Native.
240
-
241
- 3. **Profile**: Mobile devices should use `profile: 'transaction'` for battery/bandwidth efficiency.
242
-
243
- 4. **Strand Filtering**: Always filter strands on mobile to avoid participating in unnecessary networks.
244
-
245
- ## Related Packages
246
-
247
- - **[@optimystic/db-core](https://github.com/gotchoices/optimystic)** - Distributed database core
248
- - **[@optimystic/db-p2p](https://github.com/gotchoices/optimystic)** - libp2p integration for Optimystic
249
- - **[@optimystic/db-p2p-storage-fs](https://github.com/gotchoices/optimystic)** - File system storage (Node.js)
250
- - **[@optimystic/db-p2p-storage-rn](https://github.com/gotchoices/optimystic)** - AsyncStorage (React Native)
251
-
252
- ## License
253
-
254
- MIT
255
-
1
+ # @serfab/cadre-core
2
+
3
+ Core library for Sereus cadre nodes—the infrastructure that enables parties to manage their personal cluster of nodes participating in distributed strand networks.
4
+
5
+ ## Why Sereus Cadre Core?
6
+
7
+ In traditional distributed systems, users depend on centralized services to manage their data and identities. **Sereus** inverts this model: each user (or "party") controls their own **cadre**—a personal cluster of nodes ranging from always-on cloud servers to intermittently-connected mobile devices.
8
+
9
+ The cadre architecture provides:
10
+
11
+ - **Sovereignty**: Your data lives on your nodes, under your control
12
+ - **Resilience**: Multiple nodes means no single point of failure
13
+ - **Flexibility**: Mix cloud servers, home NAS, laptops, and phones
14
+ - **Privacy**: Cryptographic authorization without central servers
15
+
16
+ A cadre doesn't exist in isolation. Cadres participate in **strands**—shared data spaces where multiple parties collaborate. When you join a messaging app, your cadre joins that strand. When you share a document, you're creating a strand between your cadre and theirs.
17
+
18
+ ## Architecture Overview
19
+
20
+ ```
21
+ ┌─────────────────────────────────────────────────────────────────────────┐
22
+ │ Your Cadre │
23
+ │ │
24
+ │ ┌────────────────────────────────────────────────────────────────────┐ │
25
+ │ │ Control Network │ │
26
+ │ │ (Private Optimystic DB for cadre management) │ │
27
+ │ │ │ │
28
+ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
29
+ │ │ │ Phone │──│ Laptop │──│ Cloud │──│ NAS │ │ │
30
+ │ │ │ (edge) │ │ (edge) │ │ (core) │ │ (core) │ │ │
31
+ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
32
+ │ └────────────────────────────────────────────────────────────────────┘ │
33
+ │ │ │
34
+ │ ┌─────────────────────┼─────────────────────┐ │
35
+ │ ▼ ▼ ▼ │
36
+ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
37
+ │ │ Strand A │ │ Strand B │ │ Strand C │ │
38
+ │ │ (Chat App) │ │ (Shared Docs) │ │ (Photo Backup) │ │
39
+ │ └──────────────────┘ └──────────────────┘ └──────────────────┘ │
40
+ └─────────────────────────────────────────────────────────────────────────┘
41
+ ```
42
+
43
+ Each `CadreNode` instance:
44
+ 1. **Connects to the control network** - A private Optimystic database shared only by your nodes
45
+ 2. **Watches for strand changes** - Automatically detects when you join or leave strands
46
+ 3. **Manages strand instances** - Spins up isolated libp2p networks for each strand
47
+ 4. **Handles peer enrollment** - Cryptographically authorizes new devices to join your cadre
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ npm install @serfab/cadre-core
53
+ ```
54
+
55
+ ## Quick Start
56
+
57
+ ```typescript
58
+ import { CadreNode } from '@serfab/cadre-core';
59
+ import { FileRawStorage } from '@optimystic/db-p2p-storage-fs';
60
+
61
+ const node = new CadreNode({
62
+ controlNetwork: {
63
+ partyId: 'your-unique-party-id',
64
+ bootstrapNodes: ['/ip4/192.168.1.100/tcp/4001/p2p/12D3KooW...']
65
+ },
66
+ profile: 'storage', // 'storage' for servers, 'transaction' for mobile
67
+ storage: {
68
+ // Storage provider factory - called with strandId for strand-isolated storage
69
+ provider: (strandId) => new FileRawStorage(`/data/sereus/${strandId}`),
70
+ quotaBytes: 10 * 1024 * 1024 * 1024 // 10 GB
71
+ }
72
+ });
73
+
74
+ // Start the node
75
+ await node.start();
76
+ console.log('Node started with Peer ID:', node.peerId?.toString());
77
+
78
+ // Listen for strand events
79
+ node.on('strand:started', ({ strandId }) => {
80
+ console.log('Joined strand:', strandId);
81
+ });
82
+
83
+ // Check active strands
84
+ for (const [id, strand] of node.getStrands()) {
85
+ console.log(`Strand ${id}: ${strand.status}, ${strand.connectedPeers} peers`);
86
+ }
87
+
88
+ // Graceful shutdown
89
+ await node.stop();
90
+ ```
91
+
92
+ ## Node Profiles
93
+
94
+ | Profile | Storage Role | Use Case | Ring Zulu |
95
+ |---------|--------------|----------|-----------|
96
+ | **transaction** | Arachnode disabled; transaction verification via FRET only | Mobile devices, intermittent connectivity | No |
97
+ | **storage** | Ring Zulu + storage rings (storage rings not yet implemented — stub) | Servers, NAS, always-on nodes | Yes |
98
+
99
+ Transaction-profile nodes verify transactions via FRET only, with Arachnode disabled. Storage-profile nodes additionally join Ring Zulu and (when implemented) the concentric storage rings.
100
+
101
+ > **Note:** The storage-ring subsystem is currently a no-op stub (`arachnode-stub.ts`). See [Node Profiles in `docs/architecture.md`](../../docs/architecture.md#node-profiles) for the full design.
102
+
103
+ ## Strand Filtering
104
+
105
+ Mobile apps typically shouldn't participate in all strands. Use filters to control participation:
106
+
107
+ ```typescript
108
+ // Only participate in strands for a specific app
109
+ strandFilter: { mode: 'sAppId', sAppId: 'com.example.chat' }
110
+
111
+ // Only participate in one specific strand
112
+ strandFilter: { mode: 'strandId', strandId: 'strand-abc123' }
113
+
114
+ // Control network only, no strand participation
115
+ strandFilter: { mode: 'none' }
116
+
117
+ // Participate in all strands (default for servers)
118
+ strandFilter: { mode: 'all' }
119
+ ```
120
+
121
+ ## Enrolling New Devices
122
+
123
+ Adding a new device to your cadre uses the Seed Bootstrap API:
124
+
125
+ ```typescript
126
+ // On the new device: generate identity
127
+ const enrollment = new EnrollmentService();
128
+ const { peerId, privateKey } = await enrollment.createCadrePeer();
129
+ // Store privateKey securely, send peerId + multiaddrs to owner
130
+
131
+ // On owner device: authorize the new peer and create seed
132
+ await node.authorizePeer(newDevicePeerId, newDeviceMultiaddrs);
133
+ const seed = await node.createSeed();
134
+
135
+ // Deliver seed to new device (via protocol, API, or out-of-band)
136
+ await node.deliverSeed(newDeviceMultiaddr, seed);
137
+ // Or encode for QR/link: const encoded = node.encodeSeed(seed);
138
+
139
+ // On new device: apply seed to join cadre
140
+ const result = await newNode.applySeed(seed);
141
+ ```
142
+
143
+ For provider-hosted drones, use the helper:
144
+
145
+ ```typescript
146
+ // Get drone info from provider API
147
+ const droneInfo = await provider.createContainer(plan);
148
+
149
+ // One call: authorize + create seed
150
+ const { seed, encodedSeed } = await node.addDrone({
151
+ dronePeerId: droneInfo.peerId,
152
+ droneMultiaddrs: droneInfo.multiaddrs
153
+ });
154
+
155
+ // Send to provider for drone initialization
156
+ await provider.initializeNode(droneInfo.containerId, encodedSeed);
157
+ ```
158
+
159
+ ## API Reference
160
+
161
+ ### CadreNode
162
+
163
+ The main entry point for cadre participation.
164
+
165
+ | Method | Description |
166
+ |--------|-------------|
167
+ | `start()` | Connect to control network and begin strand participation |
168
+ | `stop()` | Gracefully disconnect from all networks |
169
+ | `getStrands()` | Get all active strand instances |
170
+ | `getStrand(id)` | Get a specific strand instance |
171
+ | `addStrand(row)` | Manually add a strand (testing/direct API) |
172
+ | `stopStrand(id)` | Stop a strand on this node only (the shared `Strand` row stays; rediscovered on restart) |
173
+ | `publishStrand(id, type?, memberKey?)` | Owner-signed `Strand` row insert — makes the strand visible cadre-wide |
174
+ | `unpublishStrand(id)` | Owner-signed party-wide removal — deletes this party's `Strand` row; every node stops the strand. Destroys a closed strand's `MemberPrivateKey` |
175
+ | `getEnrollmentService()` | Access peer enrollment API |
176
+
177
+ ### Events
178
+
179
+ | Event | Payload | Description |
180
+ |-------|---------|-------------|
181
+ | `control:connected` | - | Connected to control network |
182
+ | `control:disconnected` | - | Disconnected from control network |
183
+ | `strand:started` | `{ strandId }` | Strand instance started |
184
+ | `strand:stopped` | `{ strandId }` | Strand instance stopped |
185
+ | `strand:error` | `{ strandId, error }` | Error in strand instance. A failed launch re-emits this on every retry (backing off up to 5 min), so treat it as a repeating signal, not a one-shot |
186
+
187
+ ## Related Documentation
188
+
189
+ - **[Cadre Architecture](../../docs/architecture.md)** - Deep dive into the cadre system design
190
+ - **[Strand Management](../../docs/strands.md)** - How strands connect multiple cadres
191
+ - **[Schema Guide](../../docs/schema-guide.md)** - Optimystic schema definitions
192
+
193
+ ## React Native Support
194
+
195
+ This package is compatible with React Native. For mobile apps, use the RN-specific storage provider:
196
+
197
+ ```typescript
198
+ import { CadreNode } from '@serfab/cadre-core';
199
+ import { RNRawStorage } from '@optimystic/db-p2p-storage-rn';
200
+ import { webSockets } from '@libp2p/websockets';
201
+ import { circuitRelayTransport } from '@libp2p/circuit-relay-v2';
202
+
203
+ const node = new CadreNode({
204
+ controlNetwork: {
205
+ partyId: 'your-unique-party-id',
206
+ bootstrapNodes: ['/dns4/relay.example.com/tcp/443/wss/p2p/12D3KooW...']
207
+ },
208
+ profile: 'transaction', // Mobile devices typically use transaction profile
209
+ strandFilter: { mode: 'sAppId', sAppId: 'com.example.myapp' },
210
+ storage: {
211
+ // RN storage provider creates strand-isolated AsyncStorage
212
+ provider: (strandId) => new RNRawStorage(strandId)
213
+ },
214
+ network: {
215
+ // TCP doesn't work in React Native — use WebSocket transport instead
216
+ transports: [webSockets(), circuitRelayTransport()],
217
+ listenAddrs: [] // RN nodes typically cannot listen
218
+ }
219
+ });
220
+ ```
221
+
222
+ ### Storage Provider Pattern
223
+
224
+ The `storage.provider` option accepts either:
225
+ - **An `IRawStorage` instance** - Shared storage for all strands
226
+ - **A factory function** `(strandId: string) => IRawStorage` - Creates isolated storage per strand (recommended)
227
+
228
+ Available storage implementations:
229
+ | Package | Environment | Description |
230
+ |---------|-------------|-------------|
231
+ | `@optimystic/db-p2p` | All | `MemoryRawStorage` - In-memory (testing only) |
232
+ | `@optimystic/db-p2p-storage-fs` | Node.js | `FileRawStorage` - File system storage |
233
+ | `@optimystic/db-p2p-storage-rn` | React Native | `RNRawStorage` - AsyncStorage-based |
234
+
235
+ ### React Native Considerations
236
+
237
+ 1. **Network Transport**: libp2p's TCP transport doesn't work in RN. Pass `transports: [webSockets(), circuitRelayTransport()]` in the `network` config (see example above).
238
+
239
+ 2. **Schema Loading**: The control schema is embedded in the package. Do not set `schemaPath` in React Native.
240
+
241
+ 3. **Profile**: Mobile devices should use `profile: 'transaction'` for battery/bandwidth efficiency.
242
+
243
+ 4. **Strand Filtering**: Always filter strands on mobile to avoid participating in unnecessary networks.
244
+
245
+ ## Related Packages
246
+
247
+ - **[@optimystic/db-core](https://github.com/gotchoices/optimystic)** - Distributed database core
248
+ - **[@optimystic/db-p2p](https://github.com/gotchoices/optimystic)** - libp2p integration for Optimystic
249
+ - **[@optimystic/db-p2p-storage-fs](https://github.com/gotchoices/optimystic)** - File system storage (Node.js)
250
+ - **[@optimystic/db-p2p-storage-rn](https://github.com/gotchoices/optimystic)** - AsyncStorage (React Native)
251
+
252
+ ## License
253
+
254
+ MIT
255
+