cursedbelt-server 1.0.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 (606) hide show
  1. package/LICENSE +15 -0
  2. package/dist/server/activity/index.d.ts +36 -0
  3. package/dist/server/activity/index.js +36 -0
  4. package/dist/server/activity/migrations.d.ts +21 -0
  5. package/dist/server/activity/migrations.js +55 -0
  6. package/dist/server/activity/shareSource.d.ts +11 -0
  7. package/dist/server/activity/shareSource.js +45 -0
  8. package/dist/server/activity/store.d.ts +123 -0
  9. package/dist/server/activity/store.js +178 -0
  10. package/dist/server/app.d.ts +56 -0
  11. package/dist/server/app.js +45 -0
  12. package/dist/server/audit/index.d.ts +2 -0
  13. package/dist/server/audit/index.js +2 -0
  14. package/dist/server/audit/migrations.d.ts +2 -0
  15. package/dist/server/audit/migrations.js +79 -0
  16. package/dist/server/audit/sqliteSink.d.ts +36 -0
  17. package/dist/server/audit/sqliteSink.js +95 -0
  18. package/dist/server/audit/sqliteSink.test.d.ts +1 -0
  19. package/dist/server/audit/sqliteSink.test.js +180 -0
  20. package/dist/server/auth/cookie.d.ts +22 -0
  21. package/dist/server/auth/cookie.js +35 -0
  22. package/dist/server/auth/csrf.d.ts +42 -0
  23. package/dist/server/auth/csrf.js +36 -0
  24. package/dist/server/auth/fileTokenSigner.d.ts +10 -0
  25. package/dist/server/auth/fileTokenSigner.js +22 -0
  26. package/dist/server/auth/getOnly.d.ts +20 -0
  27. package/dist/server/auth/getOnly.js +27 -0
  28. package/dist/server/auth/index.d.ts +12 -0
  29. package/dist/server/auth/index.js +15 -0
  30. package/dist/server/auth/jwt.d.ts +18 -0
  31. package/dist/server/auth/jwt.js +56 -0
  32. package/dist/server/auth/loginThrottle.d.ts +118 -0
  33. package/dist/server/auth/loginThrottle.js +204 -0
  34. package/dist/server/auth/middleware.d.ts +14 -0
  35. package/dist/server/auth/middleware.js +47 -0
  36. package/dist/server/auth/password.d.ts +14 -0
  37. package/dist/server/auth/password.js +65 -0
  38. package/dist/server/auth/sessionStore.d.ts +19 -0
  39. package/dist/server/auth/sessionStore.js +221 -0
  40. package/dist/server/auth/totp.d.ts +34 -0
  41. package/dist/server/auth/totp.js +81 -0
  42. package/dist/server/auth/types.d.ts +133 -0
  43. package/dist/server/auth/types.js +1 -0
  44. package/dist/server/auth/vault.d.ts +27 -0
  45. package/dist/server/auth/vault.js +57 -0
  46. package/dist/server/context.d.ts +35 -0
  47. package/dist/server/context.js +1 -0
  48. package/dist/server/ctgr.d.ts +32 -0
  49. package/dist/server/ctgr.js +69 -0
  50. package/dist/server/db/kysely.d.ts +14 -0
  51. package/dist/server/db/kysely.js +16 -0
  52. package/dist/server/db/plumbingTypes.d.ts +116 -0
  53. package/dist/server/db/plumbingTypes.js +12 -0
  54. package/dist/server/dropzone/duplicates.d.ts +82 -0
  55. package/dist/server/dropzone/duplicates.js +90 -0
  56. package/dist/server/dropzone/index.d.ts +13 -0
  57. package/dist/server/dropzone/index.js +13 -0
  58. package/dist/server/dropzone/scan.d.ts +166 -0
  59. package/dist/server/dropzone/scan.js +272 -0
  60. package/dist/server/errors.d.ts +7 -0
  61. package/dist/server/errors.js +7 -0
  62. package/dist/server/guard/argon2Binding.d.ts +97 -0
  63. package/dist/server/guard/argon2Binding.js +138 -0
  64. package/dist/server/guard/index.d.ts +4 -0
  65. package/dist/server/guard/index.js +6 -0
  66. package/dist/server/guard/kdfWorker.d.ts +101 -0
  67. package/dist/server/guard/kdfWorker.js +301 -0
  68. package/dist/server/guard/kdfWorkerEntry.d.ts +1 -0
  69. package/dist/server/guard/kdfWorkerEntry.js +15 -0
  70. package/dist/server/guard/passwordVerifier.d.ts +65 -0
  71. package/dist/server/guard/passwordVerifier.js +122 -0
  72. package/dist/server/guard/revocationStore.d.ts +132 -0
  73. package/dist/server/guard/revocationStore.js +211 -0
  74. package/dist/server/guard/sessionSigner.d.ts +132 -0
  75. package/dist/server/guard/sessionSigner.js +303 -0
  76. package/dist/server/guard/verifier.d.ts +21 -0
  77. package/dist/server/guard/verifier.js +1 -0
  78. package/dist/server/incidents/channels.d.ts +161 -0
  79. package/dist/server/incidents/channels.js +357 -0
  80. package/dist/server/incidents/checks.d.ts +61 -0
  81. package/dist/server/incidents/checks.js +55 -0
  82. package/dist/server/incidents/index.d.ts +51 -0
  83. package/dist/server/incidents/index.js +51 -0
  84. package/dist/server/incidents/migrations.d.ts +20 -0
  85. package/dist/server/incidents/migrations.js +58 -0
  86. package/dist/server/incidents/notifier.d.ts +79 -0
  87. package/dist/server/incidents/notifier.js +141 -0
  88. package/dist/server/incidents/reporter.d.ts +60 -0
  89. package/dist/server/incidents/reporter.js +49 -0
  90. package/dist/server/incidents/routes.d.ts +91 -0
  91. package/dist/server/incidents/routes.js +202 -0
  92. package/dist/server/incidents/store.d.ts +75 -0
  93. package/dist/server/incidents/store.js +176 -0
  94. package/dist/server/incidents/types.d.ts +107 -0
  95. package/dist/server/incidents/types.js +32 -0
  96. package/dist/server/index.d.ts +25 -0
  97. package/dist/server/index.js +53 -0
  98. package/dist/server/jobs/fireQueue.d.ts +12 -0
  99. package/dist/server/jobs/fireQueue.js +37 -0
  100. package/dist/server/jobs/index.d.ts +6 -0
  101. package/dist/server/jobs/index.js +10 -0
  102. package/dist/server/jobs/logger.d.ts +10 -0
  103. package/dist/server/jobs/logger.js +14 -0
  104. package/dist/server/jobs/plainjobAdapter.d.ts +19 -0
  105. package/dist/server/jobs/plainjobAdapter.js +48 -0
  106. package/dist/server/jobs/polling.d.ts +17 -0
  107. package/dist/server/jobs/polling.js +79 -0
  108. package/dist/server/jobs/runner.d.ts +24 -0
  109. package/dist/server/jobs/runner.js +186 -0
  110. package/dist/server/jobs/timeout.d.ts +28 -0
  111. package/dist/server/jobs/timeout.js +47 -0
  112. package/dist/server/jobs/types.d.ts +167 -0
  113. package/dist/server/jobs/types.js +12 -0
  114. package/dist/server/master-lock/guard.d.ts +53 -0
  115. package/dist/server/master-lock/guard.js +205 -0
  116. package/dist/server/master-lock/index.d.ts +21 -0
  117. package/dist/server/master-lock/index.js +21 -0
  118. package/dist/server/master-lock/lockPage.d.ts +34 -0
  119. package/dist/server/master-lock/lockPage.js +281 -0
  120. package/dist/server/master-lock/masterLock.d.ts +294 -0
  121. package/dist/server/master-lock/masterLock.js +523 -0
  122. package/dist/server/master-lock/principals.d.ts +76 -0
  123. package/dist/server/master-lock/principals.js +157 -0
  124. package/dist/server/master-lock/seed.d.ts +47 -0
  125. package/dist/server/master-lock/seed.js +93 -0
  126. package/dist/server/master-lock/store.d.ts +19 -0
  127. package/dist/server/master-lock/store.js +20 -0
  128. package/dist/server/media-bun/image.d.ts +7 -0
  129. package/dist/server/media-bun/image.js +96 -0
  130. package/dist/server/media-bun/index.d.ts +4 -0
  131. package/dist/server/media-bun/index.js +10 -0
  132. package/dist/server/media-bun/streaming.d.ts +86 -0
  133. package/dist/server/media-bun/streaming.js +237 -0
  134. package/dist/server/media-bun/video.d.ts +35 -0
  135. package/dist/server/media-bun/video.js +181 -0
  136. package/dist/server/metrics/metricsBuffer.d.ts +39 -0
  137. package/dist/server/metrics/metricsBuffer.js +36 -0
  138. package/dist/server/metrics/normalizeRoute.d.ts +5 -0
  139. package/dist/server/metrics/normalizeRoute.js +58 -0
  140. package/dist/server/middleware/correlationId.d.ts +9 -0
  141. package/dist/server/middleware/correlationId.js +20 -0
  142. package/dist/server/middleware/corsAllowlist.d.ts +38 -0
  143. package/dist/server/middleware/corsAllowlist.js +37 -0
  144. package/dist/server/middleware/errorEnvelope.d.ts +17 -0
  145. package/dist/server/middleware/errorEnvelope.js +32 -0
  146. package/dist/server/middleware/index.d.ts +9 -0
  147. package/dist/server/middleware/index.js +10 -0
  148. package/dist/server/middleware/localOrigin.d.ts +71 -0
  149. package/dist/server/middleware/localOrigin.js +119 -0
  150. package/dist/server/middleware/rateLimit.d.ts +56 -0
  151. package/dist/server/middleware/rateLimit.js +86 -0
  152. package/dist/server/middleware/requestLogger.d.ts +29 -0
  153. package/dist/server/middleware/requestLogger.js +47 -0
  154. package/dist/server/middleware/securityHeaders.d.ts +29 -0
  155. package/dist/server/middleware/securityHeaders.js +44 -0
  156. package/dist/server/middleware/zValidator.d.ts +29 -0
  157. package/dist/server/middleware/zValidator.js +57 -0
  158. package/dist/server/migration/runner.d.ts +42 -0
  159. package/dist/server/migration/runner.js +70 -0
  160. package/dist/server/migration/types.d.ts +16 -0
  161. package/dist/server/migration/types.js +1 -0
  162. package/dist/server/net/outboundGuard.d.ts +30 -0
  163. package/dist/server/net/outboundGuard.js +199 -0
  164. package/dist/server/notifications/hub.d.ts +67 -0
  165. package/dist/server/notifications/hub.js +187 -0
  166. package/dist/server/notifications/index.d.ts +47 -0
  167. package/dist/server/notifications/index.js +46 -0
  168. package/dist/server/notifications/migrations.d.ts +13 -0
  169. package/dist/server/notifications/migrations.js +20 -0
  170. package/dist/server/notifications/routes.d.ts +58 -0
  171. package/dist/server/notifications/routes.js +113 -0
  172. package/dist/server/notifications/service.d.ts +58 -0
  173. package/dist/server/notifications/service.js +41 -0
  174. package/dist/server/paths/appDataHome.d.ts +19 -0
  175. package/dist/server/paths/appDataHome.js +68 -0
  176. package/dist/server/private-media/crypto.d.ts +37 -0
  177. package/dist/server/private-media/crypto.js +111 -0
  178. package/dist/server/private-media/index-store.d.ts +31 -0
  179. package/dist/server/private-media/index-store.js +36 -0
  180. package/dist/server/private-media/mountPrivateRoutes.d.ts +26 -0
  181. package/dist/server/private-media/mountPrivateRoutes.js +219 -0
  182. package/dist/server/private-media/pipeline.d.ts +37 -0
  183. package/dist/server/private-media/pipeline.js +97 -0
  184. package/dist/server/private-media/unlock.d.ts +52 -0
  185. package/dist/server/private-media/unlock.js +55 -0
  186. package/dist/server/resume/index.d.ts +2 -0
  187. package/dist/server/resume/index.js +2 -0
  188. package/dist/server/resume/routes.d.ts +41 -0
  189. package/dist/server/resume/routes.js +50 -0
  190. package/dist/server/resume/store.d.ts +37 -0
  191. package/dist/server/resume/store.js +38 -0
  192. package/dist/server/retention/archive.d.ts +78 -0
  193. package/dist/server/retention/archive.js +284 -0
  194. package/dist/server/retention/fleet.d.ts +50 -0
  195. package/dist/server/retention/fleet.js +66 -0
  196. package/dist/server/retention/index.d.ts +38 -0
  197. package/dist/server/retention/index.js +38 -0
  198. package/dist/server/retention/policy.d.ts +263 -0
  199. package/dist/server/retention/policy.js +264 -0
  200. package/dist/server/retention/store.d.ts +101 -0
  201. package/dist/server/retention/store.js +251 -0
  202. package/dist/server/retention/sweeper.d.ts +69 -0
  203. package/dist/server/retention/sweeper.js +289 -0
  204. package/dist/server/retention/target.d.ts +143 -0
  205. package/dist/server/retention/target.js +438 -0
  206. package/dist/server/search/index.d.ts +2 -0
  207. package/dist/server/search/index.js +6 -0
  208. package/dist/server/search/pgSearchProvider.d.ts +91 -0
  209. package/dist/server/search/pgSearchProvider.js +330 -0
  210. package/dist/server/search/pgSearchProvider.pg.test.d.ts +1 -0
  211. package/dist/server/search/pgSearchProvider.pg.test.js +338 -0
  212. package/dist/server/search/pgSearchSchema.d.ts +81 -0
  213. package/dist/server/search/pgSearchSchema.js +89 -0
  214. package/dist/server/sharing/index.d.ts +18 -0
  215. package/dist/server/sharing/index.js +16 -0
  216. package/dist/server/sharing/migrations.d.ts +6 -0
  217. package/dist/server/sharing/migrations.js +79 -0
  218. package/dist/server/sharing/router.d.ts +111 -0
  219. package/dist/server/sharing/router.js +93 -0
  220. package/dist/server/sharing/sql.d.ts +67 -0
  221. package/dist/server/sharing/sql.js +88 -0
  222. package/dist/server/sharing/store.d.ts +173 -0
  223. package/dist/server/sharing/store.js +484 -0
  224. package/dist/server/sqlite/backup.d.ts +48 -0
  225. package/dist/server/sqlite/backup.js +97 -0
  226. package/dist/server/sqlite/browse.d.ts +42 -0
  227. package/dist/server/sqlite/browse.js +84 -0
  228. package/dist/server/sqlite/export.d.ts +32 -0
  229. package/dist/server/sqlite/export.js +42 -0
  230. package/dist/server/sqlite/fts5.d.ts +106 -0
  231. package/dist/server/sqlite/fts5.js +154 -0
  232. package/dist/server/sqlite/health.d.ts +44 -0
  233. package/dist/server/sqlite/health.js +52 -0
  234. package/dist/server/sqlite/import.d.ts +36 -0
  235. package/dist/server/sqlite/import.js +108 -0
  236. package/dist/server/sqlite/index.d.ts +9 -0
  237. package/dist/server/sqlite/index.js +15 -0
  238. package/dist/server/sqlite/readonlySql.d.ts +15 -0
  239. package/dist/server/sqlite/readonlySql.js +65 -0
  240. package/dist/server/sqlite/stats.d.ts +22 -0
  241. package/dist/server/sqlite/stats.js +32 -0
  242. package/dist/server/sqlite/viewer.d.ts +17 -0
  243. package/dist/server/sqlite/viewer.js +56 -0
  244. package/dist/server/storage/binaryServerProcessing.d.ts +23 -0
  245. package/dist/server/storage/binaryServerProcessing.js +81 -0
  246. package/dist/server/storage/binaryServerStore.d.ts +3 -0
  247. package/dist/server/storage/binaryServerStore.js +95 -0
  248. package/dist/server/storage/diskStore.d.ts +2 -0
  249. package/dist/server/storage/diskStore.js +88 -0
  250. package/dist/server/storage/files/adapter.d.ts +33 -0
  251. package/dist/server/storage/files/adapter.js +203 -0
  252. package/dist/server/storage/files/catalogue.d.ts +72 -0
  253. package/dist/server/storage/files/catalogue.js +1033 -0
  254. package/dist/server/storage/files/chunkedUpload.d.ts +56 -0
  255. package/dist/server/storage/files/chunkedUpload.js +70 -0
  256. package/dist/server/storage/files/fileRoutes.d.ts +51 -0
  257. package/dist/server/storage/files/fileRoutes.js +405 -0
  258. package/dist/server/storage/files/index.d.ts +9 -0
  259. package/dist/server/storage/files/index.js +11 -0
  260. package/dist/server/storage/files/migrations.d.ts +2 -0
  261. package/dist/server/storage/files/migrations.js +163 -0
  262. package/dist/server/storage/files/mimeByExt.d.ts +1 -0
  263. package/dist/server/storage/files/mimeByExt.js +13 -0
  264. package/dist/server/storage/files/stack.d.ts +136 -0
  265. package/dist/server/storage/files/stack.js +132 -0
  266. package/dist/server/storage/files/storageOps.d.ts +54 -0
  267. package/dist/server/storage/files/storageOps.js +110 -0
  268. package/dist/server/storage/files/types.d.ts +261 -0
  269. package/dist/server/storage/files/types.js +22 -0
  270. package/dist/server/storage/imageThumbnail.d.ts +3 -0
  271. package/dist/server/storage/imageThumbnail.js +50 -0
  272. package/dist/server/storage/index.d.ts +15 -0
  273. package/dist/server/storage/index.js +27 -0
  274. package/dist/server/storage/mountMediaRoutes.d.ts +28 -0
  275. package/dist/server/storage/mountMediaRoutes.js +141 -0
  276. package/dist/server/storage/objectStoreMediaStore.d.ts +4 -0
  277. package/dist/server/storage/objectStoreMediaStore.js +57 -0
  278. package/dist/server/storage/s3ObjectStore.d.ts +63 -0
  279. package/dist/server/storage/s3ObjectStore.js +187 -0
  280. package/dist/server/storage/sources.d.ts +47 -0
  281. package/dist/server/storage/sources.js +148 -0
  282. package/dist/server/storage/streamRange.d.ts +19 -0
  283. package/dist/server/storage/streamRange.js +69 -0
  284. package/dist/server/storage/streamToken.d.ts +16 -0
  285. package/dist/server/storage/streamToken.js +54 -0
  286. package/dist/server/storage/transcodeJobs.d.ts +65 -0
  287. package/dist/server/storage/transcodeJobs.js +104 -0
  288. package/dist/server/storage/types.d.ts +193 -0
  289. package/dist/server/storage/types.js +41 -0
  290. package/dist/server/storage/videoDerivativeKeys.d.ts +127 -0
  291. package/dist/server/storage/videoDerivativeKeys.js +149 -0
  292. package/dist/server/storage/videoProcessor.d.ts +30 -0
  293. package/dist/server/storage/videoProcessor.js +148 -0
  294. package/dist/server/sync/alarm.d.ts +34 -0
  295. package/dist/server/sync/alarm.js +91 -0
  296. package/dist/server/sync/commands.d.ts +87 -0
  297. package/dist/server/sync/commands.js +241 -0
  298. package/dist/server/sync/engine.d.ts +62 -0
  299. package/dist/server/sync/engine.js +184 -0
  300. package/dist/server/sync/http.d.ts +106 -0
  301. package/dist/server/sync/http.js +243 -0
  302. package/dist/server/sync/index.d.ts +41 -0
  303. package/dist/server/sync/index.js +41 -0
  304. package/dist/server/sync/opLog.d.ts +61 -0
  305. package/dist/server/sync/opLog.js +96 -0
  306. package/dist/server/sync/planner.d.ts +31 -0
  307. package/dist/server/sync/planner.js +30 -0
  308. package/dist/server/sync/status.d.ts +63 -0
  309. package/dist/server/sync/status.js +49 -0
  310. package/dist/server/sync/timer.d.ts +52 -0
  311. package/dist/server/sync/timer.js +170 -0
  312. package/dist/server/sync/tokens.d.ts +25 -0
  313. package/dist/server/sync/tokens.js +51 -0
  314. package/dist/server/sync/types.d.ts +101 -0
  315. package/dist/server/sync/types.js +29 -0
  316. package/dist/server/ws/eventBus.d.ts +14 -0
  317. package/dist/server/ws/eventBus.js +42 -0
  318. package/dist/server/ws/hub.d.ts +20 -0
  319. package/dist/server/ws/hub.js +154 -0
  320. package/dist/server/ws/index.d.ts +5 -0
  321. package/dist/server/ws/index.js +7 -0
  322. package/dist/server/ws/presence.d.ts +11 -0
  323. package/dist/server/ws/presence.js +20 -0
  324. package/dist/server/ws/room.d.ts +17 -0
  325. package/dist/server/ws/room.js +23 -0
  326. package/dist/server/ws/types.d.ts +122 -0
  327. package/dist/server/ws/types.js +1 -0
  328. package/package.json +234 -0
  329. package/src/declaredDepsAreImported.spec.ts +87 -0
  330. package/src/leafSubpathsImportNothing.spec.ts +358 -0
  331. package/src/lockfileIsTracked.spec.ts +50 -0
  332. package/src/namedSubpathsResolve.spec.ts +122 -0
  333. package/src/noPathDeps.spec.ts +24 -0
  334. package/src/publishShape.spec.ts +89 -0
  335. package/src/server/activity/index.ts +59 -0
  336. package/src/server/activity/migrations.ts +67 -0
  337. package/src/server/activity/shareSource.ts +57 -0
  338. package/src/server/activity/store.spec.ts +255 -0
  339. package/src/server/activity/store.ts +368 -0
  340. package/src/server/app.spec.ts +243 -0
  341. package/src/server/app.ts +89 -0
  342. package/src/server/audit/index.ts +2 -0
  343. package/src/server/audit/migrations.ts +88 -0
  344. package/src/server/audit/sqliteSink.test.ts +200 -0
  345. package/src/server/audit/sqliteSink.ts +162 -0
  346. package/src/server/auth/cookie.spec.ts +46 -0
  347. package/src/server/auth/cookie.ts +42 -0
  348. package/src/server/auth/csrf.spec.ts +81 -0
  349. package/src/server/auth/csrf.ts +83 -0
  350. package/src/server/auth/fileTokenSigner.ts +40 -0
  351. package/src/server/auth/getOnly.spec.ts +53 -0
  352. package/src/server/auth/getOnly.ts +32 -0
  353. package/src/server/auth/index.ts +51 -0
  354. package/src/server/auth/jwt.spec.ts +106 -0
  355. package/src/server/auth/jwt.ts +75 -0
  356. package/src/server/auth/loginThrottle.spec.ts +213 -0
  357. package/src/server/auth/loginThrottle.ts +261 -0
  358. package/src/server/auth/middleware.spec.ts +148 -0
  359. package/src/server/auth/middleware.ts +56 -0
  360. package/src/server/auth/password.spec.ts +48 -0
  361. package/src/server/auth/password.ts +71 -0
  362. package/src/server/auth/sessionStore.spec.ts +189 -0
  363. package/src/server/auth/sessionStore.ts +261 -0
  364. package/src/server/auth/totp.spec.ts +73 -0
  365. package/src/server/auth/totp.ts +92 -0
  366. package/src/server/auth/types.ts +146 -0
  367. package/src/server/auth/vault.spec.ts +51 -0
  368. package/src/server/auth/vault.ts +71 -0
  369. package/src/server/context.ts +37 -0
  370. package/src/server/ctgr.spec.ts +138 -0
  371. package/src/server/ctgr.ts +108 -0
  372. package/src/server/db/kysely.spec.ts +62 -0
  373. package/src/server/db/kysely.ts +21 -0
  374. package/src/server/db/plumbingTypes.ts +126 -0
  375. package/src/server/dropzone/duplicates.ts +121 -0
  376. package/src/server/dropzone/index.ts +30 -0
  377. package/src/server/dropzone/scan.spec.ts +255 -0
  378. package/src/server/dropzone/scan.ts +342 -0
  379. package/src/server/errors.spec.ts +76 -0
  380. package/src/server/errors.ts +22 -0
  381. package/src/server/guard/argon2Binding.spec.ts +117 -0
  382. package/src/server/guard/argon2Binding.ts +195 -0
  383. package/src/server/guard/argon2Kat.spec.ts +250 -0
  384. package/src/server/guard/guard.spec.ts +390 -0
  385. package/src/server/guard/index.ts +6 -0
  386. package/src/server/guard/kdfWorker.spec.ts +283 -0
  387. package/src/server/guard/kdfWorker.ts +367 -0
  388. package/src/server/guard/kdfWorkerEntry.ts +16 -0
  389. package/src/server/guard/passwordVerifier.spec.ts +71 -0
  390. package/src/server/guard/passwordVerifier.ts +175 -0
  391. package/src/server/guard/revocationStore.spec.ts +135 -0
  392. package/src/server/guard/revocationStore.ts +291 -0
  393. package/src/server/guard/sessionSigner.ts +425 -0
  394. package/src/server/guard/verifier.ts +27 -0
  395. package/src/server/incidents/channels.ts +487 -0
  396. package/src/server/incidents/checks.ts +120 -0
  397. package/src/server/incidents/incidents.spec.ts +463 -0
  398. package/src/server/incidents/index.ts +96 -0
  399. package/src/server/incidents/migrations.ts +66 -0
  400. package/src/server/incidents/notifier.ts +215 -0
  401. package/src/server/incidents/reporter.ts +110 -0
  402. package/src/server/incidents/routes.ts +314 -0
  403. package/src/server/incidents/store.ts +329 -0
  404. package/src/server/incidents/types.ts +116 -0
  405. package/src/server/index.ts +248 -0
  406. package/src/server/jobs/fireQueue.spec.ts +85 -0
  407. package/src/server/jobs/fireQueue.ts +37 -0
  408. package/src/server/jobs/index.ts +26 -0
  409. package/src/server/jobs/logger.ts +16 -0
  410. package/src/server/jobs/plainjobAdapter.spec.ts +34 -0
  411. package/src/server/jobs/plainjobAdapter.ts +54 -0
  412. package/src/server/jobs/polling.spec.ts +219 -0
  413. package/src/server/jobs/polling.ts +81 -0
  414. package/src/server/jobs/runner.spec.ts +213 -0
  415. package/src/server/jobs/runner.ts +213 -0
  416. package/src/server/jobs/timeout.spec.ts +35 -0
  417. package/src/server/jobs/timeout.ts +53 -0
  418. package/src/server/jobs/types.ts +188 -0
  419. package/src/server/master-lock/guard.spec.ts +347 -0
  420. package/src/server/master-lock/guard.ts +290 -0
  421. package/src/server/master-lock/index.ts +44 -0
  422. package/src/server/master-lock/lockPage.spec.ts +88 -0
  423. package/src/server/master-lock/lockPage.ts +313 -0
  424. package/src/server/master-lock/masterLock.spec.ts +338 -0
  425. package/src/server/master-lock/masterLock.ts +627 -0
  426. package/src/server/master-lock/principals.spec.ts +378 -0
  427. package/src/server/master-lock/principals.ts +175 -0
  428. package/src/server/master-lock/seed.spec.ts +69 -0
  429. package/src/server/master-lock/seed.ts +112 -0
  430. package/src/server/master-lock/store.ts +35 -0
  431. package/src/server/media-bun/image.spec.ts +115 -0
  432. package/src/server/media-bun/image.ts +134 -0
  433. package/src/server/media-bun/index.ts +52 -0
  434. package/src/server/media-bun/streaming.spec.ts +134 -0
  435. package/src/server/media-bun/streaming.ts +347 -0
  436. package/src/server/media-bun/video.spec.ts +115 -0
  437. package/src/server/media-bun/video.ts +232 -0
  438. package/src/server/metrics/metricsBuffer.ts +92 -0
  439. package/src/server/metrics/normalizeRoute.spec.ts +51 -0
  440. package/src/server/metrics/normalizeRoute.ts +63 -0
  441. package/src/server/middleware/correlationId.ts +24 -0
  442. package/src/server/middleware/corsAllowlist.spec.ts +118 -0
  443. package/src/server/middleware/corsAllowlist.ts +69 -0
  444. package/src/server/middleware/errorEnvelope.ts +38 -0
  445. package/src/server/middleware/index.ts +19 -0
  446. package/src/server/middleware/localOrigin.spec.ts +168 -0
  447. package/src/server/middleware/localOrigin.ts +139 -0
  448. package/src/server/middleware/rateLimit.spec.ts +72 -0
  449. package/src/server/middleware/rateLimit.ts +125 -0
  450. package/src/server/middleware/requestLogger.ts +95 -0
  451. package/src/server/middleware/securityHeaders.ts +78 -0
  452. package/src/server/middleware/zValidator.spec.ts +251 -0
  453. package/src/server/middleware/zValidator.ts +114 -0
  454. package/src/server/migration/runner.spec.ts +88 -0
  455. package/src/server/migration/runner.ts +87 -0
  456. package/src/server/migration/types.ts +17 -0
  457. package/src/server/net/outboundGuard.spec.ts +90 -0
  458. package/src/server/net/outboundGuard.ts +205 -0
  459. package/src/server/notifications/hub.ts +245 -0
  460. package/src/server/notifications/index.ts +77 -0
  461. package/src/server/notifications/migrations.ts +39 -0
  462. package/src/server/notifications/routes.ts +177 -0
  463. package/src/server/notifications/service.spec.ts +305 -0
  464. package/src/server/notifications/service.ts +112 -0
  465. package/src/server/paths/appDataHome.spec.ts +87 -0
  466. package/src/server/paths/appDataHome.ts +84 -0
  467. package/src/server/private-media/blobRoute.spec.ts +165 -0
  468. package/src/server/private-media/crypto.spec.ts +81 -0
  469. package/src/server/private-media/crypto.ts +155 -0
  470. package/src/server/private-media/gatePolicy.spec.ts +98 -0
  471. package/src/server/private-media/index-store.ts +83 -0
  472. package/src/server/private-media/mountPrivateRoutes.ts +265 -0
  473. package/src/server/private-media/pipeline.spec.ts +111 -0
  474. package/src/server/private-media/pipeline.ts +170 -0
  475. package/src/server/private-media/routes.spec.ts +265 -0
  476. package/src/server/private-media/unlock.ts +112 -0
  477. package/src/server/resume/index.ts +8 -0
  478. package/src/server/resume/resume.spec.ts +118 -0
  479. package/src/server/resume/routes.ts +92 -0
  480. package/src/server/resume/store.ts +82 -0
  481. package/src/server/retention/archive.spec.ts +181 -0
  482. package/src/server/retention/archive.ts +353 -0
  483. package/src/server/retention/fleet.ts +104 -0
  484. package/src/server/retention/index.ts +101 -0
  485. package/src/server/retention/policy.spec.ts +132 -0
  486. package/src/server/retention/policy.ts +392 -0
  487. package/src/server/retention/store.spec.ts +236 -0
  488. package/src/server/retention/store.ts +338 -0
  489. package/src/server/retention/sweeper.spec.ts +321 -0
  490. package/src/server/retention/sweeper.ts +367 -0
  491. package/src/server/retention/target.spec.ts +592 -0
  492. package/src/server/retention/target.ts +613 -0
  493. package/src/server/search/index.ts +12 -0
  494. package/src/server/search/pgSearchProvider.pg.test.ts +392 -0
  495. package/src/server/search/pgSearchProvider.ts +421 -0
  496. package/src/server/search/pgSearchSchema.ts +147 -0
  497. package/src/server/sharing/index.ts +65 -0
  498. package/src/server/sharing/migrations.ts +128 -0
  499. package/src/server/sharing/router.spec.ts +167 -0
  500. package/src/server/sharing/router.ts +194 -0
  501. package/src/server/sharing/sql.ts +109 -0
  502. package/src/server/sharing/store.spec.ts +408 -0
  503. package/src/server/sharing/store.ts +737 -0
  504. package/src/server/sqlite/backup.spec.ts +114 -0
  505. package/src/server/sqlite/backup.ts +154 -0
  506. package/src/server/sqlite/browse.spec.ts +70 -0
  507. package/src/server/sqlite/browse.ts +120 -0
  508. package/src/server/sqlite/export.spec.ts +43 -0
  509. package/src/server/sqlite/export.ts +74 -0
  510. package/src/server/sqlite/fts5.spec.ts +175 -0
  511. package/src/server/sqlite/fts5.ts +255 -0
  512. package/src/server/sqlite/health.spec.ts +79 -0
  513. package/src/server/sqlite/health.ts +105 -0
  514. package/src/server/sqlite/import.spec.ts +75 -0
  515. package/src/server/sqlite/import.ts +141 -0
  516. package/src/server/sqlite/index.ts +64 -0
  517. package/src/server/sqlite/readonlySql.spec.ts +58 -0
  518. package/src/server/sqlite/readonlySql.ts +67 -0
  519. package/src/server/sqlite/stats.spec.ts +21 -0
  520. package/src/server/sqlite/stats.ts +61 -0
  521. package/src/server/sqlite/viewer.spec.ts +127 -0
  522. package/src/server/sqlite/viewer.ts +72 -0
  523. package/src/server/storage/binaryServerProcessing.integration.spec.ts +222 -0
  524. package/src/server/storage/binaryServerProcessing.spec.ts +150 -0
  525. package/src/server/storage/binaryServerProcessing.ts +127 -0
  526. package/src/server/storage/binaryServerStore.ts +102 -0
  527. package/src/server/storage/chunked.integration.spec.ts +185 -0
  528. package/src/server/storage/diskStore.ts +83 -0
  529. package/src/server/storage/files/adapter.photo.display.spec.ts +169 -0
  530. package/src/server/storage/files/adapter.photo.spec.ts +128 -0
  531. package/src/server/storage/files/adapter.spec.ts +121 -0
  532. package/src/server/storage/files/adapter.ts +255 -0
  533. package/src/server/storage/files/catalogue.media-host.spec.ts +184 -0
  534. package/src/server/storage/files/catalogue.orderings.spec.ts +157 -0
  535. package/src/server/storage/files/catalogue.private.spec.ts +106 -0
  536. package/src/server/storage/files/catalogue.recover.spec.ts +131 -0
  537. package/src/server/storage/files/catalogue.sort.spec.ts +154 -0
  538. package/src/server/storage/files/catalogue.spec.ts +133 -0
  539. package/src/server/storage/files/catalogue.trash-import.spec.ts +125 -0
  540. package/src/server/storage/files/catalogue.ts +1326 -0
  541. package/src/server/storage/files/chunkedUpload.spec.ts +111 -0
  542. package/src/server/storage/files/chunkedUpload.ts +146 -0
  543. package/src/server/storage/files/collections.spec.ts +294 -0
  544. package/src/server/storage/files/fileRoutes.replace.spec.ts +88 -0
  545. package/src/server/storage/files/fileRoutes.spec.ts +327 -0
  546. package/src/server/storage/files/fileRoutes.ts +462 -0
  547. package/src/server/storage/files/index.ts +66 -0
  548. package/src/server/storage/files/migrations.ts +187 -0
  549. package/src/server/storage/files/mimeByExt.ts +14 -0
  550. package/src/server/storage/files/stack.spec.ts +258 -0
  551. package/src/server/storage/files/stack.ts +284 -0
  552. package/src/server/storage/files/storageOps.spec.ts +126 -0
  553. package/src/server/storage/files/storageOps.ts +213 -0
  554. package/src/server/storage/files/types.ts +285 -0
  555. package/src/server/storage/imageThumbnail.spec.ts +81 -0
  556. package/src/server/storage/imageThumbnail.ts +60 -0
  557. package/src/server/storage/index.ts +122 -0
  558. package/src/server/storage/mountMediaRoutes.ts +231 -0
  559. package/src/server/storage/objectStore.spec.ts +272 -0
  560. package/src/server/storage/objectStoreMediaStore.ts +63 -0
  561. package/src/server/storage/s3ObjectStore.ts +239 -0
  562. package/src/server/storage/sources.ts +245 -0
  563. package/src/server/storage/storage.spec.ts +219 -0
  564. package/src/server/storage/streamRange.ts +92 -0
  565. package/src/server/storage/streamToken.ts +76 -0
  566. package/src/server/storage/transcode.integration.spec.ts +134 -0
  567. package/src/server/storage/transcodeJobs.spec.ts +152 -0
  568. package/src/server/storage/transcodeJobs.ts +171 -0
  569. package/src/server/storage/types.ts +230 -0
  570. package/src/server/storage/videoDerivativeKeys.spec.ts +112 -0
  571. package/src/server/storage/videoDerivativeKeys.ts +172 -0
  572. package/src/server/storage/videoProcessor.posterOnly.spec.ts +106 -0
  573. package/src/server/storage/videoProcessor.ts +229 -0
  574. package/src/server/sync/alarm.spec.ts +149 -0
  575. package/src/server/sync/alarm.ts +137 -0
  576. package/src/server/sync/commands.spec.ts +145 -0
  577. package/src/server/sync/commands.ts +361 -0
  578. package/src/server/sync/engine.spec.ts +496 -0
  579. package/src/server/sync/engine.ts +255 -0
  580. package/src/server/sync/http.ts +316 -0
  581. package/src/server/sync/httpRemote.spec.ts +158 -0
  582. package/src/server/sync/index.ts +90 -0
  583. package/src/server/sync/opLog.spec.ts +110 -0
  584. package/src/server/sync/opLog.ts +189 -0
  585. package/src/server/sync/planner.spec.ts +53 -0
  586. package/src/server/sync/planner.ts +62 -0
  587. package/src/server/sync/status.spec.ts +92 -0
  588. package/src/server/sync/status.ts +108 -0
  589. package/src/server/sync/timer.spec.ts +150 -0
  590. package/src/server/sync/timer.ts +207 -0
  591. package/src/server/sync/tokens.spec.ts +38 -0
  592. package/src/server/sync/tokens.ts +94 -0
  593. package/src/server/sync/types.ts +108 -0
  594. package/src/server/ws/eventBus.spec.ts +67 -0
  595. package/src/server/ws/eventBus.ts +46 -0
  596. package/src/server/ws/hub.integration.spec.ts +93 -0
  597. package/src/server/ws/hub.spec.ts +282 -0
  598. package/src/server/ws/hub.ts +168 -0
  599. package/src/server/ws/index.ts +20 -0
  600. package/src/server/ws/presence.spec.ts +27 -0
  601. package/src/server/ws/presence.ts +26 -0
  602. package/src/server/ws/room.spec.ts +36 -0
  603. package/src/server/ws/room.ts +29 -0
  604. package/src/server/ws/types.ts +134 -0
  605. package/src/skippedTestsAreAnswered.spec.ts +215 -0
  606. package/src/testTmpRoot.spec.ts +102 -0
