@serfab/cadre-core 1.14.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 (171) hide show
  1. package/README.md +293 -274
  2. package/dist/cadre-invite-protocol.d.ts +64 -29
  3. package/dist/cadre-invite-protocol.js +207 -38
  4. package/dist/cadre-invite-protocol.js.map +1 -1
  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 +375 -79
  9. package/dist/cadre-node.js +758 -186
  10. package/dist/cadre-node.js.map +1 -1
  11. package/dist/control-database.d.ts +92 -12
  12. package/dist/control-database.js +323 -142
  13. package/dist/control-database.js.map +1 -1
  14. package/dist/control-schema.d.ts +1 -1
  15. package/dist/control-schema.js +19 -3
  16. package/dist/control-schema.js.map +1 -1
  17. package/dist/ed25519-key.d.ts +7 -9
  18. package/dist/ed25519-key.js +20 -10
  19. package/dist/ed25519-key.js.map +1 -1
  20. package/dist/fetch-impl.d.ts +12 -0
  21. package/dist/fetch-impl.js +23 -0
  22. package/dist/fetch-impl.js.map +1 -0
  23. package/dist/file-durable-slot.js +2 -1
  24. package/dist/file-durable-slot.js.map +1 -1
  25. package/dist/file-safe-name.d.ts +25 -0
  26. package/dist/file-safe-name.js +29 -0
  27. package/dist/file-safe-name.js.map +1 -0
  28. package/dist/formation-approval.js +5 -11
  29. package/dist/formation-approval.js.map +1 -1
  30. package/dist/fs-atomic.d.ts +0 -19
  31. package/dist/fs-atomic.js +3 -24
  32. package/dist/fs-atomic.js.map +1 -1
  33. package/dist/index.d.ts +12 -5
  34. package/dist/index.js +31 -6
  35. package/dist/index.js.map +1 -1
  36. package/dist/invitation-bootstrap.d.ts +50 -12
  37. package/dist/invitation-bootstrap.js +76 -13
  38. package/dist/invitation-bootstrap.js.map +1 -1
  39. package/dist/joined-strand-store.d.ts +7 -0
  40. package/dist/joined-strand-store.js +9 -0
  41. package/dist/joined-strand-store.js.map +1 -1
  42. package/dist/key-store-file.js +2 -1
  43. package/dist/key-store-file.js.map +1 -1
  44. package/dist/link-budget.d.ts +9 -4
  45. package/dist/link-budget.js +9 -4
  46. package/dist/link-budget.js.map +1 -1
  47. package/dist/machine-removal.d.ts +78 -0
  48. package/dist/machine-removal.js +166 -0
  49. package/dist/machine-removal.js.map +1 -0
  50. package/dist/membership-connection-gater.d.ts +6 -5
  51. package/dist/membership-connection-gater.js +6 -5
  52. package/dist/membership-connection-gater.js.map +1 -1
  53. package/dist/node-claim-payload.d.ts +55 -0
  54. package/dist/node-claim-payload.js +108 -4
  55. package/dist/node-claim-payload.js.map +1 -1
  56. package/dist/node-default-transports.d.ts +27 -0
  57. package/dist/node-default-transports.js +35 -0
  58. package/dist/node-default-transports.js.map +1 -0
  59. package/dist/node-transports.d.ts +30 -0
  60. package/dist/node-transports.js +40 -0
  61. package/dist/node-transports.js.map +1 -0
  62. package/dist/peer-dial.d.ts +124 -27
  63. package/dist/peer-dial.js +254 -30
  64. package/dist/peer-dial.js.map +1 -1
  65. package/dist/peer-join-backfill.js +6 -0
  66. package/dist/peer-join-backfill.js.map +1 -1
  67. package/dist/peer-record.d.ts +6 -45
  68. package/dist/peer-record.js +8 -50
  69. package/dist/peer-record.js.map +1 -1
  70. package/dist/primary-lan-address.d.ts +8 -0
  71. package/dist/primary-lan-address.js +46 -0
  72. package/dist/primary-lan-address.js.map +1 -0
  73. package/dist/provider/client.d.ts +131 -0
  74. package/dist/provider/client.js +226 -0
  75. package/dist/provider/client.js.map +1 -0
  76. package/dist/provider/descriptor.d.ts +41 -0
  77. package/dist/provider/descriptor.js +103 -0
  78. package/dist/provider/descriptor.js.map +1 -0
  79. package/dist/provider/index.d.ts +9 -0
  80. package/dist/provider/index.js +10 -0
  81. package/dist/provider/index.js.map +1 -0
  82. package/dist/provider/oauth.d.ts +125 -0
  83. package/dist/provider/oauth.js +199 -0
  84. package/dist/provider/oauth.js.map +1 -0
  85. package/dist/provider/rent-node.d.ts +101 -0
  86. package/dist/provider/rent-node.js +220 -0
  87. package/dist/provider/rent-node.js.map +1 -0
  88. package/dist/relay-addrs.d.ts +9 -4
  89. package/dist/relay-addrs.js +9 -4
  90. package/dist/relay-addrs.js.map +1 -1
  91. package/dist/relay-reservation.d.ts +51 -16
  92. package/dist/relay-reservation.js +107 -24
  93. package/dist/relay-reservation.js.map +1 -1
  94. package/dist/seed-bootstrap.d.ts +22 -5
  95. package/dist/seed-bootstrap.js +48 -25
  96. package/dist/seed-bootstrap.js.map +1 -1
  97. package/dist/strand-commit-durability.d.ts +54 -0
  98. package/dist/strand-commit-durability.js +74 -0
  99. package/dist/strand-commit-durability.js.map +1 -0
  100. package/dist/strand-database.d.ts +23 -8
  101. package/dist/strand-database.js +41 -13
  102. package/dist/strand-database.js.map +1 -1
  103. package/dist/strand-first-sync-gate.d.ts +7 -1
  104. package/dist/strand-first-sync-gate.js +7 -1
  105. package/dist/strand-first-sync-gate.js.map +1 -1
  106. package/dist/strand-formation-protocol.js +4 -1
  107. package/dist/strand-formation-protocol.js.map +1 -1
  108. package/dist/strand-fret-addrs.d.ts +19 -1
  109. package/dist/strand-fret-addrs.js +27 -9
  110. package/dist/strand-fret-addrs.js.map +1 -1
  111. package/dist/strand-instance-manager.d.ts +81 -19
  112. package/dist/strand-instance-manager.js +216 -105
  113. package/dist/strand-instance-manager.js.map +1 -1
  114. package/dist/strand-move.d.ts +124 -0
  115. package/dist/strand-move.js +438 -0
  116. package/dist/strand-move.js.map +1 -0
  117. package/dist/strand-network-config.js +7 -6
  118. package/dist/strand-network-config.js.map +1 -1
  119. package/dist/strand-peer-redial.d.ts +113 -0
  120. package/dist/strand-peer-redial.js +236 -0
  121. package/dist/strand-peer-redial.js.map +1 -0
  122. package/dist/strand-wake-protocol.js +10 -7
  123. package/dist/strand-wake-protocol.js.map +1 -1
  124. package/dist/types.d.ts +212 -4
  125. package/dist/types.js +10 -0
  126. package/dist/types.js.map +1 -1
  127. package/package.json +18 -10
  128. package/src/cadre-invite-protocol.ts +233 -45
  129. package/src/cadre-join-request.ts +167 -0
  130. package/src/cadre-node.ts +10003 -9381
  131. package/src/control-database.ts +4663 -4451
  132. package/src/control-schema.ts +19 -3
  133. package/src/ed25519-key.ts +20 -10
  134. package/src/fetch-impl.ts +23 -0
  135. package/src/file-durable-slot.ts +64 -63
  136. package/src/file-safe-name.ts +32 -0
  137. package/src/formation-approval.ts +604 -610
  138. package/src/fs-atomic.ts +92 -117
  139. package/src/index.ts +789 -722
  140. package/src/invitation-bootstrap.ts +91 -13
  141. package/src/joined-strand-store.ts +508 -498
  142. package/src/key-store-file.ts +142 -141
  143. package/src/link-budget.ts +9 -4
  144. package/src/machine-removal.ts +216 -0
  145. package/src/membership-connection-gater.ts +638 -637
  146. package/src/node-claim-payload.ts +138 -4
  147. package/src/node-default-transports.ts +38 -0
  148. package/src/node-transports.ts +49 -0
  149. package/src/peer-dial.ts +604 -336
  150. package/src/peer-join-backfill.ts +6 -0
  151. package/src/peer-record.ts +159 -202
  152. package/src/primary-lan-address.ts +47 -0
  153. package/src/provider/client.ts +326 -0
  154. package/src/provider/descriptor.ts +129 -0
  155. package/src/provider/index.ts +59 -0
  156. package/src/provider/oauth.ts +285 -0
  157. package/src/provider/rent-node.ts +275 -0
  158. package/src/relay-addrs.ts +9 -4
  159. package/src/relay-reservation.ts +1211 -1121
  160. package/src/seed-bootstrap.ts +1502 -1471
  161. package/src/strand-commit-durability.ts +116 -0
  162. package/src/strand-database.ts +46 -14
  163. package/src/strand-first-sync-gate.ts +7 -1
  164. package/src/strand-formation-protocol.ts +1039 -1036
  165. package/src/strand-fret-addrs.ts +179 -145
  166. package/src/strand-instance-manager.ts +2026 -1883
  167. package/src/strand-move.ts +585 -0
  168. package/src/strand-network-config.ts +7 -6
  169. package/src/strand-peer-redial.ts +307 -0
  170. package/src/strand-wake-protocol.ts +10 -7
  171. package/src/types.ts +220 -4
