@serfab/cadre-core 1.16.0 → 1.18.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 (268) hide show
  1. package/README.md +4 -3
  2. package/dist/bootstrap-peer-store-file.js +1 -1
  3. package/dist/bootstrap-peer-store.d.ts +1 -1
  4. package/dist/bootstrap-peer-store.js +4 -4
  5. package/dist/cadre-invite-protocol.d.ts +81 -142
  6. package/dist/cadre-invite-protocol.js +168 -458
  7. package/dist/cadre-invite-protocol.js.map +1 -1
  8. package/dist/cadre-join-request.d.ts +15 -2
  9. package/dist/cadre-join-request.js +22 -3
  10. package/dist/cadre-join-request.js.map +1 -1
  11. package/dist/cadre-node.d.ts +1089 -475
  12. package/dist/cadre-node.js +2170 -1037
  13. package/dist/cadre-node.js.map +1 -1
  14. package/dist/control-authorization.d.ts +36 -14
  15. package/dist/control-authorization.js +20 -3
  16. package/dist/control-authorization.js.map +1 -1
  17. package/dist/control-cohort.d.ts +88 -32
  18. package/dist/control-cohort.js +91 -33
  19. package/dist/control-cohort.js.map +1 -1
  20. package/dist/control-database.d.ts +856 -318
  21. package/dist/control-database.js +1422 -701
  22. package/dist/control-database.js.map +1 -1
  23. package/dist/control-formation-recorder.d.ts +46 -43
  24. package/dist/control-formation-recorder.js +77 -78
  25. package/dist/control-formation-recorder.js.map +1 -1
  26. package/dist/control-protocol-guard.d.ts +11 -4
  27. package/dist/control-protocol-guard.js +20 -5
  28. package/dist/control-protocol-guard.js.map +1 -1
  29. package/dist/control-schema-version.d.ts +97 -0
  30. package/dist/control-schema-version.js +225 -0
  31. package/dist/control-schema-version.js.map +1 -0
  32. package/dist/control-schema.d.ts +45 -5
  33. package/dist/control-schema.js +262 -78
  34. package/dist/control-schema.js.map +1 -1
  35. package/dist/control-write-retry.d.ts +45 -10
  36. package/dist/control-write-retry.js +92 -22
  37. package/dist/control-write-retry.js.map +1 -1
  38. package/dist/delegate-admission.d.ts +13 -0
  39. package/dist/delegate-admission.js +27 -0
  40. package/dist/delegate-admission.js.map +1 -1
  41. package/dist/ed25519-key.d.ts +10 -7
  42. package/dist/ed25519-key.js +23 -20
  43. package/dist/ed25519-key.js.map +1 -1
  44. package/dist/edge-node/config.d.ts +122 -0
  45. package/dist/edge-node/config.js +199 -0
  46. package/dist/edge-node/config.js.map +1 -0
  47. package/dist/edge-node/dissolve.d.ts +95 -0
  48. package/dist/edge-node/dissolve.js +184 -0
  49. package/dist/edge-node/dissolve.js.map +1 -0
  50. package/dist/edge-node/edge-join.d.ts +44 -0
  51. package/dist/edge-node/edge-join.js +227 -0
  52. package/dist/edge-node/edge-join.js.map +1 -0
  53. package/dist/edge-node/edge-node.d.ts +295 -0
  54. package/dist/edge-node/edge-node.js +704 -0
  55. package/dist/edge-node/edge-node.js.map +1 -0
  56. package/dist/edge-node/index.d.ts +18 -0
  57. package/dist/edge-node/index.js +18 -0
  58. package/dist/edge-node/index.js.map +1 -0
  59. package/dist/edge-node/join-credential.d.ts +11 -0
  60. package/dist/edge-node/join-credential.js +11 -0
  61. package/dist/edge-node/join-credential.js.map +1 -0
  62. package/dist/edge-node/lazy-raw-storage.d.ts +40 -0
  63. package/dist/edge-node/lazy-raw-storage.js +134 -0
  64. package/dist/edge-node/lazy-raw-storage.js.map +1 -0
  65. package/dist/edge-node/node-local.d.ts +60 -0
  66. package/dist/edge-node/node-local.js +58 -0
  67. package/dist/edge-node/node-local.js.map +1 -0
  68. package/dist/edge-node/options.d.ts +83 -0
  69. package/dist/edge-node/options.js +110 -0
  70. package/dist/edge-node/options.js.map +1 -0
  71. package/dist/edge-node/owner-grant.d.ts +41 -0
  72. package/dist/edge-node/owner-grant.js +38 -0
  73. package/dist/edge-node/owner-grant.js.map +1 -0
  74. package/dist/edge-node/platform.d.ts +140 -0
  75. package/dist/edge-node/platform.js +12 -0
  76. package/dist/edge-node/platform.js.map +1 -0
  77. package/dist/edge-node/strand-move.d.ts +115 -0
  78. package/dist/edge-node/strand-move.js +223 -0
  79. package/dist/edge-node/strand-move.js.map +1 -0
  80. package/dist/edge-node/strands.d.ts +28 -0
  81. package/dist/edge-node/strands.js +60 -0
  82. package/dist/edge-node/strands.js.map +1 -0
  83. package/dist/enrolled-machine-store.d.ts +3 -3
  84. package/dist/enrolled-machine-store.js +3 -3
  85. package/dist/formation-token.d.ts +48 -0
  86. package/dist/formation-token.js +26 -0
  87. package/dist/formation-token.js.map +1 -0
  88. package/dist/index.d.ts +27 -17
  89. package/dist/index.js +39 -17
  90. package/dist/index.js.map +1 -1
  91. package/dist/joined-strand-store.d.ts +29 -12
  92. package/dist/joined-strand-store.js +66 -27
  93. package/dist/joined-strand-store.js.map +1 -1
  94. package/dist/key-store.d.ts +9 -4
  95. package/dist/key-store.js +9 -4
  96. package/dist/key-store.js.map +1 -1
  97. package/dist/link-budget.d.ts +9 -7
  98. package/dist/link-budget.js +9 -7
  99. package/dist/link-budget.js.map +1 -1
  100. package/dist/machine-removal.d.ts +19 -39
  101. package/dist/machine-removal.js +24 -98
  102. package/dist/machine-removal.js.map +1 -1
  103. package/dist/member-redemption.d.ts +256 -0
  104. package/dist/member-redemption.js +490 -0
  105. package/dist/member-redemption.js.map +1 -0
  106. package/dist/membership-view.d.ts +91 -0
  107. package/dist/membership-view.js +110 -0
  108. package/dist/membership-view.js.map +1 -0
  109. package/dist/owner-anchor-sync.d.ts +4 -4
  110. package/dist/owner-anchor-sync.js +16 -15
  111. package/dist/owner-anchor-sync.js.map +1 -1
  112. package/dist/owner-grant-protocol.d.ts +251 -0
  113. package/dist/owner-grant-protocol.js +344 -0
  114. package/dist/owner-grant-protocol.js.map +1 -0
  115. package/dist/owner-key-export.d.ts +70 -0
  116. package/dist/owner-key-export.js +279 -0
  117. package/dist/owner-key-export.js.map +1 -0
  118. package/dist/owner-key-flows.d.ts +55 -0
  119. package/dist/owner-key-flows.js +60 -0
  120. package/dist/owner-key-flows.js.map +1 -0
  121. package/dist/owner-key-store.d.ts +31 -0
  122. package/dist/owner-key-store.js +81 -0
  123. package/dist/owner-key-store.js.map +1 -0
  124. package/dist/owner-keys.d.ts +78 -0
  125. package/dist/owner-keys.js +133 -0
  126. package/dist/owner-keys.js.map +1 -0
  127. package/dist/owner-signer.d.ts +54 -0
  128. package/dist/owner-signer.js +51 -0
  129. package/dist/owner-signer.js.map +1 -0
  130. package/dist/peer-addr-book.d.ts +10 -1
  131. package/dist/peer-addr-book.js +4 -3
  132. package/dist/peer-addr-book.js.map +1 -1
  133. package/dist/peer-authorization.d.ts +83 -18
  134. package/dist/peer-authorization.js +123 -53
  135. package/dist/peer-authorization.js.map +1 -1
  136. package/dist/peer-join-backfill.d.ts +89 -20
  137. package/dist/peer-join-backfill.js +265 -108
  138. package/dist/peer-join-backfill.js.map +1 -1
  139. package/dist/pending-join-runner.d.ts +48 -18
  140. package/dist/pending-join-runner.js +74 -56
  141. package/dist/pending-join-runner.js.map +1 -1
  142. package/dist/provider/rent-node.js +1 -1
  143. package/dist/provider/rent-node.js.map +1 -1
  144. package/dist/seed-bootstrap.d.ts +162 -133
  145. package/dist/seed-bootstrap.js +300 -283
  146. package/dist/seed-bootstrap.js.map +1 -1
  147. package/dist/seed-trust-policy.js +4 -4
  148. package/dist/strand-addr-protocol.d.ts +4 -1
  149. package/dist/strand-addr-protocol.js +8 -2
  150. package/dist/strand-addr-protocol.js.map +1 -1
  151. package/dist/strand-cohort.d.ts +3 -0
  152. package/dist/strand-cohort.js.map +1 -1
  153. package/dist/strand-database.d.ts +24 -2
  154. package/dist/strand-database.js +29 -6
  155. package/dist/strand-database.js.map +1 -1
  156. package/dist/strand-first-sync-gate.d.ts +176 -88
  157. package/dist/strand-first-sync-gate.js +288 -121
  158. package/dist/strand-first-sync-gate.js.map +1 -1
  159. package/dist/strand-formation-deadlines.d.ts +9 -9
  160. package/dist/strand-formation-deadlines.js +9 -9
  161. package/dist/strand-formation-manager.d.ts +23 -17
  162. package/dist/strand-formation-manager.js +46 -44
  163. package/dist/strand-formation-manager.js.map +1 -1
  164. package/dist/strand-formation-protocol.d.ts +5 -8
  165. package/dist/strand-formation-protocol.js +101 -16
  166. package/dist/strand-formation-protocol.js.map +1 -1
  167. package/dist/strand-instance-manager.d.ts +27 -18
  168. package/dist/strand-instance-manager.js +119 -50
  169. package/dist/strand-instance-manager.js.map +1 -1
  170. package/dist/strand-membership-reconciler.d.ts +16 -13
  171. package/dist/strand-membership-reconciler.js +30 -22
  172. package/dist/strand-membership-reconciler.js.map +1 -1
  173. package/dist/strand-membership-writer.d.ts +45 -22
  174. package/dist/strand-membership-writer.js +226 -131
  175. package/dist/strand-membership-writer.js.map +1 -1
  176. package/dist/strand-move.d.ts +6 -5
  177. package/dist/strand-move.js +36 -32
  178. package/dist/strand-move.js.map +1 -1
  179. package/dist/strand-peer-redial.js +2 -3
  180. package/dist/strand-peer-redial.js.map +1 -1
  181. package/dist/strand-revocation-enforcer.d.ts +134 -60
  182. package/dist/strand-revocation-enforcer.js +144 -71
  183. package/dist/strand-revocation-enforcer.js.map +1 -1
  184. package/dist/strand-solicitation.d.ts +50 -39
  185. package/dist/strand-solicitation.js +10 -3
  186. package/dist/strand-solicitation.js.map +1 -1
  187. package/dist/strand-watcher.d.ts +80 -13
  188. package/dist/strand-watcher.js +152 -48
  189. package/dist/strand-watcher.js.map +1 -1
  190. package/dist/table-change-watch.d.ts +63 -0
  191. package/dist/table-change-watch.js +209 -0
  192. package/dist/table-change-watch.js.map +1 -0
  193. package/dist/timeout-scheduler.d.ts +4 -3
  194. package/dist/timeout-scheduler.js +4 -3
  195. package/dist/timeout-scheduler.js.map +1 -1
  196. package/dist/types.d.ts +374 -150
  197. package/dist/types.js +4 -4
  198. package/dist/types.js.map +1 -1
  199. package/package.json +17 -8
  200. package/src/bootstrap-peer-store-file.ts +44 -44
  201. package/src/bootstrap-peer-store.ts +214 -214
  202. package/src/cadre-invite-protocol.ts +740 -1052
  203. package/src/cadre-join-request.ts +24 -3
  204. package/src/cadre-node.ts +2402 -1079
  205. package/src/control-authorization.ts +47 -14
  206. package/src/control-cohort.ts +204 -128
  207. package/src/control-database.ts +1749 -820
  208. package/src/control-formation-recorder.ts +359 -353
  209. package/src/control-protocol-guard.ts +23 -6
  210. package/src/control-schema-version.ts +301 -0
  211. package/src/control-schema.ts +273 -78
  212. package/src/control-write-retry.ts +641 -567
  213. package/src/delegate-admission.ts +351 -314
  214. package/src/ed25519-key.ts +174 -171
  215. package/src/edge-node/config.ts +277 -0
  216. package/src/edge-node/dissolve.ts +251 -0
  217. package/src/edge-node/edge-join.ts +319 -0
  218. package/src/edge-node/edge-node.ts +1045 -0
  219. package/src/edge-node/index.ts +59 -0
  220. package/src/edge-node/join-credential.ts +16 -0
  221. package/src/edge-node/lazy-raw-storage.ts +178 -0
  222. package/src/edge-node/node-local.ts +84 -0
  223. package/src/edge-node/options.ts +179 -0
  224. package/src/edge-node/owner-grant.ts +63 -0
  225. package/src/edge-node/platform.ts +151 -0
  226. package/src/edge-node/strand-move.ts +270 -0
  227. package/src/edge-node/strands.ts +72 -0
  228. package/src/enrolled-machine-store.ts +3 -3
  229. package/src/formation-token.ts +63 -0
  230. package/src/index.ts +899 -789
  231. package/src/joined-strand-store.ts +73 -28
  232. package/src/key-store.ts +10 -4
  233. package/src/link-budget.ts +9 -7
  234. package/src/machine-removal.ts +26 -111
  235. package/src/member-redemption.ts +649 -0
  236. package/src/membership-view.ts +144 -0
  237. package/src/owner-anchor-sync.ts +19 -18
  238. package/src/owner-grant-protocol.ts +547 -0
  239. package/src/owner-key-export.ts +348 -0
  240. package/src/owner-key-flows.ts +109 -0
  241. package/src/owner-key-store.ts +85 -0
  242. package/src/owner-keys.ts +178 -0
  243. package/src/owner-signer.ts +94 -0
  244. package/src/peer-addr-book.ts +4 -3
  245. package/src/peer-authorization.ts +558 -488
  246. package/src/peer-join-backfill.ts +971 -761
  247. package/src/pending-join-runner.ts +76 -53
  248. package/src/provider/rent-node.ts +1 -1
  249. package/src/seed-bootstrap.ts +343 -349
  250. package/src/seed-trust-policy.ts +4 -4
  251. package/src/strand-addr-protocol.ts +547 -538
  252. package/src/strand-cohort.ts +4 -0
  253. package/src/strand-database.ts +352 -319
  254. package/src/strand-first-sync-gate.ts +334 -133
  255. package/src/strand-formation-deadlines.ts +227 -227
  256. package/src/strand-formation-manager.ts +731 -725
  257. package/src/strand-formation-protocol.ts +122 -24
  258. package/src/strand-instance-manager.ts +2092 -2026
  259. package/src/strand-membership-reconciler.ts +33 -25
  260. package/src/strand-membership-writer.ts +2093 -1974
  261. package/src/strand-move.ts +50 -38
  262. package/src/strand-peer-redial.ts +2 -3
  263. package/src/strand-revocation-enforcer.ts +740 -654
  264. package/src/strand-solicitation.ts +61 -34
  265. package/src/strand-watcher.ts +175 -56
  266. package/src/table-change-watch.ts +285 -0
  267. package/src/timeout-scheduler.ts +29 -28
  268. package/src/types.ts +391 -152
