@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
package/src/fs-atomic.ts CHANGED
@@ -1,117 +1,92 @@
1
- /**
2
- * Node-only filesystem helpers: atomic file write, filename-safe encoding, and
3
- * the ENOENT check, shared by `key-store-file.ts` and `file-durable-slot.ts`.
4
- *
5
- * This module imports `node:fs/promises`, so — like its consumers — it is kept
6
- * OUT of the package's cross-platform default entry and is not itself an
7
- * exported subpath; it is only reachable through the Node-only store modules.
8
- */
9
- import debug from 'debug';
10
- import { mkdir, rm, rename, open } from 'node:fs/promises';
11
- import type { FileHandle } from 'node:fs/promises';
12
-
13
- const log = debug('sereus:cadre:fs-atomic');
14
-
15
- /** Best-effort POSIX permissions (ignored on Windows / unsupported FSes). */
16
- export const DIR_MODE = 0o700;
17
- export const FILE_MODE = 0o600;
18
-
19
- /**
20
- * Percent-encode a string into a filesystem-safe filename component. Beyond
21
- * `encodeURIComponent` (which already escapes `/`, spaces, and unicode), this
22
- * also escapes the unreserved characters `encodeURIComponent` leaves intact but
23
- * that are unsafe in filenames on some platforms (notably `*` on Windows) — and
24
- * `.`, so a file suffix appended by the caller is the only literal dot, making
25
- * suffix-stripping unambiguous — and every uppercase letter, because Windows
26
- * (NTFS) and macOS (APFS/HFS+ by default) ignore case in file names. Reversed by
27
- * `decodeURIComponent`.
28
- *
29
- * The result contains only `a-z0-9-_` and `%XX` escapes (uppercase hex). Since a
30
- * literal `%` is itself escaped, every `%` starts an escape, so ignoring case
31
- * can change only escape hex digits — which decode case-insensitively — and two
32
- * different inputs never yield names equal ignoring case. The escapes keep
33
- * `encodeURIComponent`'s uppercase hex so a name built from an id with no
34
- * capital letters (the identity slot `cadre%2Fidentity.key`, a lowercase party
35
- * id) is the same as before uppercase letters were escaped.
36
- */
37
- export function encodeFileSafeComponent(component: string): string {
38
- // NOTE: a file left from before uppercase letters were escaped (`trusted-owners.Alice.json`) has, ignoring case, the new name of the all-lowercase id (`alice`), so on Windows/macOS a lookup for that id opens it; the per-party records' envelope party-id check rejects it (cold start), and `FileKeyStore` needs two parties in one key directory whose ids fold to the same lowercase string. The release note says to delete old files; if a store ever reads another id's old file in practice, rename or remove old-format files when the store opens.
39
- return encodeURIComponent(component).replace(
40
- /(%[0-9A-F]{2})|[!'()*~.A-Z]/g,
41
- (c, escape: string | undefined) => escape ?? `%${c.charCodeAt(0).toString(16).toUpperCase()}`
42
- );
43
- }
44
-
45
- /** Whether an unknown error is a Node "file/dir not found" (ENOENT). */
46
- export function isNotFound(error: unknown): boolean {
47
- return typeof error === 'object' && error !== null
48
- && (error as { code?: unknown }).code === 'ENOENT';
49
- }
50
-
51
- /**
52
- * Crash-atomic file write: a concurrent reader sees either the complete
53
- * previous bytes or the complete new bytes, never a torn file. `dir` is created
54
- * (0700) if absent; the data is written to `tmpPath` (created exclusively,
55
- * 0600), fsync'd, then atomically renamed onto `finalPath`. A failure at any
56
- * point removes the temp file and leaves the previous file untouched.
57
- *
58
- * `tmpPath` must be a sibling of `finalPath` (same directory) so the rename
59
- * stays within one filesystem and is therefore atomic, and should carry a
60
- * random component so concurrent writers (and crash-orphaned leftovers) are
61
- * collide-free.
62
- *
63
- * Durability bar: the temp file is fsync'd before the rename so a power loss
64
- * cannot surface a file whose bytes never reached the platter, and `dir` is
65
- * fsync'd afterwards (best-effort — see {@link syncDirBestEffort}) so the
66
- * rename itself survives a crash. On POSIX `rename(2)` replaces atomically;
67
- * Node maps to `MoveFileEx(MOVEFILE_REPLACE_EXISTING)` on Windows, which is
68
- * also atomic — though it can fail with `EPERM`/`EBUSY` if another process
69
- * holds the destination open. That cross-process case is out of scope for the
70
- * single-flight in-process callers here; such a failure surfaces to the caller
71
- * with the previous file left intact.
72
- */
73
- export async function writeFileAtomically(
74
- dir: string,
75
- tmpPath: string,
76
- finalPath: string,
77
- data: Uint8Array
78
- ): Promise<void> {
79
- await mkdir(dir, { recursive: true, mode: DIR_MODE });
80
- // 'wx' creates the temp file exclusively; a name collision (astronomically
81
- // unlikely given the caller's random component) fails here before we own anything.
82
- const handle = await open(tmpPath, 'wx', FILE_MODE);
83
- try {
84
- try {
85
- await handle.writeFile(data);
86
- await handle.sync(); // land bytes on disk before the rename exposes them
87
- } finally {
88
- await handle.close().catch(() => {});
89
- }
90
- await rename(tmpPath, finalPath);
91
- } catch (error) {
92
- // Any failure after creation (write, sync, or rename): drop the temp
93
- // file so a retry leaves no `.tmp` debris. The destination is only ever
94
- // swapped by the atomic rename, so the previous bytes stay intact.
95
- await rm(tmpPath, { force: true }).catch(() => {});
96
- throw error;
97
- }
98
- await syncDirBestEffort(dir);
99
- }
100
-
101
- /**
102
- * Best-effort fsync of a directory so a fresh file's directory entry is
103
- * durable across power loss. Opening a directory for fsync is not portable
104
- * (unsupported on Windows and some filesystems), and rename atomicity does not
105
- * depend on it — so a failure here is logged and swallowed, never surfaced.
106
- */
107
- async function syncDirBestEffort(dir: string): Promise<void> {
108
- let dirHandle: FileHandle | undefined;
109
- try {
110
- dirHandle = await open(dir, 'r');
111
- await dirHandle.sync();
112
- } catch (error) {
113
- log('directory fsync skipped for %s: %o', dir, error);
114
- } finally {
115
- await dirHandle?.close().catch(() => {});
116
- }
117
- }
1
+ /**
2
+ * Node-only filesystem helpers: atomic file write and the ENOENT check, shared
3
+ * by `key-store-file.ts` and `file-durable-slot.ts`. The filename-safe encoding
4
+ * they also use is cross-platform and lives in `file-safe-name.ts`.
5
+ *
6
+ * This module imports `node:fs/promises`, so — like its consumers — it is kept
7
+ * OUT of the package's cross-platform default entry and is not itself an
8
+ * exported subpath; it is only reachable through the Node-only store modules.
9
+ */
10
+ import debug from 'debug';
11
+ import { mkdir, rm, rename, open } from 'node:fs/promises';
12
+ import type { FileHandle } from 'node:fs/promises';
13
+
14
+ const log = debug('sereus:cadre:fs-atomic');
15
+
16
+ /** Best-effort POSIX permissions (ignored on Windows / unsupported FSes). */
17
+ export const DIR_MODE = 0o700;
18
+ export const FILE_MODE = 0o600;
19
+
20
+ /** Whether an unknown error is a Node "file/dir not found" (ENOENT). */
21
+ export function isNotFound(error: unknown): boolean {
22
+ return typeof error === 'object' && error !== null
23
+ && (error as { code?: unknown }).code === 'ENOENT';
24
+ }
25
+
26
+ /**
27
+ * Crash-atomic file write: a concurrent reader sees either the complete
28
+ * previous bytes or the complete new bytes, never a torn file. `dir` is created
29
+ * (0700) if absent; the data is written to `tmpPath` (created exclusively,
30
+ * 0600), fsync'd, then atomically renamed onto `finalPath`. A failure at any
31
+ * point removes the temp file and leaves the previous file untouched.
32
+ *
33
+ * `tmpPath` must be a sibling of `finalPath` (same directory) so the rename
34
+ * stays within one filesystem and is therefore atomic, and should carry a
35
+ * random component so concurrent writers (and crash-orphaned leftovers) are
36
+ * collide-free.
37
+ *
38
+ * Durability bar: the temp file is fsync'd before the rename so a power loss
39
+ * cannot surface a file whose bytes never reached the platter, and `dir` is
40
+ * fsync'd afterwards (best-effort — see {@link syncDirBestEffort}) so the
41
+ * rename itself survives a crash. On POSIX `rename(2)` replaces atomically;
42
+ * Node maps to `MoveFileEx(MOVEFILE_REPLACE_EXISTING)` on Windows, which is
43
+ * also atomic — though it can fail with `EPERM`/`EBUSY` if another process
44
+ * holds the destination open. That cross-process case is out of scope for the
45
+ * single-flight in-process callers here; such a failure surfaces to the caller
46
+ * with the previous file left intact.
47
+ */
48
+ export async function writeFileAtomically(
49
+ dir: string,
50
+ tmpPath: string,
51
+ finalPath: string,
52
+ data: Uint8Array
53
+ ): Promise<void> {
54
+ await mkdir(dir, { recursive: true, mode: DIR_MODE });
55
+ // 'wx' creates the temp file exclusively; a name collision (astronomically
56
+ // unlikely given the caller's random component) fails here before we own anything.
57
+ const handle = await open(tmpPath, 'wx', FILE_MODE);
58
+ try {
59
+ try {
60
+ await handle.writeFile(data);
61
+ await handle.sync(); // land bytes on disk before the rename exposes them
62
+ } finally {
63
+ await handle.close().catch(() => {});
64
+ }
65
+ await rename(tmpPath, finalPath);
66
+ } catch (error) {
67
+ // Any failure after creation (write, sync, or rename): drop the temp
68
+ // file so a retry leaves no `.tmp` debris. The destination is only ever
69
+ // swapped by the atomic rename, so the previous bytes stay intact.
70
+ await rm(tmpPath, { force: true }).catch(() => {});
71
+ throw error;
72
+ }
73
+ await syncDirBestEffort(dir);
74
+ }
75
+
76
+ /**
77
+ * Best-effort fsync of a directory so a fresh file's directory entry is
78
+ * durable across power loss. Opening a directory for fsync is not portable
79
+ * (unsupported on Windows and some filesystems), and rename atomicity does not
80
+ * depend on it — so a failure here is logged and swallowed, never surfaced.
81
+ */
82
+ async function syncDirBestEffort(dir: string): Promise<void> {
83
+ let dirHandle: FileHandle | undefined;
84
+ try {
85
+ dirHandle = await open(dir, 'r');
86
+ await dirHandle.sync();
87
+ } catch (error) {
88
+ log('directory fsync skipped for %s: %o', dir, error);
89
+ } finally {
90
+ await dirHandle?.close().catch(() => {});
91
+ }
92
+ }