@@ -2,7 +2,7 @@ import { Database } from '@quereus/quereus';
2
2
  import type { SqlParameters } from '@quereus/quereus';
3
3
  import type { Libp2p } from '@libp2p/interface';
4
4
  import type { IRepo } from '@optimystic/db-core';
5
- import type { StrandRow, JoinRequestRow, JoinOutcome, PendingJoin, PeerAddressRecord, CadrePeerRow, OwnerKeyRow, RevocationRow, RevocationLedgerOpenResult, DeviceTokenRecord, DeviceTokenRow, CadreInviteRow, CadreInviteUsageRow, CadreInviteRedemptionResult, CadreInviteStatus, SeedPeer } from './types.js';
5
+ import type { StrandRow, JoinRequestRow, JoinOutcome, PendingJoin, NodeProfile, PeerAddressRecord, CadrePeerRow, OwnerKeyRow, RevocationRow, RevocationLedgerOpenResult, DeviceTokenRecord, DeviceTokenRow, CadreInviteRow, CadreInviteUsageRow, CadreInviteRedemptionResult, CadreInviteStatus, SeedPeer, MachineRemovalResult } from './types.js';
6
6
  import type { ControlTable, RevocableTable, BoundControlDomain, ControlAction, CadreInviteSignedFields } from './control-authorization.js';
