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,76 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { ApiError, isApiError, toErrorEnvelope } from './errors';
3
+
4
+ /** ISO-8601 timestamp check + strip, so the dynamic field doesn't break equality. */
5
+ const withoutTimestamp = (env: ReturnType<typeof toErrorEnvelope>) => {
6
+ expect(env.error.timestamp).toMatch(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z$/);
7
+ const { timestamp: _timestamp, ...error } = env.error;
8
+ return { ...env, error };
9
+ };
10
+
11
+ describe('ApiError + toErrorEnvelope', () => {
12
+ test('carries status, message, code, context', () => {
13
+ const err = new ApiError(422, 'bad input', {
14
+ code: 'validation_failed',
15
+ context: { issues: [{ path: 'name', message: 'required' }] },
16
+ });
17
+ expect(err.status).toBe(422);
18
+ expect(err.message).toBe('bad input');
19
+ expect(err.name).toBe('ApiError');
20
+ expect(isApiError(err)).toBe(true);
21
+ expect(err instanceof Error).toBe(true);
22
+ });
23
+
24
+ test('minimal ApiError → rich envelope (name/status/isOperational/timestamp), no undefined keys', () => {
25
+ expect(withoutTimestamp(toErrorEnvelope(new ApiError(404, 'not found')))).toEqual({
26
+ success: false,
27
+ error: { name: 'ApiError', message: 'not found', status: 404, isOperational: true },
28
+ });
29
+ });
30
+
31
+ test('full ApiError → message + code + context + rich metadata', () => {
32
+ const env = toErrorEnvelope(
33
+ new ApiError(422, 'invalid', { code: 'validation_failed', context: { issues: ['x'] } }),
34
+ );
35
+ expect(withoutTimestamp(env)).toEqual({
36
+ success: false,
37
+ error: {
38
+ name: 'ApiError',
39
+ message: 'invalid',
40
+ code: 'validation_failed',
41
+ status: 422,
42
+ context: { issues: ['x'] },
43
+ isOperational: true,
44
+ },
45
+ });
46
+ });
47
+
48
+ test('correlationId, when supplied, rides alongside the error', () => {
49
+ const env = toErrorEnvelope(new ApiError(404, 'nope'), { correlationId: 'req-1' });
50
+ expect(env.correlationId).toBe('req-1');
51
+ });
52
+
53
+ test('plain Error / non-error → sanitized generic 500 (internals never leak)', () => {
54
+ expect(withoutTimestamp(toErrorEnvelope(new Error('boom')))).toEqual({
55
+ success: false,
56
+ error: { name: 'Error', message: 'Internal Server Error', status: 500, isOperational: false },
57
+ });
58
+ expect(withoutTimestamp(toErrorEnvelope('nope'))).toEqual({
59
+ success: false,
60
+ error: { name: 'Error', message: 'Internal Server Error', status: 500, isOperational: false },
61
+ });
62
+ });
63
+
64
+ test('exposeMessage surfaces a non-AppError message (opt-in)', () => {
65
+ expect(toErrorEnvelope(new Error('boom'), { exposeMessage: true }).error.message).toBe('boom');
66
+ });
67
+
68
+ test('preserves cause for server-side logging without serializing it', () => {
69
+ const cause = new Error('root');
70
+ const err = new ApiError(500, 'wrapped', { cause });
71
+ expect(err.cause).toBe(cause);
72
+ const env = toErrorEnvelope(err);
73
+ expect(env.error.message).toBe('wrapped');
74
+ expect(JSON.stringify(env)).not.toContain('root');
75
+ });
76
+ });
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Server-side re-export of the canonical error primitives, which now live in the
3
+ * framework-free {@link ../shared/errors} module so cursedshell's plugin-pure layer
4
+ * can import the SAME `ApiError` + envelope without pulling any server code
5
+ * (`cursedbelt-core/errors` subpath). Server modules keep importing from `./errors`.
6
+ */
7
+ export {
8
+ AppError,
9
+ type AppErrorSummary,
10
+ ApiError,
11
+ type ApiErrorOptions,
12
+ type ErrorEnvelope,
13
+ isAppError,
14
+ isApiError,
15
+ type ToErrorEnvelopeOptions,
16
+ toErrorEnvelope,
17
+ type ValidationIssue,
18
+ VALIDATION_ERROR_CODE,
19
+ VALIDATION_STATUS,
20
+ toValidationIssues,
21
+ validationEnvelope,
22
+ } from 'cursedbelt-core/errors';
@@ -0,0 +1,117 @@
1
+ /**
2
+ * The argon2id binding adapter, against the REAL native module (02 §2-tail).
3
+ *
4
+ * `registerNodeRsArgon2id` existed for a whole arc with no caller and no test —
5
+ * and it was broken: `@node-rs/argon2`'s `version` option is a NAPI enum
6
+ * (`V0x10 = 0`, `V0x13 = 1`), not the argon2 version number `0x13` the envelope
7
+ * records, so every derivation threw `InvalidArg` from the native layer. Nothing
8
+ * caught it because nothing had ever registered the binding for real. Now the
9
+ * local host does at boot, so this suite drives the actual module: register,
10
+ * derive, and check the bytes.
11
+ *
12
+ * `@node-rs/argon2` is an OPTIONAL peer of this package (a generated app without
13
+ * a GuardEngine surface should not carry a native module) and a devDependency
14
+ * here, which is what lets the shipping path be proven rather than asserted.
15
+ */
16
+ import { afterEach, describe, expect, it } from 'bun:test';
17
+ import {
18
+ CC_ARGON2ID_VERSION,
19
+ deriveArgon2idKey,
20
+ hasArgon2idDeriver,
21
+ resolveArgon2idDeriver,
22
+ setArgon2idDeriver,
23
+ } from 'cwip/guard';
24
+ import { argon2idDeriverFrom, nodeRsArgon2idBinding, type RawArgon2idBinding, registerNodeRsArgon2id } from './argon2Binding';
25
+
26
+ /** Cheap parameters — this suite proves the WIRING, not the cost calibration. */
27
+ const FAST = { m: 8_192, t: 1, p: 1 };
28
+ const SALT = new Uint8Array(16).fill(7);
29
+ const password = (value: string): Uint8Array => new TextEncoder().encode(value);
30
+
31
+ // The seam is process-wide; never leak a registration into another suite.
32
+ afterEach(() => setArgon2idDeriver(null));
33
+
34
+ describe('nodeRsArgon2idBinding — the version-vocabulary translation', () => {
35
+ const calls: Array<Record<string, unknown>> = [];
36
+ const spy: RawArgon2idBinding = {
37
+ hashRawSync(_password, options) {
38
+ calls.push({ ...options });
39
+ return new Uint8Array(options.outputLen);
40
+ },
41
+ };
42
+
43
+ it('maps the argon2 version number the envelope records onto node-rs’s enum', () => {
44
+ calls.length = 0;
45
+ const binding = nodeRsArgon2idBinding(spy);
46
+ binding.hashRawSync(password('x'), { salt: SALT, memoryCost: 8, timeCost: 1, parallelism: 1, outputLen: 32, version: 0x13 });
47
+ binding.hashRawSync(password('x'), { salt: SALT, memoryCost: 8, timeCost: 1, parallelism: 1, outputLen: 32, version: 0x10 });
48
+ // 0x13 → 1, 0x10 → 0. Passing 0x13 straight through is the `InvalidArg` bug.
49
+ expect(calls.map((call) => call.version)).toEqual([1, 0]);
50
+ });
51
+
52
+ it('defaults to the platform version when the caller names none', () => {
53
+ calls.length = 0;
54
+ nodeRsArgon2idBinding(spy).hashRawSync(password('x'), {
55
+ salt: SALT,
56
+ memoryCost: 8,
57
+ timeCost: 1,
58
+ parallelism: 1,
59
+ outputLen: 32,
60
+ });
61
+ expect(CC_ARGON2ID_VERSION).toBe(0x13);
62
+ expect(calls[0]?.version).toBe(1);
63
+ });
64
+
65
+ it('refuses a version the binding cannot implement instead of silently deriving different bytes', () => {
66
+ expect(() =>
67
+ nodeRsArgon2idBinding(spy).hashRawSync(password('x'), {
68
+ salt: SALT,
69
+ memoryCost: 8,
70
+ timeCost: 1,
71
+ parallelism: 1,
72
+ outputLen: 32,
73
+ version: 0x11,
74
+ }),
75
+ ).toThrow(/not one @node-rs\/argon2 implements/);
76
+ });
77
+ });
78
+
79
+ describe('registerNodeRsArgon2id — the real native binding, end to end', () => {
80
+ it('arms the process-wide seam and derives raw bytes of the requested length', async () => {
81
+ expect(hasArgon2idDeriver()).toBe(false);
82
+ await registerNodeRsArgon2id();
83
+ expect(hasArgon2idDeriver()).toBe(true);
84
+
85
+ // Straight through the SAME entry point every envelope/verifier uses. Before
86
+ // the version fix this threw `InvalidArg` here, on the first derivation.
87
+ const key = deriveArgon2idKey({ password: password('correct horse'), salt: SALT, params: FAST, keyLength: 32, label: 'test' });
88
+ const bytes = key.use((raw) => new Uint8Array(raw));
89
+ expect(bytes.byteLength).toBe(32);
90
+ expect(bytes.some((byte) => byte !== 0)).toBe(true);
91
+ key.destroy();
92
+ });
93
+
94
+ it('is deterministic for the same inputs and diverges on the password, the salt and the version', async () => {
95
+ await registerNodeRsArgon2id();
96
+ const deriver = resolveArgon2idDeriver();
97
+ const hex = (bytes: Uint8Array): string => Buffer.from(bytes).toString('hex');
98
+
99
+ const base = hex(deriver({ password: password('pw-one'), salt: SALT, params: FAST, keyLength: 32, version: 0x13 }));
100
+ // Re-deriving must reproduce the key, or no sealed envelope could ever be opened.
101
+ expect(hex(deriver({ password: password('pw-one'), salt: SALT, params: FAST, keyLength: 32, version: 0x13 }))).toBe(base);
102
+ expect(hex(deriver({ password: password('pw-two'), salt: SALT, params: FAST, keyLength: 32, version: 0x13 }))).not.toBe(base);
103
+ expect(
104
+ hex(deriver({ password: password('pw-one'), salt: new Uint8Array(16).fill(9), params: FAST, keyLength: 32, version: 0x13 })),
105
+ ).not.toBe(base);
106
+ // The reason the envelope records `ver` at all: 0x10 and 0x13 are different
107
+ // functions of the same inputs, and this is the proof the binding honors it.
108
+ expect(hex(deriver({ password: password('pw-one'), salt: SALT, params: FAST, keyLength: 32, version: 0x10 }))).not.toBe(base);
109
+ });
110
+
111
+ it('refuses a binding that hands back a string — key material that cannot be zeroized', () => {
112
+ const stringy = { hashRawSync: () => 'deadbeef' } as unknown as RawArgon2idBinding;
113
+ expect(() =>
114
+ argon2idDeriverFrom(stringy)({ password: password('x'), salt: SALT, params: FAST, keyLength: 32 }),
115
+ ).toThrow(/must be raw bytes/);
116
+ });
117
+ });
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Wiring a REAL argon2id into the KDF seam (01 §3.3/§3.4, finding 1.2).
3
+ *
4
+ * cwip ships no KDF — it is zero-dependency by contract — so
5
+ * `setArgon2idDeriver` sat inert: nothing registered a binding, and every
6
+ * `unlock()` would have thrown `GUARD_KDF_UNAVAILABLE` the first time a real
7
+ * user tried it in production. `assertArgon2idReady()` converts that into a boot
8
+ * failure; this module is the other half — the adapter that makes registering a
9
+ * binding a one-liner, and the reason a host has no excuse to hand-roll one.
10
+ *
11
+ * The requirements on a binding are not negotiable, and the adapter enforces
12
+ * them rather than documenting them:
13
+ *
14
+ * * **Raw bytes out.** A binding that returns a PHC/base64 STRING is a
15
+ * `key-material-as-string` violation regardless of how strong the KDF is —
16
+ * a JS string is immutable and GC-managed, so the key lingers in
17
+ * unallocated RAM until the collector happens to overwrite it.
18
+ * * **Caller-supplied salt.** Anything that generates its own salt (e.g.
19
+ * `Bun.password`) cannot re-derive the key for an existing envelope, so it
20
+ * cannot open a secret it sealed yesterday.
21
+ * * **Synchronous.** The seam is sync on purpose and its throughput ceiling is
22
+ * documented on `Argon2idDeriver`; an async binding would hand back a
23
+ * promise AND (usually) a string.
24
+ */
25
+ import {
26
+ type Argon2idDeriver,
27
+ type Argon2idInput,
28
+ assertArgon2idReady,
29
+ CC_ARGON2ID_VERSION,
30
+ setArgon2idDeriver,
31
+ } from 'cwip/guard';
32
+ import { AppError } from '../errors';
33
+
34
+ /**
35
+ * The shape of a raw, synchronous argon2id binding, in the REFERENCE
36
+ * implementation's vocabulary: `algorithm` is the argon2 variant number
37
+ * (2 = argon2id) and `version` is the argon2 algorithm version as the spec
38
+ * writes it (`0x10` / `0x13`) — the same number the envelope's `kdf.ver`
39
+ * records.
40
+ *
41
+ * `@node-rs/argon2`'s `hashRawSync` is shape-compatible but NOT
42
+ * vocabulary-compatible: its `version` is a NAPI enum (`Version.V0x10 = 0`,
43
+ * `Version.V0x13 = 1`), so handing it `0x13` is an `InvalidArg` throw on every
44
+ * single derivation. {@link nodeRsArgon2idBinding} is the (tiny) translation,
45
+ * and {@link registerNodeRsArgon2id} applies it — libsodium's `crypto_pwhash`
46
+ * needs a comparable shim.
47
+ */
48
+ export interface RawArgon2idBinding {
49
+ hashRawSync(
50
+ password: Uint8Array,
51
+ options: {
52
+ salt: Uint8Array;
53
+ /** Memory cost in KiB. */
54
+ memoryCost: number;
55
+ /** Time cost (iterations). */
56
+ timeCost: number;
57
+ /** Parallelism (lanes). */
58
+ parallelism: number;
59
+ /** Output length in bytes. */
60
+ outputLen: number;
61
+ /** argon2 variant: 2 = argon2id. */
62
+ algorithm?: number;
63
+ /** argon2 algorithm version (0x10 | 0x13). */
64
+ version?: number;
65
+ },
66
+ ): Uint8Array;
67
+ }
68
+
69
+ /** argon2id's variant number in the `@node-rs/argon2` / reference-implementation vocabulary. */
70
+ export const ARGON2ID_ALGORITHM = 2;
71
+
72
+ /**
73
+ * Build a deriver from a raw binding, checking the two properties that make it
74
+ * usable as key material. The length check is not paranoia: a binding that
75
+ * silently truncates would weaken every secret sealed under it, and the failure
76
+ * would look like nothing at all.
77
+ */
78
+ export const argon2idDeriverFrom = (binding: RawArgon2idBinding): Argon2idDeriver => {
79
+ return (input: Argon2idInput): Uint8Array => {
80
+ const output = binding.hashRawSync(input.password, {
81
+ salt: input.salt,
82
+ memoryCost: input.params.m,
83
+ timeCost: input.params.t,
84
+ parallelism: input.params.p,
85
+ outputLen: input.keyLength,
86
+ algorithm: ARGON2ID_ALGORITHM,
87
+ // Honor the version the ENVELOPE was sealed with, not the binding's
88
+ // compile-time default: 0x10 and 0x13 produce different bytes for
89
+ // identical m/t/p/salt/password, which is why the envelope records it.
90
+ version: input.version ?? CC_ARGON2ID_VERSION,
91
+ });
92
+
93
+ if (typeof output === 'string')
94
+ throw new AppError(
95
+ 'The registered argon2id binding returned a string. Key material must be raw bytes — a string cannot be zeroized (01 §3.4).',
96
+ { code: 'GUARD_KDF_BINDING_INVALID', status: 500, category: 'security', isOperational: false },
97
+ );
98
+ if (!(output instanceof Uint8Array))
99
+ throw new AppError('The registered argon2id binding did not return a Uint8Array.', {
100
+ code: 'GUARD_KDF_BINDING_INVALID',
101
+ status: 500,
102
+ category: 'security',
103
+ isOperational: false,
104
+ });
105
+ return output;
106
+ };
107
+ };
108
+
109
+ /**
110
+ * `@node-rs/argon2`'s `Version` enum, keyed by the argon2 version number the
111
+ * ENVELOPE records. Held here rather than imported so the adapter never has to
112
+ * load the optional native module to know how to talk to it.
113
+ */
114
+ // 0x10 and 0x13 ARE the Argon2 version identifiers: the spec, the reference
115
+ // implementation and every envelope on disk write them in hex. Biome's autofix
116
+ // rewrites them as 16 and 19 — the same map and a worse one, because nobody
117
+ // reading an `$argon2id$v=19$` record would recognise the decimal.
118
+ // biome-ignore lint/complexity/useSimpleNumberKeys: see above — hex IS the spec's spelling
119
+ const NODE_RS_VERSION: Readonly<Record<number, number>> = { 0x10: 0, 0x13: 1 };
120
+
121
+ /**
122
+ * Translate a `@node-rs/argon2` module into a {@link RawArgon2idBinding}.
123
+ *
124
+ * The whole translation is the `version` field, and skipping it is not a
125
+ * cosmetic bug: node-rs validates the enum, so an unmapped `0x13` throws
126
+ * `InvalidArg` from the native layer on EVERY derivation — the first unlock, the
127
+ * first login, every password verify. That failure is invisible until a host
128
+ * actually registers this binding, which is exactly what the local host now does
129
+ * at boot.
130
+ */
131
+ export const nodeRsArgon2idBinding = (module: RawArgon2idBinding): RawArgon2idBinding => ({
132
+ hashRawSync(password, options) {
133
+ const declared = options.version ?? CC_ARGON2ID_VERSION;
134
+ const mapped = NODE_RS_VERSION[declared];
135
+ if (mapped === undefined)
136
+ throw new AppError(
137
+ `argon2 version 0x${declared.toString(16)} is not one @node-rs/argon2 implements (0x10 or 0x13). The envelope asking for it cannot be opened by this binding — reseal it, or register a binding that supports the version.`,
138
+ {
139
+ code: 'GUARD_KDF_BINDING_INVALID',
140
+ status: 500,
141
+ category: 'security',
142
+ isOperational: false,
143
+ context: { version: declared },
144
+ },
145
+ );
146
+ return module.hashRawSync(password, { ...options, version: mapped });
147
+ },
148
+ });
149
+
150
+ /** Register a raw binding as the process-wide deriver, then prove the seam is armed. */
151
+ export const registerArgon2idBinding = (binding: RawArgon2idBinding): void => {
152
+ setArgon2idDeriver(argon2idDeriverFrom(binding));
153
+ // Fail here rather than at the first unlock: this call is the whole point.
154
+ assertArgon2idReady();
155
+ };
156
+
157
+ /**
158
+ * Register `@node-rs/argon2` if the host installed it.
159
+ *
160
+ * It is an OPTIONAL peer dependency: a generated app with no GuardEngine surface
161
+ * should not carry a native module, and an app that has one must make the choice
162
+ * explicitly. The specifier is held in a variable so bundlers and `tsc` do not
163
+ * try to resolve a package that may not be there.
164
+ *
165
+ * @throws {AppError} `GUARD_KDF_BINDING_MISSING` naming the install command —
166
+ * an unhelpful `ERR_MODULE_NOT_FOUND` at boot is exactly the failure this
167
+ * module exists to prevent.
168
+ *
169
+ * The same indirection that hides the specifier from a bundler hides it from
170
+ * every static import walker, so the guard's peer map would report
171
+ * this edge as absent. The pragma is what puts it back.
172
+ *
173
+ * @peer @node-rs/argon2
174
+ */
175
+ export const registerNodeRsArgon2id = async (): Promise<void> => {
176
+ const specifier = '@node-rs/argon2';
177
+ let loaded: { hashRawSync?: unknown };
178
+ try {
179
+ loaded = (await import(specifier)) as { hashRawSync?: unknown };
180
+ } catch (cause) {
181
+ throw new AppError(
182
+ 'GuardEngine needs an argon2id binding and `@node-rs/argon2` is not installed. Run `bun add @node-rs/argon2` in the app, or call registerArgon2idBinding(...) with your own raw, synchronous binding.',
183
+ { code: 'GUARD_KDF_BINDING_MISSING', status: 500, category: 'security', isOperational: false, cause },
184
+ );
185
+ }
186
+ if (typeof loaded.hashRawSync !== 'function')
187
+ throw new AppError('`@node-rs/argon2` is installed but exposes no `hashRawSync` — it is too old for the raw-bytes seam.', {
188
+ code: 'GUARD_KDF_BINDING_INVALID',
189
+ status: 500,
190
+ category: 'security',
191
+ isOperational: false,
192
+ });
193
+
194
+ registerArgon2idBinding(nodeRsArgon2idBinding(loaded as unknown as RawArgon2idBinding));
195
+ };
@@ -0,0 +1,250 @@
1
+ /**
2
+ * argon2id KNOWN-ANSWER TESTS (05 §B6).
3
+ *
4
+ * Every other guard suite in this repo injects a FAKE deriver — `guard.spec.ts`
5
+ * says so in its header — and `argon2Binding.spec.ts` drives the real native
6
+ * module but only asserts SHAPE properties: right length, non-zero, deterministic,
7
+ * diverges on password/salt/version. All of that is equally true of a wrong
8
+ * KDF. Until this file existed, "does the KDF produce a CORRECT key" had zero
9
+ * evidence anywhere in the monorepo — a binding that computed argon2i, or the
10
+ * wrong argon2 version, or silently ignored the parallelism lane count would
11
+ * have passed every gate we had while sealing every envelope in the product with
12
+ * keys no other argon2 implementation can reproduce.
13
+ *
14
+ * That failure mode is not hypothetical and it is not recoverable. Key material
15
+ * is written into envelopes at rest; by the time an interoperability problem
16
+ * surfaces, the secrets sealed under the wrong function are the customer's, and
17
+ * "reseal everything" needs the plaintext we no longer have.
18
+ *
19
+ * Two independent kinds of evidence, because each covers the other's blind spot:
20
+ *
21
+ * 1. **Reference vectors** (`REFERENCE_VECTORS`) — fixed inputs and fixed
22
+ * outputs from the argon2 REFERENCE implementation's own test suite
23
+ * (`phc-winner-argon2`, `src/test.c`, the `ARGON2_VERSION_NUMBER = 0x13`
24
+ * block). These pin the absolute function: no implementation of ours can
25
+ * satisfy them by being self-consistent.
26
+ * 2. **Cross-implementation agreement** (the last describe) — Bun's built-in
27
+ * argon2id against the registered `@node-rs/argon2` binding on the SAME salt
28
+ * and parameters. Two unrelated codebases (Zig/C vs Rust) agreeing bit for
29
+ * bit is what catches a vector transcription error, which a fixed table
30
+ * cannot catch about itself.
31
+ *
32
+ * The negative controls matter as much as the positives: argon2**i** and version
33
+ * **0x10** are wrong answers that are otherwise indistinguishable from right
34
+ * ones — same length, same determinism, same divergence behavior — so the
35
+ * vectors assert they produce DIFFERENT bytes. Without those two rows a binding
36
+ * defaulting to the wrong variant or the wrong version passes this file.
37
+ *
38
+ * Everything runs through `deriveArgon2idKey`, the entry point every envelope,
39
+ * row signature and password verify uses — not against the module directly.
40
+ * A KAT that bypasses the shipped seam proves the library and not the product.
41
+ */
42
+ import { afterEach, describe, expect, it } from 'bun:test';
43
+ import { deriveArgon2idKey, resolveArgon2idDeriver, setArgon2idDeriver } from 'cwip/guard';
44
+ import { type Algorithm, hashRawSync, type Version } from '@node-rs/argon2';
45
+
46
+ import { argon2idDeriverFrom, nodeRsArgon2idBinding, registerNodeRsArgon2id } from './argon2Binding';
47
+ /*
48
+ * `Algorithm` and `Version` are declared as AMBIENT CONST ENUMS in the binding's
49
+ * `.d.ts`, and `verbatimModuleSyntax` refuses to read a member off one — the
50
+ * import that would carry it is erased, so TypeScript cannot prove the object
51
+ * survives to runtime. It does: napi-rs emits both onto `module.exports`, which
52
+ * is why these lines have always RUN correctly and only ever failed to compile.
53
+ *
54
+ * So the enums come in as TYPES and the ordinals are named here, checked against
55
+ * the binding below. **The ordinals are NOT the argon2 wire constants** —
56
+ * `Version.V0x13` is `1`, not `19` — which is exactly the mistake a reader
57
+ * inlining "0x13" from the member's name would make, and it would quietly
58
+ * compute a different KDF rather than fail.
59
+ */
60
+ const ARGON2ID = 2 as Algorithm;
61
+ const ARGON2I = 1 as Algorithm;
62
+ const V0X13 = 1 as Version;
63
+ const V0X10 = 0 as Version;
64
+
65
+
66
+ const enc = new TextEncoder();
67
+ const hex = (bytes: Uint8Array): string => Buffer.from(bytes).toString('hex');
68
+
69
+ // The seam is process-wide; never leak a registration into another suite.
70
+ afterEach(() => setArgon2idDeriver(null));
71
+
72
+ /**
73
+ * argon2id, version 0x13, password `"password"`, salt `"somesalt"`, 32-byte tag —
74
+ * the reference implementation's own `hashtest(version, t, log2(m), p, …)` rows.
75
+ *
76
+ * The `m` column is written as the KiB value the envelope records, which is
77
+ * `2^log2m` in the reference suite's vocabulary: 2^8 = 256, 2^16 = 65536,
78
+ * 2^18 = 262144. Getting that exponent↔value translation wrong is precisely the
79
+ * kind of mistake this table exists to catch, so it is spelled out rather than
80
+ * computed.
81
+ */
82
+ const REFERENCE_VECTORS: readonly { m: number; t: number; p: number; tag: string }[] = [
83
+ { m: 65_536, t: 2, p: 1, tag: '09316115d5cf24ed5a15a31a3ba326e5cf32edc24702987c02b6566f61913cf7' },
84
+ { m: 262_144, t: 2, p: 1, tag: '78fe1ec91fb3aa5657d72e710854e4c3d9b9198c742f9616c2f085bed95b2e8c' },
85
+ { m: 256, t: 2, p: 1, tag: '9dfeb910e80bad0311fee20f9c0e2b12c17987b4cac90c2ef54d5b3021c68bfe' },
86
+ { m: 256, t: 2, p: 2, tag: '6d093c501fd5999645e0ea3bf620d7b8be7fd2db59c20d9fff9539da2bf57037' },
87
+ { m: 65_536, t: 1, p: 1, tag: 'f6a5adc1ba723dddef9b5ac1d464e180fcd9dffc9d1cbf76cca2fed795d9ca98' },
88
+ { m: 65_536, t: 4, p: 1, tag: '9025d48e68ef7395cca9079da4c4ec3affb3c8911fe4f86d1a2520856f63172c' },
89
+ ];
90
+
91
+ const PASSWORD = enc.encode('password');
92
+ const SALT = enc.encode('somesalt');
93
+
94
+ describe('argon2id known-answer vectors — the shipped seam computes argon2id 0x13', () => {
95
+ for (const { m, t, p, tag } of REFERENCE_VECTORS) {
96
+ it(`m=${m} t=${t} p=${p} reproduces the reference tag`, async () => {
97
+ await registerNodeRsArgon2id();
98
+
99
+ // Through `deriveArgon2idKey`, so the KAT covers the parameter validation,
100
+ // the version default and the length check as well as the primitive.
101
+ const key = deriveArgon2idKey({
102
+ password: PASSWORD,
103
+ salt: SALT,
104
+ params: { m, t, p },
105
+ keyLength: 32,
106
+ label: 'kat',
107
+ });
108
+ const derived = key.use((raw) => hex(raw));
109
+ key.destroy();
110
+
111
+ expect(derived).toBe(tag);
112
+ });
113
+ }
114
+
115
+ it('is NOT argon2i — the variant selector reaches the binding', () => {
116
+ // Same inputs as the first reference row. argon2i is a real, strong KDF that
117
+ // fails no shape assertion in `argon2Binding.spec.ts`; only a vector tells
118
+ // the two apart, and `algorithm: 2` is the single field that decides which
119
+ // one every envelope in the product is sealed with.
120
+ const argon2i = hashRawSync(PASSWORD, {
121
+ salt: SALT,
122
+ memoryCost: 65_536,
123
+ timeCost: 2,
124
+ parallelism: 1,
125
+ outputLen: 32,
126
+ algorithm: ARGON2I,
127
+ version: V0X13,
128
+ });
129
+ expect(hex(argon2i)).not.toBe(REFERENCE_VECTORS[0]?.tag);
130
+ });
131
+
132
+ it('is NOT version 0x10 — the envelope’s recorded version reaches the binding', () => {
133
+ // The whole reason `kdf.ver` is recorded per envelope (see CC_ARGON2ID_VERSION).
134
+ // 0x10 and 0x13 are different functions of identical inputs, and a binding
135
+ // that ignored the field would seal secrets no 0x13 reader can open.
136
+ const v0x10 = hashRawSync(PASSWORD, {
137
+ salt: SALT,
138
+ memoryCost: 65_536,
139
+ timeCost: 2,
140
+ parallelism: 1,
141
+ outputLen: 32,
142
+ algorithm: ARGON2ID,
143
+ version: V0X10,
144
+ });
145
+ expect(hex(v0x10)).not.toBe(REFERENCE_VECTORS[0]?.tag);
146
+ });
147
+ });
148
+
149
+ describe('cross-implementation agreement — Bun’s argon2id vs the registered binding', () => {
150
+ /**
151
+ * The vectors above are transcribed by a human; this test is not. Bun ships
152
+ * its own argon2id (a different language, a different codebase, no shared
153
+ * lineage with the Rust `argon2` crate behind `@node-rs/argon2`), and its PHC
154
+ * output RECORDS the salt it chose — which is what makes the comparison
155
+ * possible at all, since `Bun.password` generates its own salt and cannot be
156
+ * asked to re-derive.
157
+ *
158
+ * So: let Bun hash, read back the salt and parameters it used, and drive the
159
+ * registered binding with them. Two implementations landing on the same 32
160
+ * bytes is independent of anything either this file or the adapter believes.
161
+ */
162
+ const parsePhc = (
163
+ phc: string,
164
+ ): { version: number; m: number; t: number; p: number; salt: Uint8Array; tag: Uint8Array } => {
165
+ const match = /^\$argon2id\$v=(\d+)\$m=(\d+),t=(\d+),p=(\d+)\$([^$]+)\$([^$]+)$/.exec(phc);
166
+ if (match === null) throw new Error(`Bun produced a PHC string this test cannot parse: ${phc}`);
167
+ // PHC uses unpadded base64url.
168
+ const b64 = (value: string): Uint8Array =>
169
+ new Uint8Array(
170
+ Buffer.from(value.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat((4 - (value.length % 4)) % 4), 'base64'),
171
+ );
172
+ return {
173
+ version: Number(match[1]),
174
+ m: Number(match[2]),
175
+ t: Number(match[3]),
176
+ p: Number(match[4]),
177
+ salt: b64(match[5] as string),
178
+ tag: b64(match[6] as string),
179
+ };
180
+ };
181
+
182
+ it('derives byte-identical keys from the same password, salt and parameters', async () => {
183
+ const phc = await Bun.password.hash('correct horse battery staple', {
184
+ algorithm: 'argon2id',
185
+ memoryCost: 2_048,
186
+ timeCost: 2,
187
+ });
188
+ const { version, m, t, p, salt, tag } = parsePhc(phc);
189
+ // 19 === 0x13. If Bun ever defaults to something else, the comparison below
190
+ // would be against a different function and must not silently pass.
191
+ expect(version).toBe(0x13);
192
+
193
+ await registerNodeRsArgon2id();
194
+ const ours = resolveArgon2idDeriver()({
195
+ password: enc.encode('correct horse battery staple'),
196
+ salt,
197
+ params: { m, t, p },
198
+ keyLength: tag.byteLength,
199
+ version,
200
+ });
201
+
202
+ expect(hex(ours)).toBe(hex(tag));
203
+ });
204
+
205
+ it('disagrees when the salt differs — proving the agreement above is not vacuous', async () => {
206
+ // A comparison that passes no matter what is worse than no comparison. If
207
+ // the binding ignored the salt (or the parser handed it the wrong bytes),
208
+ // the previous test would still be green; this one would not.
209
+ const phc = await Bun.password.hash('correct horse battery staple', {
210
+ algorithm: 'argon2id',
211
+ memoryCost: 2_048,
212
+ timeCost: 2,
213
+ });
214
+ const { m, t, p, salt, tag } = parsePhc(phc);
215
+
216
+ const tampered = new Uint8Array(salt);
217
+ tampered[0] = (tampered[0] ?? 0) ^ 0xff;
218
+
219
+ // Through the same translation the shipping path uses — node-rs's `version`
220
+ // is a NAPI enum, so a raw `{ hashRawSync }` would throw `InvalidArg`.
221
+ const deriver = argon2idDeriverFrom(nodeRsArgon2idBinding({ hashRawSync }));
222
+ const ours = deriver({
223
+ password: enc.encode('correct horse battery staple'),
224
+ salt: tampered,
225
+ params: { m, t, p },
226
+ keyLength: tag.byteLength,
227
+ version: 0x13,
228
+ });
229
+
230
+ expect(hex(ours)).not.toBe(hex(tag));
231
+ });
232
+
233
+ /*
234
+ * The ordinals above are inlined because `verbatimModuleSyntax` cannot read a
235
+ * member off an ambient const enum. That is a copy, and a copy needs a gate: if
236
+ * a napi-rs regeneration ever renumbers `Algorithm` or `Version`, every KAT
237
+ * above would keep passing while silently exercising a different variant.
238
+ * Reading the RUNTIME objects here is what makes the copy safe.
239
+ */
240
+ it('the inlined enum ordinals still match the binding', async () => {
241
+ const binding = (await import('@node-rs/argon2')) as unknown as {
242
+ Algorithm: Record<string, number>;
243
+ Version: Record<string, number>;
244
+ };
245
+ expect(binding.Algorithm.Argon2id).toBe(ARGON2ID as unknown as number);
246
+ expect(binding.Algorithm.Argon2i).toBe(ARGON2I as unknown as number);
247
+ expect(binding.Version.V0x13).toBe(V0X13 as unknown as number);
248
+ expect(binding.Version.V0x10).toBe(V0X10 as unknown as number);
249
+ });
250
+ });