sealkeep 0.9.0 → 0.11.1

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 (274) hide show
  1. package/ARCHITECTURE.md +163 -14
  2. package/CHANGELOG.md +252 -1
  3. package/CONTROL_PLANE.md +2 -2
  4. package/LICENSE +1 -1
  5. package/README.md +108 -22
  6. package/THIRD_PARTY.md +2 -2
  7. package/THREAT_MODEL.md +23 -4
  8. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/chunk-access.d.ts +26 -4
  9. package/dist/packages/sealkeep-crypto/src/chunk-access.js +219 -0
  10. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/envelope.d.ts +11 -1
  11. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/envelope.js +58 -21
  12. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/format.d.ts +2 -2
  13. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/index.d.ts +1 -0
  14. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/index.js +1 -0
  15. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/recipients.d.ts +1 -0
  16. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/recipients.js +53 -13
  17. package/dist/packages/sealkeep-crypto/src/squeeze.d.ts +6 -0
  18. package/dist/packages/sealkeep-crypto/src/squeeze.js +39 -0
  19. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/stream.d.ts +36 -3
  20. package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/stream.js +196 -37
  21. package/dist/site/index.html +1808 -1904
  22. package/dist/site/llms.txt +67 -0
  23. package/dist/site/trust/architecture-data-flow.html +53 -0
  24. package/dist/site/trust/audit-roadmap.html +37 -0
  25. package/dist/site/trust/deployment-responsibility.html +11 -0
  26. package/dist/site/trust/dpa-sample.html +30 -0
  27. package/dist/site/trust/release-provenance.html +21 -0
  28. package/dist/site/trust/subprocessors.html +15 -0
  29. package/dist/site/trust/threat-model.html +34 -0
  30. package/dist/site/trust/toms.html +41 -0
  31. package/dist/site/trust-document.css +32 -0
  32. package/dist/site/trust.html +73 -0
  33. package/dist/site/visual/assets/index-8Nxnkc7o.js +207 -0
  34. package/dist/site/visual/assets/index-oxLy1bpy.css +1 -0
  35. package/dist/site/visual/index.html +18 -0
  36. package/dist/site.zip +0 -0
  37. package/dist/src/activity.d.ts +9 -0
  38. package/dist/src/activity.js +90 -1
  39. package/dist/src/adapters.d.ts +175 -5
  40. package/dist/src/adapters.js +961 -73
  41. package/dist/src/agent-context.d.ts +135 -0
  42. package/dist/src/agent-context.js +1059 -0
  43. package/dist/src/archive-copies.d.ts +47 -0
  44. package/dist/src/archive-copies.js +179 -0
  45. package/dist/src/audit.d.ts +1 -1
  46. package/dist/src/audit.js +29 -4
  47. package/dist/src/autopilot.d.ts +52 -7
  48. package/dist/src/autopilot.js +143 -25
  49. package/dist/src/background-bandwidth.d.ts +46 -0
  50. package/dist/src/background-bandwidth.js +301 -0
  51. package/dist/src/background-cpu.d.ts +82 -0
  52. package/dist/src/background-cpu.js +212 -0
  53. package/dist/src/background-worker-error.d.ts +12 -0
  54. package/dist/src/background-worker-error.js +18 -0
  55. package/dist/src/branding.d.ts +24 -1
  56. package/dist/src/branding.js +31 -1
  57. package/dist/src/bridge.d.ts +233 -0
  58. package/dist/src/bridge.js +604 -0
  59. package/dist/src/byte-stream.d.ts +91 -0
  60. package/dist/src/byte-stream.js +385 -0
  61. package/dist/src/chunk-store.d.ts +41 -8
  62. package/dist/src/chunk-store.js +161 -65
  63. package/dist/src/cli.js +1698 -163
  64. package/dist/src/cloud.d.ts +841 -31
  65. package/dist/src/cloud.js +3196 -277
  66. package/dist/src/context-background.d.ts +37 -0
  67. package/dist/src/context-background.js +309 -0
  68. package/dist/src/context-drain-child.d.ts +1 -0
  69. package/dist/src/context-drain-child.js +98 -0
  70. package/dist/src/context-reader.d.ts +118 -0
  71. package/dist/src/context-reader.js +447 -0
  72. package/dist/src/control-plane/auth.d.ts +32 -4
  73. package/dist/src/control-plane/auth.js +85 -24
  74. package/dist/src/control-plane/server.js +19 -6
  75. package/dist/src/control-plane.d.ts +17 -1
  76. package/dist/src/control-plane.js +32 -6
  77. package/dist/src/crypto.d.ts +1 -1
  78. package/dist/src/crypto.js +5 -5
  79. package/dist/src/daemon-lease.d.ts +70 -0
  80. package/dist/src/daemon-lease.js +420 -0
  81. package/dist/src/daemon.d.ts +94 -1
  82. package/dist/src/daemon.js +1082 -105
  83. package/dist/src/darwin-service-policy.d.ts +41 -0
  84. package/dist/src/darwin-service-policy.js +60 -0
  85. package/dist/src/dashboard-cli.js +15 -15
  86. package/dist/src/device-authorization.d.ts +37 -0
  87. package/dist/src/device-authorization.js +199 -0
  88. package/dist/src/device-enrollment.d.ts +91 -0
  89. package/dist/src/device-enrollment.js +349 -0
  90. package/dist/src/disk.d.ts +17 -12
  91. package/dist/src/disk.js +43 -17
  92. package/dist/src/doctor.d.ts +35 -1
  93. package/dist/src/doctor.js +332 -41
  94. package/dist/src/durable-ticket-lock.d.ts +24 -0
  95. package/dist/src/durable-ticket-lock.js +232 -0
  96. package/dist/src/enroll.d.ts +1 -1
  97. package/dist/src/enroll.js +13 -7
  98. package/dist/src/env.d.ts +10 -1
  99. package/dist/src/env.js +11 -3
  100. package/dist/src/errors.d.ts +8 -8
  101. package/dist/src/errors.js +6 -6
  102. package/dist/src/flush.d.ts +12 -0
  103. package/dist/src/flush.js +37 -0
  104. package/dist/src/heartbeat.d.ts +86 -12
  105. package/dist/src/heartbeat.js +415 -29
  106. package/dist/src/index-background-watchdog.d.ts +1 -0
  107. package/dist/src/index-background-watchdog.js +94 -0
  108. package/dist/src/index-background-work.d.ts +21 -0
  109. package/dist/src/index-background-work.js +25 -0
  110. package/dist/src/index-background.d.ts +64 -0
  111. package/dist/src/index-background.js +394 -0
  112. package/dist/src/index-build-child.d.ts +1 -0
  113. package/dist/src/index-build-child.js +109 -0
  114. package/dist/src/index-manifest.d.ts +52 -0
  115. package/dist/src/index-manifest.js +444 -0
  116. package/dist/src/index-publication-proof.d.ts +84 -0
  117. package/dist/src/index-publication-proof.js +380 -0
  118. package/dist/src/index-publication-state.d.ts +149 -0
  119. package/dist/src/index-publication-state.js +696 -0
  120. package/dist/src/index-publication-verifier.d.ts +89 -0
  121. package/dist/src/index-publication-verifier.js +341 -0
  122. package/dist/src/index-publish.d.ts +62 -0
  123. package/dist/src/index-publish.js +540 -0
  124. package/dist/src/index-scratch-cleanup.d.ts +19 -0
  125. package/dist/src/index-scratch-cleanup.js +166 -0
  126. package/dist/src/index-segment-types.d.ts +132 -0
  127. package/dist/src/index-segment-types.js +21 -0
  128. package/dist/src/index-segments.d.ts +9 -0
  129. package/dist/src/index-segments.js +516 -0
  130. package/dist/src/index-store.d.ts +123 -0
  131. package/dist/src/index-store.js +495 -0
  132. package/dist/src/index-sync.d.ts +91 -15
  133. package/dist/src/index-sync.js +286 -53
  134. package/dist/src/index-upgrade-publication.d.ts +30 -0
  135. package/dist/src/index-upgrade-publication.js +179 -0
  136. package/dist/src/integration-manager.d.ts +32 -0
  137. package/dist/src/integration-manager.js +394 -0
  138. package/dist/src/leakscan.js +1 -1
  139. package/dist/src/local-api.d.ts +56 -15
  140. package/dist/src/local-api.js +4979 -541
  141. package/dist/src/machine-settings.d.ts +51 -0
  142. package/dist/src/machine-settings.js +166 -0
  143. package/dist/src/managed-chunks.d.ts +5 -2
  144. package/dist/src/managed-chunks.js +14 -14
  145. package/dist/src/mcp-install.d.ts +11 -9
  146. package/dist/src/mcp-install.js +73 -28
  147. package/dist/src/mcp-workspace.d.ts +18 -0
  148. package/dist/src/mcp-workspace.js +50 -0
  149. package/dist/src/mcp.js +294 -25
  150. package/dist/src/migrate.js +27 -21
  151. package/dist/src/notify.d.ts +1 -1
  152. package/dist/src/notify.js +5 -5
  153. package/dist/src/offload.d.ts +201 -14
  154. package/dist/src/offload.js +1848 -140
  155. package/dist/src/onboarding.d.ts +8 -1
  156. package/dist/src/onboarding.js +4 -4
  157. package/dist/src/packages.d.ts +2 -2
  158. package/dist/src/packages.js +10 -2
  159. package/dist/src/passkey.d.ts +0 -1
  160. package/dist/src/passkey.js +2 -7
  161. package/dist/src/password-lock.d.ts +2 -2
  162. package/dist/src/password-lock.js +6 -6
  163. package/dist/src/paths.d.ts +2 -0
  164. package/dist/src/paths.js +2 -0
  165. package/dist/src/presence.d.ts +86 -0
  166. package/dist/src/presence.js +240 -0
  167. package/dist/src/progress-deadline.d.ts +21 -0
  168. package/dist/src/progress-deadline.js +91 -0
  169. package/dist/src/project-repair.d.ts +55 -0
  170. package/dist/src/project-repair.js +131 -0
  171. package/dist/src/providers/gcs.d.ts +28 -7
  172. package/dist/src/providers/gcs.js +35 -24
  173. package/dist/src/providers/gdrive.d.ts +71 -8
  174. package/dist/src/providers/gdrive.js +223 -50
  175. package/dist/src/providers/index.d.ts +11 -3
  176. package/dist/src/providers/index.js +30 -10
  177. package/dist/src/providers/s3.d.ts +30 -8
  178. package/dist/src/providers/s3.js +41 -30
  179. package/dist/src/providers/safe-storage-fetch.d.ts +12 -0
  180. package/dist/src/providers/safe-storage-fetch.js +72 -0
  181. package/dist/src/queue.d.ts +136 -19
  182. package/dist/src/queue.js +862 -96
  183. package/dist/src/reclaim-transaction.d.ts +156 -0
  184. package/dist/src/reclaim-transaction.js +1027 -0
  185. package/dist/src/recovery-codes.d.ts +32 -0
  186. package/dist/src/recovery-codes.js +338 -0
  187. package/dist/src/recovery.js +12 -9
  188. package/dist/src/rehydrate.d.ts +25 -22
  189. package/dist/src/rehydrate.js +319 -23
  190. package/dist/src/restore.d.ts +57 -4
  191. package/dist/src/restore.js +272 -36
  192. package/dist/src/resume-stub.d.ts +92 -0
  193. package/dist/src/resume-stub.js +417 -0
  194. package/dist/src/retention.d.ts +98 -7
  195. package/dist/src/retention.js +1066 -62
  196. package/dist/src/rotate.js +3 -3
  197. package/dist/src/search.d.ts +566 -8
  198. package/dist/src/search.js +5940 -297
  199. package/dist/src/secrets.d.ts +51 -7
  200. package/dist/src/secrets.js +316 -24
  201. package/dist/src/service.d.ts +49 -11
  202. package/dist/src/service.js +776 -35
  203. package/dist/src/share.js +3 -3
  204. package/dist/src/shared-spaces.d.ts +98 -0
  205. package/dist/src/shared-spaces.js +214 -0
  206. package/dist/src/source-reader.d.ts +73 -0
  207. package/dist/src/source-reader.js +715 -0
  208. package/dist/src/spool.d.ts +1 -1
  209. package/dist/src/spool.js +1 -1
  210. package/dist/src/start-tui.js +2 -1
  211. package/dist/src/start.js +2 -2
  212. package/dist/src/storage-endpoint.d.ts +21 -0
  213. package/dist/src/storage-endpoint.js +122 -0
  214. package/dist/src/storage-setup.js +12 -12
  215. package/dist/src/storage-targets.d.ts +109 -6
  216. package/dist/src/storage-targets.js +975 -67
  217. package/dist/src/stream-to-cloud.d.ts +5 -1
  218. package/dist/src/stream-to-cloud.js +34 -14
  219. package/dist/src/sync-rules.d.ts +31 -6
  220. package/dist/src/sync-rules.js +153 -14
  221. package/dist/src/team-backfill-scheduling.d.ts +8 -0
  222. package/dist/src/team-backfill-scheduling.js +33 -0
  223. package/dist/src/team-backfill.d.ts +116 -0
  224. package/dist/src/team-backfill.js +1429 -0
  225. package/dist/src/team-index-cache.d.ts +16 -0
  226. package/dist/src/team-index-cache.js +152 -0
  227. package/dist/src/team-offboarding.d.ts +38 -0
  228. package/dist/src/team-offboarding.js +1043 -0
  229. package/dist/src/team-presence.d.ts +127 -0
  230. package/dist/src/team-presence.js +904 -0
  231. package/dist/src/team-publication-policy.d.ts +20 -0
  232. package/dist/src/team-publication-policy.js +140 -0
  233. package/dist/src/team-realtime.d.ts +68 -0
  234. package/dist/src/team-realtime.js +816 -0
  235. package/dist/src/team-source-facts-cache.d.ts +23 -0
  236. package/dist/src/team-source-facts-cache.js +255 -0
  237. package/dist/src/trash.d.ts +1 -1
  238. package/dist/src/trash.js +2 -2
  239. package/dist/src/tui.js +11 -12
  240. package/dist/src/types.d.ts +173 -7
  241. package/dist/src/types.js +20 -0
  242. package/dist/src/ui-server.d.ts +163 -35
  243. package/dist/src/ui-server.js +712 -72
  244. package/dist/src/ui.d.ts +1 -2
  245. package/dist/src/ui.js +1 -2
  246. package/dist/src/upload.d.ts +27 -0
  247. package/dist/src/upload.js +383 -43
  248. package/dist/src/vault.d.ts +226 -30
  249. package/dist/src/vault.js +1776 -192
  250. package/dist/src/watcher.d.ts +7 -1
  251. package/dist/src/watcher.js +198 -55
  252. package/dist/src/worker.d.ts +27 -3
  253. package/dist/src/worker.js +274 -55
  254. package/package.json +33 -12
  255. package/scripts/native-reboot-rehearsal.mjs +90 -0
  256. package/web/app.js +6032 -343
  257. package/web/bootstrap.js +17 -0
  258. package/web/index.html +255 -57
  259. package/web/rail.js +317 -40
  260. package/web/retention.html +2 -2
  261. package/web/rules-view.js +188 -16
  262. package/web/sessions-view.js +485 -62
  263. package/web/sessions.html +2 -2
  264. package/web/setup-api.js +152 -29
  265. package/web/setup-logic.js +68 -9
  266. package/web/setup.html +113 -44
  267. package/web/setup.js +604 -71
  268. package/web/style.css +513 -98
  269. package/dist/packages/vaultline-crypto/src/chunk-access.js +0 -93
  270. /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/aead.d.ts +0 -0
  271. /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/aead.js +0 -0
  272. /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/format.js +0 -0
  273. /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/sha256-stream.d.ts +0 -0
  274. /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/sha256-stream.js +0 -0