7
7
  import type { ControlRetryAbandonment } from './control-retry.js';
8
8
  export type { ControlTable, RevocableTable, ControlDomain, ControlAction } from './control-authorization.js';
@@ -100,6 +100,12 @@ export interface FormationUsageResult {
100
100
  /** The single-use nonce written to `UsageStampId` (the row's primary key) — the caller's, or a freshly minted one. */
101
101
  usageStampId: string;
102
102
  }
103
+ /**
104
+ * Parse a nullable stored `datetime` column into epoch ms, mapping both "absent"
105
+ * and "unparseable" to null — i.e. "no expiry". Shared by every `ExpiresAt`
106
+ * reader so the NaN guard cannot drift between them.
107
+ */
108
+ export declare function parseNullableStoredDatetimeMs(value: string | number | null | undefined): number | null;
103
109
  /**
104
110
  * Build the canonical authorization message that owner signatures are bound to.
105
111
  *
@@ -325,6 +331,18 @@ export declare function projectSeedPeers(rows: readonly CadrePeerRow[], ownerKey
325
331
  */
326
332
  export declare const REAPABLE_TABLES: readonly ["CadrePeer", "DeviceToken", "ValidationKey", "JoinedStrand", "JoinRequest"];
327
333
  export type ReapableTable = (typeof REAPABLE_TABLES)[number];
334
+ /** A `FormationInvite` row as {@link ControlDatabase.queryFormationInvite} reads it. */
335
+ export interface FormationInviteRow {
336
+ token: string;
337
+ sAppId: string;
338
+ /** Parsed epoch ms of the stored `datetime`; null when the invitation never expires. */
339
+ expiresAtMs: number | null;
340
+ /** Null is unlimited. */
341
+ totalUses: number | null;
342
+ validationUrl: string | null;
343
+ /** A bound invitation's host strand; null on the responder-provisions path. */
344
+ strandId: string | null;
345
+ }
328
346
  /**
329
347
  * Notified after a membership write (a `CadreControl.CadrePeer` or `OwnerKey` row, see
330
348
  * {@link ControlDatabase.mutateCadrePeer}) has COMMITTED.
@@ -343,6 +361,8 @@ export type MembershipChangeListener = (reason: string) => Promise<void>;
343
361
  * the writer that files the row supplies it.
344
362
  */
345
363
  export type RevokedRowRef = Pick<RevocationRow, 'tableName' | 'rowKey' | 'stampId'>;
