@antasphere/chassis-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 (798) hide show
  1. package/LICENSE +82 -0
  2. package/dist/accounts/deletion.d.ts +163 -0
  3. package/dist/accounts/deletion.d.ts.map +1 -0
  4. package/dist/accounts/deletion.js +360 -0
  5. package/dist/accounts/deletion.js.map +1 -0
  6. package/dist/accounts/erasure-log.d.ts +66 -0
  7. package/dist/accounts/erasure-log.d.ts.map +1 -0
  8. package/dist/accounts/erasure-log.js +117 -0
  9. package/dist/accounts/erasure-log.js.map +1 -0
  10. package/dist/accounts/index.d.ts +6 -0
  11. package/dist/accounts/index.d.ts.map +1 -0
  12. package/dist/accounts/index.js +6 -0
  13. package/dist/accounts/index.js.map +1 -0
  14. package/dist/accounts/mint-refusal.d.ts +47 -0
  15. package/dist/accounts/mint-refusal.d.ts.map +1 -0
  16. package/dist/accounts/mint-refusal.js +59 -0
  17. package/dist/accounts/mint-refusal.js.map +1 -0
  18. package/dist/accounts/signup-duplicate.d.ts +14 -0
  19. package/dist/accounts/signup-duplicate.d.ts.map +1 -0
  20. package/dist/accounts/signup-duplicate.js +25 -0
  21. package/dist/accounts/signup-duplicate.js.map +1 -0
  22. package/dist/accounts/superadmin.d.ts +27 -0
  23. package/dist/accounts/superadmin.d.ts.map +1 -0
  24. package/dist/accounts/superadmin.js +46 -0
  25. package/dist/accounts/superadmin.js.map +1 -0
  26. package/dist/api/apikeys.d.ts +14 -0
  27. package/dist/api/apikeys.d.ts.map +1 -0
  28. package/dist/api/apikeys.js +124 -0
  29. package/dist/api/apikeys.js.map +1 -0
  30. package/dist/api/audit.d.ts +4 -0
  31. package/dist/api/audit.d.ts.map +1 -0
  32. package/dist/api/audit.js +75 -0
  33. package/dist/api/audit.js.map +1 -0
  34. package/dist/api/break-glass.d.ts +48 -0
  35. package/dist/api/break-glass.d.ts.map +1 -0
  36. package/dist/api/break-glass.js +217 -0
  37. package/dist/api/break-glass.js.map +1 -0
  38. package/dist/api/cli-auth.d.ts +84 -0
  39. package/dist/api/cli-auth.d.ts.map +1 -0
  40. package/dist/api/cli-auth.js +177 -0
  41. package/dist/api/cli-auth.js.map +1 -0
  42. package/dist/api/create-api.d.ts +123 -0
  43. package/dist/api/create-api.d.ts.map +1 -0
  44. package/dist/api/create-api.js +1165 -0
  45. package/dist/api/create-api.js.map +1 -0
  46. package/dist/api/demo-passes.d.ts +45 -0
  47. package/dist/api/demo-passes.d.ts.map +1 -0
  48. package/dist/api/demo-passes.js +143 -0
  49. package/dist/api/demo-passes.js.map +1 -0
  50. package/dist/api/errors.d.ts +5 -0
  51. package/dist/api/errors.d.ts.map +1 -0
  52. package/dist/api/errors.js +5 -0
  53. package/dist/api/errors.js.map +1 -0
  54. package/dist/api/export.d.ts +63 -0
  55. package/dist/api/export.d.ts.map +1 -0
  56. package/dist/api/export.js +393 -0
  57. package/dist/api/export.js.map +1 -0
  58. package/dist/api/files.d.ts +40 -0
  59. package/dist/api/files.d.ts.map +1 -0
  60. package/dist/api/files.js +150 -0
  61. package/dist/api/files.js.map +1 -0
  62. package/dist/api/index.d.ts +17 -0
  63. package/dist/api/index.d.ts.map +1 -0
  64. package/dist/api/index.js +17 -0
  65. package/dist/api/index.js.map +1 -0
  66. package/dist/api/invitations.d.ts +50 -0
  67. package/dist/api/invitations.d.ts.map +1 -0
  68. package/dist/api/invitations.js +313 -0
  69. package/dist/api/invitations.js.map +1 -0
  70. package/dist/api/members.d.ts +25 -0
  71. package/dist/api/members.d.ts.map +1 -0
  72. package/dist/api/members.js +522 -0
  73. package/dist/api/members.js.map +1 -0
  74. package/dist/api/onboarding.d.ts +26 -0
  75. package/dist/api/onboarding.d.ts.map +1 -0
  76. package/dist/api/onboarding.js +23 -0
  77. package/dist/api/onboarding.js.map +1 -0
  78. package/dist/api/openapi-doc.d.ts +21 -0
  79. package/dist/api/openapi-doc.d.ts.map +1 -0
  80. package/dist/api/openapi-doc.js +25 -0
  81. package/dist/api/openapi-doc.js.map +1 -0
  82. package/dist/api/projects.d.ts +24 -0
  83. package/dist/api/projects.d.ts.map +1 -0
  84. package/dist/api/projects.js +624 -0
  85. package/dist/api/projects.js.map +1 -0
  86. package/dist/api/scope-routes.d.ts +12 -0
  87. package/dist/api/scope-routes.d.ts.map +1 -0
  88. package/dist/api/scope-routes.js +2 -0
  89. package/dist/api/scope-routes.js.map +1 -0
  90. package/dist/api/sso-connect.d.ts +70 -0
  91. package/dist/api/sso-connect.d.ts.map +1 -0
  92. package/dist/api/sso-connect.js +164 -0
  93. package/dist/api/sso-connect.js.map +1 -0
  94. package/dist/api/sso-logout.d.ts +41 -0
  95. package/dist/api/sso-logout.d.ts.map +1 -0
  96. package/dist/api/sso-logout.js +34 -0
  97. package/dist/api/sso-logout.js.map +1 -0
  98. package/dist/api/teams.d.ts +43 -0
  99. package/dist/api/teams.d.ts.map +1 -0
  100. package/dist/api/teams.js +447 -0
  101. package/dist/api/teams.js.map +1 -0
  102. package/dist/api/workspaces.d.ts +114 -0
  103. package/dist/api/workspaces.d.ts.map +1 -0
  104. package/dist/api/workspaces.js +462 -0
  105. package/dist/api/workspaces.js.map +1 -0
  106. package/dist/apikeys/index.d.ts +3 -0
  107. package/dist/apikeys/index.d.ts.map +1 -0
  108. package/dist/apikeys/index.js +3 -0
  109. package/dist/apikeys/index.js.map +1 -0
  110. package/dist/apikeys/peppers.d.ts +53 -0
  111. package/dist/apikeys/peppers.d.ts.map +1 -0
  112. package/dist/apikeys/peppers.js +97 -0
  113. package/dist/apikeys/peppers.js.map +1 -0
  114. package/dist/apikeys/service.d.ts +56 -0
  115. package/dist/apikeys/service.d.ts.map +1 -0
  116. package/dist/apikeys/service.js +196 -0
  117. package/dist/apikeys/service.js.map +1 -0
  118. package/dist/app.d.ts +65 -0
  119. package/dist/app.d.ts.map +1 -0
  120. package/dist/app.js +142 -0
  121. package/dist/app.js.map +1 -0
  122. package/dist/audit/filters.d.ts +38 -0
  123. package/dist/audit/filters.d.ts.map +1 -0
  124. package/dist/audit/filters.js +97 -0
  125. package/dist/audit/filters.js.map +1 -0
  126. package/dist/audit/index.d.ts +3 -0
  127. package/dist/audit/index.d.ts.map +1 -0
  128. package/dist/audit/index.js +3 -0
  129. package/dist/audit/index.js.map +1 -0
  130. package/dist/audit/service.d.ts +65 -0
  131. package/dist/audit/service.d.ts.map +1 -0
  132. package/dist/audit/service.js +103 -0
  133. package/dist/audit/service.js.map +1 -0
  134. package/dist/boot.d.ts +8 -0
  135. package/dist/boot.d.ts.map +1 -0
  136. package/dist/boot.js +822 -0
  137. package/dist/boot.js.map +1 -0
  138. package/dist/constant-time.d.ts +3 -0
  139. package/dist/constant-time.d.ts.map +1 -0
  140. package/dist/constant-time.js +10 -0
  141. package/dist/constant-time.js.map +1 -0
  142. package/dist/email/driver.d.ts +16 -0
  143. package/dist/email/driver.d.ts.map +1 -0
  144. package/dist/email/driver.js +136 -0
  145. package/dist/email/driver.js.map +1 -0
  146. package/dist/email/index.d.ts +4 -0
  147. package/dist/email/index.d.ts.map +1 -0
  148. package/dist/email/index.js +4 -0
  149. package/dist/email/index.js.map +1 -0
  150. package/dist/email/shell.d.ts +72 -0
  151. package/dist/email/shell.d.ts.map +1 -0
  152. package/dist/email/shell.js +146 -0
  153. package/dist/email/shell.js.map +1 -0
  154. package/dist/email/templates.d.ts +83 -0
  155. package/dist/email/templates.d.ts.map +1 -0
  156. package/dist/email/templates.js +96 -0
  157. package/dist/email/templates.js.map +1 -0
  158. package/dist/entitlements/check.d.ts +140 -0
  159. package/dist/entitlements/check.d.ts.map +1 -0
  160. package/dist/entitlements/check.js +276 -0
  161. package/dist/entitlements/check.js.map +1 -0
  162. package/dist/entitlements/clock-skew.d.ts +27 -0
  163. package/dist/entitlements/clock-skew.d.ts.map +1 -0
  164. package/dist/entitlements/clock-skew.js +58 -0
  165. package/dist/entitlements/clock-skew.js.map +1 -0
  166. package/dist/entitlements/gate.d.ts +113 -0
  167. package/dist/entitlements/gate.d.ts.map +1 -0
  168. package/dist/entitlements/gate.js +430 -0
  169. package/dist/entitlements/gate.js.map +1 -0
  170. package/dist/entitlements/hub-machine-token.d.ts +83 -0
  171. package/dist/entitlements/hub-machine-token.d.ts.map +1 -0
  172. package/dist/entitlements/hub-machine-token.js +175 -0
  173. package/dist/entitlements/hub-machine-token.js.map +1 -0
  174. package/dist/entitlements/hub-subject.d.ts +32 -0
  175. package/dist/entitlements/hub-subject.d.ts.map +1 -0
  176. package/dist/entitlements/hub-subject.js +68 -0
  177. package/dist/entitlements/hub-subject.js.map +1 -0
  178. package/dist/entitlements/index.d.ts +9 -0
  179. package/dist/entitlements/index.d.ts.map +1 -0
  180. package/dist/entitlements/index.js +9 -0
  181. package/dist/entitlements/index.js.map +1 -0
  182. package/dist/entitlements/poster.d.ts +81 -0
  183. package/dist/entitlements/poster.d.ts.map +1 -0
  184. package/dist/entitlements/poster.js +169 -0
  185. package/dist/entitlements/poster.js.map +1 -0
  186. package/dist/entitlements/profiles.d.ts +121 -0
  187. package/dist/entitlements/profiles.d.ts.map +1 -0
  188. package/dist/entitlements/profiles.js +212 -0
  189. package/dist/entitlements/profiles.js.map +1 -0
  190. package/dist/entitlements/slot.d.ts +33 -0
  191. package/dist/entitlements/slot.d.ts.map +1 -0
  192. package/dist/entitlements/slot.js +75 -0
  193. package/dist/entitlements/slot.js.map +1 -0
  194. package/dist/env.d.ts +293 -0
  195. package/dist/env.d.ts.map +1 -0
  196. package/dist/env.js +383 -0
  197. package/dist/env.js.map +1 -0
  198. package/dist/files/http.d.ts +23 -0
  199. package/dist/files/http.d.ts.map +1 -0
  200. package/dist/files/http.js +66 -0
  201. package/dist/files/http.js.map +1 -0
  202. package/dist/files/index.d.ts +5 -0
  203. package/dist/files/index.d.ts.map +1 -0
  204. package/dist/files/index.js +5 -0
  205. package/dist/files/index.js.map +1 -0
  206. package/dist/files/serve.d.ts +43 -0
  207. package/dist/files/serve.d.ts.map +1 -0
  208. package/dist/files/serve.js +71 -0
  209. package/dist/files/serve.js.map +1 -0
  210. package/dist/files/service.d.ts +69 -0
  211. package/dist/files/service.d.ts.map +1 -0
  212. package/dist/files/service.js +188 -0
  213. package/dist/files/service.js.map +1 -0
  214. package/dist/files/spool.d.ts +21 -0
  215. package/dist/files/spool.d.ts.map +1 -0
  216. package/dist/files/spool.js +47 -0
  217. package/dist/files/spool.js.map +1 -0
  218. package/dist/identity/better-auth.d.ts +3927 -0
  219. package/dist/identity/better-auth.d.ts.map +1 -0
  220. package/dist/identity/better-auth.js +896 -0
  221. package/dist/identity/better-auth.js.map +1 -0
  222. package/dist/identity/demo-pass-plugin.d.ts +29 -0
  223. package/dist/identity/demo-pass-plugin.d.ts.map +1 -0
  224. package/dist/identity/demo-pass-plugin.js +88 -0
  225. package/dist/identity/demo-pass-plugin.js.map +1 -0
  226. package/dist/identity/demo-pass-rules.d.ts +84 -0
  227. package/dist/identity/demo-pass-rules.d.ts.map +1 -0
  228. package/dist/identity/demo-pass-rules.js +223 -0
  229. package/dist/identity/demo-pass-rules.js.map +1 -0
  230. package/dist/identity/demo-pass.d.ts +159 -0
  231. package/dist/identity/demo-pass.d.ts.map +1 -0
  232. package/dist/identity/demo-pass.js +319 -0
  233. package/dist/identity/demo-pass.js.map +1 -0
  234. package/dist/identity/hub-grant.d.ts +182 -0
  235. package/dist/identity/hub-grant.d.ts.map +1 -0
  236. package/dist/identity/hub-grant.js +594 -0
  237. package/dist/identity/hub-grant.js.map +1 -0
  238. package/dist/identity/hub-jwt.d.ts +22 -0
  239. package/dist/identity/hub-jwt.d.ts.map +1 -0
  240. package/dist/identity/hub-jwt.js +77 -0
  241. package/dist/identity/hub-jwt.js.map +1 -0
  242. package/dist/identity/hub-live-gate.d.ts +17 -0
  243. package/dist/identity/hub-live-gate.d.ts.map +1 -0
  244. package/dist/identity/hub-live-gate.js +146 -0
  245. package/dist/identity/hub-live-gate.js.map +1 -0
  246. package/dist/identity/hub-logout.d.ts +88 -0
  247. package/dist/identity/hub-logout.d.ts.map +1 -0
  248. package/dist/identity/hub-logout.js +158 -0
  249. package/dist/identity/hub-logout.js.map +1 -0
  250. package/dist/identity/hub-projection.d.ts +103 -0
  251. package/dist/identity/hub-projection.d.ts.map +1 -0
  252. package/dist/identity/hub-projection.js +130 -0
  253. package/dist/identity/hub-projection.js.map +1 -0
  254. package/dist/identity/hub-reconcile.d.ts +151 -0
  255. package/dist/identity/hub-reconcile.d.ts.map +1 -0
  256. package/dist/identity/hub-reconcile.js +400 -0
  257. package/dist/identity/hub-reconcile.js.map +1 -0
  258. package/dist/identity/hub-sso.d.ts +363 -0
  259. package/dist/identity/hub-sso.d.ts.map +1 -0
  260. package/dist/identity/hub-sso.js +536 -0
  261. package/dist/identity/hub-sso.js.map +1 -0
  262. package/dist/identity/hub-user-client.d.ts +222 -0
  263. package/dist/identity/hub-user-client.d.ts.map +1 -0
  264. package/dist/identity/hub-user-client.js +406 -0
  265. package/dist/identity/hub-user-client.js.map +1 -0
  266. package/dist/identity/index.d.ts +13 -0
  267. package/dist/identity/index.d.ts.map +1 -0
  268. package/dist/identity/index.js +13 -0
  269. package/dist/identity/index.js.map +1 -0
  270. package/dist/identity/oauth-jwt.d.ts +32 -0
  271. package/dist/identity/oauth-jwt.d.ts.map +1 -0
  272. package/dist/identity/oauth-jwt.js +122 -0
  273. package/dist/identity/oauth-jwt.js.map +1 -0
  274. package/dist/identity/resolve-membership.d.ts +28 -0
  275. package/dist/identity/resolve-membership.d.ts.map +1 -0
  276. package/dist/identity/resolve-membership.js +69 -0
  277. package/dist/identity/resolve-membership.js.map +1 -0
  278. package/dist/identity/signing-key.d.ts +49 -0
  279. package/dist/identity/signing-key.d.ts.map +1 -0
  280. package/dist/identity/signing-key.js +144 -0
  281. package/dist/identity/signing-key.js.map +1 -0
  282. package/dist/index.d.ts +21 -0
  283. package/dist/index.d.ts.map +1 -0
  284. package/dist/index.js +11 -0
  285. package/dist/index.js.map +1 -0
  286. package/dist/invitations/index.d.ts +2 -0
  287. package/dist/invitations/index.d.ts.map +1 -0
  288. package/dist/invitations/index.js +2 -0
  289. package/dist/invitations/index.js.map +1 -0
  290. package/dist/invitations/service.d.ts +36 -0
  291. package/dist/invitations/service.d.ts.map +1 -0
  292. package/dist/invitations/service.js +122 -0
  293. package/dist/invitations/service.js.map +1 -0
  294. package/dist/jobs/index.d.ts +3 -0
  295. package/dist/jobs/index.d.ts.map +1 -0
  296. package/dist/jobs/index.js +3 -0
  297. package/dist/jobs/index.js.map +1 -0
  298. package/dist/jobs/pgboss.d.ts +119 -0
  299. package/dist/jobs/pgboss.d.ts.map +1 -0
  300. package/dist/jobs/pgboss.js +521 -0
  301. package/dist/jobs/pgboss.js.map +1 -0
  302. package/dist/jobs/timers.d.ts +127 -0
  303. package/dist/jobs/timers.d.ts.map +1 -0
  304. package/dist/jobs/timers.js +228 -0
  305. package/dist/jobs/timers.js.map +1 -0
  306. package/dist/logger.d.ts +17 -0
  307. package/dist/logger.d.ts.map +1 -0
  308. package/dist/logger.js +64 -0
  309. package/dist/logger.js.map +1 -0
  310. package/dist/mcp/errors.d.ts +37 -0
  311. package/dist/mcp/errors.d.ts.map +1 -0
  312. package/dist/mcp/errors.js +77 -0
  313. package/dist/mcp/errors.js.map +1 -0
  314. package/dist/mcp/http.d.ts +35 -0
  315. package/dist/mcp/http.d.ts.map +1 -0
  316. package/dist/mcp/http.js +80 -0
  317. package/dist/mcp/http.js.map +1 -0
  318. package/dist/mcp/index.d.ts +8 -0
  319. package/dist/mcp/index.d.ts.map +1 -0
  320. package/dist/mcp/index.js +8 -0
  321. package/dist/mcp/index.js.map +1 -0
  322. package/dist/mcp/inputs.d.ts +19 -0
  323. package/dist/mcp/inputs.d.ts.map +1 -0
  324. package/dist/mcp/inputs.js +28 -0
  325. package/dist/mcp/inputs.js.map +1 -0
  326. package/dist/mcp/projects.d.ts +31 -0
  327. package/dist/mcp/projects.d.ts.map +1 -0
  328. package/dist/mcp/projects.js +315 -0
  329. package/dist/mcp/projects.js.map +1 -0
  330. package/dist/mcp/server.d.ts +52 -0
  331. package/dist/mcp/server.d.ts.map +1 -0
  332. package/dist/mcp/server.js +93 -0
  333. package/dist/mcp/server.js.map +1 -0
  334. package/dist/mcp/teams.d.ts +7 -0
  335. package/dist/mcp/teams.d.ts.map +1 -0
  336. package/dist/mcp/teams.js +62 -0
  337. package/dist/mcp/teams.js.map +1 -0
  338. package/dist/mcp/tool-kit.d.ts +67 -0
  339. package/dist/mcp/tool-kit.d.ts.map +1 -0
  340. package/dist/mcp/tool-kit.js +74 -0
  341. package/dist/mcp/tool-kit.js.map +1 -0
  342. package/dist/members/removal.d.ts +95 -0
  343. package/dist/members/removal.d.ts.map +1 -0
  344. package/dist/members/removal.js +143 -0
  345. package/dist/members/removal.js.map +1 -0
  346. package/dist/middleware/auth-body.d.ts +25 -0
  347. package/dist/middleware/auth-body.d.ts.map +1 -0
  348. package/dist/middleware/auth-body.js +55 -0
  349. package/dist/middleware/auth-body.js.map +1 -0
  350. package/dist/middleware/auth-context.d.ts +170 -0
  351. package/dist/middleware/auth-context.d.ts.map +1 -0
  352. package/dist/middleware/auth-context.js +251 -0
  353. package/dist/middleware/auth-context.js.map +1 -0
  354. package/dist/middleware/body-cap.d.ts +17 -0
  355. package/dist/middleware/body-cap.d.ts.map +1 -0
  356. package/dist/middleware/body-cap.js +31 -0
  357. package/dist/middleware/body-cap.js.map +1 -0
  358. package/dist/middleware/body-refusal.d.ts +20 -0
  359. package/dist/middleware/body-refusal.d.ts.map +1 -0
  360. package/dist/middleware/body-refusal.js +5 -0
  361. package/dist/middleware/body-refusal.js.map +1 -0
  362. package/dist/middleware/cross-site.d.ts +61 -0
  363. package/dist/middleware/cross-site.d.ts.map +1 -0
  364. package/dist/middleware/cross-site.js +59 -0
  365. package/dist/middleware/cross-site.js.map +1 -0
  366. package/dist/middleware/hub-managed.d.ts +45 -0
  367. package/dist/middleware/hub-managed.d.ts.map +1 -0
  368. package/dist/middleware/hub-managed.js +56 -0
  369. package/dist/middleware/hub-managed.js.map +1 -0
  370. package/dist/middleware/idempotency.d.ts +14 -0
  371. package/dist/middleware/idempotency.d.ts.map +1 -0
  372. package/dist/middleware/idempotency.js +222 -0
  373. package/dist/middleware/idempotency.js.map +1 -0
  374. package/dist/middleware/index.d.ts +12 -0
  375. package/dist/middleware/index.d.ts.map +1 -0
  376. package/dist/middleware/index.js +12 -0
  377. package/dist/middleware/index.js.map +1 -0
  378. package/dist/middleware/json-depth.d.ts +32 -0
  379. package/dist/middleware/json-depth.d.ts.map +1 -0
  380. package/dist/middleware/json-depth.js +80 -0
  381. package/dist/middleware/json-depth.js.map +1 -0
  382. package/dist/middleware/no-store.d.ts +21 -0
  383. package/dist/middleware/no-store.d.ts.map +1 -0
  384. package/dist/middleware/no-store.js +32 -0
  385. package/dist/middleware/no-store.js.map +1 -0
  386. package/dist/middleware/oauth-public.d.ts +16 -0
  387. package/dist/middleware/oauth-public.d.ts.map +1 -0
  388. package/dist/middleware/oauth-public.js +50 -0
  389. package/dist/middleware/oauth-public.js.map +1 -0
  390. package/dist/middleware/rate-limit.d.ts +182 -0
  391. package/dist/middleware/rate-limit.d.ts.map +1 -0
  392. package/dist/middleware/rate-limit.js +277 -0
  393. package/dist/middleware/rate-limit.js.map +1 -0
  394. package/dist/middleware/request-id.d.ts +10 -0
  395. package/dist/middleware/request-id.d.ts.map +1 -0
  396. package/dist/middleware/request-id.js +42 -0
  397. package/dist/middleware/request-id.js.map +1 -0
  398. package/dist/middleware/scopes.d.ts +33 -0
  399. package/dist/middleware/scopes.d.ts.map +1 -0
  400. package/dist/middleware/scopes.js +139 -0
  401. package/dist/middleware/scopes.js.map +1 -0
  402. package/dist/middleware/security-headers.d.ts +53 -0
  403. package/dist/middleware/security-headers.d.ts.map +1 -0
  404. package/dist/middleware/security-headers.js +96 -0
  405. package/dist/middleware/security-headers.js.map +1 -0
  406. package/dist/observability/index.d.ts +3 -0
  407. package/dist/observability/index.d.ts.map +1 -0
  408. package/dist/observability/index.js +3 -0
  409. package/dist/observability/index.js.map +1 -0
  410. package/dist/observability/metrics.d.ts +17 -0
  411. package/dist/observability/metrics.d.ts.map +1 -0
  412. package/dist/observability/metrics.js +82 -0
  413. package/dist/observability/metrics.js.map +1 -0
  414. package/dist/observability/otel.d.ts +20 -0
  415. package/dist/observability/otel.d.ts.map +1 -0
  416. package/dist/observability/otel.js +54 -0
  417. package/dist/observability/otel.js.map +1 -0
  418. package/dist/pagination.d.ts +57 -0
  419. package/dist/pagination.d.ts.map +1 -0
  420. package/dist/pagination.js +90 -0
  421. package/dist/pagination.js.map +1 -0
  422. package/dist/platform/edition.d.ts +57 -0
  423. package/dist/platform/edition.d.ts.map +1 -0
  424. package/dist/platform/edition.js +90 -0
  425. package/dist/platform/edition.js.map +1 -0
  426. package/dist/platform/entitlements.d.ts +26 -0
  427. package/dist/platform/entitlements.d.ts.map +1 -0
  428. package/dist/platform/entitlements.js +28 -0
  429. package/dist/platform/entitlements.js.map +1 -0
  430. package/dist/platform/events.d.ts +87 -0
  431. package/dist/platform/events.d.ts.map +1 -0
  432. package/dist/platform/events.js +32 -0
  433. package/dist/platform/events.js.map +1 -0
  434. package/dist/platform/index.d.ts +8 -0
  435. package/dist/platform/index.d.ts.map +1 -0
  436. package/dist/platform/index.js +8 -0
  437. package/dist/platform/index.js.map +1 -0
  438. package/dist/platform/local-identity.d.ts +39 -0
  439. package/dist/platform/local-identity.d.ts.map +1 -0
  440. package/dist/platform/local-identity.js +87 -0
  441. package/dist/platform/local-identity.js.map +1 -0
  442. package/dist/platform/registry.d.ts +23 -0
  443. package/dist/platform/registry.d.ts.map +1 -0
  444. package/dist/platform/registry.js +5 -0
  445. package/dist/platform/registry.js.map +1 -0
  446. package/dist/platform/usage.d.ts +11 -0
  447. package/dist/platform/usage.d.ts.map +1 -0
  448. package/dist/platform/usage.js +12 -0
  449. package/dist/platform/usage.js.map +1 -0
  450. package/dist/platform/workspaces.d.ts +25 -0
  451. package/dist/platform/workspaces.d.ts.map +1 -0
  452. package/dist/platform/workspaces.js +44 -0
  453. package/dist/platform/workspaces.js.map +1 -0
  454. package/dist/projects/access.d.ts +40 -0
  455. package/dist/projects/access.d.ts.map +1 -0
  456. package/dist/projects/access.js +133 -0
  457. package/dist/projects/access.js.map +1 -0
  458. package/dist/projects/index.d.ts +2 -0
  459. package/dist/projects/index.d.ts.map +1 -0
  460. package/dist/projects/index.js +2 -0
  461. package/dist/projects/index.js.map +1 -0
  462. package/dist/route-label.d.ts +23 -0
  463. package/dist/route-label.d.ts.map +1 -0
  464. package/dist/route-label.js +29 -0
  465. package/dist/route-label.js.map +1 -0
  466. package/dist/routes/health.d.ts +30 -0
  467. package/dist/routes/health.d.ts.map +1 -0
  468. package/dist/routes/health.js +54 -0
  469. package/dist/routes/health.js.map +1 -0
  470. package/dist/routes/index.d.ts +3 -0
  471. package/dist/routes/index.d.ts.map +1 -0
  472. package/dist/routes/index.js +3 -0
  473. package/dist/routes/index.js.map +1 -0
  474. package/dist/routes/wellknown.d.ts +11 -0
  475. package/dist/routes/wellknown.d.ts.map +1 -0
  476. package/dist/routes/wellknown.js +45 -0
  477. package/dist/routes/wellknown.js.map +1 -0
  478. package/dist/secret.d.ts +30 -0
  479. package/dist/secret.d.ts.map +1 -0
  480. package/dist/secret.js +97 -0
  481. package/dist/secret.js.map +1 -0
  482. package/dist/setup-transport.d.ts +23 -0
  483. package/dist/setup-transport.d.ts.map +1 -0
  484. package/dist/setup-transport.js +52 -0
  485. package/dist/setup-transport.js.map +1 -0
  486. package/dist/state.d.ts +21 -0
  487. package/dist/state.d.ts.map +1 -0
  488. package/dist/state.js +4 -0
  489. package/dist/state.js.map +1 -0
  490. package/dist/storage/driver.d.ts +38 -0
  491. package/dist/storage/driver.d.ts.map +1 -0
  492. package/dist/storage/driver.js +14 -0
  493. package/dist/storage/driver.js.map +1 -0
  494. package/dist/storage/factory.d.ts +4 -0
  495. package/dist/storage/factory.d.ts.map +1 -0
  496. package/dist/storage/factory.js +20 -0
  497. package/dist/storage/factory.js.map +1 -0
  498. package/dist/storage/index.d.ts +4 -0
  499. package/dist/storage/index.d.ts.map +1 -0
  500. package/dist/storage/index.js +4 -0
  501. package/dist/storage/index.js.map +1 -0
  502. package/dist/storage/local.d.ts +21 -0
  503. package/dist/storage/local.d.ts.map +1 -0
  504. package/dist/storage/local.js +88 -0
  505. package/dist/storage/local.js.map +1 -0
  506. package/dist/storage/s3.d.ts +29 -0
  507. package/dist/storage/s3.d.ts.map +1 -0
  508. package/dist/storage/s3.js +82 -0
  509. package/dist/storage/s3.js.map +1 -0
  510. package/dist/testing/fake-hub.d.ts +425 -0
  511. package/dist/testing/fake-hub.d.ts.map +1 -0
  512. package/dist/testing/fake-hub.js +1074 -0
  513. package/dist/testing/fake-hub.js.map +1 -0
  514. package/dist/testing/helpers.d.ts +124 -0
  515. package/dist/testing/helpers.d.ts.map +1 -0
  516. package/dist/testing/helpers.js +174 -0
  517. package/dist/testing/helpers.js.map +1 -0
  518. package/dist/testing/index.d.ts +12 -0
  519. package/dist/testing/index.d.ts.map +1 -0
  520. package/dist/testing/index.js +12 -0
  521. package/dist/testing/index.js.map +1 -0
  522. package/dist/testing/setup-origins.d.ts +21 -0
  523. package/dist/testing/setup-origins.d.ts.map +1 -0
  524. package/dist/testing/setup-origins.js +57 -0
  525. package/dist/testing/setup-origins.js.map +1 -0
  526. package/dist/testing/sso-helpers.d.ts +43 -0
  527. package/dist/testing/sso-helpers.d.ts.map +1 -0
  528. package/dist/testing/sso-helpers.js +159 -0
  529. package/dist/testing/sso-helpers.js.map +1 -0
  530. package/dist/tool-definition.d.ts +382 -0
  531. package/dist/tool-definition.d.ts.map +1 -0
  532. package/dist/tool-definition.js +46 -0
  533. package/dist/tool-definition.js.map +1 -0
  534. package/dist/util/index.d.ts +8 -0
  535. package/dist/util/index.d.ts.map +1 -0
  536. package/dist/util/index.js +8 -0
  537. package/dist/util/index.js.map +1 -0
  538. package/package.json +161 -0
  539. package/src/accounts/deletion.ts +389 -0
  540. package/src/accounts/erasure-log.ts +140 -0
  541. package/src/accounts/index.ts +5 -0
  542. package/src/accounts/mint-refusal.ts +67 -0
  543. package/src/accounts/signup-duplicate.ts +21 -0
  544. package/src/accounts/superadmin.ts +46 -0
  545. package/src/api/apikeys.ts +147 -0
  546. package/src/api/audit.ts +83 -0
  547. package/src/api/break-glass.ts +294 -0
  548. package/src/api/cli-auth.ts +308 -0
  549. package/src/api/create-api.ts +1403 -0
  550. package/src/api/demo-passes.ts +207 -0
  551. package/src/api/errors.ts +13 -0
  552. package/src/api/export.ts +482 -0
  553. package/src/api/files.ts +200 -0
  554. package/src/api/index.ts +29 -0
  555. package/src/api/invitations.ts +396 -0
  556. package/src/api/members.ts +617 -0
  557. package/src/api/onboarding.ts +50 -0
  558. package/src/api/openapi-doc.ts +34 -0
  559. package/src/api/projects.ts +788 -0
  560. package/src/api/scope-routes.ts +12 -0
  561. package/src/api/sso-connect.ts +246 -0
  562. package/src/api/sso-logout.ts +76 -0
  563. package/src/api/teams.ts +575 -0
  564. package/src/api/workspaces.ts +649 -0
  565. package/src/apikeys/index.ts +2 -0
  566. package/src/apikeys/peppers.ts +118 -0
  567. package/src/apikeys/service.ts +223 -0
  568. package/src/app.ts +228 -0
  569. package/src/audit/filters.ts +101 -0
  570. package/src/audit/index.ts +9 -0
  571. package/src/audit/service.ts +154 -0
  572. package/src/boot.ts +953 -0
  573. package/src/constant-time.ts +9 -0
  574. package/src/email/driver.ts +139 -0
  575. package/src/email/index.ts +26 -0
  576. package/src/email/shell.ts +201 -0
  577. package/src/email/templates.ts +184 -0
  578. package/src/entitlements/check.ts +435 -0
  579. package/src/entitlements/clock-skew.ts +58 -0
  580. package/src/entitlements/gate.ts +636 -0
  581. package/src/entitlements/hub-machine-token.ts +205 -0
  582. package/src/entitlements/hub-subject.ts +77 -0
  583. package/src/entitlements/index.ts +37 -0
  584. package/src/entitlements/poster.ts +265 -0
  585. package/src/entitlements/profiles.ts +303 -0
  586. package/src/entitlements/slot.ts +105 -0
  587. package/src/env.ts +456 -0
  588. package/src/files/http.ts +77 -0
  589. package/src/files/index.ts +4 -0
  590. package/src/files/serve.ts +115 -0
  591. package/src/files/service.ts +209 -0
  592. package/src/files/spool.ts +58 -0
  593. package/src/identity/better-auth.ts +1103 -0
  594. package/src/identity/demo-pass-plugin.ts +121 -0
  595. package/src/identity/demo-pass-rules.ts +229 -0
  596. package/src/identity/demo-pass.ts +390 -0
  597. package/src/identity/hub-grant.ts +704 -0
  598. package/src/identity/hub-jwt.ts +78 -0
  599. package/src/identity/hub-live-gate.ts +167 -0
  600. package/src/identity/hub-logout.ts +174 -0
  601. package/src/identity/hub-projection.ts +251 -0
  602. package/src/identity/hub-reconcile.ts +572 -0
  603. package/src/identity/hub-sso.ts +696 -0
  604. package/src/identity/hub-user-client.ts +554 -0
  605. package/src/identity/index.ts +56 -0
  606. package/src/identity/oauth-jwt.ts +128 -0
  607. package/src/identity/resolve-membership.ts +104 -0
  608. package/src/identity/signing-key.ts +164 -0
  609. package/src/index.ts +65 -0
  610. package/src/invitations/index.ts +1 -0
  611. package/src/invitations/service.ts +171 -0
  612. package/src/jobs/index.ts +25 -0
  613. package/src/jobs/pgboss.ts +594 -0
  614. package/src/jobs/timers.ts +287 -0
  615. package/src/logger.ts +67 -0
  616. package/src/mcp/errors.ts +111 -0
  617. package/src/mcp/http.ts +129 -0
  618. package/src/mcp/index.ts +28 -0
  619. package/src/mcp/inputs.ts +33 -0
  620. package/src/mcp/projects.ts +480 -0
  621. package/src/mcp/server.ts +174 -0
  622. package/src/mcp/teams.ts +97 -0
  623. package/src/mcp/tool-kit.ts +148 -0
  624. package/src/members/removal.ts +216 -0
  625. package/src/middleware/auth-body.ts +58 -0
  626. package/src/middleware/auth-context.ts +384 -0
  627. package/src/middleware/body-cap.ts +33 -0
  628. package/src/middleware/body-refusal.ts +23 -0
  629. package/src/middleware/cross-site.ts +122 -0
  630. package/src/middleware/hub-managed.ts +61 -0
  631. package/src/middleware/idempotency.ts +264 -0
  632. package/src/middleware/index.ts +33 -0
  633. package/src/middleware/json-depth.ts +74 -0
  634. package/src/middleware/no-store.ts +30 -0
  635. package/src/middleware/oauth-public.ts +54 -0
  636. package/src/middleware/rate-limit.ts +432 -0
  637. package/src/middleware/request-id.ts +54 -0
  638. package/src/middleware/scopes.ts +150 -0
  639. package/src/middleware/security-headers.ts +129 -0
  640. package/src/observability/index.ts +2 -0
  641. package/src/observability/metrics.ts +102 -0
  642. package/src/observability/otel.ts +79 -0
  643. package/src/pagination.ts +118 -0
  644. package/src/platform/edition.ts +140 -0
  645. package/src/platform/entitlements.ts +47 -0
  646. package/src/platform/events.ts +71 -0
  647. package/src/platform/index.ts +7 -0
  648. package/src/platform/local-identity.ts +90 -0
  649. package/src/platform/registry.ts +28 -0
  650. package/src/platform/usage.ts +13 -0
  651. package/src/platform/workspaces.ts +41 -0
  652. package/src/projects/access.ts +160 -0
  653. package/src/projects/index.ts +7 -0
  654. package/src/route-label.ts +29 -0
  655. package/src/routes/health.ts +79 -0
  656. package/src/routes/index.ts +2 -0
  657. package/src/routes/wellknown.ts +67 -0
  658. package/src/secret.ts +115 -0
  659. package/src/setup-transport.ts +46 -0
  660. package/src/state.ts +23 -0
  661. package/src/storage/driver.ts +48 -0
  662. package/src/storage/factory.ts +34 -0
  663. package/src/storage/index.ts +3 -0
  664. package/src/storage/local.ts +93 -0
  665. package/src/storage/s3.ts +109 -0
  666. package/src/testing/fake-hub.ts +1316 -0
  667. package/src/testing/helpers.ts +254 -0
  668. package/src/testing/index.ts +11 -0
  669. package/src/testing/setup-origins.ts +58 -0
  670. package/src/testing/sso-helpers.ts +178 -0
  671. package/src/tool-definition.ts +463 -0
  672. package/src/util/index.ts +17 -0
  673. package/test/fixtures/onboarding-backfill.sql +13 -0
  674. package/test/fixtures/operator-user-id-backfill.sql +18 -0
  675. package/test/host/dr-lib.sh +253 -0
  676. package/test/host/drizzle/0000_chassis_baseline.sql +438 -0
  677. package/test/host/drizzle/0001_last_owner_guard.sql +42 -0
  678. package/test/host/drizzle/meta/0000_snapshot.json +3566 -0
  679. package/test/host/drizzle/meta/_journal.json +20 -0
  680. package/test/host/drizzle.config.ts +17 -0
  681. package/test/host/identity.ts +41 -0
  682. package/test/host/minimal-host.ts +35 -0
  683. package/test/host/minimal-tool.ts +123 -0
  684. package/test/integration/account-oracle-uniformity.test.ts +284 -0
  685. package/test/integration/account-recovery.test.ts +236 -0
  686. package/test/integration/account.test.ts +120 -0
  687. package/test/integration/api-quota.test.ts +208 -0
  688. package/test/integration/apikey-expiry.test.ts +178 -0
  689. package/test/integration/apikey-pepper-rotation.test.ts +190 -0
  690. package/test/integration/boot-posture.test.ts +169 -0
  691. package/test/integration/break-glass.test.ts +484 -0
  692. package/test/integration/cloud-lifecycle.test.ts +233 -0
  693. package/test/integration/credential-boundaries.test.ts +195 -0
  694. package/test/integration/default-workspace.test.ts +414 -0
  695. package/test/integration/demo-pass-rules.test.ts +332 -0
  696. package/test/integration/demo-pass.test.ts +1222 -0
  697. package/test/integration/dr-verify-real-dump.test.ts +154 -0
  698. package/test/integration/edition.test.ts +868 -0
  699. package/test/integration/email-change.test.ts +394 -0
  700. package/test/integration/empty-tool.test.ts +267 -0
  701. package/test/integration/entitlements-null-hook.test.ts +187 -0
  702. package/test/integration/entitlements.test.ts +693 -0
  703. package/test/integration/export-entries.test.ts +321 -0
  704. package/test/integration/files.test.ts +320 -0
  705. package/test/integration/first-boot-claim.test.ts +131 -0
  706. package/test/integration/gdpr.test.ts +532 -0
  707. package/test/integration/helpers.ts +29 -0
  708. package/test/integration/hub-grant.test.ts +505 -0
  709. package/test/integration/hub-live-gate.test.ts +612 -0
  710. package/test/integration/hub-managed-membership.test.ts +559 -0
  711. package/test/integration/hub-org-selector.test.ts +281 -0
  712. package/test/integration/hub-reconcile.test.ts +646 -0
  713. package/test/integration/hub-sso.test.ts +704 -0
  714. package/test/integration/hub-teams.test.ts +771 -0
  715. package/test/integration/idempotency.test.ts +261 -0
  716. package/test/integration/invite-verification.test.ts +210 -0
  717. package/test/integration/last-owner-race.test.ts +263 -0
  718. package/test/integration/m9-fixes.test.ts +290 -0
  719. package/test/integration/mcp-projects.test.ts +463 -0
  720. package/test/integration/member-removal-race.test.ts +466 -0
  721. package/test/integration/member-removal.test.ts +794 -0
  722. package/test/integration/oauth-mcp.test.ts +477 -0
  723. package/test/integration/oauth-workspace.test.ts +371 -0
  724. package/test/integration/observability.test.ts +183 -0
  725. package/test/integration/onboarding.test.ts +268 -0
  726. package/test/integration/orphan-purge.test.ts +451 -0
  727. package/test/integration/pagination.test.ts +344 -0
  728. package/test/integration/platform-core.test.ts +305 -0
  729. package/test/integration/platform.test.ts +246 -0
  730. package/test/integration/pool-survives-restart.test.ts +80 -0
  731. package/test/integration/projects-cloud.test.ts +193 -0
  732. package/test/integration/projects.test.ts +1310 -0
  733. package/test/integration/security-fixes.test.ts +190 -0
  734. package/test/integration/setup-transport.test.ts +118 -0
  735. package/test/integration/signin-wall.test.ts +103 -0
  736. package/test/integration/signing-key-rotation.test.ts +349 -0
  737. package/test/integration/sso-helpers.ts +15 -0
  738. package/test/integration/sso-logout.test.ts +283 -0
  739. package/test/integration/teams-cloud.test.ts +194 -0
  740. package/test/integration/teams.test.ts +551 -0
  741. package/test/integration/timers.test.ts +377 -0
  742. package/test/integration/two-factor.test.ts +301 -0
  743. package/test/integration/usage-window.test.ts +161 -0
  744. package/test/integration/workspace-create-cloud.test.ts +523 -0
  745. package/test/integration/workspace-create.test.ts +510 -0
  746. package/test/integration/workspace-scoping.test.ts +530 -0
  747. package/test/integration/zz-adv-replay-owner.test.ts +205 -0
  748. package/test/unit/apikey-peppers.test.ts +110 -0
  749. package/test/unit/apikey-prefix.test.ts +34 -0
  750. package/test/unit/audit-filters.test.ts +135 -0
  751. package/test/unit/auth-origins.test.ts +92 -0
  752. package/test/unit/body-cap.test.ts +84 -0
  753. package/test/unit/clock-skew.test.ts +68 -0
  754. package/test/unit/cross-site.test.ts +166 -0
  755. package/test/unit/declared-credits.test.ts +13 -0
  756. package/test/unit/demo-pass-rules.test.ts +192 -0
  757. package/test/unit/email-driver.test.ts +127 -0
  758. package/test/unit/end-pool.test.ts +181 -0
  759. package/test/unit/entitlement-check.test.ts +492 -0
  760. package/test/unit/entitlement-gate.test.ts +1333 -0
  761. package/test/unit/entitlement-profiles.test.ts +289 -0
  762. package/test/unit/erasure-log.test.ts +72 -0
  763. package/test/unit/error-mapping.test.ts +89 -0
  764. package/test/unit/export-entries.test.ts +85 -0
  765. package/test/unit/export-sanitize.test.ts +27 -0
  766. package/test/unit/files-head-route.test.ts +155 -0
  767. package/test/unit/files-http.test.ts +97 -0
  768. package/test/unit/head-rewrite.test.ts +72 -0
  769. package/test/unit/hub-create-org.test.ts +181 -0
  770. package/test/unit/hub-jwt.test.ts +93 -0
  771. package/test/unit/hub-sso-params.test.ts +39 -0
  772. package/test/unit/hub-subject.test.ts +83 -0
  773. package/test/unit/idempotency-crypto.test.ts +45 -0
  774. package/test/unit/json-depth.test.ts +88 -0
  775. package/test/unit/logger-redaction.test.ts +66 -0
  776. package/test/unit/mcp-errors.test.ts +110 -0
  777. package/test/unit/mcp-inputs.test.ts +106 -0
  778. package/test/unit/mcp-project-hints.test.ts +26 -0
  779. package/test/unit/no-store.test.ts +49 -0
  780. package/test/unit/no-tool-migration-filename.test.ts +70 -0
  781. package/test/unit/observability.test.ts +64 -0
  782. package/test/unit/openapi-doc.test.ts +93 -0
  783. package/test/unit/rate-limit-consume-on.test.ts +76 -0
  784. package/test/unit/rate-limit.test.ts +39 -0
  785. package/test/unit/readyz-storage.test.ts +157 -0
  786. package/test/unit/request-quota.test.ts +146 -0
  787. package/test/unit/scopes.test.ts +66 -0
  788. package/test/unit/security-headers.test.ts +131 -0
  789. package/test/unit/serve-content-type.test.ts +74 -0
  790. package/test/unit/setup-transport.test.ts +29 -0
  791. package/test/unit/storage-local.test.ts +53 -0
  792. package/test/unit/superadmin.test.ts +32 -0
  793. package/test/unit/timers.test.ts +287 -0
  794. package/test/unit/tool-definition-guards.test.ts +166 -0
  795. package/test/unit/tool-entitlements-guard.test.ts +168 -0
  796. package/test/unit/usage-poster.test.ts +542 -0
  797. package/test/unit/usage-window.test.ts +48 -0
  798. package/test/unit/workspace-create-audit.test.ts +48 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/util/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,MAAM,EACN,YAAY,EACZ,iBAAiB,EACjB,MAAM,EAEP,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC9F,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAqB,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,161 @@
