cursedbelt-server 4.11.1 → 4.13.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 (471) hide show
  1. package/dist/server/_bunOnly.d.ts +1 -0
  2. package/dist/server/_bunOnly.js +12 -0
  3. package/dist/server/activity/index.d.ts +4 -4
  4. package/dist/server/activity/index.js +3 -3
  5. package/dist/server/activity/migrations.d.ts +1 -1
  6. package/dist/server/activity/shareSource.d.ts +1 -1
  7. package/dist/server/activity/shareSource.js +1 -1
  8. package/dist/server/activity/store.js +2 -2
  9. package/dist/server/analytics/index.d.ts +2 -2
  10. package/dist/server/analytics/index.js +2 -2
  11. package/dist/server/app.d.ts +4 -4
  12. package/dist/server/app.js +6 -6
  13. package/dist/server/audit/index.d.ts +2 -2
  14. package/dist/server/audit/index.js +2 -2
  15. package/dist/server/audit/migrations.d.ts +1 -1
  16. package/dist/server/audit/sqliteSink.js +2 -2
  17. package/dist/server/audit/sqliteSink.test.js +1 -1
  18. package/dist/server/auth/csrf.js +1 -1
  19. package/dist/server/auth/fileTokenSigner.d.ts +1 -1
  20. package/dist/server/auth/getOnly.d.ts +1 -1
  21. package/dist/server/auth/getOnly.js +1 -1
  22. package/dist/server/auth/index.d.ts +12 -12
  23. package/dist/server/auth/index.js +11 -11
  24. package/dist/server/auth/jwt.d.ts +1 -1
  25. package/dist/server/auth/middleware.d.ts +1 -1
  26. package/dist/server/auth/middleware.js +3 -3
  27. package/dist/server/auth/sessionStore.d.ts +2 -2
  28. package/dist/server/auth/types.d.ts +1 -1
  29. package/dist/server/bench/assert.d.ts +1 -1
  30. package/dist/server/bench/assert.js +1 -1
  31. package/dist/server/bench/cpuBudget.d.ts +4 -4
  32. package/dist/server/bench/cpuBudget.js +3 -3
  33. package/dist/server/bench/index.d.ts +6 -6
  34. package/dist/server/bench/index.js +6 -6
  35. package/dist/server/bench/recorder.d.ts +2 -2
  36. package/dist/server/bench/recorder.js +1 -1
  37. package/dist/server/bench/runBench.d.ts +2 -2
  38. package/dist/server/ctgr.d.ts +1 -1
  39. package/dist/server/ctgr.js +1 -1
  40. package/dist/server/d1/fakeD1.d.ts +1 -1
  41. package/dist/server/d1/fakeD1.js +1 -1
  42. package/dist/server/d1/index.d.ts +8 -8
  43. package/dist/server/d1/index.js +8 -8
  44. package/dist/server/d1/invocation.d.ts +1 -1
  45. package/dist/server/d1/invocation.js +2 -2
  46. package/dist/server/d1/kysely.d.ts +2 -2
  47. package/dist/server/d1/kysely.js +2 -2
  48. package/dist/server/d1/limits.d.ts +1 -1
  49. package/dist/server/d1/limits.js +1 -1
  50. package/dist/server/d1/local.d.ts +1 -1
  51. package/dist/server/d1/local.js +3 -3
  52. package/dist/server/d1/localBackup.d.ts +1 -1
  53. package/dist/server/d1/localBackup.js +2 -2
  54. package/dist/server/d1/remote.d.ts +1 -1
  55. package/dist/server/d1/remote.js +3 -3
  56. package/dist/server/d1/scheduling.d.ts +1 -1
  57. package/dist/server/d1/values.d.ts +1 -1
  58. package/dist/server/d1/values.js +1 -1
  59. package/dist/server/db/kysely.d.ts +1 -1
  60. package/dist/server/dropzone/index.d.ts +2 -2
  61. package/dist/server/dropzone/index.js +2 -2
  62. package/dist/server/guard/argon2Binding.js +1 -1
  63. package/dist/server/guard/index.d.ts +5 -5
  64. package/dist/server/guard/index.js +5 -5
  65. package/dist/server/guard/kdfWorker.d.ts +1 -1
  66. package/dist/server/guard/kdfWorker.js +3 -3
  67. package/dist/server/guard/kdfWorkerEntry.js +1 -1
  68. package/dist/server/guard/passwordVerifier.js +1 -1
  69. package/dist/server/guard/sessionSigner.d.ts +2 -2
  70. package/dist/server/guard/sessionSigner.js +1 -1
  71. package/dist/server/incidents/checks.d.ts +2 -2
  72. package/dist/server/incidents/index.d.ts +8 -8
  73. package/dist/server/incidents/index.js +8 -8
  74. package/dist/server/incidents/migrations.d.ts +1 -1
  75. package/dist/server/incidents/notifier.d.ts +3 -3
  76. package/dist/server/incidents/reporter.d.ts +1 -1
  77. package/dist/server/incidents/routes.d.ts +4 -4
  78. package/dist/server/incidents/routes.js +1 -1
  79. package/dist/server/incidents/store.d.ts +1 -1
  80. package/dist/server/incidents/store.js +3 -3
  81. package/dist/server/index.d.ts +26 -26
  82. package/dist/server/index.js +23 -23
  83. package/dist/server/jobs/fireQueue.d.ts +1 -1
  84. package/dist/server/jobs/fireQueue.js +3 -3
  85. package/dist/server/jobs/index.d.ts +7 -7
  86. package/dist/server/jobs/index.js +7 -7
  87. package/dist/server/jobs/logger.d.ts +1 -1
  88. package/dist/server/jobs/polling.d.ts +1 -1
  89. package/dist/server/jobs/polling.js +1 -1
  90. package/dist/server/jobs/runner.d.ts +1 -1
  91. package/dist/server/jobs/runner.js +5 -5
  92. package/dist/server/master-lock/accountsPage.d.ts +1 -1
  93. package/dist/server/master-lock/accountsPage.js +1 -1
  94. package/dist/server/master-lock/guard.d.ts +2 -2
  95. package/dist/server/master-lock/guard.js +2 -2
  96. package/dist/server/master-lock/index.d.ts +7 -7
  97. package/dist/server/master-lock/index.js +7 -7
  98. package/dist/server/master-lock/masterLock.d.ts +26 -49
  99. package/dist/server/master-lock/masterLock.js +48 -15
  100. package/dist/server/master-lock/principals.d.ts +1 -1
  101. package/dist/server/master-lock/principals.js +1 -1
  102. package/dist/server/master-lock/seed.d.ts +1 -1
  103. package/dist/server/master-lock/seed.js +1 -1
  104. package/dist/server/master-lock/store.d.ts +6 -1
  105. package/dist/server/master-lock/store.js +5 -0
  106. package/dist/server/media-bun/index.d.ts +3 -3
  107. package/dist/server/media-bun/index.js +3 -3
  108. package/dist/server/media-bun/video.d.ts +1 -1
  109. package/dist/server/media-bun/video.js +1 -1
  110. package/dist/server/metrics/telemetrySink.d.ts +1 -1
  111. package/dist/server/metrics/telemetrySink.js +1 -1
  112. package/dist/server/middleware/correlationId.d.ts +1 -1
  113. package/dist/server/middleware/errorEnvelope.d.ts +1 -1
  114. package/dist/server/middleware/errorEnvelope.js +1 -1
  115. package/dist/server/middleware/index.d.ts +10 -10
  116. package/dist/server/middleware/index.js +10 -10
  117. package/dist/server/middleware/rateLimit.js +1 -1
  118. package/dist/server/middleware/requestLogger.d.ts +3 -3
  119. package/dist/server/middleware/requestLogger.js +3 -3
  120. package/dist/server/middleware/zValidator.d.ts +1 -1
  121. package/dist/server/middleware/zValidator.js +1 -1
  122. package/dist/server/migration/runner.d.ts +1 -1
  123. package/dist/server/net/outboundGuard.js +1 -1
  124. package/dist/server/notifications/index.d.ts +7 -7
  125. package/dist/server/notifications/index.js +4 -4
  126. package/dist/server/notifications/migrations.d.ts +1 -1
  127. package/dist/server/notifications/routes.d.ts +1 -1
  128. package/dist/server/notifications/service.d.ts +1 -1
  129. package/dist/server/notifications/service.js +2 -2
  130. package/dist/server/resume/index.d.ts +2 -2
  131. package/dist/server/resume/index.js +2 -2
  132. package/dist/server/resume/routes.d.ts +1 -1
  133. package/dist/server/resume/routes.js +1 -1
  134. package/dist/server/retention/archive.js +1 -1
  135. package/dist/server/retention/fleet.d.ts +2 -2
  136. package/dist/server/retention/fleet.js +2 -2
  137. package/dist/server/retention/index.d.ts +6 -6
  138. package/dist/server/retention/index.js +6 -6
  139. package/dist/server/retention/store.d.ts +1 -1
  140. package/dist/server/retention/store.js +2 -2
  141. package/dist/server/retention/sweeper.d.ts +3 -3
  142. package/dist/server/retention/sweeper.js +2 -2
  143. package/dist/server/retention/target.d.ts +3 -3
  144. package/dist/server/retention/target.js +2 -2
  145. package/dist/server/search/index.d.ts +2 -2
  146. package/dist/server/search/index.js +2 -2
  147. package/dist/server/search/pgSearchProvider.js +2 -2
  148. package/dist/server/search/pgSearchProvider.pg.test.js +2 -2
  149. package/dist/server/sharing/index.d.ts +8 -8
  150. package/dist/server/sharing/index.js +5 -5
  151. package/dist/server/sharing/migrations.d.ts +1 -1
  152. package/dist/server/sharing/router.d.ts +1 -1
  153. package/dist/server/sharing/sql.js +1 -1
  154. package/dist/server/sharing/store.js +3 -3
  155. package/dist/server/sqlite/browse.js +1 -1
  156. package/dist/server/sqlite/export.js +1 -1
  157. package/dist/server/sqlite/import.js +1 -1
  158. package/dist/server/sqlite/index.d.ts +9 -9
  159. package/dist/server/sqlite/index.js +9 -9
  160. package/dist/server/sqlite/stats.js +1 -1
  161. package/dist/server/sqlite/viewer.js +2 -2
  162. package/dist/server/storage/binaryServerProcessing.d.ts +1 -1
  163. package/dist/server/storage/binaryServerProcessing.js +2 -2
  164. package/dist/server/storage/binaryServerStore.d.ts +2 -2
  165. package/dist/server/storage/binaryStore.d.ts +1 -1
  166. package/dist/server/storage/binaryStoreFake.d.ts +1 -1
  167. package/dist/server/storage/diskStore.d.ts +1 -1
  168. package/dist/server/storage/files/adapter.d.ts +4 -4
  169. package/dist/server/storage/files/adapter.js +1 -1
  170. package/dist/server/storage/files/catalogue.d.ts +3 -3
  171. package/dist/server/storage/files/catalogue.js +6 -6
  172. package/dist/server/storage/files/chunkedUpload.d.ts +3 -3
  173. package/dist/server/storage/files/chunkedUpload.js +1 -1
  174. package/dist/server/storage/files/fileRoutes.d.ts +4 -4
  175. package/dist/server/storage/files/fileRoutes.js +3 -3
  176. package/dist/server/storage/files/index.d.ts +9 -9
  177. package/dist/server/storage/files/index.js +8 -8
  178. package/dist/server/storage/files/migrations.d.ts +1 -1
  179. package/dist/server/storage/files/stack.d.ts +7 -7
  180. package/dist/server/storage/files/stack.js +10 -10
  181. package/dist/server/storage/files/storageOps.d.ts +1 -1
  182. package/dist/server/storage/files/types.d.ts +1 -1
  183. package/dist/server/storage/imageThumbnail.js +1 -1
  184. package/dist/server/storage/index.d.ts +15 -15
  185. package/dist/server/storage/index.js +14 -14
  186. package/dist/server/storage/mountMediaRoutes.d.ts +3 -3
  187. package/dist/server/storage/mountMediaRoutes.js +2 -2
  188. package/dist/server/storage/objectStoreMediaStore.d.ts +1 -1
  189. package/dist/server/storage/s3ObjectStore.js +1 -1
  190. package/dist/server/storage/sources.d.ts +2 -2
  191. package/dist/server/storage/sources.js +2 -2
  192. package/dist/server/storage/streamRange.d.ts +1 -1
  193. package/dist/server/storage/streamToken.js +1 -1
  194. package/dist/server/storage/videoProcessor.d.ts +1 -1
  195. package/dist/server/storage/videoProcessor.js +2 -2
  196. package/dist/server/sync/engine.d.ts +2 -2
  197. package/dist/server/sync/engine.js +1 -1
  198. package/dist/server/sync/http.d.ts +4 -4
  199. package/dist/server/sync/http.js +3 -3
  200. package/dist/server/sync/index.d.ts +11 -11
  201. package/dist/server/sync/index.js +11 -11
  202. package/dist/server/sync/opLog.d.ts +1 -1
  203. package/dist/server/sync/signal.d.ts +1 -1
  204. package/dist/server/sync/timer.d.ts +3 -3
  205. package/dist/server/sync/timer.js +1 -1
  206. package/dist/server/ws/eventBus.d.ts +1 -1
  207. package/dist/server/ws/hub.d.ts +1 -1
  208. package/dist/server/ws/hub.js +3 -3
  209. package/dist/server/ws/index.d.ts +5 -5
  210. package/dist/server/ws/index.js +4 -4
  211. package/dist/server/ws/presence.d.ts +1 -1
  212. package/dist/server/ws/room.d.ts +1 -1
  213. package/dist/server/ws/types.d.ts +1 -1
  214. package/package.json +9 -9
  215. package/src/publicSurface.spec.ts +1 -1
  216. package/src/server/_bunOnly.ts +15 -0
  217. package/src/server/activity/index.ts +4 -4
  218. package/src/server/activity/migrations.ts +1 -1
  219. package/src/server/activity/shareSource.ts +2 -2
  220. package/src/server/activity/store.spec.ts +3 -3
  221. package/src/server/activity/store.ts +2 -2
  222. package/src/server/analytics/index.ts +2 -2
  223. package/src/server/analytics/injectedScripts.spec.ts +3 -3
  224. package/src/server/analytics/webAnalytics.spec.ts +1 -1
  225. package/src/server/app.spec.ts +9 -9
  226. package/src/server/app.ts +7 -7
  227. package/src/server/audit/index.ts +2 -2
  228. package/src/server/audit/migrations.ts +1 -1
  229. package/src/server/audit/sqliteSink.test.ts +1 -1
  230. package/src/server/audit/sqliteSink.ts +2 -2
  231. package/src/server/auth/cookie.spec.ts +1 -1
  232. package/src/server/auth/csrf.spec.ts +1 -1
  233. package/src/server/auth/csrf.ts +1 -1
  234. package/src/server/auth/fileTokenSigner.ts +1 -1
  235. package/src/server/auth/getOnly.spec.ts +2 -2
  236. package/src/server/auth/getOnly.ts +2 -2
  237. package/src/server/auth/index.ts +12 -12
  238. package/src/server/auth/jwt.spec.ts +2 -2
  239. package/src/server/auth/jwt.ts +1 -1
  240. package/src/server/auth/loginThrottle.spec.ts +1 -1
  241. package/src/server/auth/middleware.spec.ts +7 -7
  242. package/src/server/auth/middleware.ts +4 -4
  243. package/src/server/auth/password.spec.ts +1 -1
  244. package/src/server/auth/sessionStore.spec.ts +3 -3
  245. package/src/server/auth/sessionStore.ts +3 -3
  246. package/src/server/auth/totp.spec.ts +1 -1
  247. package/src/server/auth/types.ts +1 -1
  248. package/src/server/auth/vault.spec.ts +1 -1
  249. package/src/server/bench/assert.ts +2 -2
  250. package/src/server/bench/budget.spec.ts +2 -2
  251. package/src/server/bench/cpuBudget.spec.ts +6 -6
  252. package/src/server/bench/cpuBudget.ts +5 -5
  253. package/src/server/bench/cpuClock.spec.ts +2 -2
  254. package/src/server/bench/index.ts +6 -6
  255. package/src/server/bench/recorder.ts +2 -2
  256. package/src/server/bench/runBench.ts +2 -2
  257. package/src/server/ctgr.spec.ts +2 -2
  258. package/src/server/ctgr.ts +2 -2
  259. package/src/server/d1/backup.spec.ts +2 -2
  260. package/src/server/d1/fakeD1.ts +2 -2
  261. package/src/server/d1/index.ts +8 -8
  262. package/src/server/d1/invocation.spec.ts +6 -6
  263. package/src/server/d1/invocation.ts +2 -2
  264. package/src/server/d1/kysely.spec.ts +5 -5
  265. package/src/server/d1/kysely.ts +3 -3
  266. package/src/server/d1/limits.spec.ts +3 -3
  267. package/src/server/d1/limits.ts +1 -1
  268. package/src/server/d1/local.ts +3 -3
  269. package/src/server/d1/localBackup.ts +3 -3
  270. package/src/server/d1/remote.ts +3 -3
  271. package/src/server/d1/sameShape.spec.ts +4 -4
  272. package/src/server/d1/scheduling.spec.ts +1 -1
  273. package/src/server/d1/scheduling.ts +1 -1
  274. package/src/server/d1/values.ts +1 -1
  275. package/src/server/db/kysely.spec.ts +1 -1
  276. package/src/server/db/kysely.ts +1 -1
  277. package/src/server/dropzone/index.ts +2 -2
  278. package/src/server/dropzone/scan.spec.ts +1 -1
  279. package/src/server/errors.spec.ts +1 -1
  280. package/src/server/guard/argon2Binding.spec.ts +1 -1
  281. package/src/server/guard/argon2Binding.ts +1 -1
  282. package/src/server/guard/argon2Kat.spec.ts +1 -1
  283. package/src/server/guard/guard.spec.ts +3 -3
  284. package/src/server/guard/index.ts +5 -5
  285. package/src/server/guard/kdfWorker.spec.ts +3 -3
  286. package/src/server/guard/kdfWorker.ts +3 -3
  287. package/src/server/guard/kdfWorkerEntry.ts +1 -1
  288. package/src/server/guard/passwordVerifier.spec.ts +1 -1
  289. package/src/server/guard/passwordVerifier.ts +1 -1
  290. package/src/server/guard/revocationStore.spec.ts +2 -2
  291. package/src/server/guard/sessionSigner.ts +3 -3
  292. package/src/server/incidents/checks.ts +2 -2
  293. package/src/server/incidents/incidents.spec.ts +7 -7
  294. package/src/server/incidents/index.ts +8 -8
  295. package/src/server/incidents/migrations.ts +1 -1
  296. package/src/server/incidents/notifier.ts +3 -3
  297. package/src/server/incidents/reporter.ts +1 -1
  298. package/src/server/incidents/routes.ts +5 -5
  299. package/src/server/incidents/store.ts +3 -3
  300. package/src/server/index.ts +26 -26
  301. package/src/server/jobs/fireQueue.spec.ts +1 -1
  302. package/src/server/jobs/fireQueue.ts +3 -3
  303. package/src/server/jobs/index.ts +7 -7
  304. package/src/server/jobs/logger.ts +1 -1
  305. package/src/server/jobs/plainjobAdapter.spec.ts +1 -1
  306. package/src/server/jobs/polling.spec.ts +1 -1
  307. package/src/server/jobs/polling.ts +2 -2
  308. package/src/server/jobs/runner.spec.ts +3 -3
  309. package/src/server/jobs/runner.ts +5 -5
  310. package/src/server/jobs/timeout.spec.ts +1 -1
  311. package/src/server/master-lock/accounts.spec.ts +2 -2
  312. package/src/server/master-lock/accountsPage.spec.ts +4 -4
  313. package/src/server/master-lock/accountsPage.ts +1 -1
  314. package/src/server/master-lock/beaconNeverReachesTheWall.spec.ts +4 -4
  315. package/src/server/master-lock/guard.spec.ts +3 -3
  316. package/src/server/master-lock/guard.ts +3 -3
  317. package/src/server/master-lock/index.ts +8 -7
  318. package/src/server/master-lock/lockPage.spec.ts +2 -2
  319. package/src/server/master-lock/masterLock.spec.ts +59 -2
  320. package/src/server/master-lock/masterLock.ts +76 -14
  321. package/src/server/master-lock/principals.spec.ts +4 -4
  322. package/src/server/master-lock/principals.ts +1 -1
  323. package/src/server/master-lock/seed.spec.ts +3 -3
  324. package/src/server/master-lock/seed.ts +1 -1
  325. package/src/server/master-lock/store.ts +7 -1
  326. package/src/server/media-bun/image.spec.ts +1 -1
  327. package/src/server/media-bun/index.ts +3 -3
  328. package/src/server/media-bun/streaming.spec.ts +1 -1
  329. package/src/server/media-bun/video.spec.ts +1 -1
  330. package/src/server/media-bun/video.ts +1 -1
  331. package/src/server/metrics/normalizeRoute.spec.ts +1 -1
  332. package/src/server/metrics/telemetrySink.spec.ts +3 -3
  333. package/src/server/metrics/telemetrySink.ts +1 -1
  334. package/src/server/middleware/bodyLimit.spec.ts +1 -1
  335. package/src/server/middleware/correlationId.ts +1 -1
  336. package/src/server/middleware/corsAllowlist.spec.ts +2 -2
  337. package/src/server/middleware/errorEnvelope.ts +2 -2
  338. package/src/server/middleware/index.ts +10 -10
  339. package/src/server/middleware/localOrigin.spec.ts +1 -1
  340. package/src/server/middleware/rateLimit.spec.ts +1 -1
  341. package/src/server/middleware/rateLimit.ts +1 -1
  342. package/src/server/middleware/requestLogger.ts +5 -5
  343. package/src/server/middleware/zValidator.spec.ts +4 -4
  344. package/src/server/middleware/zValidator.ts +2 -2
  345. package/src/server/migration/runner.spec.ts +2 -2
  346. package/src/server/migration/runner.ts +1 -1
  347. package/src/server/net/outboundGuard.spec.ts +1 -1
  348. package/src/server/net/outboundGuard.ts +1 -1
  349. package/src/server/notifications/index.ts +7 -7
  350. package/src/server/notifications/migrations.ts +1 -1
  351. package/src/server/notifications/routes.ts +1 -1
  352. package/src/server/notifications/service.spec.ts +3 -3
  353. package/src/server/notifications/service.ts +2 -2
  354. package/src/server/paths/appDataHome.spec.ts +1 -1
  355. package/src/server/resume/index.ts +2 -2
  356. package/src/server/resume/resume.spec.ts +2 -2
  357. package/src/server/resume/routes.ts +1 -1
  358. package/src/server/retention/archive.spec.ts +2 -2
  359. package/src/server/retention/archive.ts +1 -1
  360. package/src/server/retention/fleet.ts +4 -4
  361. package/src/server/retention/index.ts +6 -6
  362. package/src/server/retention/policy.spec.ts +1 -1
  363. package/src/server/retention/store.spec.ts +3 -3
  364. package/src/server/retention/store.ts +3 -3
  365. package/src/server/retention/sweeper.spec.ts +5 -5
  366. package/src/server/retention/sweeper.ts +5 -5
  367. package/src/server/retention/target.spec.ts +3 -3
  368. package/src/server/retention/target.ts +5 -5
  369. package/src/server/search/index.ts +2 -2
  370. package/src/server/search/pgSearchProvider.pg.test.ts +2 -2
  371. package/src/server/search/pgSearchProvider.ts +2 -2
  372. package/src/server/sharing/index.ts +8 -8
  373. package/src/server/sharing/migrations.ts +1 -1
  374. package/src/server/sharing/router.spec.ts +2 -2
  375. package/src/server/sharing/router.ts +1 -1
  376. package/src/server/sharing/sql.ts +1 -1
  377. package/src/server/sharing/store.spec.ts +2 -2
  378. package/src/server/sharing/store.ts +3 -3
  379. package/src/server/sqlite/backup.spec.ts +1 -1
  380. package/src/server/sqlite/browse.spec.ts +1 -1
  381. package/src/server/sqlite/browse.ts +1 -1
  382. package/src/server/sqlite/export.spec.ts +2 -2
  383. package/src/server/sqlite/export.ts +1 -1
  384. package/src/server/sqlite/fts5.spec.ts +1 -1
  385. package/src/server/sqlite/health.spec.ts +1 -1
  386. package/src/server/sqlite/import.spec.ts +3 -3
  387. package/src/server/sqlite/import.ts +1 -1
  388. package/src/server/sqlite/index.ts +9 -9
  389. package/src/server/sqlite/readonlySql.spec.ts +1 -1
  390. package/src/server/sqlite/stats.spec.ts +1 -1
  391. package/src/server/sqlite/stats.ts +1 -1
  392. package/src/server/sqlite/viewer.spec.ts +1 -1
  393. package/src/server/sqlite/viewer.ts +2 -2
  394. package/src/server/storage/binaryServerProcessing.integration.spec.ts +2 -2
  395. package/src/server/storage/binaryServerProcessing.spec.ts +2 -2
  396. package/src/server/storage/binaryServerProcessing.ts +3 -3
  397. package/src/server/storage/binaryServerStore.ts +3 -3
  398. package/src/server/storage/binaryStore.spec.ts +2 -2
  399. package/src/server/storage/binaryStore.ts +1 -1
  400. package/src/server/storage/binaryStoreFake.ts +1 -1
  401. package/src/server/storage/chunked.integration.spec.ts +4 -4
  402. package/src/server/storage/diskStore.ts +1 -1
  403. package/src/server/storage/fileUploadClient-chunked.integration.spec.ts +6 -6
  404. package/src/server/storage/fileUploadClient.integration.spec.ts +7 -7
  405. package/src/server/storage/files/adapter.photo.display.spec.ts +10 -10
  406. package/src/server/storage/files/adapter.photo.spec.ts +9 -9
  407. package/src/server/storage/files/adapter.spec.ts +8 -8
  408. package/src/server/storage/files/adapter.ts +5 -5
  409. package/src/server/storage/files/catalogue.media-host.spec.ts +6 -6
  410. package/src/server/storage/files/catalogue.orderings.spec.ts +6 -6
  411. package/src/server/storage/files/catalogue.recover.spec.ts +5 -5
  412. package/src/server/storage/files/catalogue.sort.spec.ts +6 -6
  413. package/src/server/storage/files/catalogue.spec.ts +7 -7
  414. package/src/server/storage/files/catalogue.trash-import.spec.ts +6 -6
  415. package/src/server/storage/files/catalogue.ts +6 -6
  416. package/src/server/storage/files/chunkedUpload.spec.ts +7 -7
  417. package/src/server/storage/files/chunkedUpload.ts +4 -4
  418. package/src/server/storage/files/collections.spec.ts +6 -6
  419. package/src/server/storage/files/fileRoutes.replace.spec.ts +8 -8
  420. package/src/server/storage/files/fileRoutes.spec.ts +8 -8
  421. package/src/server/storage/files/fileRoutes.ts +6 -6
  422. package/src/server/storage/files/index.ts +9 -9
  423. package/src/server/storage/files/migrations.ts +1 -1
  424. package/src/server/storage/files/stack.spec.ts +5 -5
  425. package/src/server/storage/files/stack.ts +12 -12
  426. package/src/server/storage/files/storageOps.spec.ts +4 -4
  427. package/src/server/storage/files/storageOps.ts +1 -1
  428. package/src/server/storage/files/types.ts +1 -1
  429. package/src/server/storage/imageThumbnail.spec.ts +2 -2
  430. package/src/server/storage/imageThumbnail.ts +1 -1
  431. package/src/server/storage/index.ts +15 -15
  432. package/src/server/storage/mountMediaRoutes.ts +4 -4
  433. package/src/server/storage/objectStore.spec.ts +1 -1
  434. package/src/server/storage/objectStoreMediaStore.ts +1 -1
  435. package/src/server/storage/s3ObjectStore.ts +1 -1
  436. package/src/server/storage/sources.ts +3 -3
  437. package/src/server/storage/storage.spec.ts +6 -6
  438. package/src/server/storage/streamRange.ts +1 -1
  439. package/src/server/storage/streamToken.ts +1 -1
  440. package/src/server/storage/transcodeJobs.spec.ts +1 -1
  441. package/src/server/storage/videoDerivativeKeys.spec.ts +1 -1
  442. package/src/server/storage/videoProcessor.posterOnly.spec.ts +4 -4
  443. package/src/server/storage/videoProcessor.ts +2 -2
  444. package/src/server/sync/alarm.spec.ts +1 -1
  445. package/src/server/sync/commands.spec.ts +1 -1
  446. package/src/server/sync/engine.spec.ts +4 -4
  447. package/src/server/sync/engine.ts +2 -2
  448. package/src/server/sync/http.ts +5 -5
  449. package/src/server/sync/httpRemote.spec.ts +1 -1
  450. package/src/server/sync/index.ts +11 -11
  451. package/src/server/sync/opLog.spec.ts +1 -1
  452. package/src/server/sync/opLog.ts +1 -1
  453. package/src/server/sync/planner.spec.ts +1 -1
  454. package/src/server/sync/signal.spec.ts +1 -1
  455. package/src/server/sync/signal.ts +1 -1
  456. package/src/server/sync/status.spec.ts +1 -1
  457. package/src/server/sync/timer.spec.ts +3 -3
  458. package/src/server/sync/timer.ts +3 -3
  459. package/src/server/sync/tokens.spec.ts +1 -1
  460. package/src/server/ws/eventBus.spec.ts +2 -2
  461. package/src/server/ws/eventBus.ts +1 -1
  462. package/src/server/ws/hub.integration.spec.ts +5 -5
  463. package/src/server/ws/hub.spec.ts +3 -3
  464. package/src/server/ws/hub.ts +4 -4
  465. package/src/server/ws/index.ts +5 -5
  466. package/src/server/ws/presence.spec.ts +3 -3
  467. package/src/server/ws/presence.ts +1 -1
  468. package/src/server/ws/room.spec.ts +2 -2
  469. package/src/server/ws/room.ts +1 -1
  470. package/src/server/ws/types.ts +1 -1
  471. package/src/testTmpRoot.spec.ts +1 -1
