@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,485 @@
1
+ /**
2
+ * The member-dialed redemption exchange shared by cadre invitations
3
+ * (`cadre-invite-protocol.ts`) and owner grants (`owner-grant-protocol.ts`): a holder dials ANY
4
+ * member machine its bundle names, proves possession in one signed frame, and the member writes
5
+ * rows on the issuer's behalf and answers in one frame. The issuer need not be online.
6
+ *
7
+ * What is the same for both credentials lives here, generic over the request and reply types:
8
+ *
9
+ * - the member side's hardening and sequencing ({@link MemberRedemptionHandler}): a concurrency
10
+ * cap, a read timeout, the checks before any database work, the push of this member's control
11
+ * store to the holder for a credential still live here, and the writes under that admission;
12
+ * - the holder side's dialing ({@link dialRedemption}, {@link dialRedemptionInPasses}): the
13
+ * bundle's addresses one at a time under a per-address deadline, one signed request for every
14
+ * address and every pass, and the pacing between passes;
15
+ * - the shape checks both directions apply off the wire, and the frame cap.
16
+ *
17
+ * What differs — the bundle, the request's fields and signatures, the refusal codes, the rows
18
+ * written and the reply's contents — stays in each protocol module, which passes it in. Same
19
+ * shape as the seed and wake protocols underneath: one length-prefixed JSON frame each way over
20
+ * the primitives in `control-stream.ts`.
21
+ */
22
+ import debug from 'debug';
23
+ import { multiaddr } from '@multiformats/multiaddr';
24
+ import { withDeadline, exchangeFrame, readStreamToEnd, replyAndClose } from './control-stream.js';
25
+ import { decodeLengthPrefixedFrame } from './seed-bootstrap.js';
26
+ import { relayedRequestBudgetMs } from './link-budget.js';
27
+ import { IssuedRowNotHereYetError, parseNullableStoredDatetimeMs } from './control-database.js';
28
+ import { requireEd25519PublicKeyB64 } from './ed25519-key.js';
29
+ import { dialAddr, nextMacrotask, trailingPeerId } from './peer-dial.js';
30
+ import { chainMessages } from './control-retry.js';
31
+ import { isRetriableControlWriteFailure } from './control-write-retry.js';
32
+ const log = debug('sereus:cadre:member-redemption');
33
+ /**
34
+ * Cap on either frame of either protocol. A request is one signed row, two signatures and a few
35
+ * addresses; a reply adds at most a peer list of a cadre's size. Both fit in a few KB; 64 KiB is
36
+ * the defensive bound a stranger can make the handler buffer.
37
+ */
38
+ export const MAX_REDEMPTION_FRAME_BYTES = 64 * 1024;
39
+ /**
40
+ * Default time the member waits for the request frame before aborting the read (ms). The read
41
+ * only: a stranger opens the stream, so the bound is on a caller that opens one and never sends.
42
+ * Same figure as the seed handler's.
43
+ */
44
+ // eslint-disable-next-line no-restricted-syntax -- link-independent: a receiver cap on one frame on a stream the peer already opened; it bounds a peer that opens a stream and never sends, not the dial
45
+ const DEFAULT_REDEEM_READ_TIMEOUT_MS = 10000;
46
+ /** Default cap on concurrent inbound redemptions; over it the handler answers `busy`. */
47
+ const DEFAULT_MAX_CONCURRENT_REDEMPTIONS = 100;
48
+ /** Longest string field accepted off the wire; a stamp is 342 base64url chars, a signature 86. */
49
+ export const MAX_WIRE_FIELD_LENGTH = 1024;
50
+ const BASE64URL = /^[A-Za-z0-9_-]+$/;
51
+ // ── Shape checks (both directions, both protocols) ───────────────────────────
52
+ export function isBoundedString(value, max = MAX_WIRE_FIELD_LENGTH) {
53
+ return typeof value === 'string' && value.length > 0 && value.length <= max;
54
+ }
55
+ export function isEd25519KeyB64(value) {
56
+ if (!isBoundedString(value) || !BASE64URL.test(value))
57
+ return false;
58
+ try {
59
+ requireEd25519PublicKeyB64(value, 'key');
60
+ return true;
61
+ }
62
+ catch {
63
+ return false;
64
+ }
65
+ }
66
+ /** Is `value` a null-or-bounded-string column, as the nullable text columns of a travelling row are? */
67
+ export function isNullableBoundedString(value, max = MAX_WIRE_FIELD_LENGTH) {
68
+ return value === null || isBoundedString(value, max);
69
+ }
70
+ /** Is `value` a null-or-positive-integer use cap? */
71
+ export function isNullableUseCap(value) {
72
+ return value === null || (Number.isInteger(value) && value >= 1);
73
+ }
74
+ export function rejection(code, reason) {
75
+ return { accepted: false, code, reason };
76
+ }
77
+ /** Which named constraint, if any, refused the write — read off the error's cause chain. */
78
+ export function constraintRefusal(error, table) {
79
+ if (!(error instanceof Error))
80
+ return null;
81
+ const messages = chainMessages(error);
82
+ for (const [pattern, code, reason] of table) {
83
+ if (messages.some((message) => pattern.test(message)))
84
+ return rejection(code, reason);
85
+ }
86
+ return null;
87
+ }
88
+ /**
89
+ * A write failure that is not a refusal: this member cannot read the credential row the cadre
90
+ * holds yet, or the retry gave up (`conflict`), or something else (`internal`).
91
+ */
92
+ export function writeFailureRefusal(error) {
93
+ if (error instanceof IssuedRowNotHereYetError) {
94
+ return rejection('conflict', 'This member has not synced the credential yet; nothing was recorded');
95
+ }
96
+ // NOTE: a seat torn on a member alone with an uncaught-up device was measured answering
97
+ // `internal`, not `conflict` (cadre-invite-redeemed-at-a-member-without-the-row). Both are
98
+ // retryable, so the holder is unaffected; if the two codes ever diverge in retryability,
99
+ // find which tear `isRetriableControlWriteFailure` does not recognise.
100
+ return isRetriableControlWriteFailure(error)
101
+ ? rejection('conflict', 'The redemption write failed transiently; nothing was recorded')
102
+ : rejection('internal', 'Internal redemption error');
103
+ }
104
+ /**
105
+ * Member side of one redemption protocol: registers the handler and drives each inbound stream
106
+ * through the checks in cost order — shape, then whatever the protocol checks without database
107
+ * work (party, signatures, and for an invitation the connecting identity and target device) —
108
+ * so no database work happens for a stranger that cannot prove anything; then admits the holder
109
+ * to this member's control replication (`admitDevice`) when the credential is live here, and
110
+ * runs the protocol's writes. Hardened as the seed handler is: a concurrency cap (over it,
111
+ * `busy`) and a read timeout.
112
+ *
113
+ * Registered only by a started `CadreNode`, after its control database is up, like the
114
+ * formation responder; nothing here is reachable on a node without one.
115
+ *
116
+ * A subclass supplies the five credential-specific pieces: the request's shape, the checks after
117
+ * it, the liveness read behind the push, the writes, and the two fixed refusals the base sends
118
+ * on its own (`busy`, and `internal` for a frame it could not read or a failure it did not
119
+ * expect).
120
+ */
121
+ export class MemberRedemptionHandler {
122
+ /**
123
+ * @param label - Names the credential in log lines and stream labels ("Cadre invite", "Owner grant").
124
+ * @param protocolId - The protocol id {@link register} handles by default.
125
+ */
126
+ constructor(label, protocolId, options) {
127
+ this.label = label;
128
+ this.protocolId = protocolId;
129
+ this.registered = new Set();
130
+ this.active = 0;
131
+ this.partyId = options.partyId;
132
+ this.admitDevice = options.admitDevice;
133
+ this.readTimeoutMs = options.redeemReadTimeoutMs ?? DEFAULT_REDEEM_READ_TIMEOUT_MS;
134
+ this.maxConcurrent = options.maxConcurrentRedemptions ?? DEFAULT_MAX_CONCURRENT_REDEMPTIONS;
135
+ }
136
+ /** In-flight inbound redemptions. */
137
+ get activeCount() {
138
+ return this.active;
139
+ }
140
+ /**
141
+ * Register on `node`; rejects when libp2p refuses the registration. `runOnLimitedConnection`
142
+ * because a phone redeems through a relay, whose connection libp2p marks limited; the
143
+ * exchange is two small frames, well inside any relay's cap.
144
+ */
145
+ async register(node, protocolId = this.protocolId) {
146
+ if (this.registered.has(node))
147
+ return;
148
+ await node.handle(protocolId, async (rawStream, rawConnection) => {
149
+ await this.handleStream(rawStream, rawConnection.remotePeer.toString());
150
+ }, { runOnLimitedConnection: true });
151
+ this.registered.add(node);
152
+ log('%s handler registered (%s)', this.label, protocolId);
153
+ }
154
+ async unregister(node, protocolId = this.protocolId) {
155
+ if (!this.registered.has(node))
156
+ return;
157
+ await node.unhandle(protocolId);
158
+ this.registered.delete(node);
159
+ log('%s handler unregistered (%s)', this.label, protocolId);
160
+ }
161
+ /**
162
+ * One inbound redemption: read the frame, decide, reply, close. Public as the seam a protocol
163
+ * spec drives directly; `remotePeerId` is the connection's authenticated peer.
164
+ */
165
+ async handleStream(stream, remotePeerId) {
166
+ if (this.active >= this.maxConcurrent) {
167
+ log('%s: refusing redemption from %s: %d in flight at cap %d', this.label, remotePeerId, this.active, this.maxConcurrent);
168
+ await replyAndClose(stream, this.busyRefusal, this.label);
169
+ return;
170
+ }
171
+ this.active++;
172
+ try {
173
+ const reply = await this.decide(stream, remotePeerId);
174
+ log('%s: redemption from %s: %s', this.label, remotePeerId, reply.accepted ? 'accepted' : `refused (${reply.code})`);
175
+ await replyAndClose(stream, reply, this.label);
176
+ }
177
+ catch (error) {
178
+ log('%s: redemption from %s failed: %o', this.label, remotePeerId, error);
179
+ await replyAndClose(stream, this.internalRefusal, this.label);
180
+ }
181
+ finally {
182
+ this.active--;
183
+ }
184
+ }
185
+ /**
186
+ * Read the request frame to EOF, bounded and size-capped; JSON only, shape is the subclass's.
187
+ * Throws on a frame that cannot be read, which {@link handleStream} answers `internal`: a read
188
+ * timeout or a cut frame proves nothing about the credential, so the holder must stay free to
189
+ * try the next address.
190
+ */
191
+ async readRequest(stream) {
192
+ const data = await readStreamToEnd(stream, { maxBytes: MAX_REDEMPTION_FRAME_BYTES, timeoutMs: this.readTimeoutMs, label: this.label });
193
+ return JSON.parse(new TextDecoder().decode(decodeLengthPrefixedFrame(data, MAX_REDEMPTION_FRAME_BYTES)));
194
+ }
195
+ async decide(stream, remotePeerId) {
196
+ const request = await this.readRequest(stream);
197
+ if (!this.isWellFormedRequest(request))
198
+ return this.malformedRefusal;
199
+ const refused = this.checkRequest(request, remotePeerId);
200
+ if (refused)
201
+ return refused;
202
+ const release = await this.admitIfLive(request, remotePeerId);
203
+ try {
204
+ return await this.redeemAdmitted(request, remotePeerId);
205
+ }
206
+ finally {
207
+ release();
208
+ }
209
+ }
210
+ /**
211
+ * Admit the holder to this member's control replication before the first write of the
212
+ * exchange (the seat, when this member does not hold the row; otherwise the redemption), and
213
+ * resolve to the release that ends the admission. The holder is not a member, so only this
214
+ * puts it in this member's control write cohort, and it must be there to hold the rows the
215
+ * exchange writes. It is pushed the control store first, because a cohort node with no base
216
+ * revision of a block refuses a commit to it; a member alone with the holder would then have
217
+ * half its cohort refusing, and the write would tear instead of committing.
218
+ *
219
+ * Only for a credential still live here ({@link isLive}, judged on the row this member holds
220
+ * or, before the seat, on the bundle's copy once its issuer signature verifies): the holder
221
+ * of a withdrawn, expired or spent one, or of a forged copy naming a real owner, is refused
222
+ * without being sent anything. The retry of a redemption already written can read as spent
223
+ * too and is sent nothing; it needs nothing, since the rows it would hold are written and
224
+ * the ordinary catch-up reaches a member. Best-effort: a failed check or push is logged and
225
+ * the writes go ahead without the holder admitted, then commit or are answered retryably.
226
+ */
227
+ async admitIfLive(request, remotePeerId) {
228
+ const notAdmitted = () => undefined;
229
+ if (!this.admitDevice)
230
+ return notAdmitted;
231
+ try {
232
+ if (!await this.isLive(request)) {
233
+ log('%s: the credential is not live here; %s is not admitted before the write', this.label, remotePeerId);
234
+ return notAdmitted;
235
+ }
236
+ // NOTE: accepted tradeoff — when a write then fails (the seat refused, the last use
237
+ // taken by a race, the row expiring between this check and the write), the holder keeps
238
+ // a copy of a control store it was not admitted to. It holds a live owner-signed
239
+ // credential and proved possession and consent, and a seed hands an un-enrolled machine
240
+ // the same information. Revisit if a control table ever carries data a non-member must not see.
241
+ return await this.admitDevice(remotePeerId);
242
+ }
243
+ catch (error) {
244
+ log('%s: admitting %s before its write failed (writing anyway): %o', this.label, remotePeerId, error);
245
+ return notAdmitted;
246
+ }
247
+ }
248
+ }
249
+ /**
250
+ * Dial `addr`, open one stream to it, send the request, half-close, read the one reply frame.
251
+ * `signal` is the per-address deadline: it cancels the dial and resets a live stream.
252
+ *
253
+ * The address is dialed outside libp2p's dial queue (`dialAddr`), so another dial of the
254
+ * member in flight cannot capture this attempt; an open connection is still reused.
255
+ */
256
+ async function exchangeRedemption(node, addr, options, signal, budgetMs) {
257
+ const connection = await dialAddr(node, addr, signal, `${options.label} redemption`);
258
+ // `runOnLimitedConnection`: a member may be reachable only through a relay (see `register`).
259
+ const rawStream = await connection.newStream(options.protocolId, { runOnLimitedConnection: true, signal });
260
+ return await exchangeFrame(rawStream, signal, options.request, async (stream) => {
261
+ const data = await readStreamToEnd(stream, { maxBytes: MAX_REDEMPTION_FRAME_BYTES, timeoutMs: budgetMs, label: `${options.label} reply` });
262
+ return decodeReply(JSON.parse(new TextDecoder().decode(decodeLengthPrefixedFrame(data, MAX_REDEMPTION_FRAME_BYTES))), options);
263
+ }, `${options.label} redemption aborted by its deadline`);
264
+ }
265
+ /**
266
+ * Read a reply frame into a typed reply. A refusal keeps only its code and reason, as sent;
267
+ * an acceptance is the protocol's to read. Throws on a frame that is neither.
268
+ */
269
+ function decodeReply(value, options) {
270
+ if (typeof value !== 'object' || value === null) {
271
+ throw new Error(`${options.label} reply is not an object`);
272
+ }
273
+ const reply = value;
274
+ if (reply.accepted !== true) {
275
+ return { accepted: false, code: reply.code, reason: reply.reason };
276
+ }
277
+ return options.decodeAccepted(reply);
278
+ }
279
+ function isRefusal(reply) {
280
+ return reply.accepted === false;
281
+ }
282
+ function asError(error) {
283
+ if (error instanceof Error)
284
+ return error;
285
+ // A WebSocket transport rejects a refused dial with an `ErrorEvent`, whose text is its `message`.
286
+ const message = error?.message;
287
+ return new Error(typeof message === 'string' && message.length > 0 ? message : String(error), { cause: error });
288
+ }
289
+ /**
290
+ * Run `op` with a signal that aborts when either `deadline` or the caller's `signal` does.
291
+ * Relayed by hand rather than with `AbortSignal.any`, which the phone runtimes lack.
292
+ */
293
+ async function underDeadlineAndCaller(deadline, caller, op) {
294
+ if (!caller)
295
+ return await op(deadline);
296
+ const either = new AbortController();
297
+ const abort = () => either.abort();
298
+ deadline.addEventListener('abort', abort, { once: true });
299
+ caller.addEventListener('abort', abort, { once: true });
300
+ // An abort between the caller's last check and the listener above fired no event.
301
+ if (caller.aborted)
302
+ abort();
303
+ try {
304
+ return await op(either.signal);
305
+ }
306
+ finally {
307
+ deadline.removeEventListener('abort', abort);
308
+ caller.removeEventListener('abort', abort);
309
+ }
310
+ }
311
+ /**
312
+ * One pass over the bundle's addresses, one at a time, each under its own deadline, stopping at
313
+ * the first acceptance or the first final refusal. A dial failure, a malformed reply or a
314
+ * retryable refusal moves to the next address. A final refusal and an acceptance that fails
315
+ * the holder's check are thrown from outside the per-address catch, so neither is ever
316
+ * mistaken for an address that could not be reached.
317
+ */
318
+ async function dialOnce(node, options) {
319
+ const budgetMs = options.addressBudgetMs ?? relayedRequestBudgetMs(options.linkRoundTripMs);
320
+ const outcomes = [];
321
+ let memberAnswered = false;
322
+ for (const addr of options.members) {
323
+ options.signal?.throwIfAborted();
324
+ // Between attempts: a relayed address's relay leg goes through libp2p's dial queue, which
325
+ // needs a macrotask to drop an aborted job (see `tryAddrsInTurn`).
326
+ if (outcomes.length > 0)
327
+ await nextMacrotask();
328
+ let parsed;
329
+ try {
330
+ parsed = multiaddr(addr);
331
+ }
332
+ catch (error) {
333
+ outcomes.push({ addr, error: new Error('not a multiaddr', { cause: error }) });
334
+ continue;
335
+ }
336
+ let reply;
337
+ try {
338
+ reply = await withDeadline(budgetMs, `${options.label} redemption via ${addr}`, (deadline) => underDeadlineAndCaller(deadline, options.signal, (signal) => exchangeRedemption(node, parsed, options, signal, budgetMs)));
339
+ }
340
+ catch (error) {
341
+ // The caller's abort cut this attempt short; its reason, not the cut, is the answer.
342
+ options.signal?.throwIfAborted();
343
+ log('%s redemption via %s failed before an answer (%s); trying the next address', options.label, addr, asError(error).message);
344
+ outcomes.push({ addr, error: asError(error) });
345
+ continue;
346
+ }
347
+ if (isRefusal(reply)) {
348
+ const refusal = options.refusal(reply.code, reply.reason);
349
+ if (!refusal.retryable)
350
+ throw refusal;
351
+ log('%s redemption via %s refused retryably (%s); trying the next address', options.label, addr, refusal.message);
352
+ outcomes.push({ addr, error: refusal });
353
+ memberAnswered = true;
354
+ continue;
355
+ }
356
+ options.verifyAccepted(reply);
357
+ return { redeemed: { reply, memberPeerId: trailingPeerId(parsed), memberAddr: addr } };
358
+ }
359
+ return { outcomes, memberAnswered };
360
+ }
361
+ /**
362
+ * Holder side: try the bundle's addresses one at a time ({@link dialOnce}) and throw the
363
+ * protocol's unreachable error when none accepts.
364
+ *
365
+ * Every address gets the SAME request, with one usage nonce: the usage table is keyed by it,
366
+ * so a member the holder gave up on that was still committing cannot redeem it twice, and a
367
+ * retry at the next member after a dropped reply is answered as already done.
368
+ *
369
+ * @throws the protocol's rejected error on a final refusal, its reply-invalid error on an
370
+ * acceptance that fails `verifyAccepted`, its unreachable error when every address was tried
371
+ * and none accepted, and the reason of `options.signal` once it aborts.
372
+ */
373
+ export async function dialRedemption(node, options) {
374
+ const outcome = await dialOnce(node, options);
375
+ if ('redeemed' in outcome)
376
+ return outcome.redeemed;
377
+ throw options.unreachable(outcome.outcomes);
378
+ }
379
+ /** Pause after the first pass in which no member accepted; doubled after each further one. */
380
+ const REDEEM_PASS_FIRST_PAUSE_MS = 5000;
381
+ /** Longest pause between passes. */
382
+ const REDEEM_PASS_MAX_PAUSE_MS = 60000;
383
+ /**
384
+ * Holder side, in passes: one {@link dialOnce} pass over the bundle's addresses, or, with
385
+ * `waitForMember`, as many as it takes, pausing between them (5 s after the first, doubling to
386
+ * 60 s, never past the credential's expiry). Every pass sends the same signed request, so a
387
+ * member that wrote the redemption on a pass whose reply was lost answers a later pass as
388
+ * already done, with no second usage row.
389
+ *
390
+ * A wait ends at the first acceptance, at a final refusal or an invalid reply (thrown as
391
+ * {@link dialRedemption} throws them), when the credential expires by this holder's clock
392
+ * (thrown as `expired()`), when `signal` aborts (its reason thrown), and when `node` stops. A
393
+ * bundle that names no address has nothing to wait for and throws at once.
394
+ */
395
+ export async function dialRedemptionInPasses(node, options) {
396
+ for (let pass = 1;; pass++) {
397
+ if (options.waitForMember) {
398
+ // One pass leaves expiry to the member, whose clock decides; a wait has nothing else to end it.
399
+ refuseIfExpired(options);
400
+ refuseIfStopped(node, options.label);
401
+ }
402
+ reportProgress(options.onProgress, { state: 'redeeming', pass });
403
+ const outcome = await dialOnce(node, options);
404
+ if ('redeemed' in outcome)
405
+ return outcome.redeemed;
406
+ if (!options.waitForMember || outcome.outcomes.length === 0)
407
+ throw options.unreachable(outcome.outcomes);
408
+ const pauseMs = pauseAfterPass(pass, options.expiresAt);
409
+ reportProgress(options.onProgress, {
410
+ state: 'waiting',
411
+ pass,
412
+ retryAtMs: Date.now() + pauseMs,
413
+ // Any refusal means a member answered and is not ready yet; otherwise none answered at all.
414
+ reason: outcome.memberAnswered ? 'member-not-ready' : 'no-member-answered',
415
+ outcomes: outcome.outcomes
416
+ });
417
+ log('%s redemption pass %d found no member ready; the next pass in %d ms', options.label, pass, pauseMs);
418
+ await pause(pauseMs, options.signal, node);
419
+ }
420
+ }
421
+ // NOTE: judged by this holder's clock, so one running ahead ends a wait before the members
422
+ // would refuse it. If waits are seen ending early on skewed phones, allow a grace margin here.
423
+ function refuseIfExpired(options) {
424
+ const expiresAtMs = parseNullableStoredDatetimeMs(options.expiresAt);
425
+ if (expiresAtMs !== null && expiresAtMs <= Date.now()) {
426
+ throw options.expired();
427
+ }
428
+ }
429
+ /** A stopped node's every dial fails at once, which would otherwise read as "no member answered" until expiry. */
430
+ function refuseIfStopped(node, label) {
431
+ if (node.status !== 'started') {
432
+ throw new Error(`${label} redemption stopped waiting: the node is ${node.status}`);
433
+ }
434
+ }
435
+ /** The pause after pass `pass`, cut short at the credential's expiry so the next check ends the wait then. */
436
+ function pauseAfterPass(pass, expiresAt) {
437
+ const backoffMs = Math.min(REDEEM_PASS_FIRST_PAUSE_MS * 2 ** (pass - 1), REDEEM_PASS_MAX_PAUSE_MS);
438
+ const expiresAtMs = parseNullableStoredDatetimeMs(expiresAt);
439
+ return expiresAtMs === null ? backoffMs : Math.max(0, Math.min(backoffMs, expiresAtMs - Date.now()));
440
+ }
441
+ /** A UI callback must not break the join. */
442
+ function reportProgress(onProgress, progress) {
443
+ if (!onProgress)
444
+ return;
445
+ try {
446
+ onProgress(progress);
447
+ }
448
+ catch (error) {
449
+ log('redemption progress callback threw (ignored): %o', error);
450
+ }
451
+ }
452
+ /**
453
+ * Wait `ms`, or reject with `signal`'s reason as soon as it aborts. Ends early, resolving, when
454
+ * `node` stops (or is already stopped), so the next pass's stop check ends the wait without a
455
+ * timer holding the process open. Listeners and timer are cleared however it ends.
456
+ */
457
+ function pause(ms, signal, node) {
458
+ return new Promise((resolve, reject) => {
459
+ if (signal?.aborted) {
460
+ reject(signal.reason);
461
+ return;
462
+ }
463
+ if (node.status !== 'started') {
464
+ resolve();
465
+ return;
466
+ }
467
+ const settle = () => {
468
+ clearTimeout(timer);
469
+ signal?.removeEventListener('abort', onAbort);
470
+ node.removeEventListener('stop', onStop);
471
+ };
472
+ const onAbort = () => {
473
+ settle();
474
+ reject(signal.reason);
475
+ };
476
+ const onStop = () => {
477
+ settle();
478
+ resolve();
479
+ };
480
+ const timer = setTimeout(onStop, ms);
481
+ signal?.addEventListener('abort', onAbort, { once: true });
482
+ node.addEventListener('stop', onStop, { once: true });
483
+ });
484
+ }
485
+ //# sourceMappingURL=member-redemption.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"member-redemption.js","sourceRoot":"","sources":["../src/member-redemption.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import type { TrustSource } from './trusted-owner-store.js';
2
- import type { InvitationChain, OwnerKeyRow, RevocationRow } from './types.js';
2
+ import type { OwnerGrantChain, OwnerKeyRow, RevocationRow } from './types.js';
3
3
  export interface OwnerAnchorInputs {
4
4
  /** The party whose table is being read; every stored proof is judged for it, so a voucher or tombstone signed for another party neither seats nor removes a key. */
5
5
  partyId: string;
@@ -9,8 +9,8 @@ export interface OwnerAnchorInputs {
9
9
  rows: readonly OwnerKeyRow[];
10
10
  /** `Revocation` rows; only those with `tableName === 'OwnerKey'` are consulted. */
11
11
  tombstones: readonly RevocationRow[];
12
- /** The usage and invitation rows, when some owner row is invitation-admitted; null skips those rows. */
13
- chain: InvitationChain | null;
12
+ /** The usage and grant rows, when some owner row is grant-seated; null when none is, which skips every consent-seated row. */
13
+ grantChain: OwnerGrantChain | null;
14
14
  }
15
15
  export interface OwnerAnchorDerivation {
16
16
  /** The keys the anchor should hold after this pass. */
@@ -19,4 +19,4 @@ export interface OwnerAnchorDerivation {
19
19
  refusedRemovals: readonly string[];
20
20
  }
21
21
  /** The rule above, as one pure function. */
22
- export declare function deriveOwnerAnchor({ partyId, anchor, rows, tombstones, chain }: OwnerAnchorInputs): OwnerAnchorDerivation;
22
+ export declare function deriveOwnerAnchor({ partyId, anchor, rows, tombstones, grantChain }: OwnerAnchorInputs): OwnerAnchorDerivation;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * How the node-local trusted-owner anchor follows the replicated `OwnerKey` table along
3
3
  * VERIFIABLE chains — the pure rule, with no I/O, so it can be tested without a database.
4
- * `CadreNode.syncOwnerAnchor` reads the table, the tombstones and the invitation chain,
4
+ * `CadreNode.syncOwnerAnchor` reads the table, the tombstones and the owner-grant chain,
5
5
  * calls {@link deriveOwnerAnchor}, and applies the difference to the store.
6
6
  *
7
7
  * Why not simply trust the table: an empty local copy can seat any key through the
@@ -19,9 +19,10 @@
19
19
  * retired, although nothing could re-derive it from the remaining base.
20
20
  * - **derivable from S**: a live row whose stored proof verifies against S — owner-signed
21
21
  * (`vouchSig` set): `vouchOwner` is in S and `verifyOwnerKeyVoucher` holds;
22
- * invitation-admitted (`vouchSig` null, `vouchUsage` set): `verifyInvitationOwnerAdmission`
23
- * holds with S as the anchor. The founding row (all three null) is never derivable:
24
- * founders enter anchors only out of band.
22
+ * grant-seated (`vouchSig` null, `vouchUsage` naming an `OwnerGrantUsage` row):
23
+ * `verifyOwnerGrantAdmission` holds with S as the anchor. The founding row (all three null)
24
+ * is never derivable: founders enter anchors only out of band. A machine invitation admits a
25
+ * machine and never seats an owner row, so no `CadreInviteUsage` row is read here.
25
26
  * - **derived**: the least fixpoint from base ∪ kept — add every key whose live row is
26
27
  * derivable from what is already in the set, until nothing changes. **chainDerived** is the
27
28
  * keys whose live row was derivable in this pass, base keys included.
@@ -45,9 +46,9 @@
45
46
  * two cannot be told apart from replicated state; keeping is the only rule under which a
46
47
  * sole owner can rotate at all.
47
48
  */
48
- import { verifyInvitationOwnerAdmission, verifyOwnerKeyVoucher, verifyRevocationSigner } from './peer-authorization.js';
49
+ import { verifyOwnerGrantAdmission, verifyOwnerKeyVoucher, verifyRevocationSigner } from './peer-authorization.js';
49
50
  /** The rule above, as one pure function. */
50
- export function deriveOwnerAnchor({ partyId, anchor, rows, tombstones, chain }) {
51
+ export function deriveOwnerAnchor({ partyId, anchor, rows, tombstones, grantChain }) {
51
52
  const ownerTombstones = tombstones.filter(tombstone => tombstone.tableName === 'OwnerKey');
52
53
  // NOTE: a stamp is retired by any tombstone that names it, verified or not — the same
53
54
  // reading `queryCadrePeers` and `getOwnerKeys` give the table. A forged tombstone can
@@ -56,7 +57,7 @@ export function deriveOwnerAnchor({ partyId, anchor, rows, tombstones, chain })
56
57
  const retired = new Set(ownerTombstones.map(tombstone => tombstone.stampId));
57
58
  const liveRows = rows.filter(row => !retired.has(row.stampId));
58
59
  const derived = passStartSet(anchor, liveRows);
59
- const chainDerived = deriveFixpoint(partyId, derived, liveRows, chain);
60
+ const chainDerived = deriveFixpoint(partyId, derived, liveRows, grantChain);
60
61
  const removed = removedKeys(partyId, derived, chainDerived, ownerTombstones);
61
62
  if (removed.size > 0 && removed.size === derived.size) {
62
63
  return { target: derived, refusedRemovals: Array.from(removed) };
@@ -81,7 +82,7 @@ function passStartSet(anchor, liveRows) {
81
82
  * trust only grows within a pass, and a proof that failed against a trusted voucher cannot
82
83
  * pass later, so no row is verified twice.
83
84
  */
84
- function deriveFixpoint(partyId, derived, liveRows, chain) {
85
+ function deriveFixpoint(partyId, derived, liveRows, grantChain) {
85
86
  const chainDerived = new Set();
86
87
  const pending = new Set(liveRows);
87
88
  let progress = true;
@@ -92,7 +93,7 @@ function deriveFixpoint(partyId, derived, liveRows, chain) {
92
93
  continue;
93
94
  }
94
95
  pending.delete(row);
95
- if (proofVerifies(partyId, row, derived, chain)) {
96
+ if (proofVerifies(partyId, row, derived, grantChain)) {
96
97
  chainDerived.add(row.key);
97
98
  derived.add(row.key);
98
99
  progress = true;
@@ -102,22 +103,22 @@ function deriveFixpoint(partyId, derived, liveRows, chain) {
102
103
  return chainDerived;
103
104
  }
104
105
  /** Does `row`'s stored proof verify against `trusted`, for `partyId`, by the kind of proof it carries (see the module doc)? */
105
- function proofVerifies(partyId, row, trusted, chain) {
106
+ function proofVerifies(partyId, row, trusted, grantChain) {
106
107
  if (row.vouchOwner === null) {
107
108
  return false;
108
109
  }
109
110
  if (row.vouchSig !== null) {
110
- return verifyOwnerKeyVoucher(partyId, row.key, row.stampId, row.vouchOwner, row.vouchSig);
111
+ return verifyOwnerKeyVoucher(partyId, row.key, row.label, row.stampId, row.vouchOwner, row.vouchSig);
111
112
  }
112
113
  if (row.vouchUsage === null) {
113
114
  return false;
114
115
  }
115
- const usage = chain?.usages.get(row.vouchUsage);
116
- const invite = usage === undefined ? undefined : chain?.invites.get(usage.inviteKey);
117
- if (usage === undefined || invite === undefined) {
116
+ const grantUsage = grantChain?.usages.get(row.vouchUsage);
117
+ const grant = grantUsage === undefined ? undefined : grantChain?.grants.get(grantUsage.grantKey);
118
+ if (grantUsage === undefined || grant === undefined) {
118
119
  return false;
119
120
  }
120
- return verifyInvitationOwnerAdmission(partyId, row, usage, invite, key => trusted.has(key));
121
+ return verifyOwnerGrantAdmission(partyId, row, grantUsage, grant, (key) => trusted.has(key));
121
122
  }
122
123
  /**
123
124
  * The keys of `derived` that a verifiable tombstone removes (see the module doc), every
@@ -1 +1 @@
1
- {"version":3,"file":"owner-anchor-sync.js","sourceRoot":"","sources":["../src/owner-anchor-sync.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"owner-anchor-sync.js","sourceRoot":"","sources":["../src/owner-anchor-sync.ts"],"names":[],"mappings":"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"}