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,167 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ /**
3
+ * The cursedbelt job runner's public types — the engine both apps share.
4
+ *
5
+ * cursedbelt owns the *mechanism* (durable cron scheduling, restart-surviving
6
+ * pollers, the due-row fire queue, run auditing); the *job definitions* —
7
+ * reminder delivery, recurrence materialization, backup, metrics flush, caSync —
8
+ * stay app-business and consume these primitives.
9
+ *
10
+ * Three primitives cover every in-process scheduled-work shape:
11
+ * - {@link JobRunner} — durable cron jobs (plainjob-backed; survive restart).
12
+ * - PollingJob — a periodic timer that records its last successful run.
13
+ * - FireQueue — the "select due rows → deliver → mark sent" pattern.
14
+ *
15
+ * (cwip/taskq is the *other* model — lease-locked, multi-process, cross-machine —
16
+ * and stays in cwip. These primitives are single-server / in-process only.)
17
+ */
18
+ /**
19
+ * A minimal structured logger (winston-compatible, so it can be passed straight
20
+ * through to the underlying plainjob queue/worker). Consumers inject their app
21
+ * logger; the default ({@link defaultJobLogger}) writes warn/info/error to the
22
+ * console and silences `debug` (plainjob logs at debug every poll).
23
+ */
24
+ export interface JobLogger {
25
+ error(message: string, ...meta: unknown[]): void;
26
+ warn(message: string, ...meta: unknown[]): void;
27
+ info(message: string, ...meta: unknown[]): void;
28
+ debug(message: string, ...meta: unknown[]): void;
29
+ }
30
+ /** Everything a job handler is handed when it runs. */
31
+ export interface JobContext {
32
+ /** The shared SQLite connection (same instance the app uses). */
33
+ db: Database;
34
+ /**
35
+ * Aborted when the runner/poller is stopped, or when a {@link JobDef.timeout}
36
+ * elapses. Long-running handlers should observe it and short-circuit; a promise
37
+ * cannot be force-killed, so cooperation is the only way to stop work early.
38
+ */
39
+ signal: AbortSignal;
40
+ logger: JobLogger;
41
+ }
42
+ /** A durable, cron-scheduled job registered with a {@link JobRunner}. */
43
+ export interface JobDef {
44
+ /** Unique name — the audit key (`job_runs.job_name`) and plainjob job type. */
45
+ name: string;
46
+ /**
47
+ * Cron expression. 5-field (minute precision) or 6-field with a leading
48
+ * seconds column — e.g. `0 3 * * *` is 3am daily, and a seconds step of the
49
+ * form `(asterisk-slash-10) * * * * *` fires every 10s. Omit for a job that is
50
+ * only ever fired manually via {@link JobRunner.runOnce}.
51
+ */
52
+ cron?: string;
53
+ /** The work. Rejecting (or timing out) records an `error` run. */
54
+ run(context: JobContext): Promise<void> | void;
55
+ /**
56
+ * Max run time in ms before the context signal is aborted and the run is
57
+ * recorded as a timeout error. Default {@link DEFAULT_JOB_TIMEOUT_MS}. Set a
58
+ * larger value (or `0` to disable) for legitimately long jobs like backups.
59
+ */
60
+ timeout?: number;
61
+ }
62
+ /** A single row of a job's run history (newest first via {@link JobRunner.getLastRun}). */
63
+ export interface JobRunRecord {
64
+ startedAt: string;
65
+ /** Null while the run is still in flight (or if the process died mid-run). */
66
+ endedAt?: string;
67
+ /** `running` | `ok` | `error`. */
68
+ status: string;
69
+ /** Present only on an `error` run. */
70
+ error?: string;
71
+ }
72
+ export interface CreateJobRunnerOptions {
73
+ logger?: JobLogger;
74
+ /**
75
+ * How often each worker polls for due cron jobs, in ms. Default 1000. Set lower
76
+ * only if you need sub-second cron precision; the cost is one cheap SQLite query
77
+ * per worker per interval.
78
+ */
79
+ pollIntervalMs?: number;
80
+ /**
81
+ * Bounds on the runner's persisted history. Without bounds a high-frequency
82
+ * cron (e.g. a 5s engine janitor) grows `job_runs` and plainjob's internal
83
+ * `plainjob_jobs` forever — thousands of rows per day on a completely idle
84
+ * daemon. Retention is ON by default ({@link DEFAULT_JOB_RETENTION});
85
+ * override a field to tune it, or set a field to `0` to disable that bound.
86
+ */
87
+ retention?: JobRetentionOptions;
88
+ }
89
+ /** Retention bounds for {@link JobRunner} history. `0` disables that bound. */
90
+ export interface JobRetentionOptions {
91
+ /** Delete `job_runs` audit rows older than this. Default 7 days. */
92
+ auditMaxAgeMs?: number;
93
+ /**
94
+ * Keep at most this many newest `job_runs` rows PER job name — the bound that
95
+ * matters for sub-minute crons, where an age window alone still admits tens of
96
+ * thousands of rows. Default 2000.
97
+ */
98
+ auditMaxRowsPerJob?: number;
99
+ /** Minimum gap between prune passes (pruning is lazy, after a run finishes). Default 1 hour. */
100
+ pruneIntervalMs?: number;
101
+ /** plainjob maintenance: remove its internal done-job rows older than this. Default 6 hours. */
102
+ doneJobsMaxAgeMs?: number;
103
+ /** plainjob maintenance: remove its internal failed-job rows older than this. Default 7 days. */
104
+ failedJobsMaxAgeMs?: number;
105
+ }
106
+ /** Default {@link JobRetentionOptions} — bounded history out of the box. */
107
+ export declare const DEFAULT_JOB_RETENTION: Required<JobRetentionOptions>;
108
+ /**
109
+ * The durable cron job runner — a thin facade over plainjob (SQLite-backed, no
110
+ * Redis). Schedules persist in the shared DB, so a registered cron survives a
111
+ * restart. (plainjob does NOT retroactively fire a cron missed while the process
112
+ * was down; for "must catch up on boot" work use a PollingJob with `runOnBoot`.)
113
+ */
114
+ export interface JobRunner {
115
+ /** Register a job. Call before {@link start}; registering a cron after start spins its worker up immediately. */
116
+ register(job: JobDef): void;
117
+ /** Begin processing: one worker per cron job starts polling. Idempotent. */
118
+ start(): void;
119
+ /** Stop all workers (signaling in-flight handlers) and release the queue. Does NOT close the shared DB. */
120
+ stop(): Promise<void>;
121
+ /** Run a registered job once, right now (admin/manual trigger). Bypasses the schedule; still audited. Throws ApiError(404) if unknown. */
122
+ runOnce(name: string): Promise<void>;
123
+ /** The most recent run of `name`, or null if it has never run. */
124
+ getLastRun(name: string): JobRunRecord | null;
125
+ }
126
+ /** A periodic timer started/stopped as a unit. Returned by `createPollingJob`/`createFireQueue`. */
127
+ export interface PollingJob {
128
+ name: string;
129
+ /** Start the timer (and fire once immediately unless `runOnBoot` is false). Idempotent. */
130
+ start(): void;
131
+ /** Stop the timer and signal any in-flight run via its abort signal. */
132
+ stop(): void;
133
+ }
134
+ export interface PollingJobOpts {
135
+ name: string;
136
+ /** Fire cadence in ms. */
137
+ intervalMs: number;
138
+ /** Fire once immediately on {@link PollingJob.start}. Default true. */
139
+ runOnBoot?: boolean;
140
+ /** The shared SQLite connection. `job_state.last_run_at` is upserted after each successful run. */
141
+ db: Database;
142
+ run(context: JobContext): Promise<void> | void;
143
+ logger?: JobLogger;
144
+ }
145
+ /** A {@link PollingJob} specialized for the "deliver every due row" pattern. */
146
+ export type FireQueue = PollingJob;
147
+ export interface FireQueueOpts<T> {
148
+ name: string;
149
+ db: Database;
150
+ /** Poll cadence in ms. Default {@link DEFAULT_FIRE_INTERVAL_MS} (30s). */
151
+ intervalMs?: number;
152
+ /** Fire once immediately on start. Default true. */
153
+ runOnBoot?: boolean;
154
+ /** Return the rows currently due for delivery. */
155
+ getDue(db: Database): T[];
156
+ /** Deliver one item. Throwing skips `markSent` for that item and routes to `onError`; the loop continues. */
157
+ deliver(item: T, db: Database): Promise<void> | void;
158
+ /** Mark one item delivered (only called after a successful `deliver`). */
159
+ markSent(item: T, db: Database): void;
160
+ /** Per-item failure hook (the loop still continues to the next item). */
161
+ onError?(item: T, err: unknown): void;
162
+ logger?: JobLogger;
163
+ }
164
+ /** Default per-run timeout for a cron {@link JobDef} (1 minute). */
165
+ export declare const DEFAULT_JOB_TIMEOUT_MS = 60000;
166
+ /** Default poll cadence for a {@link FireQueue} (30 seconds). */
167
+ export declare const DEFAULT_FIRE_INTERVAL_MS = 30000;
@@ -0,0 +1,12 @@
1
+ /** Default {@link JobRetentionOptions} — bounded history out of the box. */
2
+ export const DEFAULT_JOB_RETENTION = {
3
+ auditMaxAgeMs: 7 * 24 * 60 * 60 * 1000,
4
+ auditMaxRowsPerJob: 2000,
5
+ pruneIntervalMs: 60 * 60 * 1000,
6
+ doneJobsMaxAgeMs: 6 * 60 * 60 * 1000,
7
+ failedJobsMaxAgeMs: 7 * 24 * 60 * 60 * 1000,
8
+ };
9
+ /** Default per-run timeout for a cron {@link JobDef} (1 minute). */
10
+ export const DEFAULT_JOB_TIMEOUT_MS = 60_000;
11
+ /** Default poll cadence for a {@link FireQueue} (30 seconds). */
12
+ export const DEFAULT_FIRE_INTERVAL_MS = 30_000;
@@ -0,0 +1,53 @@
1
+ import { type LockPageOptions } from "./lockPage";
2
+ import type { MasterLock } from "./masterLock";
3
+ export interface MasterLockGuardOptions extends LockPageOptions {
4
+ /**
5
+ * The lock this guard enforces.
6
+ *
7
+ * A {@link MasterLock} for an app with one app-wide password (collections, the
8
+ * binary-server inspector). A FUNCTION for an app where each person has their own
9
+ * (`principals.ts`): it is handed the request and returns that caller's lock.
10
+ *
11
+ * 🔴 The function must resolve the principal from the request's own verified session
12
+ * and NOTHING else. A resolver that reads a header, a query parameter or a body field
13
+ * hands the caller the choice of whose lock to be measured against, which is the one
14
+ * way this feature can be turned inside out. Returning `null` means "no principal" —
15
+ * the guard then stands aside for ordinary paths (the app's own sign-in owns the
16
+ * request) and refuses `/__lock/*` outright, since there is nobody to unlock for.
17
+ */
18
+ lock: MasterLock | ((req: Request) => MasterLock | null);
19
+ /**
20
+ * Paths that stay reachable while locked, beyond `/__lock/*`.
21
+ *
22
+ * 🔴 This is where a MACHINE surface is kept out of the lock, and getting it wrong is
23
+ * the one way this feature can break something else. binary-server's inspector is a
24
+ * UI and is locked whole; binary-server's STORE serves the fleet's bytes and is a
25
+ * different process that never mounts this at all. Within one process, a health check
26
+ * or a token-authenticated ingest route belongs here — a page does not.
27
+ */
28
+ allow?: (url: URL, req: Request) => boolean;
29
+ /**
30
+ * What makes this app's unlock cookie ITS OWN — `master_lock_<appKey>`.
31
+ *
32
+ * 🔴 Set it on every app that can ever share a hostname with another locked app, which
33
+ * on this fleet means every app: they are all `localhost:<port>` on the owner's Mac, and
34
+ * a cookie is scoped by HOST, not by origin. Left unset the cookie is the bare
35
+ * `master_lock`, and two such apps overwrite each other's token — see
36
+ * `masterLockCookieName` for what that silently breaks. Defaults to {@link appLabel},
37
+ * so an app that already names itself is fixed without being changed.
38
+ */
39
+ appKey?: string;
40
+ /**
41
+ * Is this connection HTTPS, so the cookie may carry `Secure`?
42
+ *
43
+ * Defaults to reading `x-forwarded-proto` then the URL — right for both a satellite
44
+ * behind nginx and a loopback daemon speaking plain http, where a `Secure` cookie would
45
+ * simply never be stored and the site could never be unlocked.
46
+ */
47
+ secure?: (url: URL, req: Request) => boolean;
48
+ }
49
+ export interface MasterLockGuard {
50
+ /** `Response` when handled, `null` when the app should serve the request. */
51
+ handle(req: Request): Promise<Response | null>;
52
+ }
53
+ export declare function createMasterLockGuard(options: MasterLockGuardOptions): MasterLockGuard;
@@ -0,0 +1,205 @@
1
+ /**
2
+ * The guard — one decision function, so a Hono app and a bare `Bun.serve` handler cannot
3
+ * end up enforcing two different rules.
4
+ *
5
+ * `handle(req)` answers a `Response` when it has taken responsibility for the request, and
6
+ * `null` when the app should serve it as usual. That shape is what lets it be middleware in
7
+ * one app and an early `return` in another without either of them re-deciding anything.
8
+ *
9
+ * 🔴 Registration order is the security property, exactly as it is for the SSO gate this
10
+ * sits behind. Mount it so that it runs BEFORE any route that serves content, and AFTER
11
+ * whatever proves the caller is allowed in the building at all — the master password is a
12
+ * second wall, not a replacement for the first.
13
+ */
14
+ import { MASTER_LOCK_PATHS, MASTER_LOCK_PREFIX, MASTER_LOCK_STATE_HEADER, masterLockCookieName, } from "cursedbelt-core/master-lock";
15
+ import { LOCK_SCRIPT, LOCK_STYLE, lockPageHtml } from "./lockPage";
16
+ const noStore = { "cache-control": "no-store, no-cache, must-revalidate", pragma: "no-cache" };
17
+ /**
18
+ * The lock page's own CSP. Strict enough to stand in front of `apps/vault`'s wall without
19
+ * loosening it: nothing loads from anywhere but this origin, the form posts nowhere (the
20
+ * script does the POST), and the page cannot be framed.
21
+ */
22
+ const LOCK_CSP = "default-src 'none'; style-src 'self'; script-src 'self'; connect-src 'self'; " +
23
+ "img-src data:; form-action 'none'; base-uri 'none'; frame-ancestors 'none'";
24
+ export function createMasterLockGuard(options) {
25
+ const resolve = typeof options.lock === "function" ? options.lock : () => options.lock;
26
+ const isSecure = options.secure ?? defaultSecure;
27
+ const cookieName = masterLockCookieName(options.appKey ?? options.appLabel);
28
+ const cookie = (token, secure) =>
29
+ // No Max-Age: a SESSION cookie, so closing the browser drops it. The server-side
30
+ // idle clock is the real expiry — this only keeps the token out of a profile that
31
+ // somebody else might open tomorrow.
32
+ `${cookieName}=${token}; Path=/; HttpOnly; SameSite=Strict${secure ? "; Secure" : ""}`;
33
+ const clearCookie = (secure) => `${cookieName}=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0${secure ? "; Secure" : ""}`;
34
+ /** The ONE refusal. Identical for wrong, throttled, unconfigured and not-unlocked. */
35
+ const refuse = (retryAfterMs = 0) => Response.json({ error: "locked", retryAfterMs }, { status: 401, headers: { ...noStore, [MASTER_LOCK_STATE_HEADER]: "locked" } });
36
+ /**
37
+ * 🔴 May THIS request through — and the difference between the two modes is the whole
38
+ * security property of the per-person wall.
39
+ *
40
+ * **App-wide**: `unlocked` is enough. The site has one user and one unlock; the token
41
+ * is how a caller with no cookie jar says "it's still me", not a per-browser
42
+ * credential. Reading the ambient state is the behaviour `apps/collections` and the
43
+ * binary-server inspector have always had.
44
+ *
45
+ * **Per-person**: the token is REQUIRED. `unlocked` there means only "somebody's
46
+ * device is open", and honouring that would make the unlock ambient — measured while
47
+ * building this (2026-08-25): with `unlocked ||` in front, one browser unlocking meant
48
+ * every OTHER browser signed in as that person walked straight through, having typed
49
+ * nothing. The owner's phone would ride his desk's unlock, and an invented
50
+ * `x-master-lock` value would be accepted because the check never looked at it. Two
51
+ * tests in `principals.spec.ts` fail on the `||`, which is why it is gone.
52
+ */
53
+ const opens = (lock, req) => lock.isEnrollable ? lock.presents(req) : lock.unlocked || lock.presents(req);
54
+ async function handleOwnRoute(req, url, lock) {
55
+ const path = url.pathname;
56
+ const secure = isSecure(url, req);
57
+ if (req.method === "GET" || req.method === "HEAD") {
58
+ if (path === MASTER_LOCK_PATHS.style) {
59
+ return new Response(LOCK_STYLE, {
60
+ headers: { "content-type": "text/css; charset=utf-8", ...noStore },
61
+ });
62
+ }
63
+ if (path === MASTER_LOCK_PATHS.script) {
64
+ return new Response(LOCK_SCRIPT, {
65
+ headers: { "content-type": "text/javascript; charset=utf-8", ...noStore },
66
+ });
67
+ }
68
+ if (path === MASTER_LOCK_PATHS.status) {
69
+ return Response.json(lock.status(), { headers: noStore });
70
+ }
71
+ if (path === MASTER_LOCK_PATHS.page || path === MASTER_LOCK_PREFIX) {
72
+ // Already open FOR THIS CALLER, so there is nothing to type — send them to
73
+ // the app rather than showing a password box that would do nothing. Per
74
+ // person this must be the caller's own state, not the site's: bouncing a
75
+ // locked-out phone to `/` because the desk is open is an infinite loop.
76
+ if (opens(lock, req))
77
+ return Response.redirect(new URL("/", url), 303);
78
+ return lockPage(options, lock);
79
+ }
80
+ return new Response("not found", { status: 404, headers: noStore });
81
+ }
82
+ if (req.method !== "POST") {
83
+ return new Response("method not allowed", { status: 405, headers: noStore });
84
+ }
85
+ if (path === MASTER_LOCK_PATHS.enroll) {
86
+ const body = await readJson(req);
87
+ const result = await lock.enroll({
88
+ kdf: body?.kdf,
89
+ verifier: typeof body?.verifier === "string" ? body.verifier : "",
90
+ });
91
+ // Every refusal is the SAME refusal, as everywhere else here: "you may not
92
+ // enroll" and "somebody already did" must not be distinguishable, or the route
93
+ // becomes a way to ask whether a given account has a master password yet.
94
+ if (!result.ok)
95
+ return refuse();
96
+ return Response.json({ ok: true, idleMs: lock.idleMs }, { headers: { ...noStore, "set-cookie": cookie(result.token, secure) } });
97
+ }
98
+ if (path === MASTER_LOCK_PATHS.unlock) {
99
+ const body = await readJson(req);
100
+ const verifier = typeof body?.verifier === "string" ? body.verifier : "";
101
+ const result = await lock.unlock(verifier);
102
+ if (!result.ok)
103
+ return refuse(result.retryAfterMs);
104
+ return Response.json({ ok: true, idleMs: lock.idleMs }, { headers: { ...noStore, "set-cookie": cookie(result.token, secure) } });
105
+ }
106
+ // Everything below CHANGES something and therefore needs the site already open.
107
+ if (!lock.presents(req))
108
+ return refuse();
109
+ if (path === MASTER_LOCK_PATHS.ping) {
110
+ lock.touch(req);
111
+ return Response.json({ ok: true, remainingMs: lock.remainingMs }, { headers: noStore });
112
+ }
113
+ if (path === MASTER_LOCK_PATHS.lock) {
114
+ lock.lock();
115
+ return Response.json({ ok: true }, { headers: { ...noStore, "set-cookie": clearCookie(secure) } });
116
+ }
117
+ if (path === MASTER_LOCK_PATHS.settings) {
118
+ const body = await readJson(req);
119
+ const idleMs = lock.setIdleMs(Number(body?.idleMs));
120
+ return Response.json({ ok: true, idleMs }, { headers: noStore });
121
+ }
122
+ if (path === MASTER_LOCK_PATHS.change) {
123
+ const body = await readJson(req);
124
+ const result = await lock.change({
125
+ currentVerifier: typeof body?.currentVerifier === "string" ? body.currentVerifier : "",
126
+ kdf: body?.kdf,
127
+ verifier: typeof body?.verifier === "string" ? body.verifier : "",
128
+ });
129
+ if (!result.ok) {
130
+ return Response.json({ error: result.reason }, { status: 400, headers: noStore });
131
+ }
132
+ // The rotation locked the site; drop the cookie with it so the page it came from
133
+ // does not sit there holding a token that stopped meaning anything.
134
+ return Response.json({ ok: true }, { headers: { ...noStore, "set-cookie": clearCookie(secure) } });
135
+ }
136
+ return new Response("not found", { status: 404, headers: noStore });
137
+ }
138
+ return {
139
+ async handle(req) {
140
+ const url = new URL(req.url);
141
+ const own = url.pathname === MASTER_LOCK_PREFIX || url.pathname.startsWith(`${MASTER_LOCK_PREFIX}/`);
142
+ const lock = resolve(req);
143
+ if (!lock) {
144
+ // No principal. On the app's own paths the caller is not signed in and the
145
+ // app's sign-in owns the request; on `/__lock/*` there is nobody to unlock
146
+ // FOR, and answering the ordinary refusal keeps that indistinguishable from
147
+ // being locked.
148
+ return own ? refuse() : null;
149
+ }
150
+ if (own)
151
+ return handleOwnRoute(req, url, lock);
152
+ if (options.allow?.(url, req))
153
+ return null;
154
+ if (opens(lock, req))
155
+ return null;
156
+ // A document request is answered IN PLACE with the lock page — same URL, status
157
+ // 200 — so unlocking reloads to where they were going instead of dumping them at
158
+ // the root. Everything else gets the uniform refusal.
159
+ if (wantsDocument(req))
160
+ return lockPage(options, lock);
161
+ return refuse();
162
+ },
163
+ };
164
+ }
165
+ function lockPage(options, lock) {
166
+ return new Response(lockPageHtml({ ...options, enroll: lock.awaitingEnrollment }), {
167
+ status: 200,
168
+ headers: {
169
+ "content-type": "text/html; charset=utf-8",
170
+ "content-security-policy": LOCK_CSP,
171
+ "x-content-type-options": "nosniff",
172
+ [MASTER_LOCK_STATE_HEADER]: "locked",
173
+ ...noStore,
174
+ },
175
+ });
176
+ }
177
+ /** Is this the browser asking for a PAGE, as opposed to the app asking for data? */
178
+ function wantsDocument(req) {
179
+ if (req.method !== "GET")
180
+ return false;
181
+ // `Sec-Fetch-Dest` is the browser's own answer and beats guessing from `Accept`; every
182
+ // engine this fleet runs on sends it. `Accept` is the fallback for curl and for a
183
+ // request that arrived through something that stripped it.
184
+ const dest = req.headers.get("sec-fetch-dest");
185
+ if (dest)
186
+ return dest === "document" || dest === "iframe";
187
+ return (req.headers.get("accept") ?? "").includes("text/html");
188
+ }
189
+ async function readJson(req) {
190
+ try {
191
+ const body = await req.json();
192
+ if (body === null || typeof body !== "object" || Array.isArray(body))
193
+ return null;
194
+ return body;
195
+ }
196
+ catch {
197
+ return null;
198
+ }
199
+ }
200
+ function defaultSecure(url, req) {
201
+ const forwarded = req.headers.get("x-forwarded-proto");
202
+ if (forwarded)
203
+ return forwarded.split(",")[0]?.trim() === "https";
204
+ return url.protocol === "https:";
205
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * `cursedbelt-server/master-lock` — the server half of the sitewide master-password lock.
3
+ *
4
+ * What the feature is, what it deliberately is NOT, and the four decisions behind it are in
5
+ * `masterLock.ts`'s module note. Read that first.
6
+ *
7
+ * Adopting it in an app is three things:
8
+ * 1. a {@link MasterLock} built over whatever key/value the app already has,
9
+ * 2. `createMasterLockGuard(...).handle(req)` in front of everything the app serves,
10
+ * 3. `cursedbelt/react/master-lock` in the header, for the manual lock and the rotation.
11
+ *
12
+ * Removing it is deleting those three. That is the shape the owner asked for: *"the reusable
13
+ * functionality for this should be in a place that makes it easy to share to add or remove
14
+ * from apps in the code, such as cb."*
15
+ */
16
+ export { type MasterLockGuard, type MasterLockGuardOptions, createMasterLockGuard } from "./guard";
17
+ export { LOCK_SCRIPT, LOCK_STYLE, type LockPageOptions, lockPageHtml } from "./lockPage";
18
+ export { MASTER_LOCK_PRINCIPAL_KEY_PREFIX, MasterLockDirectory, type MasterLockDirectoryOptions, masterLockPrincipalKey, } from "./principals";
19
+ export { MasterLock, type MasterLockEnrollResult, type MasterLockOptions, type MasterLockRecord, type MasterLockStore, type MasterLockUnlockResult, readRecord, } from "./masterLock";
20
+ export { DEFAULT_SEED_IDLE_MS, generateStagePassword, type MintMasterLockSeedOptions, mintMasterLockSeed, serializeMasterLockSeed, } from "./seed";
21
+ export { MASTER_LOCK_SETTING_KEY, createKvMasterLockStore, createMemoryMasterLockStore, } from "./store";
@@ -0,0 +1,21 @@
1
+ /**
2
+ * `cursedbelt-server/master-lock` — the server half of the sitewide master-password lock.
3
+ *
4
+ * What the feature is, what it deliberately is NOT, and the four decisions behind it are in
5
+ * `masterLock.ts`'s module note. Read that first.
6
+ *
7
+ * Adopting it in an app is three things:
8
+ * 1. a {@link MasterLock} built over whatever key/value the app already has,
9
+ * 2. `createMasterLockGuard(...).handle(req)` in front of everything the app serves,
10
+ * 3. `cursedbelt/react/master-lock` in the header, for the manual lock and the rotation.
11
+ *
12
+ * Removing it is deleting those three. That is the shape the owner asked for: *"the reusable
13
+ * functionality for this should be in a place that makes it easy to share to add or remove
14
+ * from apps in the code, such as cb."*
15
+ */
16
+ export { createMasterLockGuard } from "./guard";
17
+ export { LOCK_SCRIPT, LOCK_STYLE, lockPageHtml } from "./lockPage";
18
+ export { MASTER_LOCK_PRINCIPAL_KEY_PREFIX, MasterLockDirectory, masterLockPrincipalKey, } from "./principals";
19
+ export { MasterLock, readRecord, } from "./masterLock";
20
+ export { DEFAULT_SEED_IDLE_MS, generateStagePassword, mintMasterLockSeed, serializeMasterLockSeed, } from "./seed";
21
+ export { MASTER_LOCK_SETTING_KEY, createKvMasterLockStore, createMemoryMasterLockStore, } from "./store";
@@ -0,0 +1,34 @@
1
+ /** Everything the page needs to know about the app it is standing in front of. */
2
+ export interface LockPageOptions {
3
+ /** What to call this app — "Collections", "binary-server inspector". */
4
+ appLabel: string;
5
+ /** Shown under the title. Defaults to the sentence that explains the second wall. */
6
+ note?: string;
7
+ /**
8
+ * Render the CHOOSE-a-password form rather than the type-it form.
9
+ *
10
+ * Set by the guard from `lock.awaitingEnrollment`, i.e. per-person mode with no record
11
+ * for this account yet. It is a rendering hint and nothing more — the server refuses
12
+ * `POST /__lock/enroll` on its own record's state, so a page asking to enroll cannot
13
+ * make enrollment legal.
14
+ */
15
+ enroll?: boolean;
16
+ }
17
+ /**
18
+ * The document. Deliberately says the app's NAME and nothing else about it — whoever is
19
+ * looking already reached the origin, so the hostname told them that much, and a page that
20
+ * would not say which app it is cannot tell the owner which password to type.
21
+ */
22
+ export declare function lockPageHtml(options: LockPageOptions): string;
23
+ /** The stylesheet. Follows the viewer's theme and commits to nothing else. */
24
+ export declare const LOCK_STYLE = ":root{color-scheme:light dark;\n --ml-bg:#f4f5f7;--ml-card:#ffffff;--ml-text:#14171c;--ml-muted:#69707c;--ml-border:#d9dde3;\n --ml-accent:#1f6feb;--ml-accent-text:#ffffff;--ml-danger:#b42318;\n --ml-shadow:0 18px 48px rgba(16,18,22,.14)}\n@media (prefers-color-scheme:dark){:root{\n --ml-bg:#0d0f13;--ml-card:#171a20;--ml-text:#e7eaef;--ml-muted:#8b93a1;--ml-border:#2b3039;\n --ml-accent:#4f8cf7;--ml-accent-text:#0b0d11;--ml-danger:#f97066;\n --ml-shadow:0 18px 48px rgba(0,0,0,.55)}}\n*{box-sizing:border-box}\nbody{margin:0;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px;\n background:var(--ml-bg);color:var(--ml-text);\n font:15px/1.55 ui-sans-serif,system-ui,-apple-system,\"Segoe UI\",sans-serif}\n.card{width:100%;max-width:23rem;background:var(--ml-card);border:1px solid var(--ml-border);\n border-radius:16px;padding:28px 26px 24px;box-shadow:var(--ml-shadow);text-align:center}\n.glyph{font-size:30px;line-height:1;margin-bottom:10px}\nh1{margin:0 0 6px;font-size:18px;font-weight:650;letter-spacing:-.01em}\n.note{margin:0 0 20px;font-size:13px;color:var(--ml-muted)}\n.field{display:block;text-align:left;margin-bottom:14px}\n.field span{display:block;font-size:12px;font-weight:600;color:var(--ml-muted);margin-bottom:6px}\ninput{width:100%;height:40px;padding:0 12px;border:1px solid var(--ml-border);border-radius:9px;\n background:var(--ml-bg);color:inherit;font:inherit}\ninput:focus{outline:2px solid var(--ml-accent);outline-offset:1px;border-color:transparent}\nbutton{width:100%;height:40px;border:0;border-radius:9px;background:var(--ml-accent);\n color:var(--ml-accent-text);font:inherit;font-weight:600;cursor:pointer}\nbutton[disabled]{opacity:.6;cursor:progress}\n.status{margin:14px 0 0;min-height:1.2em;font-size:12.5px;color:var(--ml-muted)}\n.status[data-tone=\"error\"]{color:var(--ml-danger)}\n";
25
+ /**
26
+ * The page's script.
27
+ *
28
+ * Derives in the browser and sends only the verifier — the password itself never crosses
29
+ * the network, which is what makes "saved nowhere" true rather than aspirational.
30
+ *
31
+ * 600,000 PBKDF2 rounds take a beat on a phone, so the button says so; a page that looks
32
+ * frozen gets clicked again, and every extra click is another 600k rounds.
33
+ */
34
+ export declare const LOCK_SCRIPT: string;