@@ -0,0 +1,301 @@
1
+ /**
2
+ * The off-thread KDF (05 §B6) — a long-lived worker that owns every byte of key
3
+ * material it produces.
4
+ *
5
+ * ## The problem
6
+ *
7
+ * argon2id at the platform parameters is ~150–250 ms of *pure CPU*. The KDF seam
8
+ * is synchronous by design (see `Argon2idDeriver` — the async bindings return
9
+ * un-zeroizable PHC strings), so that time is spent on the event loop with
10
+ * nothing else able to run. There is no race and no correctness bug; there is a
11
+ * hard throughput ceiling. A saas node handling a burst of logins serializes
12
+ * them AND stalls every unrelated request for the duration, so one tenant's
13
+ * login storm is every other tenant's latency spike.
14
+ *
15
+ * ## The design, and the one thing that makes it worth building
16
+ *
17
+ * The naive off-thread KDF derives in a worker and posts the KEY BACK. That
18
+ * moves the CPU but weakens custody: the key crosses a structured-clone boundary
19
+ * (a copy the sender cannot zeroize, because clone semantics leave the original
20
+ * behind and the receiver adopts a second allocation), and it lands in a host
21
+ * that must remember to destroy it.
22
+ *
23
+ * **Nothing here posts a key.** The worker runs the SHIPPED
24
+ * `sealPasswordVerifier` / `verifyPasswordEnvelope` — the same functions, the
25
+ * same envelope format, the same one KDF — so the derived key is born, used and
26
+ * zeroized entirely inside the worker thread. What crosses back is a boolean or
27
+ * the sealed *ciphertext*. The password crosses inward, because it arrives from
28
+ * the request and must; the worker zeroizes its copy on every path, and the host
29
+ * zeroizes its own.
30
+ *
31
+ * That is why this is a worker for the whole VERIFY rather than a worker for the
32
+ * derivation: relocating the derivation alone would have traded a throughput
33
+ * problem for a custody problem.
34
+ *
35
+ * ## Long-lived, never per-call
36
+ *
37
+ * Thread startup plus module load (a native argon2 binding!) is tens of
38
+ * milliseconds, and a spawn-per-login design lets a login storm fork unbounded
39
+ * OS threads — which is the same denial of service the ceiling above, only
40
+ * worse. One thread, multiplexed by request id. This is the mistake
41
+ * `createMarshalWorkerClient` documents in `core/wire/marshalWorker.ts`; it is
42
+ * repeated here deliberately in the same shape so both read alike.
43
+ */
44
+ import { AppError } from '../errors';
45
+ import { createInProcessPasswordVerifier, sealPasswordVerifier, verifyPasswordEnvelope, } from './passwordVerifier';
46
+ import { registerNodeRsArgon2id } from './argon2Binding';
47
+ /** Raised when the worker thread dies, fails to start, or is disposed mid-flight. */
48
+ export const GUARD_KDF_WORKER_FAILED = 'GUARD_KDF_WORKER_FAILED';
49
+ /** Raised when the worker does not answer within the configured budget. */
50
+ export const GUARD_KDF_WORKER_TIMEOUT = 'GUARD_KDF_WORKER_TIMEOUT';
51
+ /**
52
+ * Default per-request ceiling. Generous relative to a ~250 ms derivation on
53
+ * purpose: this is a liveness backstop for a wedged thread, not a latency SLO,
54
+ * and a tight budget here would turn a loaded machine into a login outage.
55
+ */
56
+ export const KDF_WORKER_DEFAULT_TIMEOUT_MS = 15_000;
57
+ const nodeWorkerScope = async () => {
58
+ const { parentPort } = await import('node:worker_threads');
59
+ if (parentPort === null)
60
+ throw new Error('serveKdfWorker() was called outside a worker thread.');
61
+ return {
62
+ postMessage: (data) => parentPort.postMessage(data),
63
+ onMessage: (handler) => parentPort.on('message', handler),
64
+ };
65
+ };
66
+ /**
67
+ * Run the KDF worker's message loop. A worker entry module is this call and
68
+ * nothing else (see `kdfWorkerEntry.ts`).
69
+ *
70
+ * `register` is injectable so the spec can drive the loop with a cheap fake
71
+ * binding — registering the real native argon2 in a test that only checks
72
+ * message plumbing would pay 250 ms per case to prove nothing about the
73
+ * plumbing. The DEFAULT is the real binding, because a worker that silently ran
74
+ * a fake KDF would be a catastrophe rather than a bug.
75
+ */
76
+ export async function serveKdfWorker(options = {}) {
77
+ const scope = options.scope ?? (await nodeWorkerScope());
78
+ // Registration happens ONCE, at worker start, before any request is served —
79
+ // not lazily per request, or the first login of every thread pays a native
80
+ // module load inside its own timeout budget.
81
+ await (options.register ?? registerNodeRsArgon2id)();
82
+ scope.onMessage((data) => {
83
+ const request = data;
84
+ if (typeof request?.id !== 'number')
85
+ return;
86
+ // The password arrives as a structured-clone copy owned by THIS thread.
87
+ // Re-wrap anything that is not already a Uint8Array rather than trusting the
88
+ // wire type — a plain `{0:..,1:..}` would silently derive from the wrong
89
+ // bytes instead of failing — but keep the inbound view too, because it is a
90
+ // second plaintext copy this thread is responsible for.
91
+ const inbound = request.password;
92
+ const password = inbound instanceof Uint8Array ? inbound : new Uint8Array(inbound);
93
+ try {
94
+ const value = request.kind === 'seal'
95
+ ? sealPasswordVerifier(request.userId, password)
96
+ : verifyPasswordEnvelope(request.userId, request.envelopeJson, password);
97
+ scope.postMessage({ id: request.id, ok: true, value });
98
+ }
99
+ catch (error) {
100
+ // Only the CODE and message cross back. An error `context` in this module
101
+ // can hold envelope fields, so the envelope itself never leaves.
102
+ scope.postMessage({
103
+ id: request.id,
104
+ ok: false,
105
+ code: (error instanceof AppError ? error.code : undefined) ?? GUARD_KDF_WORKER_FAILED,
106
+ message: error instanceof Error ? error.message : String(error),
107
+ });
108
+ }
109
+ finally {
110
+ // Both `sealPasswordVerifier` and `verifyPasswordEnvelope` consume (and so
111
+ // zeroize) the buffer they are handed — but they are the far side of a
112
+ // contract, and a throw BEFORE they are reached (a malformed envelope, an
113
+ // unregistered deriver) leaves a plaintext password in worker RAM. Clearing
114
+ // again is idempotent and costs a memset.
115
+ //
116
+ // EVERY view, not just the one that was used: when the wire type is wrong
117
+ // the re-wrap above makes a second allocation, and zeroizing only the copy
118
+ // we derived from would leave the structured clone this thread was handed
119
+ // sitting in memory for the life of the worker.
120
+ password.fill(0);
121
+ if (inbound !== password && inbound instanceof Uint8Array)
122
+ inbound.fill(0);
123
+ }
124
+ });
125
+ }
126
+ function spawnHostWorker(entry) {
127
+ // Callers await `settled` rather than seeing the construction race. It is
128
+ // RESOLVED, never rejected, on every exit path including a failed spawn: a
129
+ // rejection here would surface as an unhandled rejection on a request that the
130
+ // error handler below is already failing properly.
131
+ let realPost = null;
132
+ let markSettled = () => undefined;
133
+ const settled = new Promise((resolve) => {
134
+ markSettled = resolve;
135
+ });
136
+ let terminated = false;
137
+ const messageHandlers = [];
138
+ const errorHandlers = [];
139
+ let terminateReal = null;
140
+ void (async () => {
141
+ try {
142
+ const { Worker } = await import('node:worker_threads');
143
+ if (terminated)
144
+ return;
145
+ const worker = new Worker(entry);
146
+ worker.on('message', (data) => {
147
+ for (const handler of messageHandlers)
148
+ handler(data);
149
+ });
150
+ worker.on('error', (error) => {
151
+ for (const handler of errorHandlers)
152
+ handler(error);
153
+ });
154
+ // ANY exit is a failure: this worker is long-lived by design, so it should
155
+ // outlive every request. A cleanly-exited thread that went unnoticed would
156
+ // leave each in-flight login waiting out its full timeout, and every later
157
+ // one posting into a dead thread to do the same.
158
+ worker.on('exit', (code) => {
159
+ const how = code === 0 ? 'exited cleanly while still in use' : `exited with code ${code}`;
160
+ for (const handler of errorHandlers)
161
+ handler(new Error(`KDF worker ${how}`));
162
+ });
163
+ realPost = (data) => worker.postMessage(data);
164
+ terminateReal = () => void worker.terminate();
165
+ }
166
+ catch (error) {
167
+ for (const handler of errorHandlers)
168
+ handler(error);
169
+ }
170
+ finally {
171
+ markSettled();
172
+ }
173
+ })();
174
+ return {
175
+ async post(data) {
176
+ if (realPost === null)
177
+ await settled;
178
+ // Still null ⇒ the spawn failed or we were disposed mid-construction. The
179
+ // error handler has already failed (or will fail) the request; dropping
180
+ // the message is correct, and the caller may now zeroize.
181
+ realPost?.(data);
182
+ },
183
+ onMessage: (handler) => messageHandlers.push(handler),
184
+ onError: (handler) => errorHandlers.push(handler),
185
+ terminate: () => {
186
+ terminated = true;
187
+ markSettled();
188
+ terminateReal?.();
189
+ },
190
+ };
191
+ }
192
+ /**
193
+ * A {@link PasswordVerifierPort} backed by one long-lived worker thread.
194
+ *
195
+ * Hand it to `createAccountService({ passwords })` on a host that expects login
196
+ * concurrency. Local single-user hosts should NOT — they would pay a thread and
197
+ * a native module load to serialize a workload that was never contended.
198
+ */
199
+ export function createKdfWorkerPasswordVerifier(options) {
200
+ const timeoutMs = options.timeoutMs ?? KDF_WORKER_DEFAULT_TIMEOUT_MS;
201
+ const worker = (options.spawn ?? spawnHostWorker)(options.entry);
202
+ const pending = new Map();
203
+ let nextId = 1;
204
+ let disposed = false;
205
+ const settle = (id, apply) => {
206
+ const entry = pending.get(id);
207
+ if (!entry)
208
+ return; // already timed out — a late reply is discarded
209
+ pending.delete(id);
210
+ clearTimeout(entry.timer);
211
+ apply(entry);
212
+ };
213
+ const failure = (message, code, status) => new AppError(message, { code, status, category: 'security', context: { worker: String(options.entry) } });
214
+ worker.onMessage((data) => {
215
+ const response = data;
216
+ if (typeof response?.id !== 'number')
217
+ return;
218
+ settle(response.id, (p) => {
219
+ if (response.ok) {
220
+ p.resolve(response.value);
221
+ return;
222
+ }
223
+ // Preserve the worker's own code: `SECRET_ENVELOPE_*` failures are a
224
+ // corrupt row, not a dead thread, and a caller distinguishing them must
225
+ // still be able to.
226
+ p.reject(failure(response.message, response.code, 500));
227
+ });
228
+ });
229
+ // A dead worker takes every request in flight with it — failing them now beats
230
+ // each one waiting out its own timeout.
231
+ worker.onError((error) => {
232
+ const message = error instanceof Error ? error.message : String(error);
233
+ for (const id of [...pending.keys()])
234
+ settle(id, (p) => p.reject(failure(`KDF worker failed: ${message}`, GUARD_KDF_WORKER_FAILED, 500)));
235
+ });
236
+ /**
237
+ * Post the request, zeroize the host's copy of the password as soon as the
238
+ * thread has it, then await the verdict.
239
+ *
240
+ * The zeroize placement is the point. The worker holds a structured-clone
241
+ * copy, so the host's buffer is dead weight the instant the post lands —
242
+ * and the await that follows lasts the whole ~250 ms derivation, which is
243
+ * exactly the window in which a heap snapshot, a core dump or a crash reporter
244
+ * would capture a plaintext password. Clearing it before the await instead of
245
+ * in a `finally` after it removes that window entirely.
246
+ */
247
+ const send = async (build, password) => {
248
+ if (disposed)
249
+ throw failure('The KDF worker has been disposed.', GUARD_KDF_WORKER_FAILED, 500);
250
+ const id = nextId++;
251
+ const answer = new Promise((resolve, reject) => {
252
+ const timer = setTimeout(() => {
253
+ pending.delete(id);
254
+ reject(failure(`The KDF worker did not answer within ${timeoutMs}ms.`, GUARD_KDF_WORKER_TIMEOUT, 504));
255
+ }, timeoutMs);
256
+ pending.set(id, { resolve, reject, timer });
257
+ });
258
+ try {
259
+ await worker.post(build(id));
260
+ }
261
+ finally {
262
+ password.fill(0);
263
+ }
264
+ return await answer;
265
+ };
266
+ return {
267
+ async seal(userId, password) {
268
+ const value = await send((id) => ({ id, kind: 'seal', userId, password }), password);
269
+ if (typeof value !== 'string')
270
+ throw failure('The KDF worker returned a non-string envelope for seal().', GUARD_KDF_WORKER_FAILED, 500);
271
+ return value;
272
+ },
273
+ async verify(userId, envelopeJson, password) {
274
+ const value = await send((id) => ({ id, kind: 'verify', userId, envelopeJson, password }), password);
275
+ if (typeof value !== 'boolean')
276
+ throw failure('The KDF worker returned a non-boolean verdict for verify().', GUARD_KDF_WORKER_FAILED, 500);
277
+ return value;
278
+ },
279
+ async dispose() {
280
+ disposed = true;
281
+ for (const id of [...pending.keys()])
282
+ settle(id, (p) => p.reject(failure('The KDF worker was disposed while a request was in flight.', GUARD_KDF_WORKER_FAILED, 503)));
283
+ worker.terminate();
284
+ },
285
+ };
286
+ }
287
+ /**
288
+ * The URL of the entry module {@link createKdfWorkerPasswordVerifier} spawns.
289
+ *
290
+ * Resolved relative to THIS module rather than hard-coded by the host, so an app
291
+ * never has to know where inside the package the entry lives:
292
+ *
293
+ * createKdfWorkerPasswordVerifier({ entry: kdfWorkerEntryUrl() })
294
+ *
295
+ * Under the `bun` export condition this resolves to the TypeScript source, which
296
+ * is what every first-party consumer runs (see the monorepo's zero-build-lag
297
+ * contract). A non-Bun host consuming built `dist/` names its own built entry.
298
+ */
299
+ export const kdfWorkerEntryUrl = () => new URL('./kdfWorkerEntry.ts', import.meta.url);
300
+ /** Re-exported so a host wiring the guard tier needs one import. */
301
+ export { createInProcessPasswordVerifier };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The SHIPPED KDF worker entry (05 §B6).
3
+ *
4
+ * Unlike a marshalling worker — where every marshaller declares its own entry
5
+ * because every marshaller has its own schema — there is exactly ONE password
6
+ * KDF in the platform (Law #7), so there is exactly one entry and the framework
7
+ * ships it. A host does not write this file; it points
8
+ * `createKdfWorkerPasswordVerifier({ entry: kdfWorkerEntryUrl() })` at it.
9
+ *
10
+ * The whole module is one call on purpose: everything a worker entry does that
11
+ * is not "start the loop" is code running on a thread with a live argon2id
12
+ * binding and a plaintext password in scope.
13
+ */
14
+ import { serveKdfWorker } from './kdfWorker';
15
+ await serveKdfWorker();
@@ -0,0 +1,65 @@
1
+ import { type Argon2idDeriver } from 'cwip/guard';
2
+ /**
3
+ * Seal a password verifier for `userId`, returning the serialized `SecretEnvelope`
4
+ * to store in `users.pw_envelope`. **Consumes `password`** (zeroized before return).
5
+ *
6
+ * A fresh per-user KDF salt is minted for each call, so two users with the same
7
+ * password produce different envelopes and re-deriving one never helps the other.
8
+ */
9
+ export declare function sealPasswordVerifier(userId: string, password: Uint8Array, deriver?: Argon2idDeriver): string;
10
+ /**
11
+ * Check `password` against the stored `envelopeJson` for `userId`. **Consumes
12
+ * `password`** (zeroized before return). Returns `true` iff the password is
13
+ * correct; `false` for a wrong password (GCM authentication failure). A malformed
14
+ * stored envelope is corruption, not a wrong password, and re-throws.
15
+ *
16
+ * The comparison is constant-time in the sensitive part: argon2id derivation
17
+ * dominates the timing, and GCM tag verification plus the final `timingSafeEqual`
18
+ * are constant-time, so a right and a wrong password of equal length are
19
+ * indistinguishable by timing.
20
+ */
21
+ export declare function verifyPasswordEnvelope(userId: string, envelopeJson: string, password: Uint8Array, deriver?: Argon2idDeriver): boolean;
22
+ /**
23
+ * Where password derivation RUNS (05 §B6).
24
+ *
25
+ * The two functions above are synchronous on purpose — the KDF seam is, and for
26
+ * the reason documented on `Argon2idDeriver`: every async argon2id binding hands
27
+ * back a promise AND a base64/PHC *string*, which is key material that cannot be
28
+ * zeroized. The cost is that one derivation is ~150–250 ms of pure CPU on the
29
+ * event loop, so concurrent logins SERIALIZE and stall every unrelated request
30
+ * on the process for that window. A single-user local app never notices; a saas
31
+ * node taking a burst of logins does, and so does every tenant sharing it.
32
+ *
33
+ * This port is the seam that lets a host move that burn off the request thread
34
+ * WITHOUT introducing a second KDF, a second envelope format, or an async
35
+ * deriver. Both implementations call the exact same `sealPasswordVerifier` /
36
+ * `verifyPasswordEnvelope` above; they differ only in which thread runs them.
37
+ *
38
+ * local / default {@link createInProcessPasswordVerifier} — direct calls,
39
+ * byte-for-byte the behavior that shipped before this port
40
+ * existed.
41
+ * saas / opt-in `createKdfWorkerPasswordVerifier` (`./kdfWorker`) — a
42
+ * long-lived worker thread. **No key material crosses the
43
+ * thread boundary in either direction**: the password goes
44
+ * in, the derived key is born, used and zeroized inside the
45
+ * worker, and only a boolean (or the sealed ciphertext)
46
+ * comes back.
47
+ *
48
+ * Both entry points CONSUME `password` — ownership transfers on the call, and
49
+ * the buffer is zeroized on every path including a throw.
50
+ */
51
+ export interface PasswordVerifierPort {
52
+ /** Seal a verifier for `userId`, returning the serialized envelope. Consumes `password`. */
53
+ seal(userId: string, password: Uint8Array): Promise<string>;
54
+ /** `true` iff `password` matches `envelopeJson`. Consumes `password`. */
55
+ verify(userId: string, envelopeJson: string, password: Uint8Array): Promise<boolean>;
56
+ /** Release any thread/handle the implementation owns. In-process: a no-op. */
57
+ dispose?(): Promise<void>;
58
+ }
59
+ /**
60
+ * The default port: derive on the calling thread, exactly as every caller did
61
+ * before the port existed. Nothing about the crypto changes — this is the same
62
+ * two functions behind a promise, so a host that never opts into the worker
63
+ * path pays no async penalty beyond one already-resolved microtask.
64
+ */
65
+ export declare function createInProcessPasswordVerifier(deriver?: Argon2idDeriver): PasswordVerifierPort;
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Password verifiers over the ONE KDF (02 §2, Law #7).
3
+ *
4
+ * A login password check needs a *verifier* — something stored per user that a
5
+ * presented password can be checked against — WITHOUT introducing a second hash
6
+ * function or a second at-rest secret format. The platform already ships exactly
7
+ * one of each: the argon2id seam (`cwip/.../guard/kdf.ts`) and the
8
+ * `SecretEnvelope` (`cwip/.../guard/secretEnvelope.ts`). This module builds the
9
+ * verifier out of those, adding nothing.
10
+ *
11
+ * The trick: a verifier is a `SecretEnvelope` that seals a FIXED known plaintext
12
+ * under a key argon2id-derived from the user's password. To check a password,
13
+ * re-derive the one-shot key from the presented password and try to open the
14
+ * envelope — GCM's authentication tag IS the password check. A wrong password
15
+ * derives a wrong key and GCM refuses (`SECRET_ENVELOPE_AUTH_FAILED`), which we
16
+ * translate to `false`; a right password opens and the recovered plaintext
17
+ * matches the known constant. No password hash is ever stored, compared, or even
18
+ * computed outside the KDF, and the envelope is the same `pw_envelope` string the
19
+ * registry `users.pw_envelope` column already types.
20
+ *
21
+ * Two properties that are not incidental:
22
+ *
23
+ * - **The verifier is bound to its user.** The envelope AAD is `cc-pw:<userId>`,
24
+ * so a `pw_envelope` lifted out of user A's row into user B's fails GCM
25
+ * authentication rather than letting B log in with A's password (the same
26
+ * swap defense `SecretEnvelope` gives every secret).
27
+ * - **Byte custody (01 §3.4).** Both entry points CONSUME the password buffer:
28
+ * the underlying manager zeroizes it the moment the KDF is done with it, on
29
+ * every path including a thrown KDF. Callers hand over ownership and must not
30
+ * touch the buffer afterwards.
31
+ */
32
+ import { timingSafeEqual } from 'node:crypto';
33
+ import { createSecretEnvelopeManager, mintEnvelopeSalt, parseSecretEnvelope, serializeSecretEnvelope, zeroize, } from 'cwip/guard';
34
+ import { isAppError } from '../errors';
35
+ /**
36
+ * The reserved AAD app namespace for password verifiers. `SecretEnvelope` forbids
37
+ * a ':' in the appId (it separates the AAD fields), so this is a plain token; the
38
+ * userId rides the AAD `key` half.
39
+ */
40
+ const PW_APP = 'cc-pw';
41
+ /** The fixed plaintext every verifier seals — its recovery on open is the success signal. */
42
+ const VERIFIER_PLAINTEXT = new TextEncoder().encode('cc-guard-pw-verifier-v1');
43
+ /** Bind an envelope to one user: AAD becomes `cc-pw:<userId>`. */
44
+ const refFor = (userId) => ({ appId: PW_APP, key: userId });
45
+ /**
46
+ * Seal a password verifier for `userId`, returning the serialized `SecretEnvelope`
47
+ * to store in `users.pw_envelope`. **Consumes `password`** (zeroized before return).
48
+ *
49
+ * A fresh per-user KDF salt is minted for each call, so two users with the same
50
+ * password produce different envelopes and re-deriving one never helps the other.
51
+ */
52
+ export function sealPasswordVerifier(userId, password, deriver) {
53
+ const manager = createSecretEnvelopeManager({ salt: mintEnvelopeSalt(), deriver });
54
+ try {
55
+ // unlock derives the master key from (password, salt) and zeroizes `password`
56
+ // in its own finally — including if the KDF throws.
57
+ manager.unlock(password);
58
+ return serializeSecretEnvelope(manager.seal(refFor(userId), VERIFIER_PLAINTEXT));
59
+ }
60
+ finally {
61
+ manager.lock();
62
+ }
63
+ }
64
+ /**
65
+ * Check `password` against the stored `envelopeJson` for `userId`. **Consumes
66
+ * `password`** (zeroized before return). Returns `true` iff the password is
67
+ * correct; `false` for a wrong password (GCM authentication failure). A malformed
68
+ * stored envelope is corruption, not a wrong password, and re-throws.
69
+ *
70
+ * The comparison is constant-time in the sensitive part: argon2id derivation
71
+ * dominates the timing, and GCM tag verification plus the final `timingSafeEqual`
72
+ * are constant-time, so a right and a wrong password of equal length are
73
+ * indistinguishable by timing.
74
+ */
75
+ export function verifyPasswordEnvelope(userId, envelopeJson, password, deriver) {
76
+ let envelope;
77
+ try {
78
+ envelope = parseSecretEnvelope(envelopeJson);
79
+ }
80
+ catch (error) {
81
+ // A stored envelope that will not parse is a tampered/corrupt row — never
82
+ // silently treat it as "wrong password". Zeroize the buffer we were handed
83
+ // before propagating (the manager never got a chance to).
84
+ zeroize(password);
85
+ throw error;
86
+ }
87
+ const manager = createSecretEnvelopeManager({ salt: mintEnvelopeSalt(), deriver });
88
+ let opened = null;
89
+ try {
90
+ // openWithPassphrase re-derives a one-shot key from the envelope's own salt +
91
+ // the presented password, opens, and zeroizes `password` on every path.
92
+ opened = manager.openWithPassphrase(envelope, refFor(userId), password);
93
+ return opened.byteLength === VERIFIER_PLAINTEXT.byteLength && timingSafeEqual(opened, VERIFIER_PLAINTEXT);
94
+ }
95
+ catch (error) {
96
+ // GCM's rejection is the wrong-password verdict — the ONLY error we swallow.
97
+ if (isAppError(error) && error.code === 'SECRET_ENVELOPE_AUTH_FAILED')
98
+ return false;
99
+ throw error;
100
+ }
101
+ finally {
102
+ if (opened)
103
+ zeroize(opened);
104
+ manager.lock();
105
+ }
106
+ }
107
+ /**
108
+ * The default port: derive on the calling thread, exactly as every caller did
109
+ * before the port existed. Nothing about the crypto changes — this is the same
110
+ * two functions behind a promise, so a host that never opts into the worker
111
+ * path pays no async penalty beyond one already-resolved microtask.
112
+ */
113
+ export function createInProcessPasswordVerifier(deriver) {
114
+ return {
115
+ async seal(userId, password) {
116
+ return sealPasswordVerifier(userId, password, deriver);
117
+ },
118
+ async verify(userId, envelopeJson, password) {
119
+ return verifyPasswordEnvelope(userId, envelopeJson, password, deriver);
120
+ },
121
+ };
122
+ }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Somewhere to record the session/token ids that must no longer be accepted,
3
+ * answered synchronously.
4
+ *
5
+ * Deliberately tiny, and deliberately not a session store: the token still
6
+ * carries its own expiry and signature, so this only ever answers "was this one
7
+ * withdrawn early". `sweep` exists so the table cannot grow forever — every
8
+ * entry becomes meaningless the moment its session would have expired anyway.
9
+ *
10
+ * Epoch **milliseconds** throughout, matching `Date.now()` and the satellites'
11
+ * cookie expiry. (The cross-node {@link AsyncRevocationStore} works in seconds,
12
+ * matching the signer's `exp`; {@link createAsyncBackedRevocationStore} bridges
13
+ * the two.)
14
+ */
15
+ export interface RevocationStore {
16
+ /** Has this id been revoked? Called on every authenticated request. */
17
+ isRevoked(id: string): boolean;
18
+ /** Record a revocation until `expiresAtMs`, after which it is moot. */
19
+ revoke(id: string, expiresAtMs: number): void;
20
+ /** Drop entries whose sessions have expired. Called opportunistically. */
21
+ sweep(nowMs: number): void;
22
+ }
23
+ /**
24
+ * An in-memory sync {@link RevocationStore}.
25
+ *
26
+ * For tests, and for a single-process caller that does not need durability.
27
+ * NOT for a deployed satellite: an in-memory table would be worse than none —
28
+ * sign out, redeploy, and the session works again, which reads as the feature
29
+ * being broken rather than absent. Give a deployed door
30
+ * {@link createSqliteRevocationStore} (durable) or nothing.
31
+ */
32
+ export declare function createMemoryRevocationStore(): RevocationStore & {
33
+ size(): number;
34
+ };
35
+ /**
36
+ * A durable sync {@link RevocationStore} backed by a sqlite file.
37
+ *
38
+ * Because an in-memory table would be worse than none (see above), and the
39
+ * deploy engine persists each release's data directory, so durability costs one
40
+ * file. The table is self-limiting: an entry is meaningless once the session it
41
+ * names would have expired anyway, and `sweep` drops those on logout — rare,
42
+ * cheap, and exactly when the table has just grown, so there is no background
43
+ * timer to own.
44
+ *
45
+ * Its OWN file, not a table in an app's database: keeping a credential surface
46
+ * out of an app's data file bounds the blast radius of a corrupt or deleted
47
+ * file to "everyone signs in again".
48
+ */
49
+ export declare function createSqliteRevocationStore(dbPath: string): RevocationStore & {
50
+ close(): void;
51
+ };
52
+ /**
53
+ * Where revoked jtis live so every node sees them (03 §4a). Local profile:
54
+ * unused (a per-process `Map` is enough for one process). SaaS: a Redis hash
55
+ * with per-entry TTL, or a row in the shared Postgres — the contract is
56
+ * identical, which is the point.
57
+ *
58
+ * Only `set`/`get`/`prune` and no bulk `list`, because the consumer's hot path
59
+ * is a SYNCHRONOUS `verify` (see {@link createSessionSigner}) that consults the
60
+ * store read-through by id off the hot path — never a full scan on every verify.
61
+ *
62
+ * Epoch **seconds** throughout, matching a token's own `exp`.
63
+ */
64
+ export interface AsyncRevocationStore {
65
+ /**
66
+ * Record `jti` as revoked until `expEpochSeconds`. Idempotent; a re-revoke of
67
+ * the same jti is a no-op (or extends the exp, which is harmless — a denylist
68
+ * entry that outlives its token only wastes a little space until `prune`).
69
+ */
70
+ set(jti: string, expEpochSeconds: number): Promise<void>;
71
+ /** The `exp` of a revoked jti (epoch seconds), or `null` when it is not revoked. */
72
+ get(jti: string): Promise<number | null>;
73
+ /** Drop every entry whose `exp <= nowEpochSeconds`. Returns the count removed. */
74
+ prune(nowEpochSeconds: number): Promise<number>;
75
+ }
76
+ /**
77
+ * An in-memory {@link AsyncRevocationStore}.
78
+ *
79
+ * Two uses: a single-process saas test that wants the seam exercised without a
80
+ * real Redis, and — crucially — the **two-signer replay probe**, where one
81
+ * shared instance is handed to signer A and signer B so "revoke on A, verify on
82
+ * B" becomes provable in one process (V7.3). It is NOT a cross-process store:
83
+ * two OS processes do not share this Map, which is the whole reason a real
84
+ * deployment injects Redis/Postgres instead.
85
+ *
86
+ * (Named `…Async…` to stand apart from the sync {@link createMemoryRevocationStore};
87
+ * before DEDUP-7 both shapes shared the bare name across two packages and that
88
+ * collision was the footgun this file exists to remove.)
89
+ */
90
+ export declare function createMemoryAsyncRevocationStore(): AsyncRevocationStore & {
91
+ size(): number;
92
+ };
93
+ export interface AsyncBackedRevocationStoreOptions {
94
+ /** Injected clock in **milliseconds** (default `Date.now`), for tests. */
95
+ now?: () => number;
96
+ /**
97
+ * How long a store verdict is trusted before `isRevoked` re-reads it (ms).
98
+ * This is the cross-node revocation LATENCY BOUND: a revocation written by
99
+ * another node is seen here within this window (plus the store's own read
100
+ * latency), because `isRevoked` stays synchronous — it answers from the cache
101
+ * and refreshes it off the hot path rather than awaiting a round trip. Default
102
+ * 5000 (03 §4a's ≤5s).
103
+ */
104
+ cacheTtlMs?: number;
105
+ /**
106
+ * Reports a backing read/write that failed. A failed write-through means a
107
+ * revocation did not reach other nodes — the local cache still refuses the id
108
+ * HERE, but the host may want to retry or alarm. Default: swallow, because a
109
+ * sync store must never throw out of `isRevoked`/`revoke` on the request path.
110
+ */
111
+ onError?: (op: 'get' | 'set' | 'prune', error: unknown) => void;
112
+ }
113
+ /**
114
+ * A SYNC {@link RevocationStore} whose durable backing is an async cross-node
115
+ * {@link AsyncRevocationStore} — the bridge that gives a satellite the cross-node
116
+ * seam without its `isAuthenticated` becoming a promise.
117
+ *
118
+ * It is the same read-through-cache trick {@link createSessionSigner} keeps
119
+ * internally, packaged so a plain sync consumer can use it: `revoke` writes
120
+ * through fire-and-forget and caches locally so the id is refused HERE the
121
+ * instant it returns; `isRevoked` answers from the cache and schedules a refresh
122
+ * when the entry is missing or older than `cacheTtlMs`, never blocking. Ids and
123
+ * expiries cross the ms↔seconds boundary here (the sync contract is ms, the
124
+ * async one seconds).
125
+ *
126
+ * {@link flush} settles the fire-and-forget work — for shutdown/handover (a node
127
+ * draining must not lose an unwritten revocation) and for tests that need the
128
+ * cross-node state to have settled.
129
+ */
130
+ export declare function createAsyncBackedRevocationStore(backing: AsyncRevocationStore, options?: AsyncBackedRevocationStoreOptions): RevocationStore & {
131
+ flush(): Promise<void>;
132
+ };