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,290 @@
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 {
15
+ MASTER_LOCK_PATHS,
16
+ MASTER_LOCK_PREFIX,
17
+ MASTER_LOCK_STATE_HEADER,
18
+ masterLockCookieName,
19
+ } from "cursedbelt-core/master-lock";
20
+ import { LOCK_SCRIPT, LOCK_STYLE, type LockPageOptions, lockPageHtml } from "./lockPage";
21
+ import type { MasterLock } from "./masterLock";
22
+
23
+ export interface MasterLockGuardOptions extends LockPageOptions {
24
+ /**
25
+ * The lock this guard enforces.
26
+ *
27
+ * A {@link MasterLock} for an app with one app-wide password (collections, the
28
+ * binary-server inspector). A FUNCTION for an app where each person has their own
29
+ * (`principals.ts`): it is handed the request and returns that caller's lock.
30
+ *
31
+ * ๐Ÿ”ด The function must resolve the principal from the request's own verified session
32
+ * and NOTHING else. A resolver that reads a header, a query parameter or a body field
33
+ * hands the caller the choice of whose lock to be measured against, which is the one
34
+ * way this feature can be turned inside out. Returning `null` means "no principal" โ€”
35
+ * the guard then stands aside for ordinary paths (the app's own sign-in owns the
36
+ * request) and refuses `/__lock/*` outright, since there is nobody to unlock for.
37
+ */
38
+ lock: MasterLock | ((req: Request) => MasterLock | null);
39
+ /**
40
+ * Paths that stay reachable while locked, beyond `/__lock/*`.
41
+ *
42
+ * ๐Ÿ”ด This is where a MACHINE surface is kept out of the lock, and getting it wrong is
43
+ * the one way this feature can break something else. binary-server's inspector is a
44
+ * UI and is locked whole; binary-server's STORE serves the fleet's bytes and is a
45
+ * different process that never mounts this at all. Within one process, a health check
46
+ * or a token-authenticated ingest route belongs here โ€” a page does not.
47
+ */
48
+ allow?: (url: URL, req: Request) => boolean;
49
+ /**
50
+ * What makes this app's unlock cookie ITS OWN โ€” `master_lock_<appKey>`.
51
+ *
52
+ * ๐Ÿ”ด Set it on every app that can ever share a hostname with another locked app, which
53
+ * on this fleet means every app: they are all `localhost:<port>` on the owner's Mac, and
54
+ * a cookie is scoped by HOST, not by origin. Left unset the cookie is the bare
55
+ * `master_lock`, and two such apps overwrite each other's token โ€” see
56
+ * `masterLockCookieName` for what that silently breaks. Defaults to {@link appLabel},
57
+ * so an app that already names itself is fixed without being changed.
58
+ */
59
+ appKey?: string;
60
+ /**
61
+ * Is this connection HTTPS, so the cookie may carry `Secure`?
62
+ *
63
+ * Defaults to reading `x-forwarded-proto` then the URL โ€” right for both a satellite
64
+ * behind nginx and a loopback daemon speaking plain http, where a `Secure` cookie would
65
+ * simply never be stored and the site could never be unlocked.
66
+ */
67
+ secure?: (url: URL, req: Request) => boolean;
68
+ }
69
+
70
+ const noStore = { "cache-control": "no-store, no-cache, must-revalidate", pragma: "no-cache" } as const;
71
+
72
+ /**
73
+ * The lock page's own CSP. Strict enough to stand in front of `apps/vault`'s wall without
74
+ * loosening it: nothing loads from anywhere but this origin, the form posts nowhere (the
75
+ * script does the POST), and the page cannot be framed.
76
+ */
77
+ const LOCK_CSP =
78
+ "default-src 'none'; style-src 'self'; script-src 'self'; connect-src 'self'; " +
79
+ "img-src data:; form-action 'none'; base-uri 'none'; frame-ancestors 'none'";
80
+
81
+ export interface MasterLockGuard {
82
+ /** `Response` when handled, `null` when the app should serve the request. */
83
+ handle(req: Request): Promise<Response | null>;
84
+ }
85
+
86
+ export function createMasterLockGuard(options: MasterLockGuardOptions): MasterLockGuard {
87
+ const resolve: (req: Request) => MasterLock | null =
88
+ typeof options.lock === "function" ? options.lock : () => options.lock as MasterLock;
89
+ const isSecure = options.secure ?? defaultSecure;
90
+ const cookieName = masterLockCookieName(options.appKey ?? options.appLabel);
91
+
92
+ const cookie = (token: string, secure: boolean): string =>
93
+ // No Max-Age: a SESSION cookie, so closing the browser drops it. The server-side
94
+ // idle clock is the real expiry โ€” this only keeps the token out of a profile that
95
+ // somebody else might open tomorrow.
96
+ `${cookieName}=${token}; Path=/; HttpOnly; SameSite=Strict${secure ? "; Secure" : ""}`;
97
+ const clearCookie = (secure: boolean): string =>
98
+ `${cookieName}=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0${secure ? "; Secure" : ""}`;
99
+
100
+ /** The ONE refusal. Identical for wrong, throttled, unconfigured and not-unlocked. */
101
+ const refuse = (retryAfterMs = 0): Response =>
102
+ Response.json(
103
+ { error: "locked", retryAfterMs },
104
+ { status: 401, headers: { ...noStore, [MASTER_LOCK_STATE_HEADER]: "locked" } },
105
+ );
106
+
107
+ /**
108
+ * ๐Ÿ”ด May THIS request through โ€” and the difference between the two modes is the whole
109
+ * security property of the per-person wall.
110
+ *
111
+ * **App-wide**: `unlocked` is enough. The site has one user and one unlock; the token
112
+ * is how a caller with no cookie jar says "it's still me", not a per-browser
113
+ * credential. Reading the ambient state is the behaviour `apps/collections` and the
114
+ * binary-server inspector have always had.
115
+ *
116
+ * **Per-person**: the token is REQUIRED. `unlocked` there means only "somebody's
117
+ * device is open", and honouring that would make the unlock ambient โ€” measured while
118
+ * building this (2026-08-25): with `unlocked ||` in front, one browser unlocking meant
119
+ * every OTHER browser signed in as that person walked straight through, having typed
120
+ * nothing. The owner's phone would ride his desk's unlock, and an invented
121
+ * `x-master-lock` value would be accepted because the check never looked at it. Two
122
+ * tests in `principals.spec.ts` fail on the `||`, which is why it is gone.
123
+ */
124
+ const opens = (lock: MasterLock, req: Request): boolean =>
125
+ lock.isEnrollable ? lock.presents(req) : lock.unlocked || lock.presents(req);
126
+
127
+ async function handleOwnRoute(req: Request, url: URL, lock: MasterLock): Promise<Response> {
128
+ const path = url.pathname;
129
+ const secure = isSecure(url, req);
130
+
131
+ if (req.method === "GET" || req.method === "HEAD") {
132
+ if (path === MASTER_LOCK_PATHS.style) {
133
+ return new Response(LOCK_STYLE, {
134
+ headers: { "content-type": "text/css; charset=utf-8", ...noStore },
135
+ });
136
+ }
137
+ if (path === MASTER_LOCK_PATHS.script) {
138
+ return new Response(LOCK_SCRIPT, {
139
+ headers: { "content-type": "text/javascript; charset=utf-8", ...noStore },
140
+ });
141
+ }
142
+ if (path === MASTER_LOCK_PATHS.status) {
143
+ return Response.json(lock.status(), { headers: noStore });
144
+ }
145
+ if (path === MASTER_LOCK_PATHS.page || path === MASTER_LOCK_PREFIX) {
146
+ // Already open FOR THIS CALLER, so there is nothing to type โ€” send them to
147
+ // the app rather than showing a password box that would do nothing. Per
148
+ // person this must be the caller's own state, not the site's: bouncing a
149
+ // locked-out phone to `/` because the desk is open is an infinite loop.
150
+ if (opens(lock, req)) return Response.redirect(new URL("/", url), 303);
151
+ return lockPage(options, lock);
152
+ }
153
+ return new Response("not found", { status: 404, headers: noStore });
154
+ }
155
+
156
+ if (req.method !== "POST") {
157
+ return new Response("method not allowed", { status: 405, headers: noStore });
158
+ }
159
+
160
+ if (path === MASTER_LOCK_PATHS.enroll) {
161
+ const body = await readJson(req);
162
+ const result = await lock.enroll({
163
+ kdf: body?.kdf as never,
164
+ verifier: typeof body?.verifier === "string" ? body.verifier : "",
165
+ });
166
+ // Every refusal is the SAME refusal, as everywhere else here: "you may not
167
+ // enroll" and "somebody already did" must not be distinguishable, or the route
168
+ // becomes a way to ask whether a given account has a master password yet.
169
+ if (!result.ok) return refuse();
170
+ return Response.json(
171
+ { ok: true, idleMs: lock.idleMs },
172
+ { headers: { ...noStore, "set-cookie": cookie(result.token, secure) } },
173
+ );
174
+ }
175
+
176
+ if (path === MASTER_LOCK_PATHS.unlock) {
177
+ const body = await readJson(req);
178
+ const verifier = typeof body?.verifier === "string" ? body.verifier : "";
179
+ const result = await lock.unlock(verifier);
180
+ if (!result.ok) return refuse(result.retryAfterMs);
181
+ return Response.json(
182
+ { ok: true, idleMs: lock.idleMs },
183
+ { headers: { ...noStore, "set-cookie": cookie(result.token, secure) } },
184
+ );
185
+ }
186
+
187
+ // Everything below CHANGES something and therefore needs the site already open.
188
+ if (!lock.presents(req)) return refuse();
189
+
190
+ if (path === MASTER_LOCK_PATHS.ping) {
191
+ lock.touch(req);
192
+ return Response.json({ ok: true, remainingMs: lock.remainingMs }, { headers: noStore });
193
+ }
194
+ if (path === MASTER_LOCK_PATHS.lock) {
195
+ lock.lock();
196
+ return Response.json(
197
+ { ok: true },
198
+ { headers: { ...noStore, "set-cookie": clearCookie(secure) } },
199
+ );
200
+ }
201
+ if (path === MASTER_LOCK_PATHS.settings) {
202
+ const body = await readJson(req);
203
+ const idleMs = lock.setIdleMs(Number(body?.idleMs));
204
+ return Response.json({ ok: true, idleMs }, { headers: noStore });
205
+ }
206
+ if (path === MASTER_LOCK_PATHS.change) {
207
+ const body = await readJson(req);
208
+ const result = await lock.change({
209
+ currentVerifier: typeof body?.currentVerifier === "string" ? body.currentVerifier : "",
210
+ kdf: body?.kdf as never,
211
+ verifier: typeof body?.verifier === "string" ? body.verifier : "",
212
+ });
213
+ if (!result.ok) {
214
+ return Response.json({ error: result.reason }, { status: 400, headers: noStore });
215
+ }
216
+ // The rotation locked the site; drop the cookie with it so the page it came from
217
+ // does not sit there holding a token that stopped meaning anything.
218
+ return Response.json(
219
+ { ok: true },
220
+ { headers: { ...noStore, "set-cookie": clearCookie(secure) } },
221
+ );
222
+ }
223
+ return new Response("not found", { status: 404, headers: noStore });
224
+ }
225
+
226
+ return {
227
+ async handle(req: Request): Promise<Response | null> {
228
+ const url = new URL(req.url);
229
+ const own =
230
+ url.pathname === MASTER_LOCK_PREFIX || url.pathname.startsWith(`${MASTER_LOCK_PREFIX}/`);
231
+ const lock = resolve(req);
232
+ if (!lock) {
233
+ // No principal. On the app's own paths the caller is not signed in and the
234
+ // app's sign-in owns the request; on `/__lock/*` there is nobody to unlock
235
+ // FOR, and answering the ordinary refusal keeps that indistinguishable from
236
+ // being locked.
237
+ return own ? refuse() : null;
238
+ }
239
+ if (own) return handleOwnRoute(req, url, lock);
240
+ if (options.allow?.(url, req)) return null;
241
+ if (opens(lock, req)) return null;
242
+
243
+ // A document request is answered IN PLACE with the lock page โ€” same URL, status
244
+ // 200 โ€” so unlocking reloads to where they were going instead of dumping them at
245
+ // the root. Everything else gets the uniform refusal.
246
+ if (wantsDocument(req)) return lockPage(options, lock);
247
+ return refuse();
248
+ },
249
+ };
250
+ }
251
+
252
+ function lockPage(options: LockPageOptions, lock: MasterLock): Response {
253
+ return new Response(lockPageHtml({ ...options, enroll: lock.awaitingEnrollment }), {
254
+ status: 200,
255
+ headers: {
256
+ "content-type": "text/html; charset=utf-8",
257
+ "content-security-policy": LOCK_CSP,
258
+ "x-content-type-options": "nosniff",
259
+ [MASTER_LOCK_STATE_HEADER]: "locked",
260
+ ...noStore,
261
+ },
262
+ });
263
+ }
264
+
265
+ /** Is this the browser asking for a PAGE, as opposed to the app asking for data? */
266
+ function wantsDocument(req: Request): boolean {
267
+ if (req.method !== "GET") return false;
268
+ // `Sec-Fetch-Dest` is the browser's own answer and beats guessing from `Accept`; every
269
+ // engine this fleet runs on sends it. `Accept` is the fallback for curl and for a
270
+ // request that arrived through something that stripped it.
271
+ const dest = req.headers.get("sec-fetch-dest");
272
+ if (dest) return dest === "document" || dest === "iframe";
273
+ return (req.headers.get("accept") ?? "").includes("text/html");
274
+ }
275
+
276
+ async function readJson(req: Request): Promise<Record<string, unknown> | null> {
277
+ try {
278
+ const body = await req.json();
279
+ if (body === null || typeof body !== "object" || Array.isArray(body)) return null;
280
+ return body as Record<string, unknown>;
281
+ } catch {
282
+ return null;
283
+ }
284
+ }
285
+
286
+ function defaultSecure(url: URL, req: Request): boolean {
287
+ const forwarded = req.headers.get("x-forwarded-proto");
288
+ if (forwarded) return forwarded.split(",")[0]?.trim() === "https";
289
+ return url.protocol === "https:";
290
+ }
@@ -0,0 +1,44 @@
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 {
19
+ MASTER_LOCK_PRINCIPAL_KEY_PREFIX,
20
+ MasterLockDirectory,
21
+ type MasterLockDirectoryOptions,
22
+ masterLockPrincipalKey,
23
+ } from "./principals";
24
+ export {
25
+ MasterLock,
26
+ type MasterLockEnrollResult,
27
+ type MasterLockOptions,
28
+ type MasterLockRecord,
29
+ type MasterLockStore,
30
+ type MasterLockUnlockResult,
31
+ readRecord,
32
+ } from "./masterLock";
33
+ export {
34
+ DEFAULT_SEED_IDLE_MS,
35
+ generateStagePassword,
36
+ type MintMasterLockSeedOptions,
37
+ mintMasterLockSeed,
38
+ serializeMasterLockSeed,
39
+ } from "./seed";
40
+ export {
41
+ MASTER_LOCK_SETTING_KEY,
42
+ createKvMasterLockStore,
43
+ createMemoryMasterLockStore,
44
+ } from "./store";
@@ -0,0 +1,88 @@
1
+ /**
2
+ * ๐Ÿ”ด The one test that stops the whole feature from failing silently.
3
+ *
4
+ * `LOCK_SCRIPT` carries a HAND COPY of `deriveMasterLockVerifier`, because the lock page is
5
+ * served to a browser as plain text and cannot import anything. If the two drift, the page
6
+ * derives a verifier that can never match, and the symptom is the owner's correct master
7
+ * password being refused forever โ€” with every health check green, which is precisely how
8
+ * `apps/collections` lost a day in 2026-08-12.
9
+ *
10
+ * So this EVALUATES the function out of the shipped script source and compares it with the
11
+ * TypeScript one on the vault's pinned vector. Change either, and this fails until you
12
+ * change the other.
13
+ */
14
+ import { describe, expect, test } from "bun:test";
15
+ import { MASTER_LOCK_PATHS, type MasterLockKdfParams, deriveMasterLockVerifier } from "cursedbelt-core/master-lock";
16
+ import { LOCK_SCRIPT, LOCK_STYLE, lockPageHtml } from "./lockPage";
17
+
18
+ const KDF: MasterLockKdfParams = {
19
+ v: 1,
20
+ alg: "PBKDF2-SHA256",
21
+ iter: 600_000,
22
+ salt: "AAECAwQFBgcICQoLDA0ODw",
23
+ };
24
+ const PASSWORD = "correct horse battery staple";
25
+
26
+ /**
27
+ * Pull `deriveVerifier` out of the served script.
28
+ *
29
+ * The script assigns it to `globalThis.__masterLockDerive` for exactly this โ€” a seam that
30
+ * costs one line in the browser and is the difference between a pinned copy and a hopeful
31
+ * one. Everything after that assignment touches `document`, so the source is cut there.
32
+ */
33
+ function extractDerive(): (password: string, kdf: MasterLockKdfParams) => Promise<string> {
34
+ const marker = "globalThis.__masterLockDerive = deriveVerifier;";
35
+ const cut = LOCK_SCRIPT.indexOf(marker);
36
+ expect(cut).toBeGreaterThan(0);
37
+ const source = LOCK_SCRIPT.slice(0, cut + marker.length);
38
+ // The prelude reads `document`, which does not exist under `bun test`; the slice above
39
+ // stops before it, but the leading `const` declarations still run โ€” so evaluate in a
40
+ // function scope with those globals stubbed.
41
+ const factory = new Function(
42
+ "document",
43
+ `${source}\nreturn globalThis.__masterLockDerive;`,
44
+ ) as (doc: unknown) => (password: string, kdf: MasterLockKdfParams) => Promise<string>;
45
+ return factory({ getElementById: () => ({ addEventListener() {}, setAttribute() {}, removeAttribute() {} }) });
46
+ }
47
+
48
+ describe("the lock page's hand-copied derivation", () => {
49
+ test("๐Ÿ”ด derives EXACTLY what the TypeScript one does", async () => {
50
+ const pageDerive = extractDerive();
51
+ const fromPage = await pageDerive(PASSWORD, KDF);
52
+ const fromModule = await deriveMasterLockVerifier(PASSWORD, KDF);
53
+ expect(fromPage).toBe(fromModule);
54
+ // And both are still the vault's pinned vector, so a seeded app opens.
55
+ expect(fromPage).toBe("xnUVRdpRFD1tjzd7Il-Jd-YpvfVTGOs-6gAdsf2VfXc");
56
+ });
57
+
58
+ test("agrees on a second, unrelated input too", async () => {
59
+ const pageDerive = extractDerive();
60
+ const kdf = { ...KDF, iter: 1, salt: "DwoNDAsKCQgHBgUEAwIB" };
61
+ expect(await pageDerive("another password", kdf)).toBe(
62
+ await deriveMasterLockVerifier("another password", kdf),
63
+ );
64
+ });
65
+ });
66
+
67
+ describe("the page itself", () => {
68
+ test("names the app, links only same-origin assets, and carries no inline script", () => {
69
+ const html = lockPageHtml({ appLabel: "Collections" });
70
+ expect(html).toContain("<title>Locked ยท Collections</title>");
71
+ expect(html).toContain(`href="${MASTER_LOCK_PATHS.style}"`);
72
+ expect(html).toContain(`src="${MASTER_LOCK_PATHS.script}"`);
73
+ // An inline <script> would need a CSP nonce; the guard's wall forbids it outright.
74
+ expect(html).not.toMatch(/<script(?![^>]*\bsrc=)/);
75
+ expect(html).toContain('name="robots" content="noindex, nofollow"');
76
+ });
77
+
78
+ test("escapes the app label โ€” it is passed in by the consumer", () => {
79
+ const html = lockPageHtml({ appLabel: '<img src=x onerror="alert(1)">' });
80
+ expect(html).not.toContain("<img");
81
+ expect(html).toContain("&lt;img");
82
+ });
83
+
84
+ test("the stylesheet needs no font, image or origin from anywhere", () => {
85
+ expect(LOCK_STYLE).not.toContain("url(");
86
+ expect(LOCK_STYLE).not.toContain("@import");
87
+ });
88
+ });