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,157 @@
1
+ /**
2
+ * PER-PERSON master locks — one record, one password and one unlock per account.
3
+ *
4
+ * ── What this changes, and what it deliberately does not ────────────────────
5
+ * The engine next door (`masterLock.ts`) was built for an app with exactly one user, and
6
+ * its module note says so at length: *"There is exactly ONE unlock at a time, not one per
7
+ * browser session … the unit is the SITE."* That is still true of `apps/collections` and
8
+ * the binary-server inspector, and this file does not touch them.
9
+ *
10
+ * It stops being true the moment a second person may open the app. The owner, 2026-08-25:
11
+ * *"I would pick one that only I know and use and she could pick one for only she knows
12
+ * just like we did for the vault app."* Two people cannot share one record — the first to
13
+ * rotate it locks the second out of an app they were granted — and they must not share one
14
+ * unlock either, or her phone opens his desk.
15
+ *
16
+ * So the unit here is the PERSON. Each principal gets its own {@link MasterLock}: its own
17
+ * KDF salt, its own `argon2id(verifier)`, its own idle clock, its own guess throttle and
18
+ * its own in-memory unlock. Everything the engine already reasons about holds per person
19
+ * instead of per app, unchanged.
20
+ *
21
+ * ── 🔴 Three properties this file is responsible for ────────────────────────
22
+ *
23
+ * **1. One principal's unlock can never open another's.** Falls out of the construction —
24
+ * a token is compared only against the session of the lock it was minted by, and the lock
25
+ * is selected by the principal on the request's own signed-in session, never by anything
26
+ * the caller sends. There is no code path that takes a token and searches for a matching
27
+ * lock, and there must never be one.
28
+ *
29
+ * **2. A principal id can never reach the storage key.** Ids in this fleet are sometimes
30
+ * uuids and sometimes raw email addresses (`apps/auth` carries ca's originals verbatim),
31
+ * so they are attacker-adjacent strings of unbounded shape. They are HASHED into the key
32
+ * rather than interpolated: a fixed-length hex suffix cannot collide with another key,
33
+ * cannot escape the namespace, and cannot smuggle a `:` that makes one person's row look
34
+ * like another's.
35
+ *
36
+ * **3. There is no seed.** `MASTER_LOCK_SEED` arms an app-wide lock from the vault's own
37
+ * record; per-person there is nothing sensible to seed a *stranger's* password from, and
38
+ * an env var that pre-set someone's password would be exactly the "one credential opens
39
+ * everybody" shape this replaces. A principal with no record is `awaitingEnrollment` —
40
+ * locked, and offered the choose-a-password form.
41
+ */
42
+ import { createHash } from "node:crypto";
43
+ import { MasterLock, readRecord } from "./masterLock";
44
+ /** Namespace for every per-person record, so one glance at the table says what they are. */
45
+ export const MASTER_LOCK_PRINCIPAL_KEY_PREFIX = "master_lock:principal:";
46
+ /**
47
+ * The storage key for one principal.
48
+ *
49
+ * SHA-256 hex of the id — see property 2 in the module note. It is not a security
50
+ * boundary (the id is not a secret and the hash is not salted); it is a SHAPE boundary,
51
+ * which is the thing that was actually at risk.
52
+ */
53
+ export function masterLockPrincipalKey(principalId) {
54
+ const digest = createHash("sha256").update(principalId, "utf8").digest("hex");
55
+ return `${MASTER_LOCK_PRINCIPAL_KEY_PREFIX}${digest}`;
56
+ }
57
+ /**
58
+ * Every person's lock, made on first sight and held for the process's life.
59
+ *
60
+ * Locks live in memory alongside their unlock, exactly as the single-app engine's do, so
61
+ * a restart re-locks everybody. That is the correct default for a wall whose whole risk
62
+ * model is "this browser is somewhere it should not be".
63
+ */
64
+ export class MasterLockDirectory {
65
+ locks = new Map();
66
+ options;
67
+ constructor(options) {
68
+ this.options = options;
69
+ }
70
+ /**
71
+ * This principal's lock, created on first sight.
72
+ *
73
+ * 🔴 `principalId` must come from the request's own verified session and never from a
74
+ * header, a query parameter or a body field. The guard's `resolve` is the one place
75
+ * that decides it; see property 1 in the module note.
76
+ */
77
+ for(principalId) {
78
+ const existing = this.locks.get(principalId);
79
+ if (existing)
80
+ return existing;
81
+ const key = masterLockPrincipalKey(principalId);
82
+ const { kv } = this.options;
83
+ const store = {
84
+ read: () => kv.get(key) ?? null,
85
+ write: (json) => kv.set(key, json),
86
+ };
87
+ const lock = new MasterLock({
88
+ store,
89
+ enrollable: true,
90
+ ...(this.options.now ? { now: this.options.now } : {}),
91
+ ...(this.options.log
92
+ ? { log: (line) => this.options.log?.(`[${principalId}] ${line}`) }
93
+ : {}),
94
+ });
95
+ this.locks.set(principalId, lock);
96
+ return lock;
97
+ }
98
+ /** Has this principal chosen a master password? Reads storage, so it is true of a
99
+ * person who enrolled in a previous boot and has not been seen in this one. */
100
+ isEnrolled(principalId) {
101
+ return this.for(principalId).configured;
102
+ }
103
+ /**
104
+ * Install a PRE-DERIVED record for one principal, if and only if it has none.
105
+ *
106
+ * ── What this is for, and what it is not ───────────────────────────────────
107
+ * A machine account — `apps/orch`'s deploy smoke is the only one so far. Its master
108
+ * password lives 0600 on the owner's Mac, so there is no browser to type it in, and
109
+ * the alternative shapes are both worse: letting the smoke `POST /__lock/enroll` makes
110
+ * it WRITE to a live production site (which `scripts/smokeCleanup.test.ts` refuses, on
111
+ * the owner's rule), and giving the smoke a bypass makes the wall unproven.
112
+ *
113
+ * 🔴 It is NOT an opening and cannot become one. `json` is `{kdf, verifierHash}` —
114
+ * already derived, the same shape `MASTER_LOCK_SEED` has carried since 2026-08-15 — so
115
+ * no password passes through this process, and whoever unlocks still has to present a
116
+ * verifier that matches. Seeding an account only decides that it HAS a password, never
117
+ * that anybody may skip it.
118
+ *
119
+ * Refuses when a record already exists, so it can never rotate one out from under the
120
+ * person holding it. Returns whether it installed anything.
121
+ */
122
+ seed(principalId, json) {
123
+ const key = masterLockPrincipalKey(principalId);
124
+ const { kv } = this.options;
125
+ if (readRecord(kv.get(key) ?? null))
126
+ return false;
127
+ const record = readRecord(json);
128
+ if (!record)
129
+ return false;
130
+ kv.set(key, JSON.stringify(record));
131
+ // Drop any lock built before the row existed, so `for()` re-reads it rather than
132
+ // serving the cached "unconfigured" one. Nothing can be unlocked yet by definition.
133
+ this.locks.delete(principalId);
134
+ return true;
135
+ }
136
+ /** Lock every open session — the "I am handing this laptop over" button, fleet-wide.
137
+ * Only ever reaches principals seen since boot, which is all that can be unlocked. */
138
+ lockAll() {
139
+ for (const lock of this.locks.values())
140
+ lock.lock();
141
+ }
142
+ /** How many principals hold a live unlock right now. For `/healthz` and the boot log;
143
+ * a COUNT discloses nothing about who. */
144
+ unlockedCount() {
145
+ let count = 0;
146
+ for (const lock of this.locks.values())
147
+ if (lock.unlocked)
148
+ count += 1;
149
+ return count;
150
+ }
151
+ /** Drop every in-memory lock — for tests between cases. */
152
+ resetForTest() {
153
+ for (const lock of this.locks.values())
154
+ lock.resetForTest();
155
+ this.locks.clear();
156
+ }
157
+ }
@@ -0,0 +1,47 @@
1
+ import { type MasterLockKdfParams } from "cursedbelt-core/master-lock/kdf";
2
+ import type { MasterLockRecord } from "./masterLock";
3
+ /**
4
+ * The idle window a freshly minted lock starts with.
5
+ *
6
+ * 🔴 It is `DEFAULT_IDLE_MS` ITSELF, not a copy that claims to match it. Until
7
+ * 2026-08-25 this read `15 * 60 * 1000` under a comment saying "matches the store's
8
+ * default", which is 5 minutes — so any app armed through `mintMasterLockSeed` would
9
+ * have silently got three times the window the owner asked for. Live prod was spared
10
+ * only by accident: both seeds omit `idleMs` entirely, so the store's own default
11
+ * applied. A second constant that must equal a first is a drift waiting for a reader
12
+ * who trusts the comment; there is now only one number.
13
+ */
14
+ export declare const DEFAULT_SEED_IDLE_MS: number;
15
+ export interface MintMasterLockSeedOptions {
16
+ /** Override the idle timeout baked into the seed. */
17
+ idleMs?: number;
18
+ /** Reuse existing params instead of generating a salt — for a rotation. */
19
+ kdf?: MasterLockKdfParams;
20
+ }
21
+ /**
22
+ * `password` → the record an app can be seeded with.
23
+ *
24
+ * Returns the parsed record; {@link serializeMasterLockSeed} is what turns it
25
+ * into the env value, because that step has its own trap (see below).
26
+ */
27
+ export declare function mintMasterLockSeed(password: string, options?: MintMasterLockSeedOptions): Promise<MasterLockRecord>;
28
+ /**
29
+ * The record as it belongs in a 0600 `.env` file.
30
+ *
31
+ * 🔴 **SINGLE-QUOTED, and this is not cosmetic.** An argon2id PHC string contains
32
+ * `$`, the file is sourced by a shell, and an unquoted value therefore arrives at
33
+ * the app with `$argon2id` expanded to nothing. `readRecord` strips exactly this
34
+ * quote pair — `apps/collections` shipped a *doubly*-quoted hash to prod on
35
+ * 2026-08-12 and spent a day being told its correct passphrase was wrong, which
36
+ * is why the strip is on the whole blob rather than on the field.
37
+ */
38
+ export declare function serializeMasterLockSeed(record: MasterLockRecord): string;
39
+ /**
40
+ * A password worth typing into a stage: unambiguous, and long enough that the
41
+ * hostname being on the internet behind Cloudflare Access is not the only thing
42
+ * protecting it.
43
+ *
44
+ * No `l`, `1`, `O` or `0` — a stage password gets read out of a terminal and
45
+ * typed into a browser by hand, and this is the one place where that matters.
46
+ */
47
+ export declare function generateStagePassword(words?: number): string;
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Minting a `MASTER_LOCK_SEED` from a password nobody has to guess.
3
+ *
4
+ * ── Why this exists (2026-08-20) ────────────────────────────────────────────
5
+ * The lock's seed is `{kdf, verifierHash, idleMs}` — a KDF descriptor and an
6
+ * argon2id hash of a PBKDF2 verifier. Every one of those steps is derivable from
7
+ * the modules beside this one, and until now every one of them was something an
8
+ * operator had to reproduce by hand in a REPL to arm a new instance. That is the
9
+ * shape of instruction that gets written into a runbook and then gets a digit
10
+ * wrong; the fleet's standing answer to that is a function
11
+ * (`fleet/rules/automation-over-documentation.md`).
12
+ *
13
+ * The immediate caller is the ephemeral **stage** environment, whose entire
14
+ * purpose is to have a master-lock password an AGENT knows, so it can open
15
+ * `collections` and the binary-server inspector and actually look at them — two
16
+ * screens no agent has ever seen, because production's password is the vault's
17
+ * and is the owner's alone.
18
+ *
19
+ * 🔴 **A different PASSWORD, never a different SCHEME.** The derivation stays
20
+ * byte-identical to `apps/vault`'s (see `../../core/master-lock/kdf.ts`), because
21
+ * a stage that unlocks under different rules from production proves nothing about
22
+ * production. This function chooses the salt and the password; it changes nothing
23
+ * else.
24
+ */
25
+ import { DEFAULT_IDLE_MS } from "cursedbelt-core/master-lock/policy";
26
+ import { deriveMasterLockVerifier, newMasterLockKdfParams, } from "cursedbelt-core/master-lock/kdf";
27
+ /**
28
+ * The idle window a freshly minted lock starts with.
29
+ *
30
+ * 🔴 It is `DEFAULT_IDLE_MS` ITSELF, not a copy that claims to match it. Until
31
+ * 2026-08-25 this read `15 * 60 * 1000` under a comment saying "matches the store's
32
+ * default", which is 5 minutes — so any app armed through `mintMasterLockSeed` would
33
+ * have silently got three times the window the owner asked for. Live prod was spared
34
+ * only by accident: both seeds omit `idleMs` entirely, so the store's own default
35
+ * applied. A second constant that must equal a first is a drift waiting for a reader
36
+ * who trusts the comment; there is now only one number.
37
+ */
38
+ export const DEFAULT_SEED_IDLE_MS = DEFAULT_IDLE_MS;
39
+ /**
40
+ * `password` → the record an app can be seeded with.
41
+ *
42
+ * Returns the parsed record; {@link serializeMasterLockSeed} is what turns it
43
+ * into the env value, because that step has its own trap (see below).
44
+ */
45
+ export async function mintMasterLockSeed(password, options = {}) {
46
+ if (password.length < 8) {
47
+ // Not a policy — a floor. A seed minted from a two-character password would
48
+ // arm a lock that reads as protection and is not.
49
+ throw new Error("master-lock password must be at least 8 characters");
50
+ }
51
+ const kdf = options.kdf ?? newMasterLockKdfParams();
52
+ const verifier = await deriveMasterLockVerifier(password, kdf);
53
+ return {
54
+ kdf,
55
+ verifierHash: await Bun.password.hash(verifier, { algorithm: "argon2id" }),
56
+ idleMs: options.idleMs ?? DEFAULT_SEED_IDLE_MS,
57
+ };
58
+ }
59
+ /**
60
+ * The record as it belongs in a 0600 `.env` file.
61
+ *
62
+ * 🔴 **SINGLE-QUOTED, and this is not cosmetic.** An argon2id PHC string contains
63
+ * `$`, the file is sourced by a shell, and an unquoted value therefore arrives at
64
+ * the app with `$argon2id` expanded to nothing. `readRecord` strips exactly this
65
+ * quote pair — `apps/collections` shipped a *doubly*-quoted hash to prod on
66
+ * 2026-08-12 and spent a day being told its correct passphrase was wrong, which
67
+ * is why the strip is on the whole blob rather than on the field.
68
+ */
69
+ export function serializeMasterLockSeed(record) {
70
+ return `'${JSON.stringify(record)}'`;
71
+ }
72
+ /**
73
+ * A password worth typing into a stage: unambiguous, and long enough that the
74
+ * hostname being on the internet behind Cloudflare Access is not the only thing
75
+ * protecting it.
76
+ *
77
+ * No `l`, `1`, `O` or `0` — a stage password gets read out of a terminal and
78
+ * typed into a browser by hand, and this is the one place where that matters.
79
+ */
80
+ export function generateStagePassword(words = 4) {
81
+ const alphabet = "abcdefghijkmnpqrstuvwxyzACDEFGHJKLMNPQRSTUVWXYZ23456789";
82
+ const bytes = new Uint8Array(words * 5);
83
+ crypto.getRandomValues(bytes);
84
+ const chunks = [];
85
+ for (let w = 0; w < words; w += 1) {
86
+ let chunk = "";
87
+ for (let i = 0; i < 5; i += 1) {
88
+ chunk += alphabet[bytes[w * 5 + i] % alphabet.length];
89
+ }
90
+ chunks.push(chunk);
91
+ }
92
+ return chunks.join("-");
93
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Two ways to keep the record, so no app has to invent a third.
3
+ *
4
+ * The record is one JSON blob on purpose: every app that will ever mount this already has
5
+ * somewhere to put a string under a key — binary-server's `settings` table, a satellite's
6
+ * `app_settings`, a `Map` in a test. Asking for a whole table would be asking each adopter
7
+ * to write a migration for three fields.
8
+ */
9
+ import type { MasterLockStore } from "./masterLock";
10
+ /** The default key, so two apps' rows look the same when somebody goes looking. */
11
+ export declare const MASTER_LOCK_SETTING_KEY = "master_lock:record";
12
+ /** Wrap an app's existing key/value settings surface. */
13
+ export declare function createKvMasterLockStore(kv: {
14
+ get(key: string): string | null | undefined;
15
+ set(key: string, value: string): void;
16
+ }, key?: string): MasterLockStore;
17
+ /** For tests, and for a runtime with nowhere to persist — where it means the lock re-seeds
18
+ * from the environment on every boot, which is the correct fallback rather than a failure. */
19
+ export declare function createMemoryMasterLockStore(initial?: string | null): MasterLockStore;
@@ -0,0 +1,20 @@
1
+ /** The default key, so two apps' rows look the same when somebody goes looking. */
2
+ export const MASTER_LOCK_SETTING_KEY = "master_lock:record";
3
+ /** Wrap an app's existing key/value settings surface. */
4
+ export function createKvMasterLockStore(kv, key = MASTER_LOCK_SETTING_KEY) {
5
+ return {
6
+ read: () => kv.get(key) ?? null,
7
+ write: (json) => kv.set(key, json),
8
+ };
9
+ }
10
+ /** For tests, and for a runtime with nowhere to persist — where it means the lock re-seeds
11
+ * from the environment on every boot, which is the correct fallback rather than a failure. */
12
+ export function createMemoryMasterLockStore(initial) {
13
+ let value = initial ?? null;
14
+ return {
15
+ read: () => value,
16
+ write: (json) => {
17
+ value = json;
18
+ },
19
+ };
20
+ }
@@ -0,0 +1,7 @@
1
+ import { type ImagePipelineSpec } from 'cursedbelt-core/media';
2
+ /**
3
+ * Render an `ImagePipelineSpec` to an encoded image buffer. Operations are applied in
4
+ * order; `compress` overrides the output quality; `format-convert` is a no-op marker
5
+ * (the output format does the conversion). Throws on an invalid spec (before sharp).
6
+ */
7
+ export declare function renderImage(spec: ImagePipelineSpec): Promise<Buffer>;
@@ -0,0 +1,96 @@
1
+ // Image rendering via sharp. sharp is an OPTIONAL peer dep, imported LAZILY inside
2
+ // renderImage so merely importing `cursedbelt-server` never loads the native binary
3
+ // — only an app that actually renders an image pays for it. The transforms are driven
4
+ // by the media-core `ImagePipelineSpec`; `remove-bg` runs the pure media-core BFS on
5
+ // sharp's raw RGBA buffer, then re-wraps the result.
6
+ import { assertValidImagePipelineSpec, clampQuality, floodFillBGS, } from 'cursedbelt-core/media';
7
+ let sharpPromise = null;
8
+ /** Lazily import sharp once; a clear error if the optional peer isn't installed. */
9
+ async function loadSharp() {
10
+ if (!sharpPromise) {
11
+ sharpPromise = import('sharp')
12
+ .then((m) => m.default)
13
+ .catch((err) => {
14
+ sharpPromise = null;
15
+ throw new Error(`cursedbelt media: 'sharp' is required for renderImage but could not be loaded. ` +
16
+ `Install it in the app (it is an optional peer dependency). Cause: ${err instanceof Error ? err.message : String(err)}`);
17
+ });
18
+ }
19
+ return sharpPromise;
20
+ }
21
+ /** Run a sharp pipeline to a fresh, transparent RGBA buffer, BFS it, re-wrap it. */
22
+ async function applyRemoveBg(sharp, img, op) {
23
+ const { data, info } = await img.ensureAlpha().raw().toBuffer({ resolveWithObject: true });
24
+ const out = floodFillBGS(new Uint8Array(data.buffer, data.byteOffset, data.byteLength), {
25
+ width: info.width,
26
+ height: info.height,
27
+ channels: 4,
28
+ tolerance: op.tolerance,
29
+ });
30
+ return sharp(Buffer.from(out.buffer, out.byteOffset, out.byteLength), {
31
+ raw: { width: info.width, height: info.height, channels: 4 },
32
+ });
33
+ }
34
+ function applyResize(img, op) {
35
+ return img.resize({
36
+ width: op.width,
37
+ height: op.height,
38
+ fit: op.fit ?? 'cover',
39
+ withoutEnlargement: false,
40
+ });
41
+ }
42
+ /**
43
+ * Render an `ImagePipelineSpec` to an encoded image buffer. Operations are applied in
44
+ * order; `compress` overrides the output quality; `format-convert` is a no-op marker
45
+ * (the output format does the conversion). Throws on an invalid spec (before sharp).
46
+ */
47
+ export async function renderImage(spec) {
48
+ assertValidImagePipelineSpec(spec);
49
+ const sharp = await loadSharp();
50
+ const source = typeof spec.input === 'string'
51
+ ? spec.input
52
+ : Buffer.from(spec.input.buffer, spec.input.byteOffset, spec.input.byteLength);
53
+ // `failOn: 'none'` keeps a slightly-truncated upload from hard-failing the pipeline.
54
+ let img = sharp(source, { failOn: 'none' });
55
+ let quality = spec.output.quality;
56
+ for (const op of spec.operations) {
57
+ switch (op.type) {
58
+ case 'resize':
59
+ img = applyResize(img, op);
60
+ break;
61
+ case 'crop':
62
+ img = img.extract({ left: op.left, top: op.top, width: op.width, height: op.height });
63
+ break;
64
+ case 'rotate':
65
+ // Default background transparent so a non-90° rotate doesn't fill with black.
66
+ img = img.rotate(op.angle, { background: { r: 0, g: 0, b: 0, alpha: 0 } });
67
+ break;
68
+ case 'flip':
69
+ img = op.direction === 'vertical' ? img.flip() : img.flop();
70
+ break;
71
+ case 'remove-bg':
72
+ img = await applyRemoveBg(sharp, img, op);
73
+ break;
74
+ case 'compress':
75
+ quality = op.quality;
76
+ break;
77
+ case 'format-convert':
78
+ break;
79
+ }
80
+ }
81
+ return encode(img, spec.output.format, quality);
82
+ }
83
+ function encode(img, format, quality) {
84
+ const q = clampQuality(quality);
85
+ switch (format) {
86
+ case 'jpeg':
87
+ return img.jpeg({ quality: q, mozjpeg: true }).toBuffer();
88
+ case 'png':
89
+ // PNG is lossless; map quality → effort/compression for a size/CPU trade-off.
90
+ return img.png({ compressionLevel: 9 }).toBuffer();
91
+ case 'webp':
92
+ return img.webp({ quality: q }).toBuffer();
93
+ case 'avif':
94
+ return img.avif({ quality: q }).toBuffer();
95
+ }
96
+ }
@@ -0,0 +1,4 @@
1
+ export { buildProbeArgs, buildThumbnailArgs, buildTrimArgs, ffmpegTimeToSeconds, type ProbeResult, parseFfmpegProgress, parseFfprobe, type VideoCodecPlan, videoCodecPlan, } from 'cursedbelt-core/media/ffmpeg';
2
+ export { renderImage } from './image';
3
+ export { aacWebAudioArgs, buildFrameJpegArgs, buildSpriteVtt, buildStreamProbeArgs, canStreamCopy, EMPTY_PROBE, FASTSTART, faststartArgs, h264WebVideoArgs, hlsLadderArgs, imageThumbnailArgs, LIBRARY_CRF, LIBRARY_SCALE, parseProgressMicros, parseStreamProbe, planSprite, posterArgs, resolveLadder, type SpritePlan, spriteArgs, type VideoProbe, } from './streaming';
4
+ export { extractFrameJpeg, extractThumbnail, isFfmpegAvailable, isFfprobeAvailable, probeMedia, probeVideoStream, runFfmpegProgress, trimVideo, } from './video';
@@ -0,0 +1,10 @@
1
+ // src/server/media-bun — the Bun server adapter, re-exported from `cursedbelt-server`, for the media-core
2
+ // specs: image rendering via sharp (lazy/optional), video trim + probe + thumbnail
3
+ // via native ffmpeg/ffprobe. App handlers keep auth, FsNode lookup, quota, and
4
+ // storeFile; this owns the actual pixel/codec work.
5
+ export { buildProbeArgs, buildThumbnailArgs, buildTrimArgs, ffmpegTimeToSeconds, parseFfmpegProgress, parseFfprobe, videoCodecPlan, } from 'cursedbelt-core/media/ffmpeg';
6
+ export { renderImage } from './image';
7
+ // Streaming pipeline: faststart remux, HLS adaptive-bitrate ladder, scrub sprite + WebVTT,
8
+ // poster, codec-aware probe — pure builders/parsers (testable without ffmpeg).
9
+ export { aacWebAudioArgs, buildFrameJpegArgs, buildSpriteVtt, buildStreamProbeArgs, canStreamCopy, EMPTY_PROBE, FASTSTART, faststartArgs, h264WebVideoArgs, hlsLadderArgs, imageThumbnailArgs, LIBRARY_CRF, LIBRARY_SCALE, parseProgressMicros, parseStreamProbe, planSprite, posterArgs, resolveLadder, spriteArgs, } from './streaming';
10
+ export { extractFrameJpeg, extractThumbnail, isFfmpegAvailable, isFfprobeAvailable, probeMedia, probeVideoStream, runFfmpegProgress, trimVideo, } from './video';
@@ -0,0 +1,86 @@
1
+ export declare const h264WebVideoArgs: (crf: string) => string[];
2
+ export declare const aacWebAudioArgs: (bitrate: string) => string[];
3
+ /** Move the moov atom to the front so playback can start before the full download. */
4
+ export declare const FASTSTART: string[];
5
+ /** Cap the long edge at ~1080p, keep aspect, force even dimensions (yuv420p needs even). */
6
+ export declare const LIBRARY_SCALE = "scale='min(1920,iw)':'-2'";
7
+ /** Higher quality than ephemeral Moments (28) — this output is retained. */
8
+ export declare const LIBRARY_CRF = "23";
9
+ export interface VideoProbe {
10
+ duration: number;
11
+ width: number;
12
+ height: number;
13
+ vcodec: string;
14
+ acodec: string;
15
+ pixfmt: string;
16
+ hasAudio: boolean;
17
+ }
18
+ export declare const EMPTY_PROBE: VideoProbe;
19
+ /** ffprobe argv: one call for everything the pipeline decides on (codecs + dims + duration). */
20
+ export declare const buildStreamProbeArgs: (filePath: string) => string[];
21
+ interface StreamProbeJson {
22
+ format?: {
23
+ duration?: string;
24
+ };
25
+ streams?: Array<{
26
+ codec_type?: string;
27
+ codec_name?: string;
28
+ width?: number;
29
+ height?: number;
30
+ pix_fmt?: string;
31
+ }>;
32
+ }
33
+ /** Parse the `buildStreamProbeArgs` JSON into a {@link VideoProbe}. */
34
+ export declare const parseStreamProbe: (json: StreamProbeJson) => VideoProbe;
35
+ export declare const canStreamCopy: (p: VideoProbe, maxWidth?: number) => boolean;
36
+ export declare const faststartArgs: (inPath: string, outPath: string, opts: {
37
+ copy: boolean;
38
+ crf?: number;
39
+ }) => string[];
40
+ /**
41
+ * Parse one ffmpeg `-progress pipe:1` line into elapsed microseconds, or null if the line
42
+ * carries no out_time. `runFfmpegProgress` turns a stream of these into a 0..99 percent.
43
+ */
44
+ export declare const parseProgressMicros: (line: string) => number | null;
45
+ /** Drop unknown/too-tall rungs (never upscale); always keep at least the source-or-720 rung. */
46
+ export declare const resolveLadder: (heights: number[], sourceHeight: number) => number[];
47
+ /**
48
+ * Build the HLS multi-rendition transcode. `outDir/<height>/` subdirs must already exist.
49
+ * Emits master.m3u8 + per-rung playlist.m3u8 + mpegts segments, variants NAMED by height
50
+ * so storage keys read `…/720/seg_00001.ts` rather than an opaque index.
51
+ */
52
+ export declare const hlsLadderArgs: (inPath: string, outDir: string, opts: {
53
+ heights: number[];
54
+ crf: number;
55
+ hasAudio: boolean;
56
+ keyInfoFile?: string;
57
+ }) => string[];
58
+ /** Poster: a frame `atSeconds` in (default ~1s, to skip black intros), scaled to the library
59
+ * width, as JPEG. Pass a smaller seek (or 0) for very short clips — seeking past EOF yields no
60
+ * frame and the extract fails, so callers that know the duration should clamp `atSeconds`. */
61
+ export declare const posterArgs: (inPath: string, outPath: string, atSeconds?: number) => string[];
62
+ /** Decode a still image to a width-capped JPEG thumbnail. The fallback for formats sharp's
63
+ * prebuilt binary can't decode — notably HEIC/HEVC (iPhone photos), which the licensed-out
64
+ * libheif build rejects but ffmpeg's hevc decoder handles. `-update 1` writes the single frame
65
+ * to a fixed filename (no image-sequence pattern); height rounds to even (`-2`) for the encoder. */
66
+ export declare const imageThumbnailArgs: (inPath: string, outPath: string, maxWidth?: number) => string[];
67
+ /** ffmpeg argv to grab one scaled JPEG frame at `atSeconds` on stdout (`-ss` before `-i`
68
+ * fast-seeks — instant on a faststart file). The spawn runner reads pipe:1. */
69
+ export declare const buildFrameJpegArgs: (filePath: string, atSeconds: number, maxWidth?: number) => string[];
70
+ export interface SpritePlan {
71
+ effInterval: number;
72
+ count: number;
73
+ cols: number;
74
+ rows: number;
75
+ thumbW: number;
76
+ thumbH: number;
77
+ }
78
+ /** Sheet geometry: sample every `interval`s, widening it so a long video still fits one sheet. */
79
+ export declare const planSprite: (duration: number, interval: number, srcW: number, srcH: number) => SpritePlan;
80
+ export declare const spriteArgs: (inPath: string, outPath: string, plan: SpritePlan) => string[];
81
+ /**
82
+ * WebVTT thumbnail track: one cue per tile pointing at a `#xywh` media fragment of the sheet.
83
+ * The sprite URL is relative (default "sprites.jpg") so it resolves against the VTT's own route.
84
+ */
85
+ export declare const buildSpriteVtt: (plan: SpritePlan, spriteName?: string) => string;
86
+ export {};