1
+ {
2
+ "name": "@antasphere/chassis-server",
3
+ "version": "1.0.0",
4
+ "license": "SEE LICENSE IN LICENSE",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/antasphere/chassis.git",
8
+ "directory": "packages/chassis-server"
9
+ },
10
+ "type": "module",
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "src",
17
+ "test",
18
+ "LICENSE"
19
+ ],
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "default": "./dist/index.js"
24
+ },
25
+ "./env": {
26
+ "types": "./dist/env.d.ts",
27
+ "default": "./dist/env.js"
28
+ },
29
+ "./logger": {
30
+ "types": "./dist/logger.d.ts",
31
+ "default": "./dist/logger.js"
32
+ },
33
+ "./util": {
34
+ "types": "./dist/util/index.d.ts",
35
+ "default": "./dist/util/index.js"
36
+ },
37
+ "./storage": {
38
+ "types": "./dist/storage/index.d.ts",
39
+ "default": "./dist/storage/index.js"
40
+ },
41
+ "./files": {
42
+ "types": "./dist/files/index.d.ts",
43
+ "default": "./dist/files/index.js"
44
+ },
45
+ "./email": {
46
+ "types": "./dist/email/index.d.ts",
47
+ "default": "./dist/email/index.js"
48
+ },
49
+ "./audit": {
50
+ "types": "./dist/audit/index.d.ts",
51
+ "default": "./dist/audit/index.js"
52
+ },
53
+ "./apikeys": {
54
+ "types": "./dist/apikeys/index.d.ts",
55
+ "default": "./dist/apikeys/index.js"
56
+ },
57
+ "./accounts": {
58
+ "types": "./dist/accounts/index.d.ts",
59
+ "default": "./dist/accounts/index.js"
60
+ },
61
+ "./identity": {
62
+ "types": "./dist/identity/index.d.ts",
63
+ "default": "./dist/identity/index.js"
64
+ },
65
+ "./middleware": {
66
+ "types": "./dist/middleware/index.d.ts",
67
+ "default": "./dist/middleware/index.js"
68
+ },
69
+ "./observability": {
70
+ "types": "./dist/observability/index.d.ts",
71
+ "default": "./dist/observability/index.js"
72
+ },
73
+ "./platform": {
74
+ "types": "./dist/platform/index.d.ts",
75
+ "default": "./dist/platform/index.js"
76
+ },
77
+ "./invitations": {
78
+ "types": "./dist/invitations/index.d.ts",
79
+ "default": "./dist/invitations/index.js"
80
+ },
81
+ "./projects": {
82
+ "types": "./dist/projects/index.d.ts",
83
+ "default": "./dist/projects/index.js"
84
+ },
85
+ "./api": {
86
+ "types": "./dist/api/index.d.ts",
87
+ "default": "./dist/api/index.js"
88
+ },
89
+ "./routes": {
90
+ "types": "./dist/routes/index.d.ts",
91
+ "default": "./dist/routes/index.js"
92
+ },
93
+ "./jobs": {
94
+ "types": "./dist/jobs/index.d.ts",
95
+ "default": "./dist/jobs/index.js"
96
+ },
97
+ "./mcp": {
98
+ "types": "./dist/mcp/index.d.ts",
99
+ "default": "./dist/mcp/index.js"
100
+ },
101
+ "./testing": {
102
+ "types": "./dist/testing/index.d.ts",
103
+ "default": "./dist/testing/index.js"
104
+ }
105
+ },
106
+ "dependencies": {
107
+ "@aws-sdk/client-s3": "^3.1079.0",
108
+ "@aws-sdk/lib-storage": "^3.1079.0",
109
+ "@better-auth/oauth-provider": "1.6.22",
110
+ "@hono/mcp": "0.3.0",
111
+ "@hono/node-server": "^1.14.0",
112
+ "@hono/zod-openapi": "^1.4.0",
113
+ "@modelcontextprotocol/sdk": "1.29.0",
114
+ "@opentelemetry/api": "^1.9.1",
115
+ "@opentelemetry/exporter-trace-otlp-http": "^0.205.0",
116
+ "@opentelemetry/resources": "^2.9.0",
117
+ "@opentelemetry/sdk-trace-base": "^2.9.0",
118
+ "@opentelemetry/sdk-trace-node": "^2.9.0",
119
+ "better-auth": "1.6.22",
120
+ "drizzle-orm": "^0.45.2",
121
+ "hono": "^4.9.0",
122
+ "ioredis": "^5.11.1",
123
+ "jose": "^6.2.3",
124
+ "nodemailer": "^9.1.1",
125
+ "pg": "^8.22.0",
126
+ "pg-boss": "^10.4.2",
127
+ "pino": "^9.7.0",
128
+ "prom-client": "^15.1.3",
129
+ "rate-limiter-flexible": "^7.4.0",
130
+ "resend": "^6.17.0",
131
+ "ulid": "^3.0.2",
132
+ "yazl": "^3.3.1",
133
+ "zod": "^4.1.0",
134
+ "@antasphere/chassis-contract": "1.0.0",
135
+ "@antasphere/chassis-db": "1.0.0"
136
+ },
137
+ "devDependencies": {
138
+ "@better-auth/core": "1.6.22",
139
+ "@better-fetch/fetch": "1.1.21",
140
+ "@testcontainers/postgresql": "^11.0.0",
141
+ "@types/adm-zip": "^0.5.8",
142
+ "@types/node": "^22.15.0",
143
+ "@types/nodemailer": "^8.0.1",
144
+ "@types/pg": "^8.20.0",
145
+ "@types/yazl": "^3.3.1",
146
+ "adm-zip": "^0.6.1",
147
+ "better-call": "1.1.8",
148
+ "drizzle-kit": "^0.31.10",
149
+ "pino-pretty": "^13.0.0",
150
+ "testcontainers": "^11.14.0",
151
+ "vitest": "^3.2.0"
152
+ },
153
+ "scripts": {
154
+ "build": "tsc -p tsconfig.json",
155
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",
156
+ "lint": "eslint src test",
157
+ "test": "vitest run --dir test/unit",
158
+ "test:integration": "vitest run --dir test/integration",
159
+ "db:generate": "drizzle-kit generate --config test/host/drizzle.config.ts --name chassis_baseline"
160
+ }
161
+ }
@@ -0,0 +1,389 @@
1
+ import pg from 'pg';
2
+ import { and, eq, isNull, ne } from 'drizzle-orm';
3
+ import { user as userTable, workspaceMembers, workspaces, type Db } from '@antasphere/chassis-db';
4
+ import type { AuditService } from '../audit/service.js';
5
+ import type { Logger } from '../logger.js';
6
+ import type { ErasureLog } from './erasure-log.js';
7
+
8
+ /**
9
+ * Account deletion (GDPR erasure), shared by both delete surfaces: the
10
+ * self-service Better Auth `/delete-user` route (via the boot-wired
11
+ * before/after hooks) and the admin `DELETE /members/{id}` endpoint.
12
+ *
13
+ * Deliberately NO storage dependency: files are WORKSPACE data, not the
14
+ * uploader's personal data — they survive the delete with `created_by`
15
+ * nulled by the FK (migration 0008, ADR 006). Blobs are never touched here.
16
+ *
17
+ * Multi-workspace (ADR 014): a user owning N workspaces has N last-owner
18
+ * obligations — the guard runs for EVERY workspace where they are an
19
+ * active owner, with the per-workspace locks acquired in deterministic
20
+ * order (sorted workspace id) so two concurrent deletes of co-owners can
21
+ * never deadlock. The completion audit row lands in every workspace the
22
+ * user belonged to.
23
+ *
24
+ * Projection exemption (D11, the cloud-binding plan): the guard
25
+ * applies ONLY to workspaces this instance owns (centralAccountId IS
26
+ * NULL). A projected (hub-origin) workspace asserts ownership hub-side —
27
+ * its local membership rows are a re-syncable projection, so a
28
+ * zero-local-owner state is legal there and the guard (app layer AND the
29
+ * 0009 trigger, migration 0019) stands aside. Structured now, inert until
30
+ * the cloud edition creates the first projection (Phase 3).
31
+ *
32
+ * Last-owner race hardening (ADR 006, M9): a zero-active-owner workspace is
33
+ * unrecoverable (setup is one-shot), so the guard is enforced in two layers:
34
+ *
35
+ * 1. A `BEFORE DELETE OR UPDATE` trigger on workspace_members (migration
36
+ * 0009) raises SQLSTATE {@link LAST_OWNER_SQLSTATE} whenever an operation
37
+ * would drop the workspace's ACTIVE owners to zero — serialized inside
38
+ * the trigger by a per-workspace `pg_advisory_xact_lock`, so it holds
39
+ * under ANY concurrency and on any code path. This is the guarantee.
40
+ * 2. A per-workspace session-scoped advisory lock (dedicated `pg.Client`,
41
+ * mirroring the migration-lock pattern) serializes the HTTP surfaces
42
+ * around their guard re-check, so the LOSER of a race is rejected at the
43
+ * guard with a clean `LastOwnerError` (→ 400) instead of tripping the
44
+ * trigger mid-cascade (→ 500).
45
+ */
46
+
47
+ /** Deleting this account would leave the workspace without an active owner. */
48
+ export class LastOwnerError extends Error {
49
+ constructor() {
50
+ super('The workspace must keep at least one active owner');
51
+ this.name = 'LastOwnerError';
52
+ }
53
+ }
54
+
55
+ /** SQLSTATE raised by the workspace_members last-owner trigger (migration 0009). */
56
+ export const LAST_OWNER_SQLSTATE = 'P0409';
57
+
58
+ /**
59
+ * True when `cause` (or anything on its `cause` chain — drizzle wraps pg's
60
+ * DatabaseError) is the last-owner trigger rejection.
61
+ */
62
+ export function isLastOwnerDbError(cause: unknown): boolean {
63
+ for (let err: unknown = cause; err instanceof Error; err = (err as { cause?: unknown }).cause) {
64
+ if ((err as { code?: unknown }).code === LAST_OWNER_SQLSTATE) return true;
65
+ }
66
+ return false;
67
+ }
68
+
69
+ /**
70
+ * Advisory-lock namespace for serializing owner-removing operations
71
+ * (two-int form: classid 7432003, objid = hashtext(workspace id)). Distinct
72
+ * from the migration lock (7432001) and the trigger's own xact lock
73
+ * (7432002) — the session lock is held ACROSS the cascade while the trigger
74
+ * lock is taken INSIDE it, so sharing a key would self-deadlock.
75
+ */
76
+ const OWNER_GUARD_LOCK_KEY = 7_432_003;
77
+
78
+ /**
79
+ * The self-service path parks the lock between Better Auth's before/after
80
+ * hooks; if the cascade dies in between, afterDelete never runs. The
81
+ * watchdog then cuts the dedicated connection (a session lock dies with its
82
+ * session), so a leaked lock can stall owner removals for at most this long.
83
+ */
84
+ const LOCK_WATCHDOG_MS = 60_000;
85
+
86
+ interface OwnerLock {
87
+ client: pg.Client;
88
+ workspaceId: string;
89
+ released: boolean;
90
+ watchdog?: NodeJS.Timeout;
91
+ }
92
+
93
+ /** True when the workspace keeps ≥1 ACTIVE owner besides excludeUserId. */
94
+ export async function hasOtherActiveOwner(
95
+ db: Db,
96
+ workspaceId: string,
97
+ excludeUserId: string
98
+ ): Promise<boolean> {
99
+ const [row] = await db
100
+ .select({ id: workspaceMembers.id })
101
+ .from(workspaceMembers)
102
+ .where(
103
+ and(
104
+ eq(workspaceMembers.workspaceId, workspaceId),
105
+ eq(workspaceMembers.role, 'owner'),
106
+ eq(workspaceMembers.isActive, true),
107
+ ne(workspaceMembers.userId, excludeUserId)
108
+ )
109
+ )
110
+ .limit(1);
111
+ return Boolean(row);
112
+ }
113
+
114
+ export class AccountDeletionService {
115
+ /**
116
+ * before/after stash: beforeUserDelete runs with the user row still
117
+ * present (memberships + email are readable); afterUserDelete runs after
118
+ * the FK cascade, when neither is — so the system-actor audit rows pop
119
+ * their context from here. In-process by design: both hooks run in the
120
+ * same request on the same replica.
121
+ */
122
+ private readonly stash = new Map<string, { workspaceIds: string[]; email: string }>();
123
+
124
+ /** Locks parked across Better Auth's cascade (beforeUserDelete → afterUserDelete). */
125
+ private readonly heldLocks = new Map<string, OwnerLock[]>();
126
+
127
+ constructor(
128
+ private readonly db: Db,
129
+ private readonly audit: AuditService,
130
+ private readonly logger: Logger,
131
+ /** Dedicated advisory-lock connections (mirrors packages/chassis-db migrate.ts). */
132
+ private readonly connectionString: string,
133
+ /** The append-only erasure tombstone (OPS-3): every completed erasure lands a line. */
134
+ private readonly erasureLog?: ErasureLog
135
+ ) {}
136
+
137
+ /**
138
+ * Throws LastOwnerError when the user is the last ACTIVE owner of ANY of
139
+ * their non-projected workspaces (ADR 014: N owned workspaces = N
140
+ * obligations; projected ones are exempt, D11). Unlocked fast-path check —
141
+ * the race-free enforcement is withLastOwnerGuard + the 0009/0019 trigger.
142
+ */
143
+ async assertDeletable(userId: string): Promise<void> {
144
+ for (const workspaceId of await this.ownedGuardedWorkspaceIds(userId)) {
145
+ if (!(await hasOtherActiveOwner(this.db, workspaceId, userId))) {
146
+ throw new LastOwnerError();
147
+ }
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Serialize an owner-removing operation and enforce the last-owner rule
153
+ * race-free: the per-workspace advisory lock makes the guard re-check see
154
+ * every previously committed removal (the loser of a race rejects with
155
+ * LastOwnerError → 400), and the migration-0009 trigger — surfaced here as
156
+ * LastOwnerError too — backstops anything that slips past. The lock is
157
+ * released on EVERY path (finally). Projected workspaces skip the guard
158
+ * entirely (D11): ownership is asserted hub-side and the trigger stands
159
+ * aside there too (migration 0019).
160
+ */
161
+ async withLastOwnerGuard<T>(workspaceId: string, excludeUserId: string, op: () => Promise<T>): Promise<T> {
162
+ if (await this.isProjectedWorkspace(workspaceId)) {
163
+ return op();
164
+ }
165
+ const lock = await this.acquireOwnerLock(workspaceId);
166
+ try {
167
+ if (!(await hasOtherActiveOwner(this.db, workspaceId, excludeUserId))) {
168
+ throw new LastOwnerError();
169
+ }
170
+ return await op();
171
+ } catch (cause) {
172
+ if (isLastOwnerDbError(cause)) throw new LastOwnerError();
173
+ throw cause;
174
+ } finally {
175
+ await this.releaseOwnerLock(lock);
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Guard + stash. Runs while the user row still exists. For EVERY
181
+ * non-projected workspace where the user is an ACTIVE OWNER the guard
182
+ * re-check runs under that workspace's lock — locks acquired in
183
+ * deterministic order (sorted workspace id) so concurrent deletes never
184
+ * deadlock — and the locks are parked across Better Auth's cascade
185
+ * (released by afterUserDelete, or by the watchdog if the cascade dies in
186
+ * between). Of two concurrent last-owner self-deletes, the loser blocks
187
+ * here until the winner's cascade commits, re-checks, and rejects with a
188
+ * clean LastOwnerError.
189
+ */
190
+ async beforeUserDelete(userId: string): Promise<void> {
191
+ const memberships = await this.membershipsOf(userId);
192
+ if (memberships.length === 0) return; // no workspace to guard or audit against
193
+ const locks = await this.acquireOwnerLocks(userId);
194
+ if (locks.length > 0) this.parkLocks(userId, locks);
195
+ const [u] = await this.db
196
+ .select({ email: userTable.email })
197
+ .from(userTable)
198
+ .where(eq(userTable.id, userId))
199
+ .limit(1);
200
+ this.stash.set(userId, {
201
+ workspaceIds: memberships.map((m) => m.workspaceId),
202
+ email: u?.email ?? ''
203
+ });
204
+ }
205
+
206
+ /**
207
+ * Run an erasure under EVERY last-owner guard the user is subject to, with
208
+ * nothing touched when any guard refuses (PRDCT-1809). The boot-time
209
+ * tombstone replay uses this: Better Auth's cascade deletes the account
210
+ * rows BEFORE the user row, so a delete the 0009 trigger refuses mid-way
211
+ * leaves a half-erased owner (no credential, PII intact, membership
212
+ * intact). The locks are acquired in sorted order (as beforeUserDelete)
213
+ * and released on every path; a refusal surfaces as LastOwnerError before
214
+ * `op` runs, and a trigger refusal inside `op` is mapped to the same.
215
+ */
216
+ async withAllLastOwnerGuards<T>(userId: string, op: () => Promise<T>): Promise<T> {
217
+ const locks = await this.acquireOwnerLocks(userId);
218
+ try {
219
+ return await op();
220
+ } catch (cause) {
221
+ if (isLastOwnerDbError(cause)) throw new LastOwnerError();
222
+ throw cause;
223
+ } finally {
224
+ for (const lock of locks) await this.releaseOwnerLock(lock);
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Land the erasure tombstone (OPS-3) for a deletion that did NOT run
230
+ * through Better Auth's deleteUser hooks — the admin DELETE /members/{id}
231
+ * cascade calls the internal adapter directly, so afterUserDelete never
232
+ * fires there. Every GDPR surface must end here or in afterUserDelete.
233
+ */
234
+ async recordErasure(user: { id: string; email: string }): Promise<void> {
235
+ await this.erasureLog?.append(user);
236
+ }
237
+
238
+ /** Pop the stash and record the system-actor completion rows (FK-safe: the user is gone). */
239
+ async afterUserDelete(user: { id: string; email: string }): Promise<void> {
240
+ const locks = this.heldLocks.get(user.id);
241
+ if (locks) {
242
+ this.heldLocks.delete(user.id);
243
+ for (const lock of locks) await this.releaseOwnerLock(lock);
244
+ }
245
+ // The tombstone is written for EVERY completed erasure, stashed context
246
+ // or not: the audit rows below are what a restore rolls back; this line
247
+ // is what makes the erasure hold across one (OPS-3).
248
+ await this.erasureLog?.append(user);
249
+ const stashed = this.stash.get(user.id);
250
+ this.stash.delete(user.id);
251
+ if (!stashed) {
252
+ this.logger.warn({ userId: user.id }, 'account delete completed without a stashed context');
253
+ return;
254
+ }
255
+ // One row per workspace the user belonged to: every workspace's audit
256
+ // trail records that its member's account was erased (ADR 014).
257
+ for (const workspaceId of stashed.workspaceIds) {
258
+ await this.audit.write({
259
+ workspaceId,
260
+ principal: null, // → actorVia 'system'
261
+ action: 'user.account_delete',
262
+ resourceType: 'user',
263
+ resourceId: user.id,
264
+ metadata: { deletedUserId: user.id, email: stashed.email || user.email }
265
+ });
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Acquire the per-workspace lock and run the guard re-check for EVERY
271
+ * non-projected workspace where the user is an active owner, in sorted
272
+ * order (deterministic → no deadlock between concurrent deletes). On a
273
+ * refusal every lock taken so far is released and LastOwnerError is
274
+ * thrown; on success the caller owns the locks.
275
+ */
276
+ private async acquireOwnerLocks(userId: string): Promise<OwnerLock[]> {
277
+ const locks: OwnerLock[] = [];
278
+ try {
279
+ for (const workspaceId of await this.ownedGuardedWorkspaceIds(userId)) {
280
+ locks.push(await this.acquireOwnerLock(workspaceId));
281
+ if (!(await hasOtherActiveOwner(this.db, workspaceId, userId))) {
282
+ throw new LastOwnerError();
283
+ }
284
+ }
285
+ return locks;
286
+ } catch (cause) {
287
+ for (const lock of locks) await this.releaseOwnerLock(lock);
288
+ throw cause;
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Session-scoped pg_advisory_lock on a dedicated client — never the pool:
294
+ * a session lock parked on a pooled connection would poison the pool.
295
+ */
296
+ private async acquireOwnerLock(workspaceId: string): Promise<OwnerLock> {
297
+ const client = new pg.Client({ connectionString: this.connectionString });
298
+ await client.connect();
299
+ const lock: OwnerLock = { client, workspaceId, released: false };
300
+ try {
301
+ await client.query('SELECT pg_advisory_lock($1, hashtext($2))', [OWNER_GUARD_LOCK_KEY, workspaceId]);
302
+ } catch (cause) {
303
+ await this.releaseOwnerLock(lock);
304
+ throw cause;
305
+ }
306
+ return lock;
307
+ }
308
+
309
+ /** Idempotent; closing the client guarantees the session lock dies with it. */
310
+ private async releaseOwnerLock(lock: OwnerLock): Promise<void> {
311
+ if (lock.released) return;
312
+ lock.released = true;
313
+ if (lock.watchdog) clearTimeout(lock.watchdog);
314
+ try {
315
+ await lock.client.query('SELECT pg_advisory_unlock($1, hashtext($2))', [
316
+ OWNER_GUARD_LOCK_KEY,
317
+ lock.workspaceId
318
+ ]);
319
+ } catch {
320
+ // client.end() below still drops the session-scoped lock
321
+ }
322
+ await lock.client.end().catch(() => {});
323
+ }
324
+
325
+ /** Park the locks across the cascade, each watchdogged against a hook-gap leak. */
326
+ private parkLocks(userId: string, locks: OwnerLock[]): void {
327
+ for (const lock of locks) {
328
+ lock.watchdog = setTimeout(() => {
329
+ const held = this.heldLocks.get(userId)?.filter((l) => l !== lock);
330
+ if (held !== undefined) {
331
+ if (held.length === 0) this.heldLocks.delete(userId);
332
+ else this.heldLocks.set(userId, held);
333
+ }
334
+ this.logger.warn(
335
+ { userId, workspaceId: lock.workspaceId },
336
+ 'owner-delete lock watchdog fired — a delete died between the hooks; releasing the lock'
337
+ );
338
+ void this.releaseOwnerLock(lock);
339
+ }, LOCK_WATCHDOG_MS);
340
+ lock.watchdog.unref();
341
+ }
342
+ this.heldLocks.set(userId, locks);
343
+ }
344
+
345
+ /** Every membership row the user holds, active or not. */
346
+ private async membershipsOf(
347
+ userId: string
348
+ ): Promise<Array<{ workspaceId: string; role: string; isActive: boolean }>> {
349
+ return this.db
350
+ .select({
351
+ workspaceId: workspaceMembers.workspaceId,
352
+ role: workspaceMembers.role,
353
+ isActive: workspaceMembers.isActive
354
+ })
355
+ .from(workspaceMembers)
356
+ .where(eq(workspaceMembers.userId, userId));
357
+ }
358
+
359
+ /**
360
+ * Workspaces where the user is an ACTIVE OWNER and the last-owner guard
361
+ * applies — i.e. NON-PROJECTED ones (centralAccountId IS NULL, D11).
362
+ * Sorted for deterministic lock ordering.
363
+ */
364
+ private async ownedGuardedWorkspaceIds(userId: string): Promise<string[]> {
365
+ const rows = await this.db
366
+ .select({ workspaceId: workspaceMembers.workspaceId })
367
+ .from(workspaceMembers)
368
+ .innerJoin(workspaces, eq(workspaceMembers.workspaceId, workspaces.id))
369
+ .where(
370
+ and(
371
+ eq(workspaceMembers.userId, userId),
372
+ eq(workspaceMembers.role, 'owner'),
373
+ eq(workspaceMembers.isActive, true),
374
+ isNull(workspaces.centralAccountId)
375
+ )
376
+ );
377
+ return rows.map((r) => r.workspaceId).sort();
378
+ }
379
+
380
+ /** True when the workspace is a hub projection (ownership asserted hub-side, D11). */
381
+ private async isProjectedWorkspace(workspaceId: string): Promise<boolean> {
382
+ const [row] = await this.db
383
+ .select({ centralAccountId: workspaces.centralAccountId })
384
+ .from(workspaces)
385
+ .where(eq(workspaces.id, workspaceId))
386
+ .limit(1);
387
+ return Boolean(row?.centralAccountId);
388
+ }
389
+ }
@@ -0,0 +1,140 @@
1
+ import { createHmac } from 'node:crypto';
2
+ import { appendFile, mkdir, readFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import type { Logger } from '../logger.js';
5
+
6
+ /**
7
+ * The append-only ERASURE TOMBSTONE (OPS-3, PRDCT-1357).
8
+ *
9
+ * A GDPR erasure that lives only in the database is undone by the next
10
+ * restore from an older dump: the subject's row comes back, their password
11
+ * works again, and the audit row saying they were erased is rolled back
12
+ * with everything else. The tombstone lives OUTSIDE the dump — one JSON
13
+ * line per erased user in `$DATA_DIR/erasures.jsonl` — so it survives the
14
+ * database swap; restore.sh carries the live volume's file forward into
15
+ * the restored tree, and boot REPLAYS it: any tombstoned user present in
16
+ * the database is deleted again, with an audit row recording the replay.
17
+ *
18
+ * The line carries the user id (the replay key) and a KEYED fingerprint of
19
+ * the lowercased email (a second correlation handle for operators, never
20
+ * the address itself — the file is the one artifact that must outlive the
21
+ * erasure it records). Keyed (PRDCT-1811): the file rides in the
22
+ * UNENCRYPTED data tarball of every backup, and a plain hash of a
23
+ * low-entropy value is a dictionary lookup away from the address — an
24
+ * off-site backup would list who asked to be forgotten. HMAC-SHA256 under
25
+ * the instance's auth secret (which travels only in the passphrase-
26
+ * encrypted config archive) keeps the operator's correlation and denies it
27
+ * to anyone holding the tarball alone. Rotating the auth secret changes
28
+ * the fingerprints written from then on; old lines keep their old key.
29
+ */
30
+ export const ERASURE_LOG_FILE = 'erasures.jsonl';
31
+
32
+ /** What a replay did: the ids re-erased, and the tombstones it could NOT apply. */
33
+ export interface ErasureReplay {
34
+ replayed: string[];
35
+ refused: Array<{ userId: string; erasedAt: string; cause: unknown }>;
36
+ }
37
+
38
+ export interface ErasureTombstone {
39
+ userId: string;
40
+ emailHash: string;
41
+ at: string;
42
+ }
43
+
44
+ export function emailHash(email: string, key: string): string {
45
+ return createHmac('sha256', key).update(email.trim().toLowerCase()).digest('hex');
46
+ }
47
+
48
+ export class ErasureLog {
49
+ private readonly path: string;
50
+
51
+ constructor(
52
+ dataDir: string,
53
+ private readonly logger: Logger,
54
+ /** The fingerprint key: the resolved auth secret (never stored beside the file). */
55
+ private readonly fingerprintKey: string
56
+ ) {
57
+ this.path = join(dataDir, ERASURE_LOG_FILE);
58
+ }
59
+
60
+ /** Append one tombstone. Best-effort on I/O failure: loud, never blocking the erasure itself. */
61
+ async append(user: { id: string; email: string }): Promise<void> {
62
+ const line: ErasureTombstone = {
63
+ userId: user.id,
64
+ emailHash: emailHash(user.email, this.fingerprintKey),
65
+ at: new Date().toISOString()
66
+ };
67
+ try {
68
+ await mkdir(join(this.path, '..'), { recursive: true });
69
+ await appendFile(this.path, JSON.stringify(line) + '\n', { mode: 0o600 });
70
+ } catch (err) {
71
+ this.logger.error(
72
+ { err, path: this.path },
73
+ 'erasure tombstone: could not append — a restore may resurrect this user'
74
+ );
75
+ }
76
+ }
77
+
78
+ /** Every tombstone on disk (malformed lines skipped, loudly). Empty when the file is absent. */
79
+ async read(): Promise<ErasureTombstone[]> {
80
+ let raw: string;
81
+ try {
82
+ raw = await readFile(this.path, 'utf8');
83
+ } catch {
84
+ return [];
85
+ }
86
+ const out: ErasureTombstone[] = [];
87
+ for (const line of raw.split('\n')) {
88
+ if (!line.trim()) continue;
89
+ try {
90
+ const parsed = JSON.parse(line) as Partial<ErasureTombstone>;
91
+ if (typeof parsed.userId === 'string' && parsed.userId) {
92
+ out.push({
93
+ userId: parsed.userId,
94
+ emailHash: String(parsed.emailHash ?? ''),
95
+ at: String(parsed.at ?? '')
96
+ });
97
+ }
98
+ } catch {
99
+ this.logger.warn({ path: this.path }, 'erasure tombstone: skipping a malformed line');
100
+ }
101
+ }
102
+ return out;
103
+ }
104
+
105
+ /**
106
+ * Replay: for every tombstoned user still (or again) present, delete them
107
+ * through `deleteUser` (Better Auth's cascade, the same path every GDPR
108
+ * surface uses) and count it. A tombstone whose delete FAILS is reported
109
+ * as refused, never swallowed (PRDCT-1809): the caller decides what a
110
+ * present-but-unerasable subject means for the boot (it refuses to serve).
111
+ */
112
+ async replay(
113
+ exists: (userId: string) => Promise<boolean>,
114
+ deleteUser: (userId: string) => Promise<void>
115
+ ): Promise<ErasureReplay> {
116
+ const replayed: string[] = [];
117
+ const refused: ErasureReplay['refused'] = [];
118
+ const seen = new Set<string>();
119
+ for (const tomb of await this.read()) {
120
+ if (seen.has(tomb.userId)) continue;
121
+ seen.add(tomb.userId);
122
+ if (!(await exists(tomb.userId))) continue;
123
+ try {
124
+ await deleteUser(tomb.userId);
125
+ replayed.push(tomb.userId);
126
+ this.logger.warn(
127
+ { userId: tomb.userId, erasedAt: tomb.at },
128
+ 'erasure tombstone replayed: a previously erased user was present again (restore from an older backup?) and has been re-erased'
129
+ );
130
+ } catch (err) {
131
+ refused.push({ userId: tomb.userId, erasedAt: tomb.at, cause: err });
132
+ this.logger.error(
133
+ { err, userId: tomb.userId },
134
+ 'erasure tombstone: replay delete refused — the user is STILL PRESENT'
135
+ );
136
+ }
137
+ }
138
+ return { replayed, refused };
139
+ }
140
+ }