@serfab/cadre-core 1.16.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 +11 -2
  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 +652 -1052
  151. package/src/cadre-join-request.ts +24 -3
  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 +886 -789
  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
@@ -1,16 +1,14 @@
1
1
  /**
2
- * Removing a machine from a cadre: the listing an app shows (`CadreNode.listMachines`), the
3
- * read-only plan behind its confirmation step (`CadreNode.planMachineRemoval`), and the
4
- * check that a requested re-vouch stays inside that plan. Pure projections over rows the
5
- * node has already read; the reads and writes themselves are `CadreNode.removeMachine`'s and
6
- * `ControlDatabase.removeMachine`'s.
2
+ * Removing a machine from a cadre: the listing an app shows (`CadreNode.listMachines`) and the
3
+ * read-only plan behind its confirmation step (`CadreNode.planMachineRemoval`). Pure
4
+ * projections over rows the node has already read; the reads and writes themselves are
5
+ * `CadreNode.removeMachine`'s and `ControlDatabase.removeMachine`'s.
7
6
  *
8
- * A machine's owner key is its own identity key: an owner-granting invitation seats
9
- * `OwnerKey.Key = CadrePeer.PublicKey`, and `addOwner` takes the key behind a peer id. So
10
- * "this machine is an owner" is "the ed25519 key behind its peer id is a live `OwnerKey`",
11
- * and an owner key with no machine row is still listed, as the machine it names.
7
+ * A machine is a `CadrePeer` row and nothing more: an owner key is the person's, so removing a
8
+ * machine leaves every owner key standing, the one the machine's user signed with included
9
+ * (removing one is `CadreNode.removeOwner`, `owner-keys.ts`).
12
10
  */
13
- import { ed25519PublicKeyB64FromPeerId, peerIdFromEd25519PublicKeyB64 } from './ed25519-key.js';
11
+ import { ed25519PublicKeyB64FromPeerId } from './ed25519-key.js';
14
12
  import { verifyPeerRecordSignature } from './peer-record.js';
15
13
  /**
16
14
  * `CadreNode.removeMachine` refused by name, before any write. `code` says which rule
@@ -26,41 +24,18 @@ export class MachineRemovalRefusedError extends Error {
26
24
  }
27
25
  const MACHINE_REMOVAL_REFUSAL_REASON = {
28
26
  'not-an-owner': 'this node holds no enrolled owner key',
29
- 'self': 'an owner cannot remove its own machine; ask another owner',
30
- 'last-owner': 'it holds the only live owner key',
27
+ 'self': 'it is this node; leaving a cadre is stopping the node and deleting its data',
31
28
  };
32
29
  /**
33
- * One entry per live `CadrePeer` row, then one per live `OwnerKey` whose key derives a peer
34
- * id with no row. `profile` and `lastPublishedAt` are reported only when the row's
35
- * self-signature verifies under the key behind its peer id, as `CadreNode.resolvePeerAddrs`
36
- * judges the same record: no insert rule constrains `Profile`, and an owner re-vouch may
37
- * bump `UpdatedAt` without re-signing.
30
+ * One entry per live `CadrePeer` row. `profile` and `lastPublishedAt` are reported only when
31
+ * the row's self-signature verifies under the key behind its peer id, as
32
+ * `CadreNode.resolvePeerAddrs` judges the same record: no insert rule constrains `Profile`, and
33
+ * an owner re-vouch may bump `UpdatedAt` without re-signing.
38
34
  */
39
35
  export function describeMachines(input) {
40
- const ownerKeys = new Set(input.owners.map((owner) => owner.key));
41
- const machines = input.rows.map((row) => describeMemberRow(input, row, ownerKeys));
42
- const listed = new Set(machines.map((machine) => machine.peerId));
43
- for (const owner of input.owners) {
44
- const peerId = peerIdFromEd25519PublicKeyB64(owner.key);
45
- if (peerId === null || listed.has(peerId)) {
46
- continue;
47
- }
48
- listed.add(peerId);
49
- machines.push({
50
- peerId,
51
- publicKey: owner.key,
52
- isSelf: peerId === input.selfPeerId,
53
- isOwner: true,
54
- isMember: false,
55
- trusted: false,
56
- profile: null,
57
- lastPublishedAt: null,
58
- vouchedBy: owner.vouchOwner,
59
- });
60
- }
61
- return machines;
36
+ return input.rows.map((row) => describeMemberRow(input, row));
62
37
  }
