@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,451 @@
1
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
2
+ import type { StartedPostgreSqlContainer } from '@testcontainers/postgresql';
3
+ import {
4
+ createDatabase,
5
+ createTestApp,
6
+ extractCookie,
7
+ host,
8
+ readJson,
9
+ startPostgres,
10
+ type TestApp
11
+ } from './helpers.js';
12
+
13
+ /**
14
+ * Orphaned-user GC (ADR 010 + user-scoped federation): the nightly sweep
15
+ * deletes Better Auth users with ZERO workspace_members rows AND no live
16
+ * (unexpired) session once they are older than ORPHAN_USER_RETENTION_HOURS.
17
+ * The LIVE SESSION is the collect/protect discriminator — a hub account
18
+ * link deliberately is NOT (a fail-closed SSO login strands a linked,
19
+ * grant-bearing user whose session was revoked; protecting on the link
20
+ * would accumulate dormant refresh families without bound). Safety
21
+ * invariants under test: a user with ANY membership row — even DEACTIVATED
22
+ * — is never touched; neither is a fresh orphan inside the grace window, an
23
+ * orphan with a live pending invitation (mid-onboarding), a zero-membership
24
+ * user with a LIVE session (the legit /me zero state), nor a
25
+ * superadmin-allowlisted address (the break-glass operator). Deletion is
26
+ * always the WHOLE user (internalAdapter cascade: sessions + accounts +
27
+ * memberships together — the no_link hard constraint) and lands a
28
+ * system-actor `user.orphan_purge` audit row. 0 disables the sweep.
29
+ */
30
+
31
+ const OWNER = { email: 'owner@gc.test', name: 'GC Owner', password: 'gc-owner-password-123' };
32
+ const MEMBER = { email: 'member@gc.test', name: 'GC Member', password: 'gc-member-password-12' };
33
+ const SUPERADMIN_EMAIL = 'breakglass@gc.test';
34
+
35
+ let container: StartedPostgreSqlContainer;
36
+
37
+ const json = (body: unknown, headers: Record<string, string> = {}) => ({
38
+ method: 'POST',
39
+ headers: { 'content-type': 'application/json', ...headers },
40
+ body: JSON.stringify(body)
41
+ });
42
+
43
+ /**
44
+ * Insert a raw user row (+ a session and credential account to prove the
45
+ * cascade). `sessionLive: false` seeds an EXPIRED session — the strand
46
+ * shape (a fail-closed login revokes; here expiry stands in for revocation
47
+ * while still proving the delete cascades session rows).
48
+ */
49
+ async function seedUser(
50
+ app: TestApp,
51
+ id: string,
52
+ email: string,
53
+ ageHours: number,
54
+ opts: { sessionLive?: boolean; hubLinked?: boolean } = {}
55
+ ): Promise<void> {
56
+ await app.db.pool.query(
57
+ `INSERT INTO "user" (id, name, email, email_verified, created_at, updated_at)
58
+ VALUES ($1, $2, $3, false, now() - make_interval(hours => $4), now())`,
59
+ [id, email, email, ageHours]
60
+ );
61
+ await app.db.pool.query(
62
+ `INSERT INTO session (id, expires_at, token, created_at, updated_at, user_id)
63
+ VALUES ($1, now() + $2::interval, $3, now(), now(), $4)`,
64
+ [`sess-${id}`, opts.sessionLive ? '7 days' : '-1 hour', `token-${id}`, id]
65
+ );
66
+ if (opts.hubLinked) {
67
+ // The fail-closed-login strand: an `antasphere` link HOLDING a stored
68
+ // offline grant (Better Auth writes tokens before the after-hook).
69
+ await app.db.pool.query(
70
+ `INSERT INTO account (id, account_id, provider_id, user_id, refresh_token, created_at, updated_at)
71
+ VALUES ($1, $2, 'antasphere', $3, 'stranded-grant-ciphertext', now(), now())`,
72
+ [`acc-${id}`, `hub-${id}`, id]
73
+ );
74
+ } else {
75
+ await app.db.pool.query(
76
+ `INSERT INTO account (id, account_id, provider_id, user_id, password, created_at, updated_at)
77
+ VALUES ($1, $2, 'credential', $3, 'hash', now(), now())`,
78
+ [`acc-${id}`, id, id]
79
+ );
80
+ }
81
+ }
82
+
83
+ async function userExists(app: TestApp, id: string): Promise<boolean> {
84
+ const res = await app.db.pool.query(`SELECT 1 FROM "user" WHERE id = $1`, [id]);
85
+ return res.rows.length > 0;
86
+ }
87
+
88
+ /** Send the purge job and wait for pg-boss to complete it (deterministic). */
89
+ async function runPurge(app: TestApp): Promise<void> {
90
+ const jobId = await app.jobs.boss.send('orphan-user-purge', {});
91
+ expect(jobId).toBeTruthy();
92
+ const deadline = Date.now() + 20_000;
93
+ for (;;) {
94
+ const job = await app.jobs.boss.getJobById('orphan-user-purge', jobId!);
95
+ if (job && (job.state === 'completed' || job.state === 'failed')) {
96
+ expect(job.state).toBe('completed');
97
+ return;
98
+ }
99
+ if (Date.now() > deadline) throw new Error('orphan purge job did not complete in time');
100
+ await new Promise((r) => setTimeout(r, 250));
101
+ }
102
+ }
103
+
104
+ beforeAll(async () => {
105
+ container = await startPostgres();
106
+ });
107
+
108
+ afterAll(async () => {
109
+ await container?.stop();
110
+ });
111
+
112
+ describe('orphan purge (default 72h grace)', () => {
113
+ let app: TestApp;
114
+
115
+ beforeAll(async () => {
116
+ app = await createTestApp(await createDatabase(container, 'gc_on'), {
117
+ SUPERADMIN_EMAILS: SUPERADMIN_EMAIL
118
+ });
119
+ await app.app.request(
120
+ '/api/v1/setup',
121
+ json({ setupToken: 'integration-test-setup-token', instanceName: 'GC', owner: OWNER })
122
+ );
123
+ const ownerCookie = extractCookie(
124
+ await app.app.request(
125
+ '/api/v1/auth/sign-in/email',
126
+ json({ email: OWNER.email, password: OWNER.password })
127
+ )
128
+ );
129
+
130
+ // A real member — then DEACTIVATED and BACKDATED past the grace window:
131
+ // the strongest "never touch a member" case (inactive + old).
132
+ const inv = await readJson(
133
+ await app.app.request(
134
+ '/api/v1/invitations',
135
+ json({ email: MEMBER.email, role: 'member' }, { cookie: ownerCookie })
136
+ )
137
+ );
138
+ const token = (inv.acceptUrl as string).split('/invite/')[1]!;
139
+ await app.app.request(
140
+ '/api/v1/invitations/accept',
141
+ json({ token, name: MEMBER.name, password: MEMBER.password })
142
+ );
143
+ const { members } = await readJson(
144
+ await app.app.request('/api/v1/members', { headers: { cookie: ownerCookie } })
145
+ );
146
+ const member = members.find((m: { email: string }) => m.email === MEMBER.email);
147
+ await app.app.request(`/api/v1/members/${member.id}`, {
148
+ ...json({ isActive: false }, { cookie: ownerCookie }),
149
+ method: 'PATCH'
150
+ });
151
+ await app.db.pool.query(`UPDATE "user" SET created_at = now() - interval '200 hours' WHERE email = $1`, [
152
+ MEMBER.email
153
+ ]);
154
+
155
+ // The GC population:
156
+ await seedUser(app, 'orphan-old', 'orphan-old@gc.test', 100); // past grace, dead session → deleted
157
+ await seedUser(app, 'orphan-fresh', 'orphan-fresh@gc.test', 1); // inside grace → kept
158
+ await seedUser(app, 'orphan-invited', 'orphan-invited@gc.test', 100); // past grace but re-invited → kept
159
+ await app.app.request(
160
+ '/api/v1/invitations',
161
+ json({ email: 'orphan-invited@gc.test', role: 'member' }, { cookie: ownerCookie })
162
+ );
163
+ // The fail-closed-login STRAND: hub-linked, grant-bearing, session
164
+ // revoked (expired here) — the link must NOT protect it.
165
+ await seedUser(app, 'orphan-strand', 'orphan-strand@gc.test', 100, { hubLinked: true });
166
+ // The LEGIT zero-org user (the /me zero state): hub-linked too, but
167
+ // holding a LIVE session — the session is the protection.
168
+ await seedUser(app, 'zero-org-live', 'zero-org-live@gc.test', 100, {
169
+ hubLinked: true,
170
+ sessionLive: true
171
+ });
172
+ // Same protection for an UNLINKED zero-membership user with a live
173
+ // session (the discriminator is the session, never the link).
174
+ await seedUser(app, 'zero-org-live-local', 'zero-org-live-local@gc.test', 100, { sessionLive: true });
175
+ // The break-glass operator: allowlisted email, no membership, session
176
+ // long gone — must survive so recovery stays possible (ADR 010).
177
+ await seedUser(app, 'superadmin-dormant', SUPERADMIN_EMAIL, 100);
178
+ // BG-2 (PRDCT-1356): the allowlist shelters a VERIFIED address only —
179
+ // exactly what break-glass would accept. seedUser inserts unverified.
180
+ await app.db.pool.query(`UPDATE "user" SET email_verified = true WHERE id = 'superadmin-dormant'`);
181
+ }, 60_000);
182
+
183
+ afterAll(async () => {
184
+ await app.stop();
185
+ });
186
+
187
+ it('deletes only the no-session zero-membership orphans and cascades sessions/accounts', async () => {
188
+ await runPurge(app);
189
+
190
+ // The old orphan is gone, cascade included.
191
+ expect(await userExists(app, 'orphan-old')).toBe(false);
192
+ const sess = await app.db.pool.query(`SELECT 1 FROM session WHERE user_id = 'orphan-old'`);
193
+ expect(sess.rows).toHaveLength(0);
194
+ const acc = await app.db.pool.query(`SELECT 1 FROM account WHERE user_id = 'orphan-old'`);
195
+ expect(acc.rows).toHaveLength(0);
196
+
197
+ // The fail-closed-login STRAND is collected — the hub link does NOT
198
+ // protect it, and its stored grant ciphertext dies with the account row
199
+ // (the whole point: no unbounded dormant-refresh-family accumulation).
200
+ expect(await userExists(app, 'orphan-strand')).toBe(false);
201
+ const strandAcc = await app.db.pool.query(`SELECT 1 FROM account WHERE user_id = 'orphan-strand'`);
202
+ expect(strandAcc.rows).toHaveLength(0);
203
+
204
+ // The LEGIT zero-org users survive: the LIVE SESSION is the
205
+ // discriminator, for linked and unlinked users alike.
206
+ expect(await userExists(app, 'zero-org-live')).toBe(true);
207
+ expect(await userExists(app, 'zero-org-live-local')).toBe(true);
208
+
209
+ // The dormant break-glass operator survives on the allowlist alone.
210
+ expect(await userExists(app, 'superadmin-dormant')).toBe(true);
211
+
212
+ // The fresh orphan survives the grace window.
213
+ expect(await userExists(app, 'orphan-fresh')).toBe(true);
214
+
215
+ // The re-invited orphan survives (live pending invitation = mid-onboarding).
216
+ expect(await userExists(app, 'orphan-invited')).toBe(true);
217
+
218
+ // THE invariant: a user with ANY membership row — even deactivated and
219
+ // far older than the grace period — is never a purge candidate.
220
+ const member = await app.db.pool.query(`SELECT 1 FROM "user" WHERE email = $1`, [MEMBER.email]);
221
+ expect(member.rows).toHaveLength(1);
222
+ const owner = await app.db.pool.query(`SELECT 1 FROM "user" WHERE email = $1`, [OWNER.email]);
223
+ expect(owner.rows).toHaveLength(1);
224
+
225
+ // ── The no_link HARD CONSTRAINT (user-scoped federation) ────────────
226
+ // The purge must never leave an `origin='hub'` membership row whose
227
+ // user lost their `antasphere` account row — that state would make the
228
+ // reconciler's fail-open no_link branch reachable for a hub-origin
229
+ // principal. The whole-user cascade makes it impossible; pin it.
230
+ const dangling = await app.db.pool.query(
231
+ `SELECT wm.id FROM workspace_members wm
232
+ WHERE wm.origin = 'hub'
233
+ AND NOT EXISTS (
234
+ SELECT 1 FROM account a WHERE a.user_id = wm.user_id AND a.provider_id = 'antasphere'
235
+ )`
236
+ );
237
+ expect(dangling.rows).toHaveLength(0);
238
+
239
+ // Audited as a system actor with the count — INSTANCE-attributed
240
+ // (workspace_id NULL, ADR 014): orphans belong to no workspace.
241
+ const audit = await app.db.pool.query(
242
+ `SELECT actor_via, actor_user_id, workspace_id, metadata FROM audit_log WHERE action = 'user.orphan_purge' ORDER BY id DESC LIMIT 1`
243
+ );
244
+ expect(audit.rows).toHaveLength(1);
245
+ expect(audit.rows[0].actor_via).toBe('system');
246
+ expect(audit.rows[0].actor_user_id).toBeNull();
247
+ expect(audit.rows[0].workspace_id).toBeNull();
248
+ expect(audit.rows[0].metadata.deleted).toBe(2);
249
+ expect(audit.rows[0].metadata.sample).toContain('orphan-old@gc.test');
250
+ expect(audit.rows[0].metadata.sample).toContain('orphan-strand@gc.test');
251
+ }, 40_000);
252
+
253
+ it('is idempotent: a second run finds nothing and writes no extra audit row', async () => {
254
+ const before = await app.db.pool.query(
255
+ `SELECT count(*)::int AS n FROM audit_log WHERE action = 'user.orphan_purge'`
256
+ );
257
+ await runPurge(app);
258
+ expect(await userExists(app, 'orphan-fresh')).toBe(true);
259
+ const after = await app.db.pool.query(
260
+ `SELECT count(*)::int AS n FROM audit_log WHERE action = 'user.orphan_purge'`
261
+ );
262
+ // No deletions → no audit row (the run itself only logs).
263
+ expect(after.rows[0].n).toBe(before.rows[0].n);
264
+ }, 40_000);
265
+ });
266
+
267
+ describe('ORPHAN_USER_RETENTION_HOURS=0 disables the sweep', () => {
268
+ let app: TestApp;
269
+
270
+ beforeAll(async () => {
271
+ app = await createTestApp(await createDatabase(container, 'gc_off'), {
272
+ ORPHAN_USER_RETENTION_HOURS: '0'
273
+ });
274
+ await app.app.request(
275
+ '/api/v1/setup',
276
+ json({ setupToken: 'integration-test-setup-token', instanceName: 'GCOff', owner: OWNER })
277
+ );
278
+ await seedUser(app, 'orphan-ancient', 'orphan-ancient@gc.test', 1000);
279
+ }, 60_000);
280
+
281
+ afterAll(async () => {
282
+ await app.stop();
283
+ });
284
+
285
+ it('leaves even ancient orphans untouched', async () => {
286
+ await runPurge(app); // the handler early-returns; the job still completes
287
+ expect(await userExists(app, 'orphan-ancient')).toBe(true);
288
+ }, 40_000);
289
+ });
290
+
291
+ /**
292
+ * The no_link HARD CONSTRAINT, at the mechanism level (user-scoped
293
+ * federation). The purge never deletes a user who HOLDS a membership — so
294
+ * the cascade the constraint leans on ("delete the WHOLE user … whose FK
295
+ * cascade removes account rows AND membership rows together") is, in every
296
+ * other suite, asserted but never exercised: all deleted users have zero
297
+ * memberships. This pins the actual DB-level guarantee that makes the
298
+ * constraint hold even under the scan→delete race — a hub-origin membership
299
+ * row can NEVER outlive its `antasphere` account row, because
300
+ * `internalAdapter.deleteUser` (the exact call the purge AND both GDPR
301
+ * delete surfaces make) removes the `user` row and Postgres cascades BOTH
302
+ * children in one shot. If this ever regressed — an ON DELETE that stops
303
+ * cascading, or a partial account-only cleanup swapped in — the reconciler's
304
+ * fail-open `no_link` branch would become reachable for a hub-origin
305
+ * principal (a live authorization hole), and only this test would catch it.
306
+ */
307
+ describe('the deletion MECHANISM cascades a hub-origin membership with its account (no_link hard constraint)', () => {
308
+ let app: TestApp;
309
+
310
+ beforeAll(async () => {
311
+ app = await createTestApp(await createDatabase(container, 'gc_cascade'));
312
+ await app.app.request(
313
+ '/api/v1/setup',
314
+ json({ setupToken: 'integration-test-setup-token', instanceName: 'GCCascade', owner: OWNER })
315
+ );
316
+ }, 60_000);
317
+
318
+ afterAll(async () => {
319
+ await app.stop();
320
+ });
321
+
322
+ it('internalAdapter.deleteUser removes the antasphere account AND the origin=hub membership together', async () => {
323
+ const userId = 'cascade-hub-user';
324
+ const wsId = '33333333-cccc-4ddd-8eee-000000000001';
325
+ const hubOrgId = 'hub-org-cascade-0001';
326
+
327
+ // A PROJECTED workspace (central_account_id set) with an active
328
+ // origin='hub' membership whose user holds a live-grant `antasphere`
329
+ // account row — the exact shape a fail-closed-login strand or any
330
+ // hub-origin principal carries.
331
+ await app.db.pool.query(
332
+ `INSERT INTO workspaces (id, name, central_account_id, hub_status, created_at)
333
+ VALUES ($1, 'Cascade Org', $2, 'active', now())`,
334
+ [wsId, hubOrgId]
335
+ );
336
+ await app.db.pool.query(
337
+ `INSERT INTO "user" (id, name, email, email_verified, created_at, updated_at)
338
+ VALUES ($1, 'Cascade User', 'cascade@gc.test', true, now(), now())`,
339
+ [userId]
340
+ );
341
+ await app.db.pool.query(
342
+ `INSERT INTO account (id, account_id, provider_id, user_id, refresh_token, created_at, updated_at)
343
+ VALUES ('acc-cascade', 'hub-cascade', 'antasphere', $1, 'live-grant-ciphertext', now(), now())`,
344
+ [userId]
345
+ );
346
+ await app.db.pool.query(
347
+ `INSERT INTO workspace_members (id, workspace_id, user_id, role, origin, is_active, created_at)
348
+ VALUES (gen_random_uuid(), $1, $2, 'member', 'hub', true, now())`,
349
+ [wsId, userId]
350
+ );
351
+
352
+ // Sanity: the pre-delete state is exactly the one the constraint protects.
353
+ const before = await app.db.pool.query(
354
+ `SELECT 1 FROM workspace_members WHERE user_id = $1 AND origin = 'hub' AND is_active = true`,
355
+ [userId]
356
+ );
357
+ expect(before.rows).toHaveLength(1);
358
+
359
+ // The mechanism under test — the exact deletion call jobs/pgboss.ts makes.
360
+ const ctx = await app.auth.$context;
361
+ await ctx.internalAdapter.deleteUser(userId);
362
+
363
+ // The whole user is gone…
364
+ expect(await userExists(app, userId)).toBe(false);
365
+ // …its antasphere account row is gone…
366
+ const acc = await app.db.pool.query(
367
+ `SELECT 1 FROM account WHERE user_id = $1 AND provider_id = 'antasphere'`,
368
+ [userId]
369
+ );
370
+ expect(acc.rows).toHaveLength(0);
371
+ // …and — the load-bearing assertion — its origin='hub' membership
372
+ // cascaded WITH the account, never outliving it.
373
+ const mem = await app.db.pool.query(`SELECT 1 FROM workspace_members WHERE user_id = $1`, [userId]);
374
+ expect(mem.rows).toHaveLength(0);
375
+
376
+ // The fail-open state is unreachable: no origin='hub' row lacks its
377
+ // antasphere account anywhere in the instance.
378
+ const dangling = await app.db.pool.query(
379
+ `SELECT wm.id FROM workspace_members wm
380
+ WHERE wm.origin = 'hub'
381
+ AND NOT EXISTS (
382
+ SELECT 1 FROM account a WHERE a.user_id = wm.user_id AND a.provider_id = 'antasphere'
383
+ )`
384
+ );
385
+ expect(dangling.rows).toHaveLength(0);
386
+
387
+ // The projected workspace itself survives (only the membership cascaded) —
388
+ // the delete is scoped to the user, not the org.
389
+ const ws = await app.db.pool.query(`SELECT 1 FROM workspaces WHERE id = $1`, [wsId]);
390
+ expect(ws.rows).toHaveLength(1);
391
+ }, 40_000);
392
+ });
393
+
394
+ describe('the setup operator is never an orphan (CLOUD-3), and the allowlist shelters VERIFIED addresses only (BG-2)', () => {
395
+ let app: TestApp;
396
+ let operatorId: string;
397
+
398
+ beforeAll(async () => {
399
+ // The CLOUD shape, for real: cloud setup mints NO membership, so the
400
+ // operator is exactly the zero-membership user the sweep collects.
401
+ // SUPERADMIN_EMAILS UNSET: the operator's survival must not depend on it.
402
+ app = await createTestApp(await createDatabase(container, 'gc_operator'), {
403
+ EDITION: 'cloud',
404
+ HUB_ISSUER_URL: 'http://hub.localhost:3300',
405
+ HUB_CLIENT_ID: host.hubClientId,
406
+ HUB_CLIENT_SECRET: 'integration-test-hub-secret-0001',
407
+ SUPERADMIN_EMAILS: '',
408
+ ORPHAN_USER_RETENTION_HOURS: '1'
409
+ });
410
+ const setup = await app.app.request(
411
+ '/api/v1/setup',
412
+ json({ setupToken: 'integration-test-setup-token', instanceName: 'GC Op', owner: OWNER })
413
+ );
414
+ expect(setup.status).toBe(201);
415
+ operatorId = (await readJson(setup)).ownerUserId;
416
+ const memberships = await app.db.pool.query(`SELECT 1 FROM workspace_members WHERE user_id = $1`, [
417
+ operatorId
418
+ ]);
419
+ expect(memberships.rows).toHaveLength(0);
420
+ // Their session has lapsed; age the row past the grace.
421
+ await app.db.pool.query(`DELETE FROM session WHERE user_id = $1`, [operatorId]);
422
+ await app.db.pool.query(`UPDATE "user" SET created_at = now() - interval '400 hours' WHERE id = $1`, [
423
+ operatorId
424
+ ]);
425
+ });
426
+
427
+ afterAll(async () => {
428
+ await app.stop();
429
+ });
430
+
431
+ it('the purge deletes a plain stale orphan but keeps the operator', async () => {
432
+ await seedUser(app, 'u-stale', 'stale@gc.test', 400);
433
+ await runPurge(app);
434
+ expect(await userExists(app, 'u-stale')).toBe(false);
435
+ expect(await userExists(app, operatorId)).toBe(true);
436
+ });
437
+
438
+ it('an UNVERIFIED allowlisted address gets no shelter (BG-2 — break-glass would not accept it either)', async () => {
439
+ await app.stop();
440
+ app = await createTestApp(await createDatabase(container, 'gc_bg2'), {
441
+ SUPERADMIN_EMAILS: 'squat@gc.test, real@gc.test',
442
+ ORPHAN_USER_RETENTION_HOURS: '1'
443
+ });
444
+ await seedUser(app, 'u-squat', 'squat@gc.test', 400); // email_verified = false
445
+ await seedUser(app, 'u-real', 'real@gc.test', 400);
446
+ await app.db.pool.query(`UPDATE "user" SET email_verified = true WHERE id = 'u-real'`);
447
+ await runPurge(app);
448
+ expect(await userExists(app, 'u-squat')).toBe(false);
449
+ expect(await userExists(app, 'u-real')).toBe(true);
450
+ });
451
+ });