@@ -33,7 +33,7 @@
33
33
  * only be watching one of them.
34
34
  */
35
35
  import { MASTER_LOCK_PATHS, MASTER_LOCK_PREFIX } from "cursedbelt-core/master-lock";
36
- import { MASTER_LOCK_DERIVE_SOURCE } from "./lockPage";
36
+ import { MASTER_LOCK_DERIVE_SOURCE } from "./lockPage.js";
37
37
  /**
38
38
  * The page's own two assets.
39
39
  *
@@ -1,5 +1,5 @@
1
- import { type LockPageOptions } from "./lockPage";
2
- import type { MasterLock } from "./masterLock";
1
+ import { type LockPageOptions } from "./lockPage.js";
2
+ import type { MasterLock } from "./masterLock.js";
3
3
  export interface MasterLockGuardOptions extends LockPageOptions {
4
4
  /**
5
5
  * The lock this guard enforces.
@@ -12,8 +12,8 @@
12
12
  * second wall, not a replacement for the first.
13
13
  */
14
14
  import { MASTER_LOCK_PATHS, MASTER_LOCK_PREFIX, MASTER_LOCK_STATE_HEADER, masterLockCookieName, } from "cursedbelt-core/master-lock";
15
- import { MASTER_LOCK_ACCOUNTS_PAGE_PATHS, MASTER_LOCK_ACCOUNTS_SCRIPT, MASTER_LOCK_ACCOUNTS_STYLE, accountsPageHtml, } from "./accountsPage";
16
- import { LOCK_SCRIPT, LOCK_STYLE, lockPageHtml } from "./lockPage";
15
+ import { MASTER_LOCK_ACCOUNTS_PAGE_PATHS, MASTER_LOCK_ACCOUNTS_SCRIPT, MASTER_LOCK_ACCOUNTS_STYLE, accountsPageHtml, } from "./accountsPage.js";
16
+ import { LOCK_SCRIPT, LOCK_STYLE, lockPageHtml } from "./lockPage.js";
17
17
  const noStore = { "cache-control": "no-store, no-cache, must-revalidate", pragma: "no-cache" };