63
- function describeMemberRow(input, row, ownerKeys) {
38
+ function describeMemberRow(input, row) {
64
39
  const publicKey = ed25519PublicKeyB64FromPeerId(row.peerId);
65
40
  const selfPublished = publicKey !== null && row.publicKey === publicKey && row.sig !== null
66
41
  && verifyPeerRecordSignature(input.partyId, {
@@ -75,8 +50,6 @@ function describeMemberRow(input, row, ownerKeys) {
75
50
  peerId: row.peerId,
76
51
  publicKey,
77
52
  isSelf: row.peerId === input.selfPeerId,
78
- isOwner: publicKey !== null && ownerKeys.has(publicKey),
79
- isMember: true,
80
53
  trusted: input.trusted(row),
81
54
  profile: selfPublished ? row.profile : null,
82
55
  lastPublishedAt: selfPublished ? row.updatedAt : null,
@@ -84,83 +57,36 @@ function describeMemberRow(input, row, ownerKeys) {
84
57
  };
85
58
  }
86
59
  /**
87
- * The plan for removing `peerId` from these rows. The refusals come first and by name: a
88
- * node that cannot sign, the node itself (an owner cannot sign its own `OwnerKey` removal;
89
- * refusing the whole self-removal keeps one rule for owners and non-owners), and a machine
90
- * holding the only live owner key. The last cannot coexist with this node being a different
91
- * owner, but the local count can be wrong under concurrency, so the name stays.
92
- *
93
- * `dependents` are the rows the machine's owner key vouched, which every reader judges
94
- * against its anchor through `VouchOwner`: once the key is removed they stop passing the
95
- * membership predicate on every machine unless re-vouched first. This node's own rows are
96
- * reported as `ownDependency` instead, since it cannot re-vouch them itself.
60
+ * The plan for removing `peerId` from these rows. The refusals come first and by name: a node
61
+ * that cannot sign, and the node itself (leaving a cadre is the embedder's flow, which stops
62
+ * the node and deletes its data; the removal notice a machine acts on is built around another
63
+ * machine removing it).
97
64
  *
98
65
  * `invitations` are the live ones the machine could redeem to come back: targeted at it,
99
66
  * or already redeemed by it and still redeemable (a multi-use invitation, or an open
100
- * owner invitation inside its lifetime). The schema's rule: keeping a device out is removing
67
+ * invitation inside its lifetime). The schema's rule: keeping a device out is removing
101
68
  * its row and withdrawing the invitation.
102
69
  */
103
70
  export function planMachineRemoval(input) {
104
- const machine = input.machines.find((candidate) => candidate.peerId === input.peerId) ?? null;
105
- const key = machine?.isOwner ? machine.publicKey : null;
106
- const refusal = refusalFor(input, machine);
107
- const removesOwnerKey = key !== null;
108
71
  return {
109
- machine,
110
- refusal,
111
- removesOwnerKey,
112
- dependents: removesOwnerKey ? dependentsOf(input, key) : { owners: [], peers: [] },
72
+ machine: input.machines.find((candidate) => candidate.peerId === input.peerId) ?? null,
73
+ refusal: refusalFor(input),
113
74
  invitations: invitationsOpenTo(input),
114
- ownDependency: removesOwnerKey && isOwnDependency(input, key),
115
75
  };
116
76
  }
117
- function refusalFor(input, machine) {
118
- if (input.selfOwnerKey === null) {
77
+ function refusalFor(input) {
78
+ if (!input.canSign) {
119
79
  return 'not-an-owner';
120
80
  }
121
81
  if (input.peerId === input.selfPeerId) {
122
82
  return 'self';
123
83
  }
124
- if (machine?.isOwner && input.owners.length <= 1) {
125
- return 'last-owner';
126
- }
127
84
  return null;
128
85
  }
129
- function dependentsOf(input, key) {
130
- return {
131
- owners: input.owners
132
- .filter((owner) => owner.vouchOwner === key && owner.key !== key && owner.key !== input.selfOwnerKey)
133
- .map((owner) => owner.key),
134
- peers: input.rows
135
- .filter((row) => row.vouchOwner === key && row.peerId !== input.peerId && row.peerId !== input.selfPeerId)
136
- .map((row) => row.peerId),
137
- };
138
- }
139
- function isOwnDependency(input, key) {
140
- const ownRow = input.rows.find((row) => row.peerId === input.selfPeerId);
141
- const ownOwner = input.owners.find((owner) => owner.key === input.selfOwnerKey);
142
- return ownRow?.vouchOwner === key || ownOwner?.vouchOwner === key;
143
- }
144
86
  function invitationsOpenTo(input) {
145
87
  const redeemed = new Set(input.usages.filter((usage) => usage.peerId === input.peerId).map((usage) => usage.inviteKey));
146
88
  return input.invites
147
89
  .filter((status) => status.live && (status.invite.peerId === input.peerId || redeemed.has(status.invite.key)))
148
90
  .map((status) => status.invite.key);
149
91
  }
150
- /**
151
- * The re-vouch a caller asked for, checked against the plan: every key and peer must be in
152
- * `plan.dependents`, so a caller cannot re-vouch a row the removed owner never vouched (nor
153
- * the machine itself, which `dependents.peers` excludes). Throws naming the offenders, before
154
- * `CadreNode.removeMachine` writes anything. Duplicates are dropped.
155
- */
156
- export function revouchWithinPlan(plan, revouch) {
157
- const owners = [...new Set(revouch?.owners ?? [])];
158
- const peers = [...new Set(revouch?.peers ?? [])];
159
- const strayOwners = owners.filter((key) => !plan.dependents.owners.includes(key));
160
- const strayPeers = peers.filter((peerId) => !plan.dependents.peers.includes(peerId));
161
- if (strayOwners.length > 0 || strayPeers.length > 0) {
162
- throw new Error(`removeMachine: re-vouch names rows the removed machine did not vouch: ${[...strayOwners, ...strayPeers].join(', ')}`);
163
- }
164
- return { owners, peers };
165
- }
166
92
  //# sourceMappingURL=machine-removal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"machine-removal.js","sourceRoot":"","sources":["../src/machine-removal.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"machine-removal.js","sourceRoot":"","sources":["../src/machine-removal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,250 @@
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 type { Libp2p } from '@libp2p/interface';
23
+ import { type ControlStream } from './control-stream.js';
24
+ import type { CadreJoinProgress } from './types.js';
25
+ /**
26
+ * Cap on either frame of either protocol. A request is one signed row, two signatures and a few
27
+ * addresses; a reply adds at most a peer list of a cadre's size. Both fit in a few KB; 64 KiB is
28
+ * the defensive bound a stranger can make the handler buffer.
29
+ */
30
+ export declare const MAX_REDEMPTION_FRAME_BYTES: number;
31
+ /** Longest string field accepted off the wire; a stamp is 342 base64url chars, a signature 86. */
32
+ export declare const MAX_WIRE_FIELD_LENGTH = 1024;
33
+ export declare function isBoundedString(value: unknown, max?: number): value is string;
34
+ export declare function isEd25519KeyB64(value: unknown): value is string;
35
+ /** Is `value` a null-or-bounded-string column, as the nullable text columns of a travelling row are? */
36
+ export declare function isNullableBoundedString(value: unknown, max?: number): value is string | null;
37
+ /** Is `value` a null-or-positive-integer use cap? */
38
+ export declare function isNullableUseCap(value: unknown): value is number | null;
39
+ /** A refusal as the member sends it: a code, a reason, nothing else. */
40
+ export interface RedemptionRejection<Code extends string> {
41
+ accepted: false;
42
+ code: Code;
43
+ reason: string;
44
+ }
45
+ /**
46
+ * A refusal as the holder reads it: the code and reason verbatim off the wire, untyped, so each
47
+ * protocol's rejected-error is the one place that classifies a code this build does not know
48
+ * (as `'unrecognized'`, retryable) rather than the decoder folding it into a code it does.
49
+ */
50
+ export interface RedemptionRefusalFrame {
51
+ accepted: false;
52
+ code: unknown;
53
+ reason: unknown;
54
+ }
55
+ export declare function rejection<Code extends string>(code: Code, reason: string): RedemptionRejection<Code>;
56
+ /** One row of a protocol's constraint table: the constraint names the schema refuses a write with, and the code and reason each maps to. */
57
+ export type ConstraintRefusalRow<Code extends string> = readonly [RegExp, Code, string];
58
+ /** Which named constraint, if any, refused the write — read off the error's cause chain. */
59
+ export declare function constraintRefusal<Code extends string>(error: unknown, table: ReadonlyArray<ConstraintRefusalRow<Code>>): RedemptionRejection<Code> | null;
60
+ /**
61
+ * A write failure that is not a refusal: this member cannot read the credential row the cadre
62
+ * holds yet, or the retry gave up (`conflict`), or something else (`internal`).
63
+ */
64
+ export declare function writeFailureRefusal(error: unknown): RedemptionRejection<'conflict' | 'internal'>;
65
+ export interface MemberRedemptionHandlerOptions {
66
+ /** This member's party id; a request for another party is refused by name. */
67
+ partyId: string;
68
+ /**
69
+ * Push this member's control store to the holder, resolving once the push has finished, and
70
+ * keep the holder counted in this member's control write cohort until the returned release
71
+ * is called. Called after the request verified and before the row is seated and the
72
+ * redemption is written; the handler releases once both have settled. Absent, the writes go
73
+ * ahead with the holder neither caught up nor in the cohort, so it does not hold them; see
74
+ * {@link MemberRedemptionHandler.admitIfLive}.
75
+ */
76
+ admitDevice?: (peerId: string) => Promise<() => void>;
77
+ /** Time to wait for the request frame; default {@link DEFAULT_REDEEM_READ_TIMEOUT_MS}. */
78
+ redeemReadTimeoutMs?: number;
79
+ /** Cap on concurrent inbound redemptions; default {@link DEFAULT_MAX_CONCURRENT_REDEMPTIONS}. */
80
+ maxConcurrentRedemptions?: number;
81
+ }
82
+ /**
83
+ * Member side of one redemption protocol: registers the handler and drives each inbound stream
84
+ * through the checks in cost order — shape, then whatever the protocol checks without database
85
+ * work (party, signatures, and for an invitation the connecting identity and target device) —
86
+ * so no database work happens for a stranger that cannot prove anything; then admits the holder
87
+ * to this member's control replication (`admitDevice`) when the credential is live here, and
88
+ * runs the protocol's writes. Hardened as the seed handler is: a concurrency cap (over it,
89
+ * `busy`) and a read timeout.
90
+ *
91
+ * Registered only by a started `CadreNode`, after its control database is up, like the
92
+ * formation responder; nothing here is reachable on a node without one.
93
+ *
94
+ * A subclass supplies the five credential-specific pieces: the request's shape, the checks after
95
+ * it, the liveness read behind the push, the writes, and the two fixed refusals the base sends
96
+ * on its own (`busy`, and `internal` for a frame it could not read or a failure it did not
97
+ * expect).
98
+ */
99
+ export declare abstract class MemberRedemptionHandler<Request, Reply extends {
100
+ accepted: boolean;
101
+ }> {
102
+ protected readonly label: string;
103
+ protected readonly protocolId: string;
104
+ protected readonly partyId: string;
105
+ private readonly admitDevice;
106
+ private readonly readTimeoutMs;
107
+ private readonly maxConcurrent;
108
+ private readonly registered;
109
+ private active;
110
+ /**
111
+ * @param label - Names the credential in log lines and stream labels ("Cadre invite", "Owner grant").
112
+ * @param protocolId - The protocol id {@link register} handles by default.
113
+ */
114
+ protected constructor(label: string, protocolId: string, options: MemberRedemptionHandlerOptions);
115
+ /** The refusal for a caller over the concurrency cap. */
116
+ protected abstract readonly busyRefusal: Reply;
117
+ /** The refusal for a request frame the member could not read, or an unexpected failure: nothing was proved either way, so the holder stays free to try the next address. */
118
+ protected abstract readonly internalRefusal: Reply;
119
+ /** The refusal for a frame that is not a well-formed request. */
120
+ protected abstract readonly malformedRefusal: Reply;
121
+ /** The request's shape, every field in its wire form and bounded. */
122
+ protected abstract isWellFormedRequest(value: unknown): value is Request;
123
+ /** The checks after the shape that cost no database work; null to go on to the writes. */
124
+ protected abstract checkRequest(request: Request, remotePeerId: string): Reply | null;
125
+ /** Is the credential the (checked) request carries still live on this member? Decides whether the holder is pushed the store (see {@link admitIfLive}). */
126
+ protected abstract isLive(request: Request): Promise<boolean>;
127
+ /** The protocol's writes under the admission, each answered by name when it fails. */
128
+ protected abstract redeemAdmitted(request: Request, remotePeerId: string): Promise<Reply>;
129
+ /** In-flight inbound redemptions. */
130
+ get activeCount(): number;
131
+ /**
132
+ * Register on `node`; rejects when libp2p refuses the registration. `runOnLimitedConnection`
133
+ * because a phone redeems through a relay, whose connection libp2p marks limited; the
134
+ * exchange is two small frames, well inside any relay's cap.
135
+ */
136
+ register(node: Libp2p, protocolId?: string): Promise<void>;
137
+ unregister(node: Libp2p, protocolId?: string): Promise<void>;
138
+ /**
139
+ * One inbound redemption: read the frame, decide, reply, close. Public as the seam a protocol
140
+ * spec drives directly; `remotePeerId` is the connection's authenticated peer.
141
+ */
142
+ handleStream(stream: ControlStream, remotePeerId: string): Promise<void>;
143
+ /**
144
+ * Read the request frame to EOF, bounded and size-capped; JSON only, shape is the subclass's.
145
+ * Throws on a frame that cannot be read, which {@link handleStream} answers `internal`: a read
146
+ * timeout or a cut frame proves nothing about the credential, so the holder must stay free to
147
+ * try the next address.
148
+ */
149
+ private readRequest;
150
+ private decide;
151
+ /**
152
+ * Admit the holder to this member's control replication before the first write of the
153
+ * exchange (the seat, when this member does not hold the row; otherwise the redemption), and
154
+ * resolve to the release that ends the admission. The holder is not a member, so only this
155
+ * puts it in this member's control write cohort, and it must be there to hold the rows the
156
+ * exchange writes. It is pushed the control store first, because a cohort node with no base
157
+ * revision of a block refuses a commit to it; a member alone with the holder would then have
158
+ * half its cohort refusing, and the write would tear instead of committing.
159
+ *
160
+ * Only for a credential still live here ({@link isLive}, judged on the row this member holds
161
+ * or, before the seat, on the bundle's copy once its issuer signature verifies): the holder
162
+ * of a withdrawn, expired or spent one, or of a forged copy naming a real owner, is refused
163
+ * without being sent anything. The retry of a redemption already written can read as spent
164
+ * too and is sent nothing; it needs nothing, since the rows it would hold are written and
165
+ * the ordinary catch-up reaches a member. Best-effort: a failed check or push is logged and
166
+ * the writes go ahead without the holder admitted, then commit or are answered retryably.
167
+ */
168
+ private admitIfLive;
169
+ }
170
+ /** What one address the bundle named came to. */
171
+ export interface RedemptionAddressOutcome {
172
+ addr: string;
173
+ /** A dial or exchange failure, or a retryable refusal. */
174
+ error: Error;
175
+ }
176
+ /** A member's refusal as the holder classifies it; `retryable` decides whether the next address is tried. */
177
+ export type RedemptionRefusal = Error & {
178
+ readonly retryable: boolean;
179
+ };
180
+ /** What a successful redemption yields the holder. */
181
+ export interface MemberRedemption<Accepted> {
182
+ reply: Accepted;
183
+ /** The answering member's peer id, from the address's trailing `/p2p/`; null when it named none. */
184
+ memberPeerId: string | null;
185
+ /** The address that answered. */
186
+ memberAddr: string;
187
+ }
188
+ export interface MemberRedemptionOptions<Accepted> {
189
+ /** Names the credential in log lines and deadline labels ("Cadre invitation", "Owner grant"). */
190
+ label: string;
191
+ protocolId: string;
192
+ /** The bundle's multiaddrs ending in `/p2p/<peerId>`, dialed in order. */
193
+ members: readonly string[];
194
+ /** The signed request; the same one goes to every address (and every pass). */
195
+ request: unknown;
196
+ /** Read an acceptance frame's fields into `Accepted`, sanitizing them; throws on a malformed one, which moves the holder to the next address. */
197
+ decodeAccepted(frame: Record<string, unknown>): Accepted;
198
+ /** A refusal frame's code and reason, as the protocol classifies them. */
199
+ refusal(code: unknown, reason: unknown): RedemptionRefusal;
200
+ /** The holder's check on an acceptance; throws when it does not check out, which ends the call. */
201
+ verifyAccepted(reply: Accepted): void;
202
+ /** The error for every address tried and none accepting. */
203
+ unreachable(outcomes: readonly RedemptionAddressOutcome[]): Error;
204
+ /**
205
+ * This machine's declared link round trip (`NetworkConfig.linkRoundTripMs`), from which the
206
+ * per-address deadline derives: a dial that may need a relay plus one request
207
+ * (`relayedRequestBudgetMs`, 28.5 s at the default declaration), as seed delivery's is.
208
+ */
209
+ linkRoundTripMs?: number;
210
+ /** Per-address deadline override; default derived from `linkRoundTripMs`. */
211
+ addressBudgetMs?: number;
212
+ /** Cancels the dial or stream in flight and ends the pass; the call throws the signal's reason. */
213
+ signal?: AbortSignal;
214
+ }
215
+ /**
216
+ * Holder side: try the bundle's addresses one at a time ({@link dialOnce}) and throw the
217
+ * protocol's unreachable error when none accepts.
218
+ *
219
+ * Every address gets the SAME request, with one usage nonce: the usage table is keyed by it,
220
+ * so a member the holder gave up on that was still committing cannot redeem it twice, and a
221
+ * retry at the next member after a dropped reply is answered as already done.
222
+ *
223
+ * @throws the protocol's rejected error on a final refusal, its reply-invalid error on an
224
+ * acceptance that fails `verifyAccepted`, its unreachable error when every address was tried
225
+ * and none accepted, and the reason of `options.signal` once it aborts.
226
+ */
227
+ export declare function dialRedemption<Accepted>(node: Libp2p, options: MemberRedemptionOptions<Accepted>): Promise<MemberRedemption<Accepted>>;
228
+ export interface MemberRedemptionPassOptions<Accepted> extends MemberRedemptionOptions<Accepted> {
229
+ /** Keep making passes while every answer is retryable; false makes exactly one. */
230
+ waitForMember: boolean;
231
+ /** Called at each pass and each pause; an exception it throws is logged and ignored. */
232
+ onProgress?: (progress: CadreJoinProgress) => void;
233
+ /** The credential's stored expiry (its row's `expiresAt`, a `datetime` string); null for none. */
234
+ expiresAt: string | null;
235
+ /** The refusal a member would give the credential once expired, thrown when this holder's own clock says it has. */
236
+ expired(): Error;
237
+ }
238
+ /**
239
+ * Holder side, in passes: one {@link dialOnce} pass over the bundle's addresses, or, with
240
+ * `waitForMember`, as many as it takes, pausing between them (5 s after the first, doubling to
241
+ * 60 s, never past the credential's expiry). Every pass sends the same signed request, so a
242
+ * member that wrote the redemption on a pass whose reply was lost answers a later pass as
243
+ * already done, with no second usage row.
244
+ *
245
+ * A wait ends at the first acceptance, at a final refusal or an invalid reply (thrown as
246
+ * {@link dialRedemption} throws them), when the credential expires by this holder's clock
247
+ * (thrown as `expired()`), when `signal` aborts (its reason thrown), and when `node` stops. A
248
+ * bundle that names no address has nothing to wait for and throws at once.
249
+ */
250
+ export declare function dialRedemptionInPasses<Accepted>(node: Libp2p, options: MemberRedemptionPassOptions<Accepted>): Promise<MemberRedemption<Accepted>>;