@serfab/cadre-core 1.15.0 → 1.17.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 (199) hide show
  1. package/README.md +1 -1
  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 +55 -138
  6. package/dist/cadre-invite-protocol.js +94 -449
  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 +530 -247
  12. package/dist/cadre-node.js +1046 -525
  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 +22 -23
  18. package/dist/control-cohort.js +24 -24
  19. package/dist/control-cohort.js.map +1 -1
  20. package/dist/control-database.d.ts +416 -136
  21. package/dist/control-database.js +757 -378
  22. package/dist/control-database.js.map +1 -1
  23. package/dist/control-protocol-guard.d.ts +5 -4
  24. package/dist/control-protocol-guard.js +6 -3
  25. package/dist/control-protocol-guard.js.map +1 -1
  26. package/dist/control-schema-version.d.ts +80 -0
  27. package/dist/control-schema-version.js +203 -0
  28. package/dist/control-schema-version.js.map +1 -0
  29. package/dist/control-schema.d.ts +45 -5
  30. package/dist/control-schema.js +254 -70
  31. package/dist/control-schema.js.map +1 -1
  32. package/dist/control-write-retry.d.ts +5 -5
  33. package/dist/control-write-retry.js +10 -8
  34. package/dist/control-write-retry.js.map +1 -1
  35. package/dist/delegate-admission.d.ts +13 -0
  36. package/dist/delegate-admission.js +27 -0
  37. package/dist/delegate-admission.js.map +1 -1
  38. package/dist/ed25519-key.d.ts +10 -7
  39. package/dist/ed25519-key.js +23 -20
  40. package/dist/ed25519-key.js.map +1 -1
  41. package/dist/edge-node/config.d.ts +122 -0
  42. package/dist/edge-node/config.js +199 -0
  43. package/dist/edge-node/config.js.map +1 -0
  44. package/dist/edge-node/dissolve.d.ts +95 -0
  45. package/dist/edge-node/dissolve.js +184 -0
  46. package/dist/edge-node/dissolve.js.map +1 -0
  47. package/dist/edge-node/edge-join.d.ts +44 -0
  48. package/dist/edge-node/edge-join.js +227 -0
  49. package/dist/edge-node/edge-join.js.map +1 -0
  50. package/dist/edge-node/edge-node.d.ts +295 -0
  51. package/dist/edge-node/edge-node.js +704 -0
  52. package/dist/edge-node/edge-node.js.map +1 -0
  53. package/dist/edge-node/index.d.ts +18 -0
  54. package/dist/edge-node/index.js +18 -0
  55. package/dist/edge-node/index.js.map +1 -0
  56. package/dist/edge-node/join-credential.d.ts +11 -0
  57. package/dist/edge-node/join-credential.js +11 -0
  58. package/dist/edge-node/join-credential.js.map +1 -0
  59. package/dist/edge-node/lazy-raw-storage.d.ts +40 -0
  60. package/dist/edge-node/lazy-raw-storage.js +134 -0
  61. package/dist/edge-node/lazy-raw-storage.js.map +1 -0
  62. package/dist/edge-node/node-local.d.ts +60 -0
  63. package/dist/edge-node/node-local.js +58 -0
  64. package/dist/edge-node/node-local.js.map +1 -0
  65. package/dist/edge-node/options.d.ts +83 -0
  66. package/dist/edge-node/options.js +110 -0
  67. package/dist/edge-node/options.js.map +1 -0
  68. package/dist/edge-node/owner-grant.d.ts +41 -0
  69. package/dist/edge-node/owner-grant.js +38 -0
  70. package/dist/edge-node/owner-grant.js.map +1 -0
  71. package/dist/edge-node/platform.d.ts +140 -0
  72. package/dist/edge-node/platform.js +12 -0
  73. package/dist/edge-node/platform.js.map +1 -0
  74. package/dist/edge-node/strand-move.d.ts +115 -0
  75. package/dist/edge-node/strand-move.js +223 -0
  76. package/dist/edge-node/strand-move.js.map +1 -0
  77. package/dist/edge-node/strands.d.ts +28 -0
  78. package/dist/edge-node/strands.js +60 -0
  79. package/dist/edge-node/strands.js.map +1 -0
  80. package/dist/index.d.ts +16 -7
  81. package/dist/index.js +26 -7
  82. package/dist/index.js.map +1 -1
  83. package/dist/joined-strand-store.d.ts +3 -3
  84. package/dist/joined-strand-store.js +3 -3
  85. package/dist/joined-strand-store.js.map +1 -1
  86. package/dist/key-store.d.ts +9 -4
  87. package/dist/key-store.js +9 -4
  88. package/dist/key-store.js.map +1 -1
  89. package/dist/machine-removal.d.ts +19 -39
  90. package/dist/machine-removal.js +24 -98
  91. package/dist/machine-removal.js.map +1 -1
  92. package/dist/member-redemption.d.ts +250 -0
  93. package/dist/member-redemption.js +485 -0
  94. package/dist/member-redemption.js.map +1 -0
  95. package/dist/owner-anchor-sync.d.ts +4 -4
  96. package/dist/owner-anchor-sync.js +16 -15
  97. package/dist/owner-anchor-sync.js.map +1 -1
  98. package/dist/owner-grant-protocol.d.ts +247 -0
  99. package/dist/owner-grant-protocol.js +330 -0
  100. package/dist/owner-grant-protocol.js.map +1 -0
  101. package/dist/owner-key-export.d.ts +70 -0
  102. package/dist/owner-key-export.js +279 -0
  103. package/dist/owner-key-export.js.map +1 -0
  104. package/dist/owner-key-flows.d.ts +55 -0
  105. package/dist/owner-key-flows.js +60 -0
  106. package/dist/owner-key-flows.js.map +1 -0
  107. package/dist/owner-key-store.d.ts +31 -0
  108. package/dist/owner-key-store.js +81 -0
  109. package/dist/owner-key-store.js.map +1 -0
  110. package/dist/owner-keys.d.ts +78 -0
  111. package/dist/owner-keys.js +133 -0
  112. package/dist/owner-keys.js.map +1 -0
  113. package/dist/owner-signer.d.ts +54 -0
  114. package/dist/owner-signer.js +51 -0
  115. package/dist/owner-signer.js.map +1 -0
  116. package/dist/peer-authorization.d.ts +83 -18
  117. package/dist/peer-authorization.js +123 -53
  118. package/dist/peer-authorization.js.map +1 -1
  119. package/dist/provider/rent-node.js +1 -1
  120. package/dist/provider/rent-node.js.map +1 -1
  121. package/dist/seed-bootstrap.d.ts +131 -109
  122. package/dist/seed-bootstrap.js +260 -248
  123. package/dist/seed-bootstrap.js.map +1 -1
  124. package/dist/seed-trust-policy.js +4 -4
  125. package/dist/strand-addr-protocol.d.ts +4 -1
  126. package/dist/strand-addr-protocol.js +8 -2
  127. package/dist/strand-addr-protocol.js.map +1 -1
  128. package/dist/strand-cohort.d.ts +3 -0
  129. package/dist/strand-cohort.js.map +1 -1
  130. package/dist/strand-database.d.ts +1 -0
  131. package/dist/strand-database.js +3 -0
  132. package/dist/strand-database.js.map +1 -1
  133. package/dist/strand-first-sync-gate.d.ts +4 -3
  134. package/dist/strand-first-sync-gate.js +4 -3
  135. package/dist/strand-first-sync-gate.js.map +1 -1
  136. package/dist/strand-instance-manager.d.ts +1 -1
  137. package/dist/strand-membership-writer.d.ts +13 -2
  138. package/dist/strand-membership-writer.js +52 -16
  139. package/dist/strand-membership-writer.js.map +1 -1
  140. package/dist/strand-move.d.ts +6 -5
  141. package/dist/strand-move.js +31 -30
  142. package/dist/strand-move.js.map +1 -1
  143. package/dist/strand-peer-redial.js +2 -3
  144. package/dist/strand-peer-redial.js.map +1 -1
  145. package/dist/types.d.ts +286 -103
  146. package/dist/types.js.map +1 -1
  147. package/package.json +16 -7
  148. package/src/bootstrap-peer-store-file.ts +44 -44
  149. package/src/bootstrap-peer-store.ts +214 -214
  150. package/src/cadre-invite-protocol.ts +131 -531
  151. package/src/cadre-join-request.ts +188 -167
  152. package/src/cadre-node.ts +1166 -541
  153. package/src/control-authorization.ts +47 -14
  154. package/src/control-cohort.ts +127 -128
  155. package/src/control-database.ts +940 -456
  156. package/src/control-protocol-guard.ts +7 -4
  157. package/src/control-schema-version.ts +251 -0
  158. package/src/control-schema.ts +265 -70
  159. package/src/control-write-retry.ts +569 -567
  160. package/src/delegate-admission.ts +37 -0
  161. package/src/ed25519-key.ts +174 -171
  162. package/src/edge-node/config.ts +277 -0
  163. package/src/edge-node/dissolve.ts +251 -0
  164. package/src/edge-node/edge-join.ts +319 -0
  165. package/src/edge-node/edge-node.ts +1045 -0
  166. package/src/edge-node/index.ts +59 -0
  167. package/src/edge-node/join-credential.ts +16 -0
  168. package/src/edge-node/lazy-raw-storage.ts +178 -0
  169. package/src/edge-node/node-local.ts +84 -0
  170. package/src/edge-node/options.ts +179 -0
  171. package/src/edge-node/owner-grant.ts +63 -0
  172. package/src/edge-node/platform.ts +151 -0
  173. package/src/edge-node/strand-move.ts +270 -0
  174. package/src/edge-node/strands.ts +72 -0
  175. package/src/index.ts +102 -5
  176. package/src/joined-strand-store.ts +4 -4
  177. package/src/key-store.ts +10 -4
  178. package/src/machine-removal.ts +26 -111
  179. package/src/member-redemption.ts +642 -0
  180. package/src/owner-anchor-sync.ts +19 -18
  181. package/src/owner-grant-protocol.ts +532 -0
  182. package/src/owner-key-export.ts +348 -0
  183. package/src/owner-key-flows.ts +109 -0
  184. package/src/owner-key-store.ts +85 -0
  185. package/src/owner-keys.ts +178 -0
  186. package/src/owner-signer.ts +94 -0
  187. package/src/peer-authorization.ts +558 -488
  188. package/src/provider/rent-node.ts +1 -1
  189. package/src/seed-bootstrap.ts +301 -312
  190. package/src/seed-trust-policy.ts +4 -4
  191. package/src/strand-addr-protocol.ts +547 -538
  192. package/src/strand-cohort.ts +4 -0
  193. package/src/strand-database.ts +3 -0
  194. package/src/strand-first-sync-gate.ts +4 -3
  195. package/src/strand-instance-manager.ts +1 -1
  196. package/src/strand-membership-writer.ts +61 -16
  197. package/src/strand-move.ts +45 -36
  198. package/src/strand-peer-redial.ts +2 -3
  199. package/src/types.ts +304 -103
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Dissolving the party this app founded and left (docs/architecture.md → "An app joins an
3
+ * existing cadre" → "Dissolving the party it left"): the check that the running party holds
4
+ * everything the left one holds, and the removal of the left party's local storage. The same
5
+ * removal deletes a party whose owner removed this app ("Removed from A"), with no check.
6
+ *
7
+ * The removal is local: it writes nothing to either party's control database. A marker in the
8
+ * node-local database names the party from before the first deletion until after the saved
9
+ * start stops naming it, so a removal that a crash or a failed step interrupts is finished by
10
+ * the next start or the next dissolve without checking again: the decision to remove was made
11
+ * before the marker was written. Every step deletes what may already be gone.
12
+ */
13
+ import { controlStorageScope } from '../storage-scope.js';
14
+ import { exportStrandBundle, unheldBundleContent } from '../strand-move.js';
15
+ import { KeyStoreJoinedStrandStore } from '../joined-strand-store.js';
16
+ import { bootstrapPeersKvKey, enrolledMachinesKvKey, strandNetworkKvKey } from './node-local.js';
17
+ import { parseSavedStart, serializeSavedStart } from './options.js';
18
+ import { toError } from './strand-move.js';
19
+ /**
20
+ * `EdgeNode.dissolveFormerParty` refused, nothing stopped: moving the former party's strands has
21
+ * not finished. A move into the running party finishes with `finishStrandMove()`; one into
22
+ * another party is adopted only there.
23
+ */
24
+ export class StrandMovePendingError extends Error {
25
+ constructor(pending) {
26
+ super(`Cannot dissolve party ${pending.fromPartyId}: moving its strands into party ${pending.toPartyId} has not finished`);
27
+ this.pending = pending;
28
+ this.name = 'StrandMovePendingError';
29
+ }
30
+ }
31
+ /** `EdgeNode.dissolveFormerParty` refused, nothing deleted: the former party holds what the running party lacks, or has other machines. */
32
+ export class FormerPartyNotEmptyError extends Error {
33
+ constructor(partyId, remains) {
34
+ super(`Cannot dissolve party ${partyId}: ${describeRemains(remains)}`);
35
+ this.name = 'FormerPartyNotEmptyError';
36
+ this.partyId = partyId;
37
+ this.otherMachines = remains.otherMachines;
38
+ this.strandIds = remains.strandIds;
39
+ this.pendingJoinIds = remains.pendingJoinIds;
40
+ }
41
+ }
42
+ export async function readFormerParty(node) {
43
+ const self = node.peerId?.toString();
44
+ const machines = new Set((await node.listMembers()).map((member) => member.peerId));
45
+ return { bundle: await exportStrandBundle(node), otherMachines: [...machines].filter((peerId) => peerId !== self) };
46
+ }
47
+ /** The refusal `content` earns against what the running party holds, or null when it may be dissolved. */
48
+ export function formerPartyRefusal(content, holdings, discard, nowMs) {
49
+ const discarded = new Set(discard);
50
+ const unheld = unheldBundleContent(content.bundle, holdings, nowMs);
51
+ const remains = {
52
+ otherMachines: content.otherMachines,
53
+ strandIds: unheld.strandIds.filter((strandId) => !discarded.has(strandId)),
54
+ pendingJoinIds: unheld.pendingJoinIds.filter((joinId) => !discarded.has(joinId)),
55
+ };
56
+ const empty = remains.otherMachines.length + remains.strandIds.length + remains.pendingJoinIds.length === 0;
57
+ return empty ? null : new FormerPartyNotEmptyError(content.bundle.partyId, remains);
58
+ }
59
+ /** The former party's start for {@link readFormerParty}: nothing to dial or reserve on, the running Noise mode. */
60
+ export function formerPartyRead(partyId, running) {
61
+ return {
62
+ partyId,
63
+ bootstrapAddrs: [],
64
+ relayAddrs: [],
65
+ ...(running.noiseCryptoMode !== undefined && { noiseCryptoMode: running.noiseCryptoMode }),
66
+ };
67
+ }
68
+ /** `options` without `formerPartyId` when it names `partyId`. */
69
+ export function withoutFormerParty(options, partyId) {
70
+ if (partyId === undefined || options.formerPartyId !== partyId)
71
+ return options;
72
+ const { formerPartyId: _dissolved, ...rest } = options;
73
+ return rest;
74
+ }
75
+ const MARKER_VERSION = 1;
76
+ /** Mark `partyId` as being removed, then remove its local storage. Only once removing it is decided: a dissolve's check passed, or an owner removed this app. */
77
+ export async function removeParty(storage, partyId) {
78
+ await storage.nodeLocal.set(storage.markerKey, JSON.stringify({ v: MARKER_VERSION, partyId }));
79
+ // On disk before the first deletion, so a crash during the removal leaves the marker that finishes it.
80
+ await storage.nodeLocal.flush();
81
+ await finishRemoving(storage, partyId);
82
+ }
83
+ /**
84
+ * The party a begun dissolve names, or `undefined`. A marker that does not parse is deleted with
85
+ * a warning: nothing can finish it, and the party it named keeps what is left of its storage.
86
+ */
87
+ async function markedParty(storage) {
88
+ const text = await storage.nodeLocal.get(storage.markerKey);
89
+ if (text === undefined)
90
+ return undefined;
91
+ const partyId = parseMarker(text);
92
+ if (partyId === undefined) {
93
+ console.warn(`[cadre-core/edge-node] deleting the dissolve marker ${storage.markerKey}: it is not one this kit wrote`);
94
+ await storage.nodeLocal.delete(storage.markerKey);
95
+ }
96
+ return partyId;
97
+ }
98
+ /** Finish the removal a marker names, if any. */
99
+ export async function finishMarkedRemoval(storage) {
100
+ const partyId = await markedParty(storage);
101
+ if (partyId !== undefined)
102
+ await finishRemoving(storage, partyId);
103
+ }
104
+ /**
105
+ * {@link finishMarkedRemoval} before a start, which must then not keep the party it names as
106
+ * `formerPartyId`, finished or not. A failure is logged and reported, and the start goes on; the
107
+ * marker stays for the next.
108
+ */
109
+ export async function finishRemovalAtStart(storage) {
110
+ let partyId;
111
+ try {
112
+ partyId = await markedParty(storage);
113
+ if (partyId !== undefined)
114
+ await finishRemoving(storage, partyId);
115
+ return { partyId };
116
+ }
117
+ catch (err) {
118
+ console.warn('[cadre-core/edge-node] finishing the removal of a dissolved party failed; the next start retries:', err);
119
+ return { partyId, error: toError(err) };
120
+ }
121
+ }
122
+ /**
123
+ * Delete what is left of `partyId`'s local storage, then the marker: its control database, its
124
+ * node-local records, its trusted-owner anchor, its unpublished joined-strand records and its move
125
+ * record; then the saved start stops naming it as `formerPartyId`.
126
+ *
127
+ * NOTE: strand databases stay. A strand's storage scope is its strand id, not the party, so a
128
+ * moved strand's database is the running party's copy; a discarded strand's is left too, for the
129
+ * running party to reuse if it joins that strand again. If discarded strands' storage ever needs
130
+ * reclaiming, delete it here only for a strand no party on this device holds.
131
+ */
132
+ async function finishRemoving(storage, partyId) {
133
+ await storage.destroyScope(controlStorageScope(partyId));
134
+ for (const key of [bootstrapPeersKvKey(partyId), enrolledMachinesKvKey(partyId), strandNetworkKvKey(partyId)]) {
135
+ await storage.nodeLocal.delete(key);
136
+ }
137
+ await storage.trustedOwnerSlot(partyId).remove();
138
+ await forgetJoinedStrands(storage.keyStore, partyId);
139
+ await storage.moves.remove(partyId);
140
+ await dropFromSavedStart(storage, partyId);
141
+ await storage.nodeLocal.delete(storage.markerKey);
142
+ // One batch: the saved start stops naming the party in the same write that deletes the marker.
143
+ await storage.nodeLocal.flush();
144
+ }
145
+ async function forgetJoinedStrands(keyStore, partyId) {
146
+ const joins = new KeyStoreJoinedStrandStore(keyStore, partyId);
147
+ for (const record of await joins.list())
148
+ await joins.forget(record.Id);
149
+ }
150
+ /** A write that throws, unlike the runtime's best-effort save, so a failure keeps the marker for the next start. */
151
+ async function dropFromSavedStart(storage, partyId) {
152
+ const saved = parseSavedStart(await storage.nodeLocal.get(storage.savedStartKey));
153
+ if (saved?.options.formerPartyId !== partyId)
154
+ return;
155
+ await storage.nodeLocal.set(storage.savedStartKey, serializeSavedStart({ ...saved, options: withoutFormerParty(saved.options, partyId) }));
156
+ }
157
+ /** The party id in a marker's `text`, or `undefined` (logged) when it is not a marker. */
158
+ function parseMarker(text) {
159
+ let raw;
160
+ try {
161
+ raw = JSON.parse(text);
162
+ }
163
+ catch (err) {
164
+ console.warn('[cadre-core/edge-node] the dissolve marker is not JSON:', err);
165
+ return undefined;
166
+ }
167
+ if (typeof raw !== 'object' || raw === null)
168
+ return undefined;
169
+ const { v, partyId } = raw;
170
+ return v === MARKER_VERSION && typeof partyId === 'string' && partyId.length > 0 ? partyId : undefined;
171
+ }
172
+ /** "other machines are in it (…); the running party does not hold strands …": the non-empty lists. */
173
+ function describeRemains(remains) {
174
+ const parts = [
175
+ listed('other machines are in it', remains.otherMachines),
176
+ listed('the running party does not hold its strands', remains.strandIds),
177
+ listed('the running party does not hold its open pending joins', remains.pendingJoinIds),
178
+ ].filter((part) => part !== null);
179
+ return parts.join('; ');
180
+ }
181
+ function listed(what, ids) {
182
+ return ids.length === 0 ? null : `${what} (${ids.join(', ')})`;
183
+ }
184
+ //# sourceMappingURL=dissolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dissolve.js","sourceRoot":"","sources":["../../src/edge-node/dissolve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
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 type { CadreNode } from '../cadre-node.js';
10
+ import type { OwnerSigner } from '../owner-signer.js';
11
+ import type { OwnerGenesisResult } from './config.js';
12
+ import type { EdgeNodeStatus, JoinCadreOptions, JoinCadreResult } from './edge-node.js';
13
+ import { type JoinCredential } from './join-credential.js';
14
+ import type { EdgeNodeOptions, SavedStart } from './options.js';
15
+ import { type StrandMoveRecords } from './strand-move.js';
16
+ /** The part of the edge-node runtime a join drives. Every call runs inside the join's own transition. */
17
+ export interface JoinRuntime {
18
+ /** The running node, or null. */
19
+ readonly node: CadreNode | null;
20
+ /** The options the running node was started with; null unless running. */
21
+ readonly runningOptions: EdgeNodeOptions | null;
22
+ readonly moves: StrandMoveRecords;
23
+ readonly status: EdgeNodeStatus;
24
+ setStatus(status: EdgeNodeStatus): void;
25
+ readSavedStart(): Promise<SavedStart | undefined>;
26
+ /** Stop the node and close every store; nothing runs afterwards. */
27
+ shutDown(): Promise<void>;
28
+ /** Build and start the node for `options`, subscriptions attached; nothing saved, nothing reported. */
29
+ bringUp(options: EdgeNodeOptions): Promise<{
30
+ node: CadreNode;
31
+ resolved: EdgeNodeOptions;
32
+ }>;
33
+ /** Start a node for `options`' party only to run `read` on it, then stop it. */
34
+ readAlone<T>(options: EdgeNodeOptions, read: (node: CadreNode) => Promise<T>, purpose: string): Promise<T>;
35
+ /** A full start: bring up, settle the owner key, adopt waiting moves, save and report `running`. */
36
+ buildAndStart(options: EdgeNodeOptions): Promise<CadreNode>;
37
+ /** The owner slot's key, created when the slot is empty: the key an owner grant seats. */
38
+ ownerSlotSigner(): Promise<OwnerSigner>;
39
+ /** `granted`: an owner grant has just seated the slot's key, so wait for the row. */
40
+ wireJoinedOwner(node: CadreNode, granted: boolean): Promise<OwnerGenesisResult>;
41
+ saveRunningStart(resolved: EdgeNodeOptions): Promise<void>;
42
+ reportRunning(node: CadreNode, resolved: EdgeNodeOptions, owner: OwnerGenesisResult): void;
43
+ }
44
+ export declare function joinCadre(runtime: JoinRuntime, credential: JoinCredential, options: JoinCadreOptions, signal: AbortSignal): Promise<JoinCadreResult>;
@@ -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":"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"}