364
+ /** What {@link ControlDatabase.removeMachine} wrote: `CadreNode.removeMachine`'s result, less the re-vouches it made first. */
365
+ export type MachineRemovalWrites = Pick<MachineRemovalResult, 'removedPeer' | 'removedOwnerKey' | 'removedDeviceToken' | 'withdrawnInvitations'>;
346
366
  /**
347
367
  * Notified after a guarded-table delete — and the `CadreControl.Revocation`
348
368
  * tombstone it writes in the same transaction — has COMMITTED.
@@ -972,6 +992,57 @@ export declare class ControlDatabase {
972
992
  * @throws when `key` is `ownerKey`, or is the only live owner.
973
993
  */
974
994
  deleteOwnerKey(key: string, ownerKey: string, signMessage: (message: Uint8Array) => string): Promise<boolean>;
995
+ /**
996
+ * Re-vouch a live `OwnerKey` row under this owner's signature: in ONE transaction, the
997
+ * current row's guarded delete plus tombstone ({@link deleteOwnerKey}'s body) and an insert
998
+ * of the same key under a fresh stamp, signed by `ownerKey` ({@link insertOwnerKeyVouched}'s
999
+ * body), as {@link rewritePendingJoin} re-seats a request. `OwnerKey` has `NoUpdate`, so
1000
+ * this is the only way to rebind a row's voucher; the anchor derivation lets the re-add
1001
+ * under a fresh stamp beat the older tombstone (`deriveOwnerAnchor`). For
1002
+ * {@link removeMachine}: the owners a machine being removed had added keep their anchor
1003
+ * place everywhere once their row stands on a remaining owner's signature.
1004
+ *
1005
+ * Through {@link mutateCadrePeer}, the membership hub. The stamp read runs inside the locked
1006
+ * body; a row whose stamp is already retired (held without its delete) is reported as absent
1007
+ * rather than tombstoned twice.
1008
+ *
1009
+ * @returns `true` when the row was re-vouched, `false` when `key` is not a live owner here.
1010
+ * @throws when `key` is `ownerKey` (an owner cannot sign its own row's removal).
1011
+ */
1012
+ revouchOwnerKey(key: string, ownerKey: string, signMessage: (message: Uint8Array) => string): Promise<boolean>;
1013
+ /**
1014
+ * Remove one machine completely, in ONE transaction: its `CadrePeer` row, its `OwnerKey`
1015
+ * row when `target.ownerKey` names one, its `DeviceToken` row when it has one, each with
1016
+ * the `Revocation` tombstone retiring its stamp ({@link deleteGuardedRow}'s statement pair,
1017
+ * composed bare as {@link deleteStrandAndPartyKey} composes it), plus a withdrawal tombstone
1018
+ * for every `CadreInvite` in `target.inviteKeys` that is still live here
1019
+ * ({@link withdrawCadreInvite}'s body). One transaction so no reader sees the machine half
1020
+ * removed: a row gone but its push token resolvable, or its owner key gone but an invitation
1021
+ * it could redeem again still open.
1022
+ *
1023
+ * Through {@link mutateCadrePeer}, the membership hub. Every stamp is read inside the locked
1024
+ * body, and a row whose stamp is already retired (held without its delete) is skipped rather
1025
+ * than tombstoned twice. The by-name owner refusals of {@link deleteOwnerKey} apply to the
1026
+ * owner-key arm. Nothing to remove writes nothing and returns all false.
1027
+ *
1028
+ * The caller decides WHAT to remove, which owner key is the machine's and which invitations
1029
+ * it could use, from its plan (`CadreNode.planMachineRemoval`); this method only refuses to
1030
+ * write an inconsistent set.
1031
+ *
1032
+ * @throws when `target.ownerKey` is `ownerKey`, or is the only live owner here.
1033
+ */
1034
+ removeMachine(target: {
1035
+ peerId: string;
1036
+ ownerKey: string | null;
1037
+ inviteKeys: readonly string[];
1038
+ }, ownerKey: string, signMessage: (message: Uint8Array) => string): Promise<MachineRemovalWrites>;
1039
+ /**
1040
+ * The guarded removals {@link removeMachine} runs, signed over each row's current stamp:
1041
+ * the `CadrePeer` row, the `OwnerKey` row when named, the `DeviceToken` row when present.
1042
+ * Inside the locked body (every read `retry: false`); one `Revocation` scan serves the
1043
+ * retired-stamp check for all three tables and the last-owner refusal.
1044
+ */
1045
+ private signMachineRemovals;
975
1046
  /**
976
1047
  * Insert a strand into the control database using an owner signature.
977
1048
  *
@@ -1214,6 +1285,7 @@ export declare class ControlDatabase {
1214
1285
  multiaddr: string;
1215
1286
  updatedAt: number;
1216
1287
  sig: string | null;
1288
+ profile: NodeProfile | null;
1217
1289
  }, ownerKey: string, signMessage: (message: Uint8Array) => string): Promise<boolean>;
1218
1290
  /**
1219
1291
  * Owner "re-touch" of an existing `CadrePeer` membership row: bump `UpdatedAt` and
@@ -1225,7 +1297,8 @@ export declare class ControlDatabase {
1225
1297
  * local-only (its block's cluster ≤1 at insert) is pushed to the cohort once it grows, by
1226
1298
  * re-issuing this monotonic bump. It is an UPDATE (not the original INSERT) because the row
1227
1299
  * already exists locally; a re-INSERT would hit the `PeerId` PK. Only the freshness stamp
1228
- * and voucher change — `PublicKey` / `Multiaddr` / `Sig` are left intact — so it is safe
1300
+ * and voucher change — `PublicKey` / `Multiaddr` / `Sig` / `Profile` are left intact (the
1301
+ * owner branch requires them kept: they are the machine's own claim) — so it is safe
1229
1302
  * over a row whose peer has not self-published (`Sig` null); the caller must skip a row
1230
1303
  * that already carries a self-`Sig` (that row is the owning peer's to refresh, and bumping
1231
1304
  * `UpdatedAt` without re-signing would invalidate its self-signature).
@@ -1449,8 +1522,8 @@ export declare class ControlDatabase {
1449
1522
  * a retired stamp, and which a revoked drone cannot produce at all. The trade is
1450
1523
  * recurring failure noise every cycle against removing the one stale row nobody else
1451
1524
  * reads from this node (every other node already filters it by tombstone). A revoked
1452
- * node therefore keeps its own copy of its own row; "a node that learns it has been
1453
- * revoked should shut itself down" is a distinct behaviour and its own ticket.
1525
+ * node therefore keeps its own copy of its own row; learning that it was removed is a
1526
+ * separate behaviour (`CadreNode.removedFromCadre`, the `cadre:removed` event).
1454
1527
  * `ValidationKey`, `JoinedStrand` and `JoinRequest` have no self notion and are not
1455
1528
  * special-cased.
1456
1529
  *
@@ -1941,14 +2014,13 @@ export declare class ControlDatabase {
1941
2014
  * locked write body — same per-call opt-out, and for the same reason, as
1942
2015
  * {@link queryStampId}'s. Every unlocked caller keeps the default retried path.
1943
2016
  */