18
18
  /**
19
19
  * The lock page's own CSP. Strict enough to stand in front of `apps/vault`'s wall without
@@ -20,10 +20,10 @@
20
20
  * server-rendered surface under `/__lock/` reaches every app that mounts the guard with no
21
21
  * bundle, no framework version to migrate, and no app-local copy to drift.
22
22
  */
23
- export { MASTER_LOCK_ACCOUNTS_PAGE_PATHS, MASTER_LOCK_ACCOUNTS_SCRIPT, MASTER_LOCK_ACCOUNTS_STYLE, MASTER_LOCK_MIN_PASSWORD_LENGTH, accountsPageHtml, } from "./accountsPage";
24
- export { MASTER_LOCK_CSP, type MasterLockGuard, type MasterLockGuardOptions, createMasterLockGuard, } from "./guard";
25
- export { LOCK_SCRIPT, LOCK_STYLE, MASTER_LOCK_DERIVE_SOURCE, type LockPageOptions, lockPageHtml, } from "./lockPage";
26
- export { MASTER_LOCK_PRINCIPAL_KEY_PREFIX, MasterLockDirectory, type MasterLockDirectoryOptions, masterLockPrincipalKey, } from "./principals";
27
- export { FIRST_ACCOUNT_ID, MasterLock, type MasterLockAccount, type MasterLockAddAccountResult, type MasterLockEnrollResult, type MasterLockOptions, type MasterLockRecord, type MasterLockStore, type MasterLockUnlockResult, readRecord, } from "./masterLock";
28
- export { DEFAULT_SEED_IDLE_MS, generateStagePassword, type MintMasterLockSeedOptions, mintMasterLockSeed, serializeMasterLockSeed, } from "./seed";
29
- export { MASTER_LOCK_SETTING_KEY, createKvMasterLockStore, createMemoryMasterLockStore, } from "./store";
23
+ export { MASTER_LOCK_ACCOUNTS_PAGE_PATHS, MASTER_LOCK_ACCOUNTS_SCRIPT, MASTER_LOCK_ACCOUNTS_STYLE, MASTER_LOCK_MIN_PASSWORD_LENGTH, accountsPageHtml, } from "./accountsPage.js";
24
+ export { MASTER_LOCK_CSP, type MasterLockGuard, type MasterLockGuardOptions, createMasterLockGuard, } from "./guard.js";
25
+ export { LOCK_SCRIPT, LOCK_STYLE, MASTER_LOCK_DERIVE_SOURCE, type LockPageOptions, lockPageHtml, } from "./lockPage.js";
26
+ export { MASTER_LOCK_PRINCIPAL_KEY_PREFIX, MasterLockDirectory, type MasterLockDirectoryOptions, masterLockPrincipalKey, } from "./principals.js";
27
+ export { FIRST_ACCOUNT_ID, MasterLock, type MasterLockAccount, type MasterLockAddAccountResult, type MasterLockEnrollResult, type MasterLockOptions, type MasterLockRecord, type MasterLockStore, type MasterLockUnlockResult, readRecord, } from "./masterLock.js";
28
+ export { DEFAULT_SEED_IDLE_MS, generateStagePassword, type MintMasterLockSeedOptions, mintMasterLockSeed, serializeMasterLockSeed, } from "./seed.js";
29
+ export { MASTER_LOCK_SETTING_KEY, MASTER_LOCK_STATE_KEY, createKvMasterLockStore, createMemoryMasterLockStore, } from "./store.js";
@@ -20,10 +20,10 @@
20
20
  * server-rendered surface under `/__lock/` reaches every app that mounts the guard with no
