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,162 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { AuditEntryInput, AuditOperation, AuditRecord } from 'cwip/audit';
3
+ import { runMigrations } from '../migration/runner';
4
+ import { AUDIT_LOG_MIGRATIONS } from './migrations';
5
+
6
+ interface AuditLogRow {
7
+ namespace: string;
8
+ mutation_key: string;
9
+ target_id: string;
10
+ version_key: number;
11
+ operation: AuditOperation;
12
+ tenant_id: string;
13
+ changed_by: string | null;
14
+ changed_at: string;
15
+ snapshot_data: string | null;
16
+ }
17
+
18
+ function rowToRecord(row: AuditLogRow): AuditRecord {
19
+ return {
20
+ namespace: row.namespace,
21
+ mutationKey: row.mutation_key,
22
+ targetId: row.target_id,
23
+ versionKey: row.version_key,
24
+ operation: row.operation,
25
+ tenantId: row.tenant_id,
26
+ changedBy: row.changed_by ?? undefined,
27
+ changedAt: row.changed_at,
28
+ snapshot: row.snapshot_data == null ? null : JSON.parse(row.snapshot_data),
29
+ };
30
+ }
31
+
32
+ /**
33
+ * The persistent audit sink: one shared `audit_log` table (see `./migrations`),
34
+ * versioned per `(namespace, targetId)`. Runs its migration on first
35
+ * construction (idempotent — safe to call once per boot per `db`).
36
+ *
37
+ * const sink = createSqliteAuditSink(db);
38
+ * const router = buildServerRouter(runtime, config, { verifyToken, audit: { sink } });
39
+ *
40
+ * Deliberately NOT annotated `: AuditSink` — see `cwip/audit`'s
41
+ * `createMemoryAuditSink` for why (this sink is fully synchronous under the
42
+ * hood; the interface's async-capable signature would erase that at every
43
+ * call site). Structurally assignable to `AuditSink` wherever one is expected.
44
+ */
45
+ export function createSqliteAuditSink(db: Database) {
46
+ runMigrations(db, AUDIT_LOG_MIGRATIONS);
47
+
48
+ // Scoped by tenant (05 §A1). `targetId` is a namespace-local business key, not
49
+ // a minted global id — a plugin's `todo` row "5" is "5" for every tenant — so
50
+ // an unscoped MAX() hands tenant B a version number tenant A already used, and
51
+ // the UNIQUE then refuses B's write over a row B may not read.
52
+ const nextVersion = db.prepare(
53
+ 'SELECT COALESCE(MAX(version_key), 0) + 1 AS v FROM audit_log WHERE tenant_id = ? AND namespace = ? AND target_id = ?',
54
+ );
55
+ const insert = db.prepare(`
56
+ INSERT INTO audit_log
57
+ (namespace, mutation_key, target_id, version_key, operation, tenant_id, changed_by, changed_at, snapshot_data)
58
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
59
+ `);
60
+ const selectTimeline = db.prepare(
61
+ 'SELECT * FROM audit_log WHERE tenant_id = ? AND namespace = ? AND target_id = ? ORDER BY version_key ASC',
62
+ );
63
+
64
+ return {
65
+ record(entry: AuditEntryInput): AuditRecord {
66
+ // No `await` between the read and the write below — bun:sqlite's calls are
67
+ // synchronous, so nothing else can interleave and race the version number.
68
+ const record = db.transaction((): AuditRecord => {
69
+ const { v: versionKey } = nextVersion.get(entry.tenantId, entry.namespace, entry.targetId) as {
70
+ v: number;
71
+ };
72
+ const changedAt = entry.changedAt ?? new Date().toISOString();
73
+ insert.run(
74
+ entry.namespace,
75
+ entry.mutationKey,
76
+ entry.targetId,
77
+ versionKey,
78
+ entry.operation,
79
+ entry.tenantId,
80
+ entry.changedBy ?? null,
81
+ changedAt,
82
+ entry.snapshot == null ? null : JSON.stringify(entry.snapshot),
83
+ );
84
+ return { ...entry, versionKey, changedAt };
85
+ })();
86
+ return record;
87
+ },
88
+
89
+ timeline(tenantId: string, namespace: string, targetId: string): AuditRecord[] {
90
+ const rows = selectTimeline.all(tenantId, namespace, targetId) as AuditLogRow[];
91
+ return rows.map(rowToRecord);
92
+ },
93
+ };
94
+ }
95
+
96
+ /**
97
+ * Every read below takes `tenantId` FIRST and as a required argument (05 §A1).
98
+ *
99
+ * `audit_log` is ONE shared table across every namespace and every tenant, and
100
+ * its lookup key `(namespace, targetId)` is a business key a plugin chose — not a
101
+ * minted id. So a read that omits the tenant is not merely broad: it returns
102
+ * another tenant's snapshot for the same row number, which is the whole audit
103
+ * trail's contents. Making the parameter optional (defaulting to "all tenants")
104
+ * would put that one keystroke away, so it is required and positioned where a
105
+ * mistaken call fails to typecheck rather than silently widening.
106
+ */
107
+
108
+ /** Query 1: the record's state as of a given moment (last version at or before `at`). */
109
+ export function auditStateAt(
110
+ db: Database,
111
+ tenantId: string,
112
+ namespace: string,
113
+ targetId: string,
114
+ at: string,
115
+ ): AuditRecord | null {
116
+ const row = db
117
+ .prepare(
118
+ 'SELECT * FROM audit_log WHERE tenant_id = ? AND namespace = ? AND target_id = ? AND changed_at <= ? ORDER BY version_key DESC LIMIT 1',
119
+ )
120
+ .get(tenantId, namespace, targetId, at) as AuditLogRow | null;
121
+ return row ? rowToRecord(row) : null;
122
+ }
123
+
124
+ /** Query 2: jump directly to a specific `versionKey`. */
125
+ export function auditStateAtVersion(
126
+ db: Database,
127
+ tenantId: string,
128
+ namespace: string,
129
+ targetId: string,
130
+ versionKey: number,
131
+ ): AuditRecord | null {
132
+ const row = db
133
+ .prepare(
134
+ 'SELECT * FROM audit_log WHERE tenant_id = ? AND namespace = ? AND target_id = ? AND version_key = ?',
135
+ )
136
+ .get(tenantId, namespace, targetId, versionKey) as AuditLogRow | null;
137
+ return row ? rowToRecord(row) : null;
138
+ }
139
+
140
+ /** Query 3: step one version back/forward from `currentVersion` (for a UI "prev/next" control). */
141
+ export function auditStep(
142
+ db: Database,
143
+ tenantId: string,
144
+ namespace: string,
145
+ targetId: string,
146
+ currentVersion: number,
147
+ direction: 'back' | 'forward',
148
+ ): AuditRecord | null {
149
+ const row =
150
+ direction === 'back'
151
+ ? (db
152
+ .prepare(
153
+ 'SELECT * FROM audit_log WHERE tenant_id = ? AND namespace = ? AND target_id = ? AND version_key < ? ORDER BY version_key DESC LIMIT 1',
154
+ )
155
+ .get(tenantId, namespace, targetId, currentVersion) as AuditLogRow | null)
156
+ : (db
157
+ .prepare(
158
+ 'SELECT * FROM audit_log WHERE tenant_id = ? AND namespace = ? AND target_id = ? AND version_key > ? ORDER BY version_key ASC LIMIT 1',
159
+ )
160
+ .get(tenantId, namespace, targetId, currentVersion) as AuditLogRow | null);
161
+ return row ? rowToRecord(row) : null;
162
+ }
@@ -0,0 +1,46 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { Hono } from 'hono';
3
+ import { AUTH_COOKIE_NAME, clearAuthCookie, readAuthCookie, writeAuthCookie } from './cookie';
4
+
5
+ const REQ = (path: string, init?: RequestInit) => new Request(`http://localhost${path}`, init);
6
+
7
+ describe('auth cookie helpers', () => {
8
+ test('writeAuthCookie sets an httpOnly, Lax, path=/ cookie with maxAge', async () => {
9
+ const app = new Hono();
10
+ app.get('/login', (c) => {
11
+ writeAuthCookie(c, 'the-jwt', 900);
12
+ return c.text('ok');
13
+ });
14
+ const res = await app.fetch(REQ('/login'));
15
+ const sc = res.headers.get('set-cookie') ?? '';
16
+ expect(sc).toContain(`${AUTH_COOKIE_NAME}=the-jwt`);
17
+ expect(sc).toContain('HttpOnly');
18
+ expect(sc).toContain('SameSite=Lax');
19
+ expect(sc).toContain('Path=/');
20
+ expect(sc).toContain('Max-Age=900');
21
+ });
22
+
23
+ test('readAuthCookie reads the cookie back', async () => {
24
+ const app = new Hono();
25
+ app.get('/me', (c) => c.json({ token: readAuthCookie(c) ?? null }));
26
+ const res = await app.fetch(REQ('/me', { headers: { cookie: `${AUTH_COOKIE_NAME}=abc123` } }));
27
+ expect(await res.json()).toEqual({ token: 'abc123' });
28
+ });
29
+
30
+ test('readAuthCookie returns undefined when absent', async () => {
31
+ const app = new Hono();
32
+ app.get('/me', (c) => c.json({ token: readAuthCookie(c) ?? null }));
33
+ expect(await (await app.fetch(REQ('/me'))).json()).toEqual({ token: null });
34
+ });
35
+
36
+ test('clearAuthCookie expires the cookie', async () => {
37
+ const app = new Hono();
38
+ app.get('/logout', (c) => {
39
+ clearAuthCookie(c);
40
+ return c.text('ok');
41
+ });
42
+ const sc = (await app.fetch(REQ('/logout'))).headers.get('set-cookie') ?? '';
43
+ expect(sc).toContain(`${AUTH_COOKIE_NAME}=`);
44
+ expect(sc).toContain('Max-Age=0');
45
+ });
46
+ });
@@ -0,0 +1,42 @@
1
+ import type { Context } from 'hono';
2
+ import { deleteCookie, getCookie, setCookie } from 'hono/cookie';
3
+
4
+ /**
5
+ * Cookie helpers on `hono/cookie` (replacing `cookie-parser` + Express
6
+ * `res.cookie`). Two cookies, two deliberately-different `sameSite` postures:
7
+ *
8
+ * - `access_token` — httpOnly, **`sameSite: Lax`** so it rides top-level
9
+ * navigations (a returning user lands authenticated). The JWT is the auth
10
+ * credential; JS must never read it.
11
+ * - `csrf_token` — JS-readable (the SPA echoes it in a header), **`sameSite:
12
+ * Strict`** so it is NOT attached to cross-site requests. The two MUST stay
13
+ * separate cookies (see {@link ./csrf}).
14
+ */
15
+
16
+ export const AUTH_COOKIE_NAME = 'access_token';
17
+
18
+ const isProd = (): boolean => process.env.NODE_ENV === 'production';
19
+
20
+ /** Read the httpOnly auth cookie (the access JWT), or undefined. */
21
+ export function readAuthCookie(c: Context): string | undefined {
22
+ return getCookie(c, AUTH_COOKIE_NAME);
23
+ }
24
+
25
+ /**
26
+ * Write the auth cookie. `maxAge` is seconds (cookie spec) — pass the access
27
+ * token's lifetime. httpOnly + `sameSite: Lax`, secure in production.
28
+ */
29
+ export function writeAuthCookie(c: Context, token: string, maxAge: number): void {
30
+ setCookie(c, AUTH_COOKIE_NAME, token, {
31
+ httpOnly: true,
32
+ secure: isProd(),
33
+ sameSite: 'Lax',
34
+ maxAge,
35
+ path: '/',
36
+ });
37
+ }
38
+
39
+ /** Clear the auth cookie (logout). */
40
+ export function clearAuthCookie(c: Context): void {
41
+ deleteCookie(c, AUTH_COOKIE_NAME, { path: '/' });
42
+ }
@@ -0,0 +1,81 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { Hono } from 'hono';
3
+ import { CSRF_COOKIE_NAME, CSRF_HEADER_NAME, createCsrfMiddleware } from './csrf';
4
+
5
+ const REQ = (path: string, init?: RequestInit) => new Request(`http://localhost${path}`, init);
6
+
7
+ const buildApp = () => {
8
+ const app = new Hono();
9
+ app.use('*', createCsrfMiddleware());
10
+ app.get('/notes', (c) => c.json({ ok: true }));
11
+ app.post('/notes', (c) => c.json({ ok: true })); // non-ctgr mutation (internal/test)
12
+ app.get('/ctgrpo/notes', (c) => c.json({ ok: true })); // a ctgr GET-twin (mutation)
13
+ return app;
14
+ };
15
+
16
+ const csrfTokenFromSetCookie = (res: Response): string => {
17
+ const sc = res.headers.get('set-cookie') ?? '';
18
+ const m = sc.match(new RegExp(`${CSRF_COOKIE_NAME}=([^;]+)`));
19
+ if (!m) throw new Error('no csrf cookie set');
20
+ return m[1];
21
+ };
22
+
23
+ describe('createCsrfMiddleware', () => {
24
+ test('a GET mints the csrf_token cookie (Strict, JS-readable)', async () => {
25
+ const res = await buildApp().fetch(REQ('/notes'));
26
+ expect(res.status).toBe(200);
27
+ const sc = res.headers.get('set-cookie') ?? '';
28
+ expect(sc).toContain(`${CSRF_COOKIE_NAME}=`);
29
+ expect(sc).toContain('SameSite=Strict');
30
+ expect(sc.toLowerCase()).not.toContain('httponly');
31
+ });
32
+
33
+ test('a GET does NOT re-mint when the cookie is already present', async () => {
34
+ const res = await buildApp().fetch(
35
+ REQ('/notes', { headers: { cookie: `${CSRF_COOKIE_NAME}=existing` } }),
36
+ );
37
+ expect(res.headers.get('set-cookie')).toBeNull();
38
+ });
39
+
40
+ test('a ctgr request with no header → 403 envelope', async () => {
41
+ const res = await buildApp().fetch(
42
+ REQ('/ctgrpo/notes', { headers: { cookie: `${CSRF_COOKIE_NAME}=abc` } }),
43
+ );
44
+ expect(res.status).toBe(403);
45
+ expect((await res.json()).error.message).toBe('Invalid CSRF token');
46
+ });
47
+
48
+ test('a ctgr request with a mismatched header → 403', async () => {
49
+ const res = await buildApp().fetch(
50
+ REQ('/ctgrpo/notes', {
51
+ headers: { cookie: `${CSRF_COOKIE_NAME}=abc`, [CSRF_HEADER_NAME]: 'xyz' },
52
+ }),
53
+ );
54
+ expect(res.status).toBe(403);
55
+ });
56
+
57
+ test('a ctgr request with header === cookie passes', async () => {
58
+ const app = buildApp();
59
+ const token = csrfTokenFromSetCookie(await app.fetch(REQ('/notes')));
60
+ const res = await app.fetch(
61
+ REQ('/ctgrpo/notes', {
62
+ headers: { cookie: `${CSRF_COOKIE_NAME}=${token}`, [CSRF_HEADER_NAME]: token },
63
+ }),
64
+ );
65
+ expect(res.status).toBe(200);
66
+ expect(await res.json()).toEqual({ ok: true });
67
+ });
68
+
69
+ test('a non-ctgr mutation is NOT CSRF-checked (internal/test traffic)', async () => {
70
+ const res = await buildApp().fetch(REQ('/notes', { method: 'POST' }));
71
+ expect(res.status).toBe(200);
72
+ });
73
+
74
+ test('a custom protected prefix is honored', async () => {
75
+ const app = new Hono();
76
+ app.use('*', createCsrfMiddleware({ protectedPrefix: '/api/mut' }));
77
+ app.post('/api/mut/x', (c) => c.json({ ok: true }));
78
+ const res = await app.fetch(REQ('/api/mut/x', { method: 'POST' }));
79
+ expect(res.status).toBe(403);
80
+ });
81
+ });
@@ -0,0 +1,83 @@
1
+ import type { MiddlewareHandler } from 'hono';
2
+ import { getCookie, setCookie } from 'hono/cookie';
3
+ import { ApiError, toErrorEnvelope } from '../errors';
4
+
5
+ /**
6
+ * Double-submit CSRF guard for the cookie-auth + ctgr GET-tunnel model.
7
+ *
8
+ * Because mutations tunnel through a GET (the ctgr twin) and the auth cookie is
9
+ * `sameSite: Lax`, a cross-site top-level navigation could otherwise trigger a
10
+ * state change. We defend with a JS-readable, `sameSite: Strict` `csrf_token`
11
+ * cookie that the SPA echoes back in the `x-csrf-token` header. A cross-origin page
12
+ * can neither read the victim's cookie nor (Strict) cause it to be sent on a
13
+ * cross-site request, so it cannot forge the header.
14
+ *
15
+ * Behavior:
16
+ * - Safe verbs (GET/HEAD) OUTSIDE the protected prefix: mint the `csrf_token`
17
+ * cookie if absent (so a returning user has it on first paint), then continue.
18
+ * Genuine reads are never CSRF-checked.
19
+ * - ANY request whose path starts with the protected prefix (default `/ctgr`),
20
+ * regardless of HTTP method: require `header === cookie`, else 403. Every ctgr
21
+ * request is a tunneled mutation — and the twin rides a *GET* — so the check must
22
+ * fire even on GET here, or the exact attack CSRF defends against (a cross-site
23
+ * navigation/`<img>` to a `/ctgr*` mutation) slips through. Enforcing on the
24
+ * prefix (not the verb) keeps the guarantee independent of where task-25's
25
+ * `unGETify` sits in the chain. (Invariant #2: ctgr protection may never weaken.)
26
+ * - Mutating verbs OUTSIDE the prefix are NOT checked here (in production all
27
+ * mutations come through ctgr; direct verbs are internal/test traffic).
28
+ *
29
+ * MOUNT GLOBALLY — `app.use('*', createCsrfMiddleware())`. It matches the prefix with
30
+ * a plain `path.startsWith`, so it is immune to Hono's `/ctgr*`-doesn't-match wildcard
31
+ * gotcha; do NOT mount it on `CTGR_ROUTE_PATTERN` (that pattern is for the `unGETify`
32
+ * decode mount) — a prefix-only mount would never run on normal reads and so would
33
+ * never mint the `csrf_token` cookie.
34
+ */
35
+ export interface CsrfOptions {
36
+ /** Path prefix whose requests are CSRF-checked. Default `/ctgr`. */
37
+ protectedPrefix?: string;
38
+ /** Cookie name. Default `csrf_token`. */
39
+ cookieName?: string;
40
+ /** Request header the SPA echoes the token in. Default `x-csrf-token`. */
41
+ headerName?: string;
42
+ }
43
+
44
+ export const CSRF_COOKIE_NAME = 'csrf_token';
45
+ export const CSRF_HEADER_NAME = 'x-csrf-token';
46
+
47
+ const isProd = (): boolean => process.env.NODE_ENV === 'production';
48
+
49
+ export function createCsrfMiddleware(opts: CsrfOptions = {}): MiddlewareHandler {
50
+ const prefix = opts.protectedPrefix ?? '/ctgr';
51
+ const cookieName = opts.cookieName ?? CSRF_COOKIE_NAME;
52
+ const headerName = opts.headerName ?? CSRF_HEADER_NAME;
53
+
54
+ return async (c, next) => {
55
+ const method = c.req.method;
56
+ const isProtected = c.req.path.startsWith(prefix);
57
+
58
+ // Genuine reads (outside the ctgr tunnel): mint the cookie, never enforce.
59
+ if (!isProtected && (method === 'GET' || method === 'HEAD')) {
60
+ if (!getCookie(c, cookieName)) {
61
+ setCookie(c, cookieName, crypto.randomUUID(), {
62
+ httpOnly: false, // the SPA must read it to echo it back
63
+ secure: isProd(),
64
+ sameSite: 'Strict',
65
+ path: '/',
66
+ });
67
+ }
68
+ return next();
69
+ }
70
+
71
+ // Every ctgr request is a tunneled mutation (the twin is a GET) — enforce here
72
+ // regardless of verb. Non-ctgr mutating verbs fall through unchecked.
73
+ if (isProtected) {
74
+ const header = c.req.header(headerName);
75
+ const cookie = getCookie(c, cookieName);
76
+ if (!header || header !== cookie) {
77
+ return c.json(toErrorEnvelope(new ApiError(403, 'Invalid CSRF token')), 403);
78
+ }
79
+ }
80
+
81
+ return next();
82
+ };
83
+ }
@@ -0,0 +1,40 @@
1
+ import { importPKCS8, SignJWT } from 'jose';
2
+ import type { FileTokenClaims, FileTokenSigner } from '../storage/binaryServerStore';
3
+
4
+ // RS256 file-token signer for binary-server tenants. binary-server holds only the tenant's
5
+ // PUBLIC key (resolved by the token's `iss` claim) and can verify but never mint; the app holds
6
+ // the private key and signs short-lived per-file tokens with it. This helper is mechanism only —
7
+ // key material and issuer identity stay the app's configuration (env), never cursedbelt's.
8
+
9
+ export type Rs256FileTokenSignerOptions = {
10
+ /** PKCS8 PEM private key. `\n` escape sequences are accepted (env-var friendly). */
11
+ privateKeyPem: string;
12
+ /** The `iss` claim — must match the issuer the tenant was registered with. */
13
+ issuer: string;
14
+ /** Token lifetime in seconds (default 6h — long enough for a paused, resumed video). */
15
+ ttlSeconds?: number;
16
+ };
17
+
18
+ export function createRs256FileTokenSigner(opts: Rs256FileTokenSignerOptions): FileTokenSigner {
19
+ const ttl = opts.ttlSeconds ?? 6 * 60 * 60;
20
+ const pem = opts.privateKeyPem.includes('-----')
21
+ ? opts.privateKeyPem.replace(/\\n/g, '\n')
22
+ : Buffer.from(opts.privateKeyPem, 'base64').toString('utf8');
23
+ // Lazy: a bad key must surface as a sign-time error, not an unhandled
24
+ // rejection parked on a module-scope promise from construction time.
25
+ let keyPromise: ReturnType<typeof importPKCS8> | null = null;
26
+ return async (claims: FileTokenClaims): Promise<string> => {
27
+ keyPromise ??= importPKCS8(pem, 'RS256');
28
+ const key = await keyPromise;
29
+ // Drop undefined values — jose serializes them as null otherwise.
30
+ const payload = Object.fromEntries(
31
+ Object.entries(claims).filter(([, v]) => v !== undefined),
32
+ );
33
+ return new SignJWT(payload)
34
+ .setProtectedHeader({ alg: 'RS256' })
35
+ .setIssuer(opts.issuer)
36
+ .setIssuedAt()
37
+ .setExpirationTime(`${ttl}s`)
38
+ .sign(key);
39
+ };
40
+ }
@@ -0,0 +1,53 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { Hono } from 'hono';
3
+ import { getOnlyEnforcement, isGetOnlyViolation } from './getOnly';
4
+ import type { AuthEnv, AuthUser } from './types';
5
+
6
+ const REQ = (path: string, init?: RequestInit) => new Request(`http://localhost${path}`, init);
7
+
8
+ describe('isGetOnlyViolation', () => {
9
+ test('blocks non-GET only for getOnly sessions', () => {
10
+ expect(isGetOnlyViolation({ getOnly: true }, 'POST')).toBe(true);
11
+ expect(isGetOnlyViolation({ getOnly: true }, 'DELETE')).toBe(true);
12
+ expect(isGetOnlyViolation({ getOnly: true }, 'GET')).toBe(false);
13
+ expect(isGetOnlyViolation({ getOnly: true }, 'HEAD')).toBe(false);
14
+ expect(isGetOnlyViolation({ getOnly: false }, 'POST')).toBe(false);
15
+ expect(isGetOnlyViolation({}, 'DELETE')).toBe(false);
16
+ });
17
+ });
18
+
19
+ const appWith = (user: AuthUser) => {
20
+ const app = new Hono<AuthEnv>();
21
+ app.use('*', async (c, next) => {
22
+ c.set('user', user);
23
+ await next();
24
+ });
25
+ app.use('*', getOnlyEnforcement());
26
+ app.get('/notes', (c) => c.json({ ok: true }));
27
+ app.post('/notes', (c) => c.json({ ok: true }));
28
+ app.get('/ctgrpo/notes', (c) => c.json({ ok: true })); // ctgr mutation rides a GET
29
+ return app;
30
+ };
31
+
32
+ describe('getOnlyEnforcement middleware', () => {
33
+ const getOnlyUser: AuthUser = { id: 'u1', roles: [], getOnly: true, sessionId: 's1' };
34
+ const normalUser: AuthUser = { id: 'u2', roles: [], getOnly: false, sessionId: 's2' };
35
+
36
+ test('getOnly session: GET passes', async () => {
37
+ expect((await appWith(getOnlyUser).fetch(REQ('/notes'))).status).toBe(200);
38
+ });
39
+
40
+ test('getOnly session: raw POST → 403 envelope', async () => {
41
+ const res = await appWith(getOnlyUser).fetch(REQ('/notes', { method: 'POST' }));
42
+ expect(res.status).toBe(403);
43
+ expect((await res.json()).error.message).toBe('GET-only session');
44
+ });
45
+
46
+ test('getOnly session: a ctgr GET-twin (transport GET) passes', async () => {
47
+ expect((await appWith(getOnlyUser).fetch(REQ('/ctgrpo/notes'))).status).toBe(200);
48
+ });
49
+
50
+ test('normal session: POST passes', async () => {
51
+ expect((await appWith(normalUser).fetch(REQ('/notes', { method: 'POST' }))).status).toBe(200);
52
+ });
53
+ });
@@ -0,0 +1,32 @@
1
+ import type { MiddlewareHandler } from 'hono';
2
+ import { ApiError, toErrorEnvelope } from '../errors';
3
+ import type { AuthEnv } from './types';
4
+
5
+ /**
6
+ * `getOnly` enforcement — the server-side half of the `/f` GET-only session
7
+ * guarantee. A getOnly session (the obfuscated `/f` login) may read freely and may
8
+ * mutate ONLY through the ctgr GET-twins (which are GETs at the transport level);
9
+ * any raw non-GET/HEAD verb from such a session is rejected with 403.
10
+ *
11
+ * Mount GLOBALLY (`app.use('*', getOnlyEnforcement())`) AFTER `createAuthMiddleware`
12
+ * — it reads the `user` the auth middleware sets and gates purely on the HTTP verb,
13
+ * so it needs no ctgr path pattern (it must see EVERY mutating request, not only
14
+ * `/ctgr*`). A non-getOnly session — or an unauthenticated request that never reached
15
+ * here — passes through untouched.
16
+ */
17
+
18
+ /** Pure predicate: does this principal+method combination violate the GET-only
19
+ * rule? Exposed for direct unit testing and reuse outside the middleware. */
20
+ export function isGetOnlyViolation(user: { getOnly?: boolean }, method: string): boolean {
21
+ return user.getOnly === true && method !== 'GET' && method !== 'HEAD';
22
+ }
23
+
24
+ export function getOnlyEnforcement(): MiddlewareHandler<AuthEnv> {
25
+ return async (c, next) => {
26
+ const user = c.get('user');
27
+ if (user && isGetOnlyViolation(user, c.req.method)) {
28
+ return c.json(toErrorEnvelope(new ApiError(403, 'GET-only session')), 403);
29
+ }
30
+ return next();
31
+ };
32
+ }
@@ -0,0 +1,51 @@
1
+ // cursedbelt server auth toolkit (task 26) — jose JWT, cookies, CSRF, the getOnly
2
+ // gate, a Kysely-backed session store, login throttle, argon2id passwords, TOTP, and
3
+ // Web-Crypto vault item crypto. The *mechanism* lives here; the *model* (roles,
4
+ // sharing, the `/f`-vs-`/login` policy, session semantics) stays in the apps.
5
+
6
+ export {
7
+ AUTH_COOKIE_NAME,
8
+ clearAuthCookie,
9
+ readAuthCookie,
10
+ writeAuthCookie,
11
+ } from './cookie';
12
+ export { CSRF_COOKIE_NAME, CSRF_HEADER_NAME, type CsrfOptions, createCsrfMiddleware } from './csrf';
13
+ export {
14
+ createRs256FileTokenSigner,
15
+ type Rs256FileTokenSignerOptions,
16
+ } from './fileTokenSigner';
17
+ export { getOnlyEnforcement, isGetOnlyViolation } from './getOnly';
18
+ export { signToken, verifyPurposeToken, verifyToken } from './jwt';
19
+ export {
20
+ clientKeyOf,
21
+ clientKeyOfContext,
22
+ createLoginThrottle,
23
+ type LoginThrottle,
24
+ type LoginThrottleOptions,
25
+ type ThrottleVerdict,
26
+ } from './loginThrottle';
27
+ export { createAuthMiddleware } from './middleware';
28
+ export { hashPassword, needsRehash, verifyPassword } from './password';
29
+ export {
30
+ createSessionStore,
31
+ type SessionStoreOptions,
32
+ } from './sessionStore';
33
+ export {
34
+ DEFAULT_TOTP_ISSUER,
35
+ generateRecoveryCodes,
36
+ generateTotpSecret,
37
+ getTotpUri,
38
+ verifyRecoveryCode,
39
+ verifyTotpCode,
40
+ } from './totp';
41
+ export type {
42
+ AppJwtPayload,
43
+ AuthEnv,
44
+ AuthUser,
45
+ AuthVariables,
46
+ CreateSessionInput,
47
+ Session,
48
+ SessionStore,
49
+ TokenPurpose,
50
+ } from './types';
51
+ export { decryptVaultItem, deriveVaultKey, encryptVaultItem } from './vault';