@@ -0,0 +1,227 @@
1
+ /**
2
+ * `EdgeNode.joinCadre`'s sequence: redeem in place on a node already running in the credential's
3
+ * party, or stop the running node, start one in that party, redeem there and go back on any
4
+ * failure (docs/architecture.md → "An app joins an existing cadre"). The credential is a machine
5
+ * invitation, which admits the app as a machine, or an owner grant, which makes it a machine and
6
+ * an owner at once ({@link JoinCredential}). Runs over a narrow view of the runtime
7
+ * ({@link JoinRuntime}), inside one of its transitions.
8
+ */
9
+ import { exportStrandBundle } from '../strand-move.js';
10
+ import { isOwnerGrant } from './join-credential.js';
11
+ import { adoptMovesInto, deferredOutcome, joinMoveRecord, toError, } from './strand-move.js';
12
+ export async function joinCadre(runtime, credential, options, signal) {
13
+ signal.throwIfAborted();
14
+ const running = runtime.node;
15
+ if (running?.isRunning && runtime.runningOptions?.partyId === credential.partyId) {
16
+ return redeemInPlace(runtime, running, runtime.runningOptions, credential, options, signal);
17
+ }
18
+ return switchParty(runtime, credential, options, signal);
19
+ }
20
+ /** Redeem on the node already running in the credential's party; nothing to switch or roll back. */
21
+ async function redeemInPlace(runtime, node, running, credential, options, signal) {
22
+ const admission = await redeemOnRunningNode(runtime, node, credential, options, signal);
23
+ reportProgress(options, { state: 'admitted', asOwner: admission.asOwner });
24
+ const owner = await runtime.wireJoinedOwner(node, admission.asOwner);
25
+ // An owner grant redeemed on a node that joined as a member finishes the move that join deferred.
26
+ const strandMove = await settleJoinMove(runtime, node, running.formerPartyId, owner);
27
+ if (owner === 'enrolled')
28
+ runtime.setStatus({ state: 'running', node, owner });
29
+ return joinResult(admission, running.formerPartyId, strandMove);
30
+ }
31
+ /**
32
+ * A machine invitation is redeemed as on a fresh node (a device already admitted is answered as
33
+ * such). An owner grant is redeemed for the owner slot's key: the node is a member of the party,
34
+ * so `redeemOwnerGrant` writes the row in its own store and the key can be wired at once.
35
+ */
36
+ async function redeemOnRunningNode(runtime, node, credential, options, signal) {
37
+ if (!isOwnerGrant(credential))
38
+ return redeemInvitation(node, credential, options, signal);
39
+ // NOTE: a member running with no control connection to the party is refused `alone`
40
+ // (`OwnerKeyAddRefusedError`) by the grant's local path, where the invitation path waits for a
41
+ // member; if apps meet that, wait for a control connection before redeeming in place.
42
+ const redeemed = await node.redeemOwnerGrant(credential, await runtime.ownerSlotSigner(), redeemOptions(options, signal));
43
+ return { peerId: redeemed.memberPeerId, redeemedAt: new Date().toISOString(), asOwner: true };
44
+ }
45
+ /**
46
+ * Stop the running node, start one in the credential's party and redeem there; on any
47
+ * failure, go back to what ran before. Nothing is saved until a member has admitted this app.
48
+ * Leaving a party this app founded, its strands are recorded for the move first, so a refusal
49
+ * leaves that party running and unchanged.
50
+ */
51
+ async function switchParty(runtime, credential, options, signal) {
52
+ const restore = runtime.runningOptions;
53
+ const leaving = restore ?? (await runtime.readSavedStart())?.options ?? null;
54
+ const founded = foundedPartyLeft(credential.partyId, leaving);
55
+ // The party this app founded, carried through any later join.
56
+ // NOTE: leaving a joined party while the move from `formerPartyId` into it is still pending
57
+ // (the key never wired as an owner there) leaves that record naming the party left, adopted
58
+ // only if the app goes back; `pendingStrandMove` keeps reporting it. If apps need the move
59
+ // to follow the app, retarget the record to the credential's party here.
60
+ const formerPartyId = founded?.partyId ?? leaving?.formerPartyId;
61
+ const joined = {
62
+ partyId: credential.partyId,
63
+ // None needed: the redemption retains the member that admits this app as a dial
64
+ // target in the party's bootstrap-peer store, which every later start reads.
65
+ bootstrapAddrs: [],
66
+ relayAddrs: options.relayAddrs ?? leaving?.relayAddrs ?? [],
67
+ origin: 'joined',
68
+ };
69
+ const noiseCryptoMode = options.noiseCryptoMode ?? leaving?.noiseCryptoMode;
70
+ if (noiseCryptoMode !== undefined)
71
+ joined.noiseCryptoMode = noiseCryptoMode;
72
+ if (formerPartyId !== undefined)
73
+ joined.formerPartyId = formerPartyId;
74
+ const movingFrom = founded ? await recordStrandMove(runtime, founded, credential) : undefined;
75
+ try {
76
+ await runtime.shutDown();
77
+ return await startAndRedeem(runtime, joined, credential, options, signal, movingFrom);
78
+ }
79
+ catch (err) {
80
+ await returnToLeftParty(runtime, restore, movingFrom);
81
+ throw err;
82
+ }
83
+ }
84
+ /**
85
+ * Export the founded party's strands and keep them in the key store for the joined party to
86
+ * adopt; the party's id when there was anything to move. Throws `StrandsNeedOwnerJoinError`, or
87
+ * the export's or the write's error: switching without the record would lose the strands.
88
+ * An empty export removes the party's record instead: one left by an earlier join that never
89
+ * finished describes strands the party no longer holds, and adopting it would bring them back.
90
+ */
91
+ async function recordStrandMove(runtime, founded, credential) {
92
+ const running = runtime.node;
93
+ const bundle = running?.isRunning
94
+ ? await exportStrandBundle(running)
95
+ : await runtime.readAlone(founded, exportStrandBundle, 'export strands');
96
+ const record = joinMoveRecord(bundle, credential);
97
+ if (!record) {
98
+ await runtime.moves.remove(founded.partyId);
99
+ return undefined;
100
+ }
101
+ await runtime.moves.save(record);
102
+ return founded.partyId;
103
+ }
104
+ /**
105
+ * Admitted, the start is saved before the strands are adopted, so an app killed mid-adoption
106
+ * relaunches into the joined party and finishes the move there; `running` is reported after.
107
+ */
108
+ async function startAndRedeem(runtime, joined, credential, options, signal, movingFrom) {
109
+ runtime.setStatus({ state: 'starting' });
110
+ const { node, resolved } = await runtime.bringUp(joined);
111
+ runtime.setStatus({ state: 'joining', node });
112
+ const admission = await redeemOnJoinedNode(runtime, node, credential, options, signal);
113
+ reportProgress(options, { state: 'admitted', asOwner: admission.asOwner });
114
+ const owner = await runtime.wireJoinedOwner(node, admission.asOwner);
115
+ await runtime.saveRunningStart(resolved);
116
+ if (movingFrom !== undefined && owner === 'enrolled')
117
+ reportProgress(options, { state: 'moving-strands' });
118
+ const strandMove = await settleJoinMove(runtime, node, resolved.formerPartyId, owner);
119
+ runtime.reportRunning(node, resolved, owner);
120
+ return joinResult(admission, resolved.formerPartyId, strandMove);
121
+ }
122
+ /**
123
+ * Redeem on the node started in the credential's party: a machine invitation admits the app, an
124
+ * owner grant makes it a machine and an owner (`CadreNode.joinWithOwnerGrant`).
125
+ *
126
+ * The grant seats the owner slot's key, created now when the slot is empty: the app's one owner
127
+ * key, which may already own the party the app founded and is leaving (one key, several cadres).
128
+ * A fresh key would need a second owner slot and a second signer per node, which the kits do not
129
+ * have, and replacing the slot's key would lose the left party, which a rollback restarts.
130
+ * Created before the redemption, the key stays in the slot when the join fails, an owner of
131
+ * nothing until the next founding or grant uses it.
132
+ */
133
+ async function redeemOnJoinedNode(runtime, node, credential, options, signal) {
134
+ if (!isOwnerGrant(credential))
135
+ return redeemInvitation(node, credential, options, signal);
136
+ // NOTE: a rollback after the grant was redeemed but the self-invitation refused leaves the
137
+ // slot's key an owner of the credential's party while the app is back in the party it left;
138
+ // a later join there, with a machine invitation too, picks the ownership up through that key.
139
+ // NOTE: `joinWithOwnerGrant` already waits up to its own bound for the key's `OwnerKey` row
140
+ // and wires it; `wireJoinedOwner(node, true)` then asks again for up to the owner-step bound
141
+ // while it is still unwired, so a row that never arrives costs both waits before `timed-out`.
142
+ // If that shows up, carry `joined.ownerWired` in the admission and skip the second wait.
143
+ const joined = await node.joinWithOwnerGrant(credential, await runtime.ownerSlotSigner(), redeemOptions(options, signal));
144
+ return { peerId: joined.peerId, redeemedAt: joined.redeemedAt, asOwner: true };
145
+ }
146
+ async function redeemInvitation(node, invitation, options, signal) {
147
+ const redemption = await node.redeemCadreInvitation(invitation, redeemOptions(options, signal));
148
+ return { ...redemption, asOwner: false };
149
+ }
150
+ /**
151
+ * The move from `formerPartyId` as an admitted join reports it: adopted with every other move
152
+ * naming this party once the app's key is wired as its owner, deferred otherwise. Never throws:
153
+ * the app is admitted, and what is not adopted now is adopted by the next start.
154
+ */
155
+ async function settleJoinMove(runtime, node, formerPartyId, owner) {
156
+ try {
157
+ if (owner === 'enrolled') {
158
+ const outcomes = await adoptMovesInto(node, runtime.moves);
159
+ return formerPartyId === undefined ? undefined : outcomes.get(formerPartyId);
160
+ }
161
+ const record = formerPartyId === undefined ? undefined : await runtime.moves.load(formerPartyId);
162
+ return record?.targetPartyId === node.partyId ? { state: 'deferred', reason: 'owner-key-not-wired' } : undefined;
163
+ }
164
+ catch (err) {
165
+ console.warn('[cadre-core/edge-node] reading the strand move records failed; the next start retries:', err);
166
+ return formerPartyId === undefined ? undefined : deferredOutcome(err);
167
+ }
168
+ }
169
+ /**
170
+ * After a failed join: discard the move record it wrote, stop the joined party's node and start
171
+ * again what ran before, if anything did.
172
+ */
173
+ async function returnToLeftParty(runtime, restore, movingFrom) {
174
+ await discardStrandMove(runtime.moves, movingFrom);
175
+ try {
176
+ await runtime.shutDown();
177
+ if (restore)
178
+ await runtime.buildAndStart(restore);
179
+ }
180
+ catch (err) {
181
+ // The join's own error is the one its caller gets; this one is only logged.
182
+ console.warn('[cadre-core/edge-node] going back to the party a failed join left failed:', err);
183
+ if (runtime.status.state !== 'failed')
184
+ runtime.setStatus({ state: 'failed', error: toError(err) });
185
+ }
186
+ }
187
+ async function discardStrandMove(moves, fromPartyId) {
188
+ if (fromPartyId === undefined)
189
+ return;
190
+ try {
191
+ await moves.remove(fromPartyId);
192
+ }
193
+ catch (err) {
194
+ // Harmless when left: it names the party the join failed to reach, and the next join from
195
+ // this party replaces it.
196
+ console.warn(`[cadre-core/edge-node] could not discard the strand move record of party ${fromPartyId}:`, err);
197
+ }
198
+ }
199
+ /** Wait for a member: an app joining by invitation is often the only machine of the party up. The grant's options are the invitation's. */
200
+ function redeemOptions(options, signal) {
201
+ return { waitForMember: true, signal, ...(options.onProgress ? { onProgress: options.onProgress } : {}) };
202
+ }
203
+ /** Tell the join's listener; one that throws must not fail a join a member has already admitted. */
204
+ function reportProgress(options, progress) {
205
+ try {
206
+ options.onProgress?.(progress);
207
+ }
208
+ catch (err) {
209
+ console.warn('[cadre-core/edge-node] join progress listener threw:', err);
210
+ }
211
+ }
212
+ /**
213
+ * The start a join to `partyId` leaves, when it is of a party this app founded: the party whose
214
+ * strands move and which is kept as `formerPartyId` for clean-up. Null when the join leaves no
215
+ * start, a party this app joined, or the party it joins.
216
+ */
217
+ function foundedPartyLeft(partyId, leaving) {
218
+ return leaving && leaving.origin !== 'joined' && leaving.partyId !== partyId ? leaving : null;
219
+ }
220
+ function joinResult(admission, formerPartyId, strandMove) {
221
+ return {
222
+ ...admission,
223
+ ...(formerPartyId !== undefined && { formerPartyId }),
224
+ ...(strandMove !== undefined && { strandMove }),
225
+ };
226
+ }
227
+ //# sourceMappingURL=edge-join.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge-join.js","sourceRoot":"","sources":["../../src/edge-node/edge-join.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAIvD,OAAO,EAAE,YAAY,EAAuB,MAAM,sBAAsB,CAAC;AAEzE,OAAO,EACN,cAAc,EACd,eAAe,EACf,cAAc,EACd,OAAO,GAGP,MAAM,kBAAkB,CAAC;AAkC1B,MAAM,CAAC,KAAK,UAAU,SAAS,CAC9B,OAAoB,EACpB,UAA0B,EAC1B,OAAyB,EACzB,MAAmB;IAEnB,MAAM,CAAC,cAAc,EAAE,CAAC;IACxB,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAC7B,IAAI,OAAO,EAAE,SAAS,IAAI,OAAO,CAAC,cAAc,EAAE,OAAO,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC;QAClF,OAAO,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,cAAc,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC7F,CAAC;IACD,OAAO,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAC1D,CAAC;AAED,oGAAoG;AACpG,KAAK,UAAU,aAAa,CAC3B,OAAoB,EACpB,IAAe,EACf,OAAwB,EACxB,UAA0B,EAC1B,OAAyB,EACzB,MAAmB;IAEnB,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACxF,cAAc,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3E,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IACrE,kGAAkG;IAClG,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;IACrF,IAAI,KAAK,KAAK,UAAU;QAAE,OAAO,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/E,OAAO,UAAU,CAAC,SAAS,EAAE,OAAO,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;AACjE,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,mBAAmB,CACjC,OAAoB,EACpB,IAAe,EACf,UAA0B,EAC1B,OAAyB,EACzB,MAAmB;IAEnB,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC;QAAE,OAAO,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1F,oFAAoF;IACpF,+FAA+F;IAC/F,sFAAsF;IACtF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,EAAE,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1H,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,YAAY,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC/F,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,WAAW,CACzB,OAAoB,EACpB,UAA0B,EAC1B,OAAyB,EACzB,MAAmB;IAEnB,MAAM,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC;IACvC,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC,EAAE,OAAO,IAAI,IAAI,CAAC;IAC7E,MAAM,OAAO,GAAG,gBAAgB,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC9D,8DAA8D;IAC9D,4FAA4F;IAC5F,4FAA4F;IAC5F,2FAA2F;IAC3F,yEAAyE;IACzE,MAAM,aAAa,GAAG,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,aAAa,CAAC;IACjE,MAAM,MAAM,GAAoB;QAC/B,OAAO,EAAE,UAAU,CAAC,OAAO;QAC3B,gFAAgF;QAChF,6EAA6E;QAC7E,cAAc,EAAE,EAAE;QAClB,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,OAAO,EAAE,UAAU,IAAI,EAAE;QAC3D,MAAM,EAAE,QAAQ;KAChB,CAAC;IACF,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,OAAO,EAAE,eAAe,CAAC;IAC5E,IAAI,eAAe,KAAK,SAAS;QAAE,MAAM,CAAC,eAAe,GAAG,eAAe,CAAC;IAC5E,IAAI,aAAa,KAAK,SAAS;QAAE,MAAM,CAAC,aAAa,GAAG,aAAa,CAAC;IACtE,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9F,IAAI,CAAC;QACJ,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzB,OAAO,MAAM,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;IACvF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;QACtD,MAAM,GAAG,CAAC;IACX,CAAC;AACF,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,gBAAgB,CAAC,OAAoB,EAAE,OAAwB,EAAE,UAA0B;IACzG,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAC7B,MAAM,MAAM,GAAG,OAAO,EAAE,SAAS;QAChC,CAAC,CAAC,MAAM,kBAAkB,CAAC,OAAO,CAAC;QACnC,CAAC,CAAC,MAAM,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,CAAC,CAAC;IAC1E,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClD,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5C,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,OAAO,OAAO,CAAC,OAAO,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,cAAc,CAC5B,OAAoB,EACpB,MAAuB,EACvB,UAA0B,EAC1B,OAAyB,EACzB,MAAmB,EACnB,UAA8B;IAE9B,OAAO,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;IACzC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACzD,OAAO,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACvF,cAAc,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3E,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IACrE,MAAM,OAAO,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,UAAU,KAAK,SAAS,IAAI,KAAK,KAAK,UAAU;QAAE,cAAc,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAC3G,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;IACtF,OAAO,CAAC,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC7C,OAAO,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;;;;GAUG;AACH,KAAK,UAAU,kBAAkB,CAChC,OAAoB,EACpB,IAAe,EACf,UAA0B,EAC1B,OAAyB,EACzB,MAAmB;IAEnB,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC;QAAE,OAAO,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1F,2FAA2F;IAC3F,4FAA4F;IAC5F,8FAA8F;IAC9F,4FAA4F;IAC5F,6FAA6F;IAC7F,8FAA8F;IAC9F,yFAAyF;IACzF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,EAAE,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1H,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAChF,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,IAAe,EAAE,UAA2B,EAAE,OAAyB,EAAE,MAAmB;IAC3H,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,UAAU,EAAE,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAChG,OAAO,EAAE,GAAG,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC1C,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,cAAc,CAC5B,OAAoB,EACpB,IAAe,EACf,aAAiC,EACjC,KAAyB;IAEzB,IAAI,CAAC;QACJ,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;YAC1B,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;YAC3D,OAAO,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QAC9E,CAAC;QACD,MAAM,MAAM,GAAG,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACjG,OAAO,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAClH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,IAAI,CAAC,wFAAwF,EAAE,GAAG,CAAC,CAAC;QAC5G,OAAO,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IACvE,CAAC;AACF,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,iBAAiB,CAAC,OAAoB,EAAE,OAA+B,EAAE,UAA8B;IACrH,MAAM,iBAAiB,CAAC,OAAO,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IACnD,IAAI,CAAC;QACJ,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzB,IAAI,OAAO;YAAE,MAAM,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,4EAA4E;QAC5E,OAAO,CAAC,IAAI,CAAC,2EAA2E,EAAE,GAAG,CAAC,CAAC;QAC/F,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,QAAQ;YAAE,OAAO,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACpG,CAAC;AACF,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,KAAwB,EAAE,WAA+B;IACzF,IAAI,WAAW,KAAK,SAAS;QAAE,OAAO;IACtC,IAAI,CAAC;QACJ,MAAM,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,0FAA0F;QAC1F,0BAA0B;QAC1B,OAAO,CAAC,IAAI,CAAC,4EAA4E,WAAW,GAAG,EAAE,GAAG,CAAC,CAAC;IAC/G,CAAC;AACF,CAAC;AAED,2IAA2I;AAC3I,SAAS,aAAa,CAAC,OAAyB,EAAE,MAAmB;IACpE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC3G,CAAC;AAED,oGAAoG;AACpG,SAAS,cAAc,CAAC,OAAyB,EAAE,QAA0B;IAC5E,IAAI,CAAC;QACJ,OAAO,CAAC,UAAU,EAAE,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,IAAI,CAAC,sDAAsD,EAAE,GAAG,CAAC,CAAC;IAC3E,CAAC;AACF,CAAC;AAED;;;;GAIG;AACH,SAAS,gBAAgB,CAAC,OAAe,EAAE,OAA+B;IACzE,OAAO,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/F,CAAC;AAED,SAAS,UAAU,CAClB,SAAoB,EACpB,aAAiC,EACjC,UAAyC;IAEzC,OAAO;QACN,GAAG,SAAS;QACZ,GAAG,CAAC,aAAa,KAAK,SAAS,IAAI,EAAE,aAAa,EAAE,CAAC;QACrD,GAAG,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,CAAC;KAC/C,CAAC;AACH,CAAC"}
@@ -0,0 +1,295 @@
1
+ /**
2
+ * `createEdgeNode` — build, start, stop and rebuild an edge device's `CadreNode` the prescribed
3
+ * way: identity and owner key in the app's key store, the four node-local stores durable and
4
+ * party-scoped, raw storage per scope from the app's storage, owner genesis, the last start
5
+ * remembered for unattended starts, and the switch to another party's cadre by machine
6
+ * invitation or owner grant.
7
+ *
8
+ * Nothing platform-specific is imported here. The app passes in its key store, its storage and
9
+ * its transports (`EdgeNodePlatform`), so a Node test runs this code over fakes, and the React
10
+ * Native kit's `createPhoneNode` is this over the secure store and rn-leveldb.
11
+ */
12
+ import { CadreNode } from '../cadre-node.js';
13
+ import type { ExportedOwnerKey, ImportedOwnerKey, OwnerKeyExportOptions } from '../owner-key-flows.js';
14
+ import { type StrandMoveReport } from '../strand-move.js';
15
+ import type { DurableSlot } from '../node-local-snapshot.js';
16
+ import type { CadreJoinProgress, CadreNodeEvents, CadreOwnerKey, CadreRemoval, CreateOwnerGrantOptions, CreateOwnerGrantResult, OwnerGrantStatus, OwnerKeyRemovalPlan, OwnerKeyRemovalResult, RedeemCadreInvitationResult, RemoveOwnerOptions } from '../types.js';
17
+ import { type OwnerGenesisResult } from './config.js';
18
+ import { type DissolveFormerPartyOptions } from './dissolve.js';
19
+ import type { JoinCredential } from './join-credential.js';
20
+ import { type KvStoreApi } from './node-local.js';
21
+ import { type EdgeNodeOptions, type NoiseCryptoMode, type SavedStart } from './options.js';
22
+ import { type RedeemedOwnerGrant } from './owner-grant.js';
23
+ import { type EdgeNodePlatform } from './platform.js';
24
+ import { type PendingStrandMove, type StrandMoveOutcome } from './strand-move.js';
25
+ export type EdgeNodeStatus = {
26
+ state: 'stopped';
27
+ } | {
28
+ state: 'starting';
29
+ }
30
+ /** Started in the party an invitation names and redeeming it there; not admitted yet, so `EdgeNode.node` is null. */
31
+ | {
32
+ state: 'joining';
33
+ node: CadreNode;
34
+ } | {
35
+ state: 'running';
36
+ node: CadreNode;
37
+ owner: OwnerGenesisResult;
38
+ }
39
+ /**
40
+ * An owner of `partyId` removed this app from it: the node is stopped and the party's local
41
+ * storage deleted. `formerPartyId` is the party this app founded and left, which the saved start
42
+ * now names with `autoStart: false`; absent when there is none. `start` and `joinCadre` work as
43
+ * from `stopped`.
44
+ */
45
+ | {
46
+ state: 'removed';
47
+ partyId: string;
48
+ removal: CadreRemoval;
49
+ formerPartyId?: string;
50
+ } | {
51
+ state: 'failed';
52
+ error: Error;
53
+ };
54
+ /** A join's progress: the redemption's passes and pauses, then the steps after a member admitted the app. */
55
+ export type EdgeJoinProgress = CadreJoinProgress
56
+ /**
57
+ * A member admitted the app; the edge node is wiring its owner key and saving the start.
58
+ * `asOwner`: the app joined with an owner grant, so its owner slot's key is an owner of the party.
59
+ */
60
+ | {
61
+ state: 'admitted';
62
+ asOwner: boolean;
63
+ }
64
+ /** The app's key is wired as an owner and the strands this join recorded are being adopted. */
65
+ | {
66
+ state: 'moving-strands';
67
+ };
68
+ /** How {@link EdgeNode.joinCadre} runs. */
69
+ export interface JoinCadreOptions {
70
+ /** Relays the joined node reserves on. Default: the running start's, else the saved one's. */
71
+ relayAddrs?: string[];
72
+ /** Default: the running start's, else the saved one's, else the platform default. */
73
+ noiseCryptoMode?: NoiseCryptoMode;
74
+ /** Cancels the join: the app goes back to the party it left, and the call throws the signal's reason. */
75
+ signal?: AbortSignal;
76
+ /**
77
+ * Each redemption pass and each pause while no member is ready, for a "waiting for an always-on
78
+ * machine" display; then `admitted`, and `moving-strands` when this join's strands are adopted.
79
+ * A listener that throws is logged and ignored.
80
+ */
81
+ onProgress?: (progress: EdgeJoinProgress) => void;
82
+ }
83
+ /** What an admitted {@link EdgeNode.joinCadre} returns. */
84
+ export interface JoinCadreResult extends RedeemCadreInvitationResult {
85
+ /** The app joined with an owner grant: its owner slot's key is an owner of the party (`status.owner` says whether it is wired). */
86
+ asOwner: boolean;
87
+ /** The party this app founded and has left, when it founded one; saved as `EdgeNodeOptions.formerPartyId`. */
88
+ formerPartyId?: string;
89
+ /** How moving that party's strands into this one settled; absent when there was nothing to move. */
90
+ strandMove?: StrandMoveOutcome;
91
+ }
92
+ /** A removal a dissolve marker names that the last start tried to finish and could not: {@link EdgeNode.unfinishedRemoval}. */
93
+ export interface UnfinishedRemoval {
94
+ partyId: string;
95
+ error: Error;
96
+ }
97
+ export interface EdgeNode {
98
+ readonly status: EdgeNodeStatus;
99
+ /** The running node, or null; null as soon as a stop or restart begins tearing it down, and while joining. */
100
+ readonly node: CadreNode | null;
101
+ /** The options the running node was started with, the Noise mode resolved; null unless running. */
102
+ readonly options: EdgeNodeOptions | null;
103
+ /**
104
+ * The removal the dissolve marker names that the last start tried to finish and could not
105
+ * (logged there too), for an app to show; null when the last start found no marker or finished
106
+ * it, or a dissolve or removal since finished it. The next start retries.
107
+ */
108
+ readonly unfinishedRemoval: UnfinishedRemoval | null;
109
+ /**
110
+ * This app's peer id, the same in every party: the node's while one is running or joining,
111
+ * answered at once; otherwise read from the key store, created there when absent, in turn
112
+ * with start, stop and join so a first start cannot generate a different key. A read the
113
+ * key store refuses throws.
114
+ */
115
+ identityPeerId(): Promise<string>;
116
+ /**
117
+ * Start, or join the start in flight; a running node is returned as is, whatever its options.
118
+ * A start called during a stop or restart runs after it. Opens the four node-local stores
119
+ * party-scoped, builds and starts the node, runs owner genesis (fail-soft, bounded), then saves
120
+ * the options with `autoStart: true`. A failed start closes everything it opened and leaves
121
+ * `status` `failed`; the next call starts afresh.
122
+ */
123
+ start(options: EdgeNodeOptions): Promise<CadreNode>;
124
+ /**
125
+ * The user's "disconnect": waits for a start in flight, saves `autoStart: false` so nothing
126
+ * starts the node unattended, stops it, and closes every store it opened — control, each
127
+ * strand, node-local — through `EdgeNodeStorage.close`. A platform that locks a store per
128
+ * name (rn-leveldb) would otherwise fail the next start on one left open.
129
+ */
130
+ stop(): Promise<void>;
131
+ /**
132
+ * Rebuild the node with new options (relays, Noise mode): libp2p reads them only when the node
133
+ * is built. `autoStart` is not cleared on the way, so a crash mid-restart still starts again.
134
+ */
135
+ restart(options: EdgeNodeOptions): Promise<CadreNode>;
136
+ /**
137
+ * Join the cadre `credential` names: this app's one node moves to that party, keeping its
138
+ * identity, so the peer id an owner targeted is the one that redeems. A machine invitation
139
+ * admits the app as a machine; an owner grant makes it a machine and an owner at once
140
+ * (`CadreNode.joinWithOwnerGrant`), seating the owner slot's key, created when the slot is
141
+ * empty, which is how an app brings the strands of the party it founded along. Runs in turn
142
+ * with start, stop and restart; a start called meanwhile runs after it, and a `stop` cancels it.
143
+ *
144
+ * Already running in that party: redeems on the running node (a device already admitted is
145
+ * answered as such; an owner grant is redeemed for the slot's key, `CadreNode.redeemOwnerGrant`)
146
+ * and wires its owner key. Otherwise: stops the running node, starts one for the credential's
147
+ * party with `origin: 'joined'` (status `joining`), and redeems there, waiting for a member to
148
+ * answer until one accepts, one refuses, the credential expires (`invite-spent`,
149
+ * `grant-spent`) or `options.signal` aborts. Admitted: wires the owner key when the party
150
+ * lists the slot's key (after an owner grant, asking again until its row arrives or the
151
+ * owner-step bound passes), saves the start (`autoStart: true`) and reports `running`.
152
+ * Anything else: stops that node, starts again what was running before (a node that was
153
+ * stopped stays stopped), and throws the join's error; a failure restarting is logged and
154
+ * leaves status `failed`.
155
+ *
156
+ * The start is saved only once admitted, so an app killed while waiting relaunches into the
157
+ * party it left. The left party's data stays on disk under its own names.
158
+ *
159
+ * Leaving a party this app founded moves its strands: they are exported before anything stops
160
+ * (from a node started for the export alone when none runs) and kept in the key store, and
161
+ * adopted once the app's key is wired as an owner of the joined party, before the join reports
162
+ * `running`; otherwise a later start adopts them (`result.strandMove` says which). Refused
163
+ * before anything stops, with `StrandsNeedOwnerJoinError`, when there is something to move and
164
+ * the credential is a machine invitation; and with the export's error when it fails.
165
+ */
166
+ joinCadre(credential: JoinCredential, options?: JoinCadreOptions): Promise<JoinCadreResult>;
167
+ /**
168
+ * The move from `formerPartyId` (the running start's, else the saved one's) still waiting to be
169
+ * adopted, or `undefined` when there is none. A read the key store refuses throws.
170
+ */
171
+ pendingStrandMove(): Promise<PendingStrandMove | undefined>;
172
+ /**
173
+ * Adopt the waiting move on the running node now, in turn with start, stop and join; `undefined`
174
+ * when no move from `formerPartyId` names the running party. On a joined party with no owner
175
+ * signer wired, first asks the party once more whether it lists the owner slot's key, so an
176
+ * owner grant whose row arrived after the join stopped waiting is picked up here,
177
+ * `status.owner` becoming `enrolled`. Throws what the adoption throws,
178
+ * `StrandMoveNotOwnerError` while the app's key is no owner there.
179
+ */
180
+ finishStrandMove(): Promise<StrandMoveReport | undefined>;
181
+ /**
182
+ * Delete the local storage of `formerPartyId`, the party this app founded and left, once the
183
+ * running party holds everything it holds and no other machine is in it. Runs in turn with
184
+ * start, stop, restart and join. Stops the running node, reads the former party from a node
185
+ * started for that alone, and starts the running party again, without `formerPartyId` once the
186
+ * former party is removed; a failure of that last start is the call's error.
187
+ *
188
+ * Refused before anything stops: with an `Error` unless running with a `formerPartyId` other
189
+ * than the running party, and with `StrandMovePendingError` while a move from it waits. Refused
190
+ * after the read, the running party started again and nothing deleted: with
191
+ * `FormerPartyNotEmptyError`, whose strand and pending-join ids `options.discard` may give up.
192
+ */
193
+ dissolveFormerParty(options?: DissolveFormerPartyOptions): Promise<void>;
194
+ /**
195
+ * The last start, or `undefined` when none is saved or the record is unusable. A read fault
196
+ * throws. Called during a stop, it answers after the stop has saved `autoStart: false`; during
197
+ * the edge node's response to a removal, after that has replaced the saved start.
198
+ */
199
+ loadSavedStart(): Promise<SavedStart | undefined>;
200
+ /**
201
+ * The slot `LinkRequestStore` (`@serfab/cadre-rn/app-link`) keeps the open link join request in,
202
+ * beside the saved start in the node-local store. Works with no node running, as
203
+ * `loadSavedStart` does; a read fault throws.
204
+ */
205
+ linkRequestSlot(): DurableSlot;
206
+ /**
207
+ * The slot `AppRecommendationStore` (`@serfab/cadre-rn/app-link`) keeps the apps the user allowed
208
+ * to join before they asked in, beside the saved start. Works with no node running, as
209
+ * `linkRequestSlot` does; a read fault throws.
210
+ */
211
+ recommendationSlot(): DurableSlot;
212
+ /**
213
+ * The node-local store as a key-value store, for records the app keeps beside the saved start
214
+ * under keys of its own: `rentalStore` (`@serfab/cadre-rn/provider`) files the provider rentals
215
+ * here, under `rentalsKvKey(partyId)`. Works with no node running, as `linkRequestSlot` does; a
216
+ * read fault throws. Keys of the form `<record>.<partyId>` are the edge node's; an app's own
217
+ * records take a dot-free key.
218
+ */
219
+ nodeLocalRecords(): KvStoreApi;
220
+ /**
221
+ * Subscribe to a node event for the life of this edge node, not of one `CadreNode`: the
222
+ * handler is re-applied to every node a start or restart builds, before it starts. Register the
223
+ * `strand:discovered` handler that attaches strands here, before `start` — strands joined from
224
+ * another party come back through it after every start. Returns an unsubscribe.
225
+ */
226
+ on<K extends keyof CadreNodeEvents>(event: K, handler: (data: CadreNodeEvents[K]) => void): () => void;
227
+ onStatus(listener: (status: EdgeNodeStatus) => void): () => void;
228
+ /**
229
+ * The public key (base64url) of the owner key the running node signs with, for pairing and
230
+ * approval prompts: the owner slot's key. Null unless running with it wired. Never private
231
+ * material.
232
+ */
233
+ ownerPublicKey(): string | null;
234
+ /**
235
+ * The cadre's live owner keys (cadre-core's `CadreNode.listOwnerKeys`): label, voucher, whether
236
+ * this app holds it and whether this device trusts it. In turn with start, stop and join, as are
237
+ * the calls below; this one and every other that names the node throws while none is running.
238
+ */
239
+ listOwnerKeys(): Promise<CadreOwnerKey[]>;
240
+ /**
241
+ * This app's own owner key, from the key store's owner slot, which is the key the node signs
242
+ * with, as export text, with the key it holds (`publicKey`). Works with no node running. Throws
243
+ * when the slot is empty: a member holds no owner key of its own.
244
+ */
245
+ exportOwnerKey(options?: OwnerKeyExportOptions): Promise<ExportedOwnerKey>;
246
+ /**
247
+ * Import owner-key text into the owner slot, unlocking it with `passphrase` when it is locked.
248
+ * With a node running, the key is wired when the party lists it as a live owner (`wired`, and
249
+ * `status.owner` becomes `enrolled`); stored otherwise, and wired by a later start once an owner
250
+ * adds it. Works with no node running. Refused with `OwnerKeySlotOccupiedError` when the slot
251
+ * holds a different key (one owner key per app), and with `OwnerKeyExportError` for text that
252
+ * is not an owner key, a locked one with no passphrase (`passphrase-required`) or the wrong
253
+ * passphrase. A strand move waiting on an owner key is adopted by the next start or
254
+ * `finishStrandMove()`.
255
+ */
256
+ importOwnerKey(text: string, passphrase?: string): Promise<ImportedOwnerKey>;
257
+ /** What `removeOwner(key)` would do, for the confirmation step (cadre-core's `CadreNode.planOwnerKeyRemoval`). */
258
+ planOwnerKeyRemoval(key: string): Promise<OwnerKeyRemovalPlan>;
259
+ /** Remove an owner key, re-vouching first the dependents `options.revouch` keeps (cadre-core's `CadreNode.removeOwner`). */
260
+ removeOwner(key: string, options?: RemoveOwnerOptions): Promise<OwnerKeyRemovalResult>;
261
+ /**
262
+ * Mint an owner grant (cadre-core's `CadreNode.createOwnerGrant`): whoever redeems the
263
+ * returned text first becomes an owner of the cadre. 15 minutes and one use by default;
264
+ * `expiresInMs: null` is a backup grant, with no expiry.
265
+ */
266
+ createOwnerGrant(options?: CreateOwnerGrantOptions): Promise<CreateOwnerGrantResult>;
267
+ /** Every owner grant the running node holds, with its standing (cadre-core's `CadreNode.listOwnerGrants`). */
268
+ listOwnerGrants(): Promise<OwnerGrantStatus[]>;
269
+ /** Withdraw an owner grant by its key (cadre-core's `CadreNode.withdrawOwnerGrant`); false when it is not held here or was already withdrawn. */
270
+ withdrawOwnerGrant(key: string): Promise<boolean>;
271
+ /**
272
+ * Become an owner of the running party with owner-grant text: the owner slot's key, created
273
+ * when the slot is empty, is seated (cadre-core's `CadreNode.redeemOwnerGrant`) and wired, and
274
+ * `status.owner` becomes `enrolled`. A slot key that is already a live owner of the party is
275
+ * answered `alreadyOwner` before anything is signed or written, connected or not. A member
276
+ * machine writes the row in its own store and wires the key at once; any other node redeems
277
+ * at the grant's members and asks for the row until the owner-step bound passes (`wired:
278
+ * false` then, and a later start wires it). A strand move waiting on an owner key is adopted
279
+ * by the next start or `finishStrandMove()`.
280
+ *
281
+ * Refused with the decoder's error for text that is not an owner grant (an owner key, an
282
+ * invitation and a join request are named), with `OwnerGrantRejectedError` `party-mismatch`
283
+ * for a grant of another party, and with what `CadreNode.redeemOwnerGrant` throws:
284
+ * `OwnerKeyAddRefusedError` `alone` on a member with no connection to the party's other
285
+ * machines, `OwnerGrantRejectedError` (`grant-spent` for an expired, withdrawn or used grant),
286
+ * `OwnerGrantIssuerUnknownError`, `OwnerGrantUnreachableError`.
287
+ */
288
+ redeemOwnerGrant(text: string): Promise<RedeemedOwnerGrant>;
289
+ }
290
+ /**
291
+ * Create the app's edge node. Call once per app. Throws when an edge node over the same storage
292
+ * (`platform.storage.id`) is starting or running in this process; a stopped or failed one is
293
+ * replaced (a development reload re-runs the module that calls this).
294
+ */
295
+ export declare function createEdgeNode(platform: EdgeNodePlatform): EdgeNode;