21
21
  * bundle, no framework version to migrate, and no app-local copy to drift.
22
22
  */
23
- export { MASTER_LOCK_ACCOUNTS_PAGE_PATHS, MASTER_LOCK_ACCOUNTS_SCRIPT, MASTER_LOCK_ACCOUNTS_STYLE, MASTER_LOCK_MIN_PASSWORD_LENGTH, accountsPageHtml, } from "./accountsPage";
24
- export { MASTER_LOCK_CSP, createMasterLockGuard, } from "./guard";
25
- export { LOCK_SCRIPT, LOCK_STYLE, MASTER_LOCK_DERIVE_SOURCE, lockPageHtml, } from "./lockPage";
26
- export { MASTER_LOCK_PRINCIPAL_KEY_PREFIX, MasterLockDirectory, masterLockPrincipalKey, } from "./principals";
27
- export { FIRST_ACCOUNT_ID, MasterLock, readRecord, } from "./masterLock";
28
- export { DEFAULT_SEED_IDLE_MS, generateStagePassword, mintMasterLockSeed, serializeMasterLockSeed, } from "./seed";
29
- export { MASTER_LOCK_SETTING_KEY, createKvMasterLockStore, createMemoryMasterLockStore, } from "./store";
23
+ export { MASTER_LOCK_ACCOUNTS_PAGE_PATHS, MASTER_LOCK_ACCOUNTS_SCRIPT, MASTER_LOCK_ACCOUNTS_STYLE, MASTER_LOCK_MIN_PASSWORD_LENGTH, accountsPageHtml, } from "./accountsPage.js";
24
+ export { MASTER_LOCK_CSP, createMasterLockGuard, } from "./guard.js";
25
+ export { LOCK_SCRIPT, LOCK_STYLE, MASTER_LOCK_DERIVE_SOURCE, lockPageHtml, } from "./lockPage.js";
26
+ export { MASTER_LOCK_PRINCIPAL_KEY_PREFIX, MasterLockDirectory, masterLockPrincipalKey, } from "./principals.js";
27
+ export { FIRST_ACCOUNT_ID, MasterLock, readRecord, } from "./masterLock.js";
28
+ export { DEFAULT_SEED_IDLE_MS, generateStagePassword, mintMasterLockSeed, serializeMasterLockSeed, } from "./seed.js";
29
+ export { MASTER_LOCK_SETTING_KEY, MASTER_LOCK_STATE_KEY, createKvMasterLockStore, createMemoryMasterLockStore, } from "./store.js";
@@ -1,52 +1,3 @@
1
- /**
2
- * The sitewide MASTER LOCK — a second wall, in front of a whole app, that the app's own
3
- * sign-in cannot open.
4
- *
5
- * ── What the owner asked for (2026-08-15, their words) ──────────────────────
6
- * *"I want to be able to share my laptop without certain apps being at risk of other people
7
- * viewing them while I am still logged in … Until the master password is entered a user
8
- * logged in as me cannot see anything about the site, not even text or images, nothing at
9
- * all."*
10
- *
11
- * So: standard auth still happens FIRST and is unchanged. This sits behind it. A request
12
- * that has passed the app's own gate and carries no live unlock is answered with the lock
13
- * page (for a document) or a 401 (for anything else) — never with the app.
14
- *
15
- * ── 🔴 BE HONEST ABOUT WHAT THIS BUYS ───────────────────────────────────────
16
- * In `apps/vault` the master password IS the encryption key: the server holds ciphertext
17
- * and a total breach yields noise. **This cannot work that way, and must never be described
18
- * as if it does.** The apps it guards serve their bytes to browsers; those bytes are
19
- * plaintext at rest by nature. This is a LOCK ON THE DOOR — an unlocked laptop, a borrowed
20
- * phone, a hijacked session still cannot open the UI. The owner said the same thing back:
21
- * *"I am aware that a tech savvy user of my laptop could still get around it by changing
22
- * code. This work does not worry about that, we are only securing app UI access."*
23
- * The same distinction is written in `fleet/rules/owner-uis-are-one-ui.md` and in
24
- * binary-server's `src/admin/passcode.ts`, which is this feature's nearest relative.
25
- *
26
- * ── The four decisions that are not obvious ─────────────────────────────────
27
- *
28
- * **1. Sessions live in MEMORY, so a restart re-locks.** The correct default for a surface
29
- * whose whole risk model is "the browser is somewhere it should not be". Same call as the
30
- * inspector passcode.
31
- *
32
- * **2. The idle clock is slid by INTERACTION, never by traffic.** The owner said *"if I
33
- * leave the site up but don't interact with it"* — and every app here polls. Sliding on any
34
- * request would let a background refetch hold the lock open all night with nobody at the
35
- * desk, which is exactly the scenario the feature exists for. So an ordinary request only
36
- * READS the clock; `touch()` — reached solely by `POST /__lock/ping`, which the browser
37
- * sends on real pointer/key/touch activity — is the only thing that moves it.
38
- *
39
- * **3. The password is never stored, and this process never sees it.** The browser derives
40
- * `verifier = PBKDF2(KEK ‖ password)` (see `../../core/master-lock/kdf.ts`); the record
41
- * holds `argon2id(verifier)`. It is seeded from the vault's OWN record so the owner's
42
- * existing vault master password works on day one without anyone knowing it.
43
- *
44
- * **4. Every refusal is the same refusal.** Wrong verifier, throttled, unconfigured and
45
- * "no unlock" answer with the identical status and body, and a dummy argon2 verify runs on
46
- * every path so the TIMING does not separate them either. The one thing that varies is the
47
- * `retryAfterMs` a throttled caller is told — which they already know, since they are the
48
- * one who has been guessing.
49
- */
50
1
  import { type MasterLockAccountSummary, type MasterLockKdfParams, type MasterLockStatus } from "cursedbelt-core/master-lock";