@@ -0,0 +1,166 @@
1
+ import { lstat, mkdir, open, readdir, rmdir, unlink } from "node:fs/promises";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { fail } from "./errors.js";
4
+ const UUID = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
5
+ const SCRATCH_GENERATION = new RegExp(`^${UUID}$`);
6
+ const OWNED_SCRATCH_FILE = new RegExp(`^(?:\\.content-index\\.${UUID}\\.jsonl|\\.scoped-index\\.${UUID}\\.jsonl|\\.vlbody-${UUID}`
7
+ + `|content-index(?:\\.[0-9a-f]{16})?\\.skindex\\.${UUID}\\.tmp)$`);
8
+ function errorCode(error) {
9
+ return typeof error === "object" && error !== null && "code" in error
10
+ ? String(error.code)
11
+ : undefined;
12
+ }
13
+ function validateGeneration(generation) {
14
+ if (!SCRATCH_GENERATION.test(generation)) {
15
+ fail("invalid_argument", "A historical-index scratch generation must be a canonical lowercase UUID");
16
+ }
17
+ }
18
+ /**
19
+ * One daemon-owned historical child gets one private scratch namespace.
20
+ * Foreground/manual builders deliberately keep using the ordinary index
21
+ * directory, so recovery of a dead child can never unlink their live files.
22
+ */
23
+ export function backgroundIndexScratchDirectory(dataDir, generation) {
24
+ validateGeneration(generation);
25
+ const indexDirectory = resolve(dataDir, "index");
26
+ const scratchDirectory = resolve(indexDirectory, `.background-index-scratch.${generation}`);
27
+ if (dirname(scratchDirectory) !== indexDirectory) {
28
+ fail("internal", "Historical-index scratch escaped its owned index directory");
29
+ }
30
+ return scratchDirectory;
31
+ }
32
+ function ownedByThisUser(info) {
33
+ return typeof process.getuid !== "function" || info.uid === process.getuid();
34
+ }
35
+ function privateDirectory(info) {
36
+ return process.platform === "win32" || (Number(info.mode) & 0o077) === 0;
37
+ }
38
+ function ownedRegularFile(info) {
39
+ return info.isFile() && !info.isSymbolicLink() && ownedByThisUser(info) && Number(info.nlink) === 1;
40
+ }
41
+ function concurrentlyUnlinkedOwnedFile(info) {
42
+ return info.isFile() && !info.isSymbolicLink() && ownedByThisUser(info) && Number(info.nlink) === 0;
43
+ }
44
+ async function syncDirectory(path) {
45
+ let handle;
46
+ try {
47
+ handle = await open(path, "r");
48
+ await handle.sync();
49
+ }
50
+ catch (error) {
51
+ // Windows does not support opening directories. POSIX must make the
52
+ // privacy cleanup durable before its child barrier can be retired.
53
+ if (process.platform !== "win32" && errorCode(error) !== "ENOENT")
54
+ throw error;
55
+ }
56
+ finally {
57
+ await handle?.close().catch(() => undefined);
58
+ }
59
+ }
60
+ /** Create or reopen only the exact namespace assigned to this worker. */
61
+ export async function ensureBackgroundIndexScratch(dataDir, generation) {
62
+ const indexDirectory = resolve(dataDir, "index");
63
+ const scratchDirectory = backgroundIndexScratchDirectory(dataDir, generation);
64
+ await mkdir(indexDirectory, { recursive: true, mode: 0o700 });
65
+ try {
66
+ await mkdir(scratchDirectory, { mode: 0o700 });
67
+ }
68
+ catch (error) {
69
+ if (errorCode(error) !== "EEXIST")
70
+ throw error;
71
+ }
72
+ const info = await lstat(scratchDirectory);
73
+ if (!info.isDirectory() || info.isSymbolicLink() || !ownedByThisUser(info) || !privateDirectory(info)) {
74
+ fail("internal", "Historical-index scratch is not an owned private directory");
75
+ }
76
+ return scratchDirectory;
77
+ }
78
+ /**
79
+ * Remove scratch for one process generation after its durable child barrier
80
+ * has proved that exact worker dead. The caller owns that proof; this helper
81
+ * intentionally cannot scan legacy/global names because another foreground
82
+ * builder may be using them.
83
+ *
84
+ * Preflight every entry before deleting any. An unknown name, symlink or
85
+ * directory leaves the entire namespace intact for diagnosis. Concurrent
86
+ * successor contenders may safely repeat the same cleanup.
87
+ */
88
+ export async function cleanupInterruptedIndexScratch(dataDir, generation) {
89
+ const scratchDirectory = backgroundIndexScratchDirectory(dataDir, generation);
90
+ let directoryInfo;
91
+ try {
92
+ directoryInfo = await lstat(scratchDirectory);
93
+ }
94
+ catch (error) {
95
+ if (errorCode(error) === "ENOENT") {
96
+ await syncDirectory(resolve(dataDir, "index"));
97
+ return;
98
+ }
99
+ throw error;
100
+ }
101
+ if (!directoryInfo.isDirectory() || directoryInfo.isSymbolicLink()
102
+ || !ownedByThisUser(directoryInfo) || !privateDirectory(directoryInfo)) {
103
+ fail("internal", "Refusing to clean an unowned historical-index scratch path");
104
+ }
105
+ let entries;
106
+ try {
107
+ entries = await readdir(scratchDirectory, { withFileTypes: true });
108
+ }
109
+ catch (error) {
110
+ // A simultaneous successor may have completed the exact cleanup after our
111
+ // lstat. Missing at any boundary is the intended final state.
112
+ if (errorCode(error) === "ENOENT") {
113
+ await syncDirectory(resolve(dataDir, "index"));
114
+ return;
115
+ }
116
+ throw error;
117
+ }
118
+ for (const entry of entries) {
119
+ if (!OWNED_SCRATCH_FILE.test(entry.name) || !entry.isFile() || entry.isSymbolicLink()) {
120
+ fail("internal", "Refusing to clean a historical-index scratch namespace containing an unknown entry");
121
+ }
122
+ try {
123
+ const info = await lstat(join(scratchDirectory, entry.name));
124
+ if (!ownedRegularFile(info) && !concurrentlyUnlinkedOwnedFile(info)) {
125
+ fail("internal", "Refusing to clean an unowned historical-index scratch file");
126
+ }
127
+ }
128
+ catch (error) {
129
+ // Another successor may have claimed the same dead generation. A file
130
+ // disappearing is success; no other lookup failure is downgraded.
131
+ if (errorCode(error) !== "ENOENT")
132
+ throw error;
133
+ }
134
+ }
135
+ for (const entry of entries) {
136
+ const path = join(scratchDirectory, entry.name);
137
+ try {
138
+ // Recheck immediately before unlink. unlink itself never follows a
139
+ // symlink; a replacement directory is refused by the operating system.
140
+ const info = await lstat(path);
141
+ // POSIX may return the inode we just looked up with nlink=0 when a
142
+ // simultaneous cleaner wins unlink between lookup and inspection. Do
143
+ // not unlink the pathname in that case: it may already name a
144
+ // replacement. The final rmdir will conservatively refuse any survivor.
145
+ if (concurrentlyUnlinkedOwnedFile(info)) {
146
+ continue;
147
+ }
148
+ if (!ownedRegularFile(info)) {
149
+ fail("internal", "Refusing to clean a replaced historical-index scratch file");
150
+ }
151
+ await unlink(path);
152
+ }
153
+ catch (error) {
154
+ if (errorCode(error) !== "ENOENT")
155
+ throw error;
156
+ }
157
+ }
158
+ try {
159
+ await rmdir(scratchDirectory);
160
+ }
161
+ catch (error) {
162
+ if (errorCode(error) !== "ENOENT")
163
+ throw error;
164
+ }
165
+ await syncDirectory(resolve(dataDir, "index"));
166
+ }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * The segmented content index — shared contracts.
3
+ *
4
+ * Sealed memories never change, so the index that describes them need not
5
+ * be rewritten either. The index becomes a manifest over immutable sealed
6
+ * SEGMENTS: each segment carries the archive rows, token postings and
7
+ * session metadata for a set of archives, written once. Adding memory
8
+ * appends a segment; a lookup opens every segment's small directory and
9
+ * reads only the blocks that hold its terms; compaction occasionally
10
+ * streams several small segments into one bigger immutable one. Nothing is
11
+ * ever rewritten in place, and no build holds more than one batch in memory.
12
+ *
13
+ * Row shapes are exactly the v3 index rows the reader already understands:
14
+ * ["a", archiveId, ...archiveLine] sorted by archiveId
15
+ * ["t", token, [posting, ...]] sorted by token; a posting is
16
+ * "<archiveId>" or "<archiveId>@c<chunk>"
17
+ * ["m", archiveId, metaObject] sorted by archiveId
18
+ * preceded by one header row `{"version":4,"segmentId":…,"builtAt":…}`.
19
+ */
20
+ /** One row of a segment body, in the order the body stores them. */
21
+ export type SegmentRow = ["a", string, ...string[]] | ["t", string, string[]] | ["m", string, Record<string, unknown>];
22
+ export type SegmentHeader = {
23
+ version: 4;
24
+ segmentId: string;
25
+ builtAt: string;
26
+ };
27
+ /** Byte ranges of the three regions in the segment's raw (decrypted) body. */
28
+ export type SegmentRegions = {
29
+ archives: [number, number];
30
+ tokens: [number, number];
31
+ meta: [number, number];
32
+ };
33
+ /**
34
+ * The sealed sidecar that makes a segment addressable without reading it:
35
+ * the raw offset of every `stride`-th token row, and the region bounds. It is
36
+ * bound to the exact sealed body by the body envelope's archive id.
37
+ */
38
+ export type SegmentDirectory = {
39
+ version: 1;
40
+ segmentId: string;
41
+ envelopeArchiveId: string;
42
+ builtAt: string;
43
+ stride: number;
44
+ regions: SegmentRegions;
45
+ /** [token, raw byte offset of its row], ascending by token. */
46
+ entries: Array<[string, number]>;
47
+ archives: number;
48
+ tokens: number;
49
+ };
50
+ /** What the manifest records about one segment; metadata only, never tokens. */
51
+ export type SegmentEntry = {
52
+ id: string;
53
+ builtAt: string;
54
+ archives: number;
55
+ tokens: number;
56
+ /** Sealed body bytes on disk. */
57
+ bytes: number;
58
+ /**
59
+ * "local" (default): rows this machine derived from its own archives.
60
+ * "remote": the account index pulled from the cloud, converted whole; it is
61
+ * replaced when a newer copy arrives and is never compacted with local ones.
62
+ */
63
+ origin?: "local" | "remote";
64
+ /**
65
+ * The archive ids this machine sealed that the segment covers, so coverage
66
+ * is known without opening it. Only these may ever be tombstoned as "no
67
+ * longer here": a build forgets what this machine indexed, never what it
68
+ * learned from another machine.
69
+ */
70
+ archiveIds?: string[];
71
+ /**
72
+ * Rows the segment also carries for archives another machine sealed (a
73
+ * migrated blob had pulled them from the account index). Searchable here,
74
+ * never pruned here, and replaced by segment publication (step 2).
75
+ */
76
+ remoteArchiveIds?: string[];
77
+ /**
78
+ * The account machine this segment came from, for an `origin: "remote"`
79
+ * entry (step 2's `pullSegments`) — which machine's `<machineId>/` prefix
80
+ * to delete from when a peer's compaction or retirement replaces it.
81
+ * Absent on a "local" entry: it is unambiguously this machine's own.
82
+ */
83
+ machineId?: string;
84
+ };
85
+ export type SegmentManifest = {
86
+ version: 1;
87
+ /** Changes whenever the segment set changes; readers use it as a cache key. */
88
+ generation: string;
89
+ /** Newest last. A lookup consults all of them. */
90
+ segments: SegmentEntry[];
91
+ /** Archive ids removed from the vault; their rows are ignored everywhere and dropped by the next compaction. */
92
+ tombstones: string[];
93
+ };
94
+ /** The result of a lookup across one or many segments: the same shape queries already consume. */
95
+ export type SegmentLookup = {
96
+ archives: Record<string, string[]>;
97
+ tokens: Record<string, string[]>;
98
+ meta: Record<string, Record<string, unknown>>;
99
+ };
100
+ /** A reader over one sealed segment; opened once, closed by the caller. */
101
+ export interface SegmentReader {
102
+ readonly id: string;
103
+ readonly directory: SegmentDirectory;
104
+ /**
105
+ * Rows for the given terms: every "a" and "m" row of the segment, and the
106
+ * "t" rows whose token equals a term or starts with one (at most
107
+ * `prefixKeep` prefix matches per term, exact matches always). Only the
108
+ * blocks holding those rows are opened.
109
+ */
110
+ lookup(terms: readonly string[], options?: {
111
+ prefixKeep?: number;
112
+ signal?: AbortSignal;
113
+ }): Promise<SegmentLookup>;
114
+ /** Every row of one region, in stored order, streamed in bounded windows. */
115
+ rows(region: keyof SegmentRegions, options?: {
116
+ signal?: AbortSignal;
117
+ }): AsyncIterable<SegmentRow>;
118
+ close(): Promise<void>;
119
+ }
120
+ /** Writes one immutable segment from rows that are already sorted region by region. */
121
+ export interface SegmentWriter {
122
+ (dataDir: string, phrase: string, input: {
123
+ segmentId: string;
124
+ builtAt: string;
125
+ rows: AsyncIterable<SegmentRow> | Iterable<SegmentRow>;
126
+ }, options?: {
127
+ signal?: AbortSignal;
128
+ onYield?: () => void | Promise<void>;
129
+ }): Promise<SegmentEntry>;
130
+ }
131
+ export declare const SEGMENT_DIRECTORY_STRIDE = 64;
132
+ export declare const MAX_POSTINGS_PER_TOKEN = 256;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The segmented content index — shared contracts.
3
+ *
4
+ * Sealed memories never change, so the index that describes them need not
5
+ * be rewritten either. The index becomes a manifest over immutable sealed
6
+ * SEGMENTS: each segment carries the archive rows, token postings and
7
+ * session metadata for a set of archives, written once. Adding memory
8
+ * appends a segment; a lookup opens every segment's small directory and
9
+ * reads only the blocks that hold its terms; compaction occasionally
10
+ * streams several small segments into one bigger immutable one. Nothing is
11
+ * ever rewritten in place, and no build holds more than one batch in memory.
12
+ *
13
+ * Row shapes are exactly the v3 index rows the reader already understands:
14
+ * ["a", archiveId, ...archiveLine] sorted by archiveId
15
+ * ["t", token, [posting, ...]] sorted by token; a posting is
16
+ * "<archiveId>" or "<archiveId>@c<chunk>"
17
+ * ["m", archiveId, metaObject] sorted by archiveId
18
+ * preceded by one header row `{"version":4,"segmentId":…,"builtAt":…}`.
19
+ */
20
+ export const SEGMENT_DIRECTORY_STRIDE = 64;
21
+ export const MAX_POSTINGS_PER_TOKEN = 256;
@@ -0,0 +1,9 @@
1
+ import { type SegmentReader, type SegmentWriter } from "./index-segment-types.js";
2
+ export declare function segmentPaths(dataDir: string, segmentId: string): Promise<{
3
+ body: string;
4
+ envelope: string;
5
+ directory: string;
6
+ }>;
7
+ export declare function removeSegment(dataDir: string, segmentId: string): Promise<void>;
8
+ export declare const writeSegment: SegmentWriter;
9
+ export declare function openSegment(dataDir: string, phrase: string, segmentId: string): Promise<SegmentReader>;