@serfab/cadre-core 1.13.0 → 1.15.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 (230) hide show
  1. package/README.md +293 -274
  2. package/dist/cadre-invite-protocol.d.ts +383 -0
  3. package/dist/cadre-invite-protocol.js +774 -0
  4. package/dist/cadre-invite-protocol.js.map +1 -0
  5. package/dist/cadre-join-request.d.ts +42 -0
  6. package/dist/cadre-join-request.js +144 -0
  7. package/dist/cadre-join-request.js.map +1 -0
  8. package/dist/cadre-node.d.ts +849 -210
  9. package/dist/cadre-node.js +1574 -374
  10. package/dist/cadre-node.js.map +1 -1
  11. package/dist/claim-proof.d.ts +45 -0
  12. package/dist/claim-proof.js +132 -0
  13. package/dist/claim-proof.js.map +1 -0
  14. package/dist/control-authorization.d.ts +80 -19
  15. package/dist/control-authorization.js +55 -13
  16. package/dist/control-authorization.js.map +1 -1
  17. package/dist/control-cohort.js +1 -1
  18. package/dist/control-cohort.js.map +1 -1
  19. package/dist/control-database.d.ts +605 -108
  20. package/dist/control-database.js +1299 -280
  21. package/dist/control-database.js.map +1 -1
  22. package/dist/control-protocol-guard.d.ts +142 -0
  23. package/dist/control-protocol-guard.js +293 -0
  24. package/dist/control-protocol-guard.js.map +1 -0
  25. package/dist/control-schema.d.ts +1 -1
  26. package/dist/control-schema.js +817 -1067
  27. package/dist/control-schema.js.map +1 -1
  28. package/dist/control-stream.d.ts +4 -0
  29. package/dist/control-stream.js +22 -7
  30. package/dist/control-stream.js.map +1 -1
  31. package/dist/delegate-admission.d.ts +1 -1
  32. package/dist/delegate-admission.js +1 -1
  33. package/dist/device-token.d.ts +11 -9
  34. package/dist/device-token.js +14 -12
  35. package/dist/device-token.js.map +1 -1
  36. package/dist/ed25519-key.d.ts +22 -9
  37. package/dist/ed25519-key.js +47 -10
  38. package/dist/ed25519-key.js.map +1 -1
  39. package/dist/fetch-impl.d.ts +12 -0
  40. package/dist/fetch-impl.js +23 -0
  41. package/dist/fetch-impl.js.map +1 -0
  42. package/dist/file-durable-slot.js +2 -1
  43. package/dist/file-durable-slot.js.map +1 -1
  44. package/dist/file-safe-name.d.ts +25 -0
  45. package/dist/file-safe-name.js +29 -0
  46. package/dist/file-safe-name.js.map +1 -0
  47. package/dist/formation-approval.js +5 -11
  48. package/dist/formation-approval.js.map +1 -1
  49. package/dist/fs-atomic.d.ts +0 -19
  50. package/dist/fs-atomic.js +3 -24
  51. package/dist/fs-atomic.js.map +1 -1
  52. package/dist/index.d.ts +22 -10
  53. package/dist/index.js +54 -11
  54. package/dist/index.js.map +1 -1
  55. package/dist/invitation-bootstrap.d.ts +50 -12
  56. package/dist/invitation-bootstrap.js +76 -13
  57. package/dist/invitation-bootstrap.js.map +1 -1
  58. package/dist/joined-strand-store.d.ts +7 -0
  59. package/dist/joined-strand-store.js +9 -0
  60. package/dist/joined-strand-store.js.map +1 -1
  61. package/dist/key-store-file.js +2 -1
  62. package/dist/key-store-file.js.map +1 -1
  63. package/dist/link-budget.d.ts +25 -33
  64. package/dist/link-budget.js +25 -35
  65. package/dist/link-budget.js.map +1 -1
  66. package/dist/machine-removal.d.ts +78 -0
  67. package/dist/machine-removal.js +166 -0
  68. package/dist/machine-removal.js.map +1 -0
  69. package/dist/membership-connection-gater.d.ts +191 -151
  70. package/dist/membership-connection-gater.js +236 -173
  71. package/dist/membership-connection-gater.js.map +1 -1
  72. package/dist/node-claim-payload.d.ts +74 -0
  73. package/dist/node-claim-payload.js +224 -0
  74. package/dist/node-claim-payload.js.map +1 -0
  75. package/dist/node-default-transports.d.ts +27 -0
  76. package/dist/node-default-transports.js +35 -0
  77. package/dist/node-default-transports.js.map +1 -0
  78. package/dist/node-transports.d.ts +30 -0
  79. package/dist/node-transports.js +40 -0
  80. package/dist/node-transports.js.map +1 -0
  81. package/dist/owner-anchor-sync.d.ts +22 -0
  82. package/dist/owner-anchor-sync.js +143 -0
  83. package/dist/owner-anchor-sync.js.map +1 -0
  84. package/dist/peer-authorization.d.ts +113 -18
  85. package/dist/peer-authorization.js +225 -29
  86. package/dist/peer-authorization.js.map +1 -1
  87. package/dist/peer-dial.d.ts +143 -27
  88. package/dist/peer-dial.js +276 -31
  89. package/dist/peer-dial.js.map +1 -1
  90. package/dist/peer-join-backfill.d.ts +26 -2
  91. package/dist/peer-join-backfill.js +60 -12
  92. package/dist/peer-join-backfill.js.map +1 -1
  93. package/dist/peer-record.d.ts +17 -52
  94. package/dist/peer-record.js +20 -58
  95. package/dist/peer-record.js.map +1 -1
  96. package/dist/pending-join-runner.d.ts +66 -58
  97. package/dist/pending-join-runner.js +136 -145
  98. package/dist/pending-join-runner.js.map +1 -1
  99. package/dist/primary-lan-address.d.ts +8 -0
  100. package/dist/primary-lan-address.js +46 -0
  101. package/dist/primary-lan-address.js.map +1 -0
  102. package/dist/provider/client.d.ts +131 -0
  103. package/dist/provider/client.js +226 -0
  104. package/dist/provider/client.js.map +1 -0
  105. package/dist/provider/descriptor.d.ts +41 -0
  106. package/dist/provider/descriptor.js +103 -0
  107. package/dist/provider/descriptor.js.map +1 -0
  108. package/dist/provider/index.d.ts +9 -0
  109. package/dist/provider/index.js +10 -0
  110. package/dist/provider/index.js.map +1 -0
  111. package/dist/provider/oauth.d.ts +125 -0
  112. package/dist/provider/oauth.js +199 -0
  113. package/dist/provider/oauth.js.map +1 -0
  114. package/dist/provider/rent-node.d.ts +101 -0
  115. package/dist/provider/rent-node.js +220 -0
  116. package/dist/provider/rent-node.js.map +1 -0
  117. package/dist/push-fanout.d.ts +8 -2
  118. package/dist/push-fanout.js +8 -2
  119. package/dist/push-fanout.js.map +1 -1
  120. package/dist/relay-addrs.d.ts +9 -4
  121. package/dist/relay-addrs.js +9 -4
  122. package/dist/relay-addrs.js.map +1 -1
  123. package/dist/relay-reservation.d.ts +51 -16
  124. package/dist/relay-reservation.js +107 -24
  125. package/dist/relay-reservation.js.map +1 -1
  126. package/dist/seed-bootstrap.d.ts +165 -123
  127. package/dist/seed-bootstrap.js +313 -275
  128. package/dist/seed-bootstrap.js.map +1 -1
  129. package/dist/seed-trust-policy.d.ts +98 -37
  130. package/dist/seed-trust-policy.js +188 -2
  131. package/dist/seed-trust-policy.js.map +1 -1
  132. package/dist/strand-addr-protocol.d.ts +24 -26
  133. package/dist/strand-addr-protocol.js +19 -26
  134. package/dist/strand-addr-protocol.js.map +1 -1
  135. package/dist/strand-commit-durability.d.ts +54 -0
  136. package/dist/strand-commit-durability.js +74 -0
  137. package/dist/strand-commit-durability.js.map +1 -0
  138. package/dist/strand-database.d.ts +23 -8
  139. package/dist/strand-database.js +41 -13
  140. package/dist/strand-database.js.map +1 -1
  141. package/dist/strand-first-sync-gate.d.ts +7 -1
  142. package/dist/strand-first-sync-gate.js +7 -1
  143. package/dist/strand-first-sync-gate.js.map +1 -1
  144. package/dist/strand-formation-protocol.js +5 -2
  145. package/dist/strand-formation-protocol.js.map +1 -1
  146. package/dist/strand-fret-addrs.d.ts +19 -1
  147. package/dist/strand-fret-addrs.js +27 -9
  148. package/dist/strand-fret-addrs.js.map +1 -1
  149. package/dist/strand-instance-manager.d.ts +81 -19
  150. package/dist/strand-instance-manager.js +216 -105
  151. package/dist/strand-instance-manager.js.map +1 -1
  152. package/dist/strand-move.d.ts +124 -0
  153. package/dist/strand-move.js +438 -0
  154. package/dist/strand-move.js.map +1 -0
  155. package/dist/strand-network-config.js +7 -6
  156. package/dist/strand-network-config.js.map +1 -1
  157. package/dist/strand-peer-redial.d.ts +113 -0
  158. package/dist/strand-peer-redial.js +236 -0
  159. package/dist/strand-peer-redial.js.map +1 -0
  160. package/dist/strand-revocation-enforcer.d.ts +11 -11
  161. package/dist/strand-revocation-enforcer.js +11 -11
  162. package/dist/strand-wake-protocol.d.ts +13 -16
  163. package/dist/strand-wake-protocol.js +23 -27
  164. package/dist/strand-wake-protocol.js.map +1 -1
  165. package/dist/trusted-owner-store.d.ts +35 -5
  166. package/dist/trusted-owner-store.js +50 -8
  167. package/dist/trusted-owner-store.js.map +1 -1
  168. package/dist/types.d.ts +512 -89
  169. package/dist/types.js +10 -0
  170. package/dist/types.js.map +1 -1
  171. package/package.json +19 -10
  172. package/src/cadre-invite-protocol.ts +1052 -0
  173. package/src/cadre-join-request.ts +167 -0
  174. package/src/cadre-node.ts +1703 -386
  175. package/src/claim-proof.ts +147 -0
  176. package/src/control-authorization.ts +203 -117
  177. package/src/control-cohort.ts +1 -1
  178. package/src/control-database.ts +4663 -3453
  179. package/src/control-protocol-guard.ts +348 -0
  180. package/src/control-schema.ts +828 -1078
  181. package/src/control-stream.ts +26 -7
  182. package/src/delegate-admission.ts +1 -1
  183. package/src/device-token.ts +14 -10
  184. package/src/ed25519-key.ts +47 -10
  185. package/src/fetch-impl.ts +23 -0
  186. package/src/file-durable-slot.ts +64 -63
  187. package/src/file-safe-name.ts +32 -0
  188. package/src/formation-approval.ts +604 -610
  189. package/src/fs-atomic.ts +92 -117
  190. package/src/index.ts +789 -648
  191. package/src/invitation-bootstrap.ts +91 -13
  192. package/src/joined-strand-store.ts +508 -498
  193. package/src/key-store-file.ts +142 -141
  194. package/src/link-budget.ts +25 -36
  195. package/src/machine-removal.ts +216 -0
  196. package/src/membership-connection-gater.ts +259 -191
  197. package/src/node-claim-payload.ts +259 -0
  198. package/src/node-default-transports.ts +38 -0
  199. package/src/node-transports.ts +49 -0
  200. package/src/owner-anchor-sync.ts +171 -0
  201. package/src/peer-authorization.ts +258 -29
  202. package/src/peer-dial.ts +604 -315
  203. package/src/peer-join-backfill.ts +62 -11
  204. package/src/peer-record.ts +159 -197
  205. package/src/pending-join-runner.ts +729 -735
  206. package/src/primary-lan-address.ts +47 -0
  207. package/src/provider/client.ts +326 -0
  208. package/src/provider/descriptor.ts +129 -0
  209. package/src/provider/index.ts +59 -0
  210. package/src/provider/oauth.ts +285 -0
  211. package/src/provider/rent-node.ts +275 -0
  212. package/src/push-fanout.ts +8 -2
  213. package/src/relay-addrs.ts +9 -4
  214. package/src/relay-reservation.ts +1211 -1121
  215. package/src/seed-bootstrap.ts +1502 -1448
  216. package/src/seed-trust-policy.ts +353 -140
  217. package/src/strand-addr-protocol.ts +26 -35
  218. package/src/strand-commit-durability.ts +116 -0
  219. package/src/strand-database.ts +46 -14
  220. package/src/strand-first-sync-gate.ts +7 -1
  221. package/src/strand-formation-protocol.ts +1039 -1036
  222. package/src/strand-fret-addrs.ts +179 -145
  223. package/src/strand-instance-manager.ts +2026 -1883
  224. package/src/strand-move.ts +585 -0
  225. package/src/strand-network-config.ts +7 -6
  226. package/src/strand-peer-redial.ts +307 -0
  227. package/src/strand-revocation-enforcer.ts +11 -11
  228. package/src/strand-wake-protocol.ts +23 -30
  229. package/src/trusted-owner-store.ts +242 -171
  230. package/src/types.ts +2687 -2242