1944
- queryFormationInvite(token: string, retry?: boolean): Promise<{
1945
- token: string;
1946
- sAppId: string;
1947
- expiresAtMs: number | null;
1948
- totalUses: number | null;
1949
- validationUrl: string | null;
1950
- strandId: string | null;
1951
- } | null>;
2017
+ queryFormationInvite(token: string, retry?: boolean): Promise<FormationInviteRow | null>;
2018
+ /**
2019
+ * Every `FormationInvite` row, expired and spent ones included, in the shape
2020
+ * {@link queryFormationInvite} returns one. Read by a strand move
2021
+ * (`exportStrandBundle`), which decides per row what is still redeemable.
2022
+ */
2023
+ queryFormationInvites(): Promise<FormationInviteRow[]>;
1952
2024
  /**
1953
2025
  * Count `FormationUsage` rows recorded against a token (uses consumed so far).
1954
2026
  *
@@ -2126,6 +2198,14 @@ export declare class ControlDatabase {
2126
2198
  * node was alone is queued for re-issue on cohort growth ({@link CadreNode.noteGuardedDelete}).
2127
2199
  */
2128
2200
  withdrawCadreInvite(key: string, ownerKey: string, signMessage: (message: Uint8Array) => string): Promise<boolean>;
2201
+ /**
2202
+ * The withdrawal tombstones for the `CadreInvite` rows among `keys` that are held here and
2203
+ * not yet withdrawn, signed and ready for {@link execTombstone}: the body
2204
+ * {@link withdrawCadreInvite} files one of and {@link removeMachine} files several of in one
2205
+ * transaction. Inside the locked body (every read `retry: false`); an absent or already
2206
+ * withdrawn key is logged and left out.
2207
+ */
2208
+ private signCadreInviteWithdrawals;
2129
2209
  /**
2130
2210
  * Redeem a seated `CadreInvite` on the owner's behalf: admit `peerId` as a cadre member and,
2131
2211
  * when the invitation grants ownership, as an owner — with NO owner signature. One