51
2
  /**
52
3
  * ONE master password, and therefore one TENANT of the app behind it.
@@ -127,8 +78,29 @@ export interface MasterLockStore {
127
78
  read(): string | null;
128
79
  write(json: string): void;
129
80
  }
81
+ /**
82
+ * Where the LIVE state goes when the process that holds it does not live — the unlocks that are
83
+ * open and the throttle's failure count. Synchronous by the same contract as
84
+ * {@link MasterLockStore}, so a Worker satisfies it from its per-invocation snapshot exactly as
85
+ * it does the record.
86
+ *
87
+ * 🔴 Why it exists (2026-09-22, the night `collections` moved to a Worker): the lock kept its
88
+ * unlocks in a `Map` on the instance, and a Worker builds the instance once per REQUEST. The
89
+ * owner typed the right password, got a 200 and a cookie, and the very next request was locked
90
+ * again — measured on `collections-stage` by `worker-stage-walk.ts`. The throttle had the same
91
+ * shape: every guess met a fresh counter, so nothing slowed a brute force at all.
92
+ *
93
+ * Omitted (the Mac's daemons), the state stays in memory, exactly as before: a restart locks.
94
+ * Tokens are never stored — only their SHA-256, so a leaked row opens nothing.
95
+ */
96
+ export interface MasterLockStateStore {
97
+ read(): string | null;
98
+ write(json: string): void;
99
+ }
130
100
  export interface MasterLockOptions {
131
101
  store: MasterLockStore;
102
+ /** See {@link MasterLockStateStore}. Required wherever the instance does not outlive a request. */
103
+ state?: MasterLockStateStore;
132
104
  /**
133
105
  * The BOOTSTRAP record, as JSON, used only when the store is empty — the same shape as
134
106
  * {@link MasterLockRecord} with `idleMs` optional.
@@ -225,6 +197,7 @@ export declare class MasterLock {
225
197
  * entered again"* — one button, every device.
226
198
  */
227
199
  private sessions;
200
+ private readonly stateStore;
228
201
  private expiryTimer;
229
202
  private failures;
230
203
  private lastFailureAt;
@@ -449,6 +422,10 @@ export declare class MasterLock {
449
422
  private write;
450
423
  /** Drop the in-memory unlock without notifying — for tests between cases. */
451
424
  resetForTest(): void;
425
+ /** Adopt the persisted live state, if there is a store and it holds a readable one. */
426
+ private loadState;
427
+ /** Persist the live state — every mutation of it calls this, or a Worker forgets it. */
428
+ private saveState;
452
429
  /** Retire a session whose idle window has passed. Called on every read of the state, so
453
430
  * correctness never depends on the timer having fired. */
454
431
  private sweep;
@@ -47,6 +47,8 @@
47
47
  * `retryAfterMs` a throttled caller is told — which they already know, since they are the
48
48
  * one who has been guessing.
49
49
  */
50
+ import { createHash } from "node:crypto";
51
+ import { constantTimeEqual } from "cwip/constant-time";
50
52
  import { clampIdleMs, isMasterLockKdfParams, windowRemaining, } from "cursedbelt-core/master-lock";
51
53
  /** The id a legacy single-password record migrates to. Fixed, because the app that adopts
52
54
  * this stamps its existing rows with it — a random id would orphan the library it is
@@ -87,11 +89,14 @@ export class MasterLock {
87
89
  * entered again"* — one button, every device.
88
90
  */
89
91
  sessions = new Map();
92
+ stateStore = null;
90
93
  expiryTimer = null;
91
94
  failures = 0;
92
95
  lastFailureAt = 0;
93
96
  constructor(options) {
94
97
  this.store = options.store;
98
+ this.stateStore = options.state ?? null;
99
+ this.loadState();
95
100
  this.now = options.now ?? Date.now;
96
101
  this.onLockedChange = options.onLockedChange;
97
102
  this.log = options.log ?? (() => undefined);
@@ -220,7 +225,7 @@ export class MasterLock {
220
225
  if (this.sessions.size === 0)
221
226
  return null;
222
227
  let found = null;
223
- for (const presented of presentedTokens(req)) {
228
+ for (const presented of presentedTokens(req).map(tokenDigest)) {
224
229
  for (const [live, session] of this.sessions) {
225
230
  // No early `return`: every candidate is compared against every live token so
226
231
  // the loop's duration does not say which cookie in the jar was the right one.
@@ -307,6 +312,7 @@ export class MasterLock {
307
312
  }
308
313
  this.failures += 1;
309
314
  this.lastFailureAt = now;
315
+ this.saveState();
310
316
  return { ok: false, retryAfterMs: windowRemaining(this.failures, this.lastFailureAt, now) };
311
317
  }
312
318
  return { ok: true, ...this.open(opened.id, now) };
@@ -322,7 +328,8 @@ export class MasterLock {
322
328
  const wasLocked = this.sessions.size === 0;
323
329
  // ADDED, never replacing: the desk and the phone are the same person and both stay
324
330
  // open. See the `sessions` note.
325
- this.sessions.set(token, { accountId, lastActivityAt: now });
331
+ this.sessions.set(tokenDigest(token), { accountId, lastActivityAt: now });
332
+ this.saveState();
326
333
  this.scheduleExpiry();
327
334
  if (wasLocked)
328
335
  this.onLockedChange?.(false);
@@ -481,7 +488,7 @@ export class MasterLock {
481
488
  // phone left on the counter locks on its own schedule while the desk stays open —
482
489
  // which is decision 2 applied per device rather than per person.
483
490
  let slid = false;
484
- for (const presented of presentedTokens(req)) {
491
+ for (const presented of presentedTokens(req).map(tokenDigest)) {
485
492
  for (const [live, session] of this.sessions) {
486
493
  // No early `return`, for the reason `accountFor` gives: the loop's duration
487
494
  // must not say which of the jar's cookies was this app's.
@@ -491,8 +498,10 @@ export class MasterLock {
491
498
  }
492
499
  }
493
500
  }
494
- if (slid)
501
+ if (slid) {
502
+ this.saveState();
495
503
  this.scheduleExpiry();
504
+ }
496
505
  return slid;
497
506
  }
498
507
  /** The manual lock — the owner's *"I want a lock option to turn the site back to needing
@@ -504,6 +513,7 @@ export class MasterLock {
504
513
  // owner's requirement is "no one can see anything until the master password is
505
514
  // entered again", and a lock that left the phone open would not be that.
506
515
  this.sessions.clear();
516
+ this.saveState();
507
517
  this.clearExpiry();
508
518
  this.onLockedChange?.(true);
509
519
  }
@@ -588,6 +598,33 @@ export class MasterLock {
588
598
  this.lastFailureAt = 0;
589
599
  this.clearExpiry();
590
600
  }
601
+ /** Adopt the persisted live state, if there is a store and it holds a readable one. */
602
+ loadState() {
603
+ if (!this.stateStore)
604
+ return;
605
+ const raw = this.stateStore.read();
606
+ if (!raw)
607
+ return;
608
+ try {
609
+ const parsed = JSON.parse(raw);
610
+ if (parsed.v !== 1)
611
+ return;
612
+ for (const [digest, session] of parsed.sessions ?? []) {
613
+ if (typeof digest === "string" && typeof session?.accountId === "string" && Number.isFinite(session.lastActivityAt)) {
614
+ this.sessions.set(digest, { accountId: session.accountId, lastActivityAt: session.lastActivityAt });
615
+ }
616
+ }
617
+ this.failures = Number.isFinite(parsed.failures) ? Number(parsed.failures) : 0;
618
+ this.lastFailureAt = Number.isFinite(parsed.lastFailureAt) ? Number(parsed.lastFailureAt) : 0;
619
+ }
620
+ catch {
621
+ // Unreadable state is LOCKED state: nothing is open, which is the safe reading.
622
+ }
623
+ }
624
+ /** Persist the live state — every mutation of it calls this, or a Worker forgets it. */
625
+ saveState() {
626
+ this.stateStore?.write(JSON.stringify({ v: 1, sessions: [...this.sessions], failures: this.failures, lastFailureAt: this.lastFailureAt }));
627
+ }
591
628
  /** Retire a session whose idle window has passed. Called on every read of the state, so
592
629
  * correctness never depends on the timer having fired. */
593
630
  sweep() {
@@ -596,11 +633,14 @@ export class MasterLock {
596
633
  return;
597
634
  const now = this.now();
598
635
  const wasOpen = this.sessions.size > 0;
636
+ const before = this.sessions.size;
599
637
  for (const [token, session] of this.sessions) {
600
638
  // Each device expires on its OWN clock, so one going idle never shortens another.
601
639
  if (now - session.lastActivityAt > record.idleMs)
602
640
  this.sessions.delete(token);
603
641
  }
642
+ if (this.sessions.size !== before)
643
+ this.saveState();
604
644
  if (wasOpen && this.sessions.size === 0) {
605
645
  this.clearExpiry();
606
646
  this.onLockedChange?.(true);
@@ -791,6 +831,10 @@ async function equalize(candidate) {
791
831
  equalizerHash ??= Bun.password.hash("master-lock-timing-equalizer", { algorithm: "argon2id" });
792
832
  await Bun.password.verify(candidate || "x", await equalizerHash).catch(() => false);
793
833
  }
834
+ /** What a session is KEYED by — never the token itself, so persisted state opens nothing. */
835
+ function tokenDigest(token) {
836
+ return createHash("sha256").update(token).digest("base64url");
837
+ }
794
838
  function randomToken() {
795
839
  const bytes = new Uint8Array(TOKEN_BYTES);
796
840
  crypto.getRandomValues(bytes);
@@ -822,14 +866,3 @@ function presentedTokens(req) {
822
866
  }
823
867
  return out;
824
868
  }
825
- function constantTimeEqual(a, b) {
826
- const x = Buffer.from(a);
827
- const y = Buffer.from(b);
828
- // A length difference is already a non-match, and `timingSafeEqual` throws on one.
829
- if (x.length !== y.length)
830
- return false;
831
- let diff = 0;
832
- for (let i = 0; i < x.length; i += 1)
833
- diff |= x[i] ^ y[i];
834
- return diff === 0;
835
- }
@@ -1,4 +1,4 @@
1
- import { MasterLock } from "./masterLock";
1
+ import { MasterLock } from "./masterLock.js";
2
2
  /** Namespace for every per-person record, so one glance at the table says what they are. */
3
3
  export declare const MASTER_LOCK_PRINCIPAL_KEY_PREFIX = "master_lock:principal:";
4
4
  /**
@@ -46,7 +46,7 @@
46
46
  * record is stored under, which is the only thing it ever really was.
47
47
  */
48
48
  import { createHash } from "node:crypto";
49
- import { MasterLock, readRecord } from "./masterLock";
49
+ import { MasterLock, readRecord } from "./masterLock.js";
50
50
  /** Namespace for every per-person record, so one glance at the table says what they are. */
51
51
  export const MASTER_LOCK_PRINCIPAL_KEY_PREFIX = "master_lock:principal:";
52
52
  /**
@@ -1,5 +1,5 @@
1
1
  import { type MasterLockKdfParams } from "cursedbelt-core/master-lock/kdf";
2
- import { type MasterLockRecord } from "./masterLock";
2
+ import { type MasterLockRecord } from "./masterLock.js";
3
3
  /**
4
4
  * The idle window a freshly minted lock starts with.
5
5
  *
@@ -24,7 +24,7 @@
24
24
  */
25
25
  import { DEFAULT_IDLE_MS } from "cursedbelt-core/master-lock/policy";
26
26
  import { deriveMasterLockVerifier, newMasterLockKdfParams, } from "cursedbelt-core/master-lock/kdf";
27
- import { FIRST_ACCOUNT_ID } from "./masterLock";
27
+ import { FIRST_ACCOUNT_ID } from "./masterLock.js";
28
28
  /**
29
29
  * The idle window a freshly minted lock starts with.
30
30
  *
@@ -6,9 +6,14 @@
6
6
  * `app_settings`, a `Map` in a test. Asking for a whole table would be asking each adopter
7
7
  * to write a migration for three fields.
8
8
  */
9
- import type { MasterLockStore } from "./masterLock";
9
+ import type { MasterLockStore } from "./masterLock.js";
10
10
  /** The default key, so two apps' rows look the same when somebody goes looking. */
11
11
  export declare const MASTER_LOCK_SETTING_KEY = "master_lock:record";
12
+ /**
13
+ * The key the LIVE state (open unlocks, the throttle) is kept under, beside the record — see
14
+ * `MasterLockStateStore`. `createKvMasterLockStore(kv, MASTER_LOCK_STATE_KEY)` is that store.
15
+ */
16
+ export declare const MASTER_LOCK_STATE_KEY = "master_lock:state";
12
17
  /** Wrap an app's existing key/value settings surface. */
13
18
  export declare function createKvMasterLockStore(kv: {
14
19
  get(key: string): string | null | undefined;
@@ -1,5 +1,10 @@
1
1
  /** The default key, so two apps' rows look the same when somebody goes looking. */
2
2
  export const MASTER_LOCK_SETTING_KEY = "master_lock:record";
3
+ /**
4
+ * The key the LIVE state (open unlocks, the throttle) is kept under, beside the record — see
5
+ * `MasterLockStateStore`. `createKvMasterLockStore(kv, MASTER_LOCK_STATE_KEY)` is that store.
6
+ */
7
+ export const MASTER_LOCK_STATE_KEY = "master_lock:state";
3
8
  /** Wrap an app's existing key/value settings surface. */
4
9
  export function createKvMasterLockStore(kv, key = MASTER_LOCK_SETTING_KEY) {
5
10
  return {
@@ -1,4 +1,4 @@
1
1
  export { buildProbeArgs, buildThumbnailArgs, buildTrimArgs, ffmpegTimeToSeconds, type ProbeResult, parseFfmpegProgress, parseFfprobe, type VideoCodecPlan, videoCodecPlan, } from 'cursedbelt-core/media/ffmpeg';
2
- export { renderImage } from './image';
3
- export { aacWebAudioArgs, buildFrameJpegArgs, buildSpriteVtt, buildStreamProbeArgs, canStreamCopy, EMPTY_PROBE, FASTSTART, faststartArgs, h264WebVideoArgs, hlsLadderArgs, imageThumbnailArgs, LIBRARY_CRF, LIBRARY_SCALE, parseProgressMicros, parseStreamProbe, planSprite, posterArgs, resolveLadder, type SpritePlan, spriteArgs, type VideoProbe, } from './streaming';
4
- export { extractFrameJpeg, extractThumbnail, isFfmpegAvailable, isFfprobeAvailable, probeMedia, probeVideoStream, runFfmpegProgress, trimVideo, } from './video';
2
+ export { renderImage } from './image.js';
3
+ export { aacWebAudioArgs, buildFrameJpegArgs, buildSpriteVtt, buildStreamProbeArgs, canStreamCopy, EMPTY_PROBE, FASTSTART, faststartArgs, h264WebVideoArgs, hlsLadderArgs, imageThumbnailArgs, LIBRARY_CRF, LIBRARY_SCALE, parseProgressMicros, parseStreamProbe, planSprite, posterArgs, resolveLadder, type SpritePlan, spriteArgs, type VideoProbe, } from './streaming.js';
4
+ export { extractFrameJpeg, extractThumbnail, isFfmpegAvailable, isFfprobeAvailable, probeMedia, probeVideoStream, runFfmpegProgress, trimVideo, } from './video.js';
@@ -3,8 +3,8 @@
3
3
  // via native ffmpeg/ffprobe. App handlers keep auth, FsNode lookup, quota, and
4
4
  // storeFile; this owns the actual pixel/codec work.
5
5
  export { buildProbeArgs, buildThumbnailArgs, buildTrimArgs, ffmpegTimeToSeconds, parseFfmpegProgress, parseFfprobe, videoCodecPlan, } from 'cursedbelt-core/media/ffmpeg';
6
- export { renderImage } from './image';
6
+ export { renderImage } from './image.js';
7
7
  // Streaming pipeline: faststart remux, HLS adaptive-bitrate ladder, scrub sprite + WebVTT,
8
8
  // poster, codec-aware probe — pure builders/parsers (testable without ffmpeg).
9
- export { aacWebAudioArgs, buildFrameJpegArgs, buildSpriteVtt, buildStreamProbeArgs, canStreamCopy, EMPTY_PROBE, FASTSTART, faststartArgs, h264WebVideoArgs, hlsLadderArgs, imageThumbnailArgs, LIBRARY_CRF, LIBRARY_SCALE, parseProgressMicros, parseStreamProbe, planSprite, posterArgs, resolveLadder, spriteArgs, } from './streaming';
10
- export { extractFrameJpeg, extractThumbnail, isFfmpegAvailable, isFfprobeAvailable, probeMedia, probeVideoStream, runFfmpegProgress, trimVideo, } from './video';
9
+ export { aacWebAudioArgs, buildFrameJpegArgs, buildSpriteVtt, buildStreamProbeArgs, canStreamCopy, EMPTY_PROBE, FASTSTART, faststartArgs, h264WebVideoArgs, hlsLadderArgs, imageThumbnailArgs, LIBRARY_CRF, LIBRARY_SCALE, parseProgressMicros, parseStreamProbe, planSprite, posterArgs, resolveLadder, spriteArgs, } from './streaming.js';
10
+ export { extractFrameJpeg, extractThumbnail, isFfmpegAvailable, isFfprobeAvailable, probeMedia, probeVideoStream, runFfmpegProgress, trimVideo, } from './video.js';
@@ -1,6 +1,6 @@
1
1
  import { type VideoProgressEvent, type VideoTrimSpec } from 'cursedbelt-core/media';
2
2
  import { type ProbeResult } from 'cursedbelt-core/media/ffmpeg';
3
- import { type VideoProbe } from './streaming';
3
+ import { type VideoProbe } from './streaming.js';
4
4
  /** Is ffmpeg runnable? Cheap probe used to skip server media ops / tests cleanly. */
5
5
  export declare function isFfmpegAvailable(bin?: string): Promise<boolean>;
6
6
  /** Is ffprobe runnable? */
@@ -4,7 +4,7 @@
4
4
  // `FFMPEG_PATH` / `FFPROBE_PATH`. Replaces ca's hand-rolled spawn boilerplate.
5
5
  import { assertValidVideoTrimSpec, } from 'cursedbelt-core/media';
6
6
  import { buildProbeArgs, buildThumbnailArgs, buildTrimArgs, parseFfmpegProgress, parseFfprobe, } from 'cursedbelt-core/media/ffmpeg';
7
- import { buildFrameJpegArgs, buildStreamProbeArgs, EMPTY_PROBE, parseProgressMicros, parseStreamProbe, } from './streaming';
7
+ import { buildFrameJpegArgs, buildStreamProbeArgs, EMPTY_PROBE, parseProgressMicros, parseStreamProbe, } from './streaming.js';
8
8
  const FFMPEG = process.env.FFMPEG_PATH || 'ffmpeg';
9
9
  const FFPROBE = process.env.FFPROBE_PATH || 'ffprobe';
10
10
  /** Is ffmpeg runnable? Cheap probe used to skip server media ops / tests cleanly. */
@@ -1,5 +1,5 @@
1
1
  import type { Database } from 'bun:sqlite';
2
- import { type MetricRow } from './metricsBuffer';
2
+ import { type MetricRow } from './metricsBuffer.js';
3
3
  /**
4
4
  * ONE seam for "this app served a request", with two backends and no third.
5
5
  *
@@ -1,4 +1,4 @@
1
- import { createMetricsBuffer } from './metricsBuffer';
1
+ import { createMetricsBuffer } from './metricsBuffer.js';
2
2
  /**
3
3
  * 🔴 **The data-point layout is a WIRE FORMAT.** Analytics Engine columns are
4
4
  * positional (`blob1`, `double1`, …), so every saved SQL query breaks if a
@@ -1,5 +1,5 @@
1
1
  import type { MiddlewareHandler } from 'hono';
2
- import type { CursedbeltEnv } from '../context';
2
+ import type { CursedbeltEnv } from '../context.js';
3
3
  /**
4
4
  * Assign every request a correlation id: reuse a safe inbound `x-correlation-id`
5
5
  * (so a trace survives the proxy hop), else mint a `crypto.randomUUID()`. Stored on
@@ -1,5 +1,5 @@
1
1
  import type { ErrorHandler } from 'hono';
2
- import type { CursedbeltEnv } from '../context';
2
+ import type { CursedbeltEnv } from '../context.js';
3
3
  /**
4
4
  * Hono `onError` handler. Converts ANY thrown value into the ONE canonical rich
5
5
  * envelope — `{ error: { name, message, code?, status?, category?, context?,
@@ -1,5 +1,5 @@
1
1
  import { HTTPException } from 'hono/http-exception';
2
- import { ApiError, isAppError, toErrorEnvelope } from '../errors';
2
+ import { ApiError, isAppError, toErrorEnvelope } from '../errors.js';
3
3
  /**
4
4
  * Hono `onError` handler. Converts ANY thrown value into the ONE canonical rich
5
5
  * envelope — `{ error: { name, message, code?, status?, category?, context?,
@@ -1,10 +1,10 @@
1
- export { correlationId } from './correlationId';
2
- export { type CorsOpts, corsAllowlist, corsOptsFromEnv } from './corsAllowlist';
3
- export { errorEnvelope } from './errorEnvelope';
4
- export { crossOriginRefusal } from './localOrigin';
5
- export { clientIp, createRateLimiter, type RateLimitOpts } from './rateLimit';
6
- export { type RequestLoggerOpts, requestLogger } from './requestLogger';
7
- export { type AnalyticsEngineDataset, createTelemetrySink, type TelemetryEvent, type TelemetrySink, type TelemetrySinkOptions, } from '../metrics/telemetrySink';
8
- export { type CspDirectives, type SecurityHeadersOpts, securityHeaders, } from './securityHeaders';
9
- export { toValidationIssues, VALIDATION_ERROR_CODE, type ValidationIssue, validationEnvelope, } from '../errors';
10
- export { type ZValidatorTarget, zValidator } from './zValidator';
1
+ export { correlationId } from './correlationId.js';
2
+ export { type CorsOpts, corsAllowlist, corsOptsFromEnv } from './corsAllowlist.js';
3
+ export { errorEnvelope } from './errorEnvelope.js';
4
+ export { crossOriginRefusal } from './localOrigin.js';
5
+ export { clientIp, createRateLimiter, type RateLimitOpts } from './rateLimit.js';
6
+ export { type RequestLoggerOpts, requestLogger } from './requestLogger.js';
7
+ export { type AnalyticsEngineDataset, createTelemetrySink, type TelemetryEvent, type TelemetrySink, type TelemetrySinkOptions, } from '../metrics/telemetrySink.js';
8
+ export { type CspDirectives, type SecurityHeadersOpts, securityHeaders, } from './securityHeaders.js';
9
+ export { toValidationIssues, VALIDATION_ERROR_CODE, type ValidationIssue, validationEnvelope, } from '../errors.js';
10
+ export { type ZValidatorTarget, zValidator } from './zValidator.js';
@@ -1,13 +1,13 @@
1
1
  // The cross-cutting cursedbelt server middleware set.
2
- export { correlationId } from './correlationId';
3
- export { corsAllowlist, corsOptsFromEnv } from './corsAllowlist';
4
- export { errorEnvelope } from './errorEnvelope';
5
- export { crossOriginRefusal } from './localOrigin';
6
- export { clientIp, createRateLimiter } from './rateLimit';
7
- export { requestLogger } from './requestLogger';
2
+ export { correlationId } from './correlationId.js';
3
+ export { corsAllowlist, corsOptsFromEnv } from './corsAllowlist.js';
4
+ export { errorEnvelope } from './errorEnvelope.js';
5
+ export { crossOriginRefusal } from './localOrigin.js';
6
+ export { clientIp, createRateLimiter } from './rateLimit.js';
7
+ export { requestLogger } from './requestLogger.js';
8
8
  // The telemetry seam `requestLogger` writes through — re-exported here so a caller
9
9
  // that reaches `./middleware` for the logger can bind Analytics Engine in the same import.
10
- export { createTelemetrySink, } from '../metrics/telemetrySink';
11
- export { securityHeaders, } from './securityHeaders';
12
- export { toValidationIssues, VALIDATION_ERROR_CODE, validationEnvelope, } from '../errors';
13
- export { zValidator } from './zValidator';
10
+ export { createTelemetrySink, } from '../metrics/telemetrySink.js';
11
+ export { securityHeaders, } from './securityHeaders.js';
12
+ export { toValidationIssues, VALIDATION_ERROR_CODE, validationEnvelope, } from '../errors.js';
13
+ export { zValidator } from './zValidator.js';
@@ -1,5 +1,5 @@
1
1
  import { getConnInfo } from 'hono/bun';
2
- import { ApiError, toErrorEnvelope } from '../errors';
2
+ import { ApiError, toErrorEnvelope } from '../errors.js';
3
3
  /**
4
4
  * The client IP for rate-limiting / logging.
5
5
  *
@@ -1,8 +1,8 @@
1
1
  import type { Database } from 'bun:sqlite';
2
2
  import type { MiddlewareHandler } from 'hono';
3
- import type { CursedbeltEnv } from '../context';
4
- import type { MetricsBuffer } from '../metrics/metricsBuffer';
5
- import { type AnalyticsEngineDataset, type TelemetrySink } from '../metrics/telemetrySink';
3
+ import type { CursedbeltEnv } from '../context.js';
4
+ import type { MetricsBuffer } from '../metrics/metricsBuffer.js';
5
+ import { type AnalyticsEngineDataset, type TelemetrySink } from '../metrics/telemetrySink.js';
6
6
  /**
7
7
  * Structured request/response logging. After each request it records one
8
8
  * telemetry event through the {@link TelemetrySink} (off the hot path) and, for
@@ -1,7 +1,7 @@
1
1
  import { HTTPException } from 'hono/http-exception';
2
- import { isApiError } from '../errors';
3
- import { normalizeRoute } from '../metrics/normalizeRoute';
4
- import { createTelemetrySink, } from '../metrics/telemetrySink';
2
+ import { isApiError } from '../errors.js';
3
+ import { normalizeRoute } from '../metrics/normalizeRoute.js';
4
+ import { createTelemetrySink, } from '../metrics/telemetrySink.js';
5
5
  function statusOfThrown(err) {
6
6
  if (isApiError(err))
7
7
  return err.status;
@@ -1,6 +1,6 @@
1
1
  import type { Env, MiddlewareHandler } from 'hono';
2
2
  import type { ZodType, z } from 'zod';
3
- import type { CursedbeltEnv } from '../context';
3
+ import type { CursedbeltEnv } from '../context.js';
4
4
  /** The request locations a schema can validate. Mirrors Hono's body/non-body split. */
5
5
  export type ZValidatorTarget = 'json' | 'form' | 'query' | 'param';
6
6
  /**
@@ -1,4 +1,4 @@
1
- import { validationEnvelope } from '../errors';
1
+ import { validationEnvelope } from '../errors.js';
2
2
  /**
3
3
  * Read the value a target validates against. The body targets (`json`/`form`) prefer
4
4
  * the ctgr-decoded body so a tunnelled mutation validates its real payload; the
@@ -1,5 +1,5 @@
1
1
  import type { Database } from 'bun:sqlite';
2
- import type { Migration } from './types';
2
+ import type { Migration } from './types.js';
3
3
  /**
4
4
  * The versioned SQLite migration runner — a tiny `_migrations`-sentinel runner, NOT
5
5
  * a schema-first ORM. It replaces the old scattered guarded-`ALTER` /
@@ -33,7 +33,7 @@
33
33
  */
34
34
  import { isIP } from 'node:net';
35
35
  import { lookup } from 'node:dns/promises';
36
- import { AppError } from '../errors';
36
+ import { AppError } from '../errors.js';
37
37
  export const OUTBOUND_URL_BLOCKED = 'OUTBOUND_URL_BLOCKED';
38
38
  const blocked = (reason, context) => new AppError(`Outbound request refused: ${reason}.`, {
39
39
  code: OUTBOUND_URL_BLOCKED,
@@ -36,12 +36,12 @@
36
36
  * route that notifies somebody else; producing is server-side code calling
37
37
  * `center.notify*`.
38
38
  */
39
- export { createNotificationHub } from "./hub";
40
- export type { NotificationHub, NotificationHubEvent, NotificationHubListener, NotificationHubOptions, } from "./hub";
41
- export { NOTIFICATION_MIGRATIONS, NOTIFICATIONS_TABLE } from "./migrations";
42
- export { mountNotificationRoutes } from "./routes";
43
- export type { NotificationRouteCenter, NotificationRoutesOptions } from "./routes";
44
- export { createNotificationService } from "./service";
45
- export type { CreateNotificationServiceOptions, NotificationService, NotificationServiceMountOptions, } from "./service";
39
+ export { createNotificationHub } from "./hub.js";
40
+ export type { NotificationHub, NotificationHubEvent, NotificationHubListener, NotificationHubOptions, } from "./hub.js";
41
+ export { NOTIFICATION_MIGRATIONS, NOTIFICATIONS_TABLE } from "./migrations.js";
42
+ export { mountNotificationRoutes } from "./routes.js";
43
+ export type { NotificationRouteCenter, NotificationRoutesOptions } from "./routes.js";
44
+ export { createNotificationService } from "./service.js";
45
+ export type { CreateNotificationServiceOptions, NotificationService, NotificationServiceMountOptions, } from "./service.js";
46
46
  export { createNotificationCenter, createMemoryNotificationStore, createSqliteNotificationStore, migrateNotificationsSchema, } from "cwip/notifications";
47
47
  export type { AppNotification, ListNotificationsQuery, NotificationAudience, NotificationCenter, NotificationCenterOptions, NotificationDraft, NotificationFilter, NotificationPage, NotificationSeverity, NotificationSink, NotificationStore, Notifier, } from "cwip/notifications";