@@ -1,23 +1,43 @@
1
1
  /**
2
- * Which of the inviting party's other machines an open invitation names, so a joiner can still
3
- * form while the machine that minted it is offline. The minting machine's own addresses go
4
- * first and are not chosen here (`CadreNode.createOpenInvitation`).
2
+ * Which of the inviting party's machines an invitation names, so a joiner can still form or
3
+ * join while the machine that minted it is offline: an open strand invitation
4
+ * (`CadreNode.createOpenInvitation`, which puts the minting machine's own addresses first
5
+ * itself) and a cadre invitation ({@link selectCadreInvitationMembers}). Also how far a cadre
6
+ * invitation reaches once its minting machine is gone ({@link cadreInvitationReach}).
5
7
  */
6
8
 
9
+ import { classifyDialAddress, destinationPeerIdOf, type DialAddressKind } from './node-claim-payload.js';
10
+ import type { CadreInvitationReach } from './types.js';
11
+
7
12
  /**
8
13
  * At most this many of the party's other machines are named, each with at most
9
14
  * {@link INVITATION_ADDRS_PER_SIBLING} addresses: 12 extra addresses of roughly 100–200
10
15
  * characters, base64url-encoded into an invitation shared as text.
11
16
  *
12
- * NOTE: if invitations are ever carried in a QR code, lower these caps or name the machines by
13
- * peer id only.
14
- * NOTE: the per-machine cap keeps the record's order (relay first, then direct addresses as the
15
- * machine published them, loopback included). If a sibling with many interfaces is seen to have
16
- * its reachable address cut, rank loopback and private addresses last before slicing.
17
+ * NOTE: a cadre invitation, which may be carried in a QR code, is capped harder
18
+ * ({@link CADRE_INVITATION_MAX_ADDRS}); an open strand invitation keeps these caps. If open
19
+ * invitations are ever carried in a QR code too, cap them the same way.
20
+ * NOTE: for an open strand invitation the per-machine cap keeps the record's order (relay first,
21
+ * then direct addresses as the machine published them, loopback included); a cadre invitation
22
+ * ranks them first. If an open invitation's sibling is seen to have its reachable address cut,
23
+ * rank the same way.
17
24
  */
18
25
  export const INVITATION_SIBLING_MACHINES = 3;
19
26
  export const INVITATION_ADDRS_PER_SIBLING = 4;
20
27
 
28
+ /**
29
+ * Most addresses a cadre invitation names in total, so its text fits one QR code. Measured
30
+ * with three owner keys: 2,206 characters encoded at 6 relayed addresses of about 155
31
+ * characters (the worst case, since ranking puts relayed addresses first), 1,972 with relayed
32
+ * and direct WebSocket addresses alternating, against 2,331 bytes in the largest QR code at
33
+ * error-correction level M (2,953 at L). Each further address costs about 170 characters, each
34
+ * further owner key about 60.
35
+ */
36
+ export const CADRE_INVITATION_MAX_ADDRS = 6;
37
+
38
+ /** Most addresses a cadre invitation names for each of the party's other machines. */
39
+ export const CADRE_INVITATION_ADDRS_PER_SIBLING = 2;
40
+
21
41
  /** One other machine of the party whose signed address record resolved. */
22
42
  export interface InvitationSibling {
23
43
  peerId: string;
@@ -28,13 +48,71 @@ export interface InvitationSibling {
28
48
  }
29
49
 
30
50
  /**
31
- * The addresses of the siblings an invitation names. Machines this node is connected to right
32
- * now come first, since a live connection is the best evidence that a machine is up; a record
33
- * shows only that it was up within the freshness window. Within each half, newest record first.
51
+ * The addresses of the siblings an invitation names, at most `perSibling` each. Machines this
52
+ * node is connected to right now come first, since a live connection is the best evidence that
53
+ * a machine is up; a record shows only that it was up within the freshness window. Within each
54
+ * half, newest record first.
34
55
  */
35
- export function selectInvitationSiblingAddrs(siblings: InvitationSibling[], connected: ReadonlySet<string>): string[] {
56
+ export function selectInvitationSiblingAddrs(
57
+ siblings: InvitationSibling[],
58
+ connected: ReadonlySet<string>,
59
+ perSibling: number = INVITATION_ADDRS_PER_SIBLING
60
+ ): string[] {
36
61
  return [...siblings]
37
62
  .sort((a, b) => Number(connected.has(b.peerId)) - Number(connected.has(a.peerId)) || b.updatedAt - a.updatedAt)
38
63
  .slice(0, INVITATION_SIBLING_MACHINES)
39
- .flatMap((sibling) => sibling.addrs.slice(0, INVITATION_ADDRS_PER_SIBLING));
64
+ .flatMap((sibling) => sibling.addrs.slice(0, perSibling));
65
+ }
66
+
67
+ /**
68
+ * The `members` of a cadre invitation: the minting machine's own addresses first (at most
69
+ * {@link INVITATION_ADDRS_PER_SIBLING}), then the other machines' in
70
+ * {@link selectInvitationSiblingAddrs} order (at most {@link CADRE_INVITATION_ADDRS_PER_SIBLING}
71
+ * each), the whole list cut to {@link CADRE_INVITATION_MAX_ADDRS}. Each machine's addresses are
72
+ * ranked for a phone before they are cut ({@link rankForPhone}): libp2p reports a machine
73
+ * listening on every interface with its loopback and private addresses ahead of its relayed one.
74
+ *
75
+ * NOTE: accepted tradeoff — with two or more other machines, only the best two addresses of
76
+ * each survive, connected machines first, so a reachable address of a sibling can be cut. Size
77
+ * was weighed over completeness, so the text fits a QR code; revisit if redemptions are seen
78
+ * failing for want of a sibling address the record held.
79
+ */
80
+ export function selectCadreInvitationMembers(
81
+ own: readonly string[],
82
+ siblings: InvitationSibling[],
83
+ connected: ReadonlySet<string>
84
+ ): string[] {
85
+ const ranked = siblings.map((sibling) => ({ ...sibling, addrs: rankForPhone(sibling.addrs) }));
86
+ return [
87
+ ...rankForPhone(own).slice(0, INVITATION_ADDRS_PER_SIBLING),
88
+ ...selectInvitationSiblingAddrs(ranked, connected, CADRE_INVITATION_ADDRS_PER_SIBLING),
89
+ ].slice(0, CADRE_INVITATION_MAX_ADDRS);
90
+ }
91
+
92
+ /** Order in which a phone is likeliest to reach an address: from anywhere, on its network, on that machine, not at all. */
93
+ const PHONE_DIAL_RANK: Record<DialAddressKind, number> = { relay: 0, name: 0, public: 0, private: 1, loopback: 2 };
94
+
95
+ /** `addrs` best first for a phone dialing them ({@link PHONE_DIAL_RANK}), ties in their given order; one a phone cannot dial (plain TCP) last. */
96
+ function rankForPhone(addrs: readonly string[]): string[] {
97
+ const rankOf = (addr: string): number => {
98
+ const kind = classifyDialAddress(addr);
99
+ return kind === undefined ? 3 : PHONE_DIAL_RANK[kind];
100
+ };
101
+ return addrs.map((addr) => ({ addr, rank: rankOf(addr) })).sort((a, b) => a.rank - b.rank).map(({ addr }) => addr);
102
+ }
103
+
104
+ /**
105
+ * Where a phone could redeem an invitation naming `members` from, judged on the machines other
106
+ * than `issuerPeerId`: in the app flows the minting app is the one closed or in the background
107
+ * when the other app redeems, and two apps on one phone cannot reach each other. An address
108
+ * names its machine by its last `/p2p/`.
109
+ */
110
+ export function cadreInvitationReach(members: readonly string[], issuerPeerId: string): CadreInvitationReach {
111
+ const others = members.flatMap((addr) => {
112
+ const kind = classifyDialAddress(addr);
113
+ // Classified, so it parses and `destinationPeerIdOf` cannot throw.
114
+ return kind === undefined || destinationPeerIdOf(addr) === issuerPeerId ? [] : [kind];
115
+ });
116
+ if (others.some((kind) => kind === 'relay' || kind === 'name' || kind === 'public')) return 'anywhere';
117
+ return others.length > 0 ? 'local-network' : 'issuer-only';
40
118
  }