@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,484 @@
1
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
2
+ import type { StartedPostgreSqlContainer } from '@testcontainers/postgresql';
3
+ import pg from 'pg';
4
+ import {
5
+ createDatabase,
6
+ createTestApp,
7
+ extractCookie,
8
+ readJson,
9
+ startPostgres,
10
+ type TestApp,
11
+ host
12
+ } from './helpers.js';
13
+
14
+ /**
15
+ * Break-glass superadmin (ADR 010):
16
+ * - recognition is SESSION + VERIFIED email + SUPERADMIN_EMAILS allowlist —
17
+ * an unverified match, a non-listed session, and a machine credential
18
+ * (even one owned by a listed user) are all rejected;
19
+ * - unset env = the capability is fully dormant (403 for everyone);
20
+ * - claim-ownership recovers an ownerless workspace (creating, reactivating,
21
+ * or promoting the membership — additive, so the 0009 trigger holds);
22
+ * - reset-2fa clears a locked-out user's factor;
23
+ * - everything is audited with the superadmin identity + before/after state.
24
+ */
25
+
26
+ const OWNER = { email: 'owner@bg.test', name: 'BG Owner', password: 'bg-owner-password-123' };
27
+ const ROOT = { email: 'root@bg.test', name: 'BG Root', password: 'bg-root-password-1234' };
28
+ const SECOND = { email: 'second@bg.test', name: 'BG Second', password: 'bg-second-password-12' };
29
+ const ADMIN = { email: 'admin@bg.test', name: 'BG Admin', password: 'bg-admin-password-1234' };
30
+ const MEMBER = { email: 'member@bg.test', name: 'BG Member', password: 'bg-member-password-12' };
31
+
32
+ let container: StartedPostgreSqlContainer;
33
+
34
+ // The break-glass wall is 10/hour per IP and TRUST_PROXY=true in tests, so
35
+ // every request carries a unique forwarded address to stay off one shared
36
+ // 'unknown' bucket.
37
+ let ipCounter = 0;
38
+ const nextIp = () => `203.0.113.${++ipCounter % 250}`;
39
+
40
+ const json = (body: unknown, headers: Record<string, string> = {}) => ({
41
+ method: 'POST',
42
+ headers: { 'content-type': 'application/json', 'x-forwarded-for': nextIp(), ...headers },
43
+ body: JSON.stringify(body)
44
+ });
45
+
46
+ async function signIn(app: TestApp, email: string, password: string): Promise<string> {
47
+ const res = await app.app.request('/api/v1/auth/sign-in/email', json({ email, password }));
48
+ expect(res.status).toBe(200);
49
+ return extractCookie(res);
50
+ }
51
+
52
+ async function inviteAndAccept(
53
+ app: TestApp,
54
+ ownerCookie: string,
55
+ person: { email: string; name: string; password: string }
56
+ ): Promise<void> {
57
+ const inv = await readJson(
58
+ await app.app.request(
59
+ '/api/v1/invitations',
60
+ json({ email: person.email, role: 'member' }, { cookie: ownerCookie })
61
+ )
62
+ );
63
+ const token = (inv.acceptUrl as string).split('/invite/')[1]!;
64
+ const accepted = await app.app.request(
65
+ '/api/v1/invitations/accept',
66
+ json({ token, name: person.name, password: person.password })
67
+ );
68
+ expect(accepted.status).toBe(200);
69
+ }
70
+
71
+ async function memberIdOf(
72
+ app: TestApp,
73
+ cookie: string,
74
+ email: string
75
+ ): Promise<{ id: string; userId: string }> {
76
+ const { members } = await readJson(
77
+ await app.app.request('/api/v1/members', { headers: { cookie, 'x-forwarded-for': nextIp() } })
78
+ );
79
+ const m = members.find((x: { email: string }) => x.email === email);
80
+ expect(m).toBeTruthy();
81
+ return { id: m.id, userId: m.userId };
82
+ }
83
+
84
+ beforeAll(async () => {
85
+ container = await startPostgres();
86
+ });
87
+
88
+ afterAll(async () => {
89
+ await container?.stop();
90
+ });
91
+
92
+ describe('with SUPERADMIN_EMAILS set', () => {
93
+ let app: TestApp;
94
+ let connectionString: string;
95
+ let ownerCookie: string;
96
+ let rootCookie: string;
97
+
98
+ beforeAll(async () => {
99
+ connectionString = await createDatabase(container, 'bg_armed');
100
+ // Deliberately messy allowlist value: mixed case + stray whitespace must
101
+ // still match (parse trims + lowercases; Better Auth stores lowercase).
102
+ app = await createTestApp(connectionString, { SUPERADMIN_EMAILS: ' Root@bg.test , second@bg.test ' });
103
+ await app.app.request(
104
+ '/api/v1/setup',
105
+ json({ setupToken: 'integration-test-setup-token', instanceName: 'BG', owner: OWNER })
106
+ );
107
+ ownerCookie = await signIn(app, OWNER.email, OWNER.password);
108
+ await inviteAndAccept(app, ownerCookie, ROOT);
109
+ await inviteAndAccept(app, ownerCookie, ADMIN);
110
+ await inviteAndAccept(app, ownerCookie, MEMBER);
111
+ const admin = await memberIdOf(app, ownerCookie, ADMIN.email);
112
+ const promoted = await app.app.request(`/api/v1/members/${admin.id}`, {
113
+ ...json({ role: 'admin' }, { cookie: ownerCookie }),
114
+ method: 'PATCH'
115
+ });
116
+ expect(promoted.status).toBe(200);
117
+ rootCookie = await signIn(app, ROOT.email, ROOT.password);
118
+ }, 60_000);
119
+
120
+ afterAll(async () => {
121
+ await app.stop();
122
+ });
123
+
124
+ it('rejects an unauthenticated call with 401', async () => {
125
+ const res = await app.app.request('/api/v1/admin/break-glass/claim-ownership', json({}));
126
+ expect(res.status).toBe(401);
127
+ });
128
+
129
+ it('rejects the allowlisted caller while their email is UNVERIFIED', async () => {
130
+ // ROOT accepted via the admin-visible copyable link — honestly unverified.
131
+ const { rows } = await app.db.pool.query(`SELECT email_verified FROM "user" WHERE email = $1`, [
132
+ ROOT.email
133
+ ]);
134
+ expect(rows[0].email_verified).toBe(false);
135
+ const res = await app.app.request(
136
+ '/api/v1/admin/break-glass/claim-ownership',
137
+ json({}, { cookie: rootCookie })
138
+ );
139
+ expect(res.status).toBe(403);
140
+ expect((await readJson(res)).error.code).toBe('forbidden');
141
+ });
142
+
143
+ it('rejects verified sessions whose email is NOT on the allowlist', async () => {
144
+ await app.db.pool.query(`UPDATE "user" SET email_verified = true WHERE email = ANY($1)`, [
145
+ [OWNER.email, ADMIN.email, MEMBER.email]
146
+ ]);
147
+ for (const person of [ADMIN, MEMBER]) {
148
+ const cookie = await signIn(app, person.email, person.password);
149
+ const res = await app.app.request('/api/v1/admin/break-glass/claim-ownership', json({}, { cookie }));
150
+ expect(res.status).toBe(403);
151
+ }
152
+ });
153
+
154
+ it('rejects a MACHINE principal fail-closed even when its owner is allowlisted + verified', async () => {
155
+ await app.db.pool.query(`UPDATE "user" SET email_verified = true WHERE email = $1`, [ROOT.email]);
156
+ // Key minted BY the superadmin themselves — ownership must not transfer.
157
+ const minted = await app.app.request(
158
+ '/api/v1/api-keys',
159
+ json({ name: 'root-key', scopes: [host.scopes.read, host.scopes.write] }, { cookie: rootCookie })
160
+ );
161
+ expect(minted.status).toBe(201);
162
+ const { key } = await readJson(minted);
163
+ const res = await app.app.request('/api/v1/admin/break-glass/claim-ownership', {
164
+ ...json({}),
165
+ headers: {
166
+ 'content-type': 'application/json',
167
+ 'x-forwarded-for': nextIp(),
168
+ authorization: `Bearer ${key}`
169
+ }
170
+ });
171
+ expect(res.status).toBe(403);
172
+ // The fail-closed scope gate, not the handler: the path is unlisted.
173
+ expect((await readJson(res)).error.code).toBe('endpoint_not_allowed');
174
+ });
175
+
176
+ it('claim-ownership recovers a workspace with NO active owner (audited, trigger satisfied)', async () => {
177
+ // Brick the workspace the only way possible: bypass the 0009 trigger the
178
+ // way an operator's broken migration/surgery would (the trigger blocks
179
+ // every normal path to a zero-owner state).
180
+ const surgical = new pg.Client({ connectionString });
181
+ await surgical.connect();
182
+ try {
183
+ await surgical.query('BEGIN');
184
+ await surgical.query(`SET LOCAL session_replication_role = replica`);
185
+ await surgical.query(
186
+ `UPDATE workspace_members SET role = 'member', is_active = false WHERE role = 'owner'`
187
+ );
188
+ await surgical.query('COMMIT');
189
+ } finally {
190
+ await surgical.end();
191
+ }
192
+ const owners = await app.db.pool.query(
193
+ `SELECT count(*)::int AS n FROM workspace_members WHERE role = 'owner' AND is_active = true`
194
+ );
195
+ expect(owners.rows[0].n).toBe(0);
196
+
197
+ const res = await app.app.request(
198
+ '/api/v1/admin/break-glass/claim-ownership',
199
+ json({}, { cookie: rootCookie })
200
+ );
201
+ expect(res.status).toBe(200);
202
+ const body = await readJson(res);
203
+ expect(body.role).toBe('owner');
204
+ expect(body.isActive).toBe(true);
205
+ expect(body.created).toBe(false); // ROOT already had a (member) row — promoted, not created
206
+
207
+ // The claim is real: the session now acts as an active owner.
208
+ const me = await readJson(
209
+ await app.app.request('/api/v1/me', { headers: { cookie: rootCookie, 'x-forwarded-for': nextIp() } })
210
+ );
211
+ expect(me.role).toBe('owner');
212
+
213
+ // Audited with the superadmin identity and before/after state.
214
+ const audit = await app.db.pool.query(
215
+ `SELECT actor_user_id, actor_via, metadata FROM audit_log WHERE action = 'break_glass.claim_ownership' ORDER BY id DESC LIMIT 1`
216
+ );
217
+ expect(audit.rows).toHaveLength(1);
218
+ expect(audit.rows[0].actor_via).toBe('session');
219
+ expect(audit.rows[0].metadata.superadminEmail).toBe(ROOT.email);
220
+ // ROOT's own row was an ACTIVE MEMBER before the claim (the surgery only
221
+ // demoted the owners) — the claim promoted it in place.
222
+ expect(audit.rows[0].metadata.before).toEqual({ role: 'member', isActive: true });
223
+ expect(audit.rows[0].metadata.after).toEqual({ role: 'owner', isActive: true });
224
+ });
225
+
226
+ it('claim-ownership for a NAMED user reactivates and promotes them', async () => {
227
+ const member = await memberIdOf(app, rootCookie, MEMBER.email);
228
+ // Deactivate the member first (root is an active owner now).
229
+ const deactivated = await app.app.request(`/api/v1/members/${member.id}`, {
230
+ ...json({ isActive: false }, { cookie: rootCookie }),
231
+ method: 'PATCH'
232
+ });
233
+ expect(deactivated.status).toBe(200);
234
+
235
+ const res = await app.app.request(
236
+ '/api/v1/admin/break-glass/claim-ownership',
237
+ json({ userId: member.userId }, { cookie: rootCookie })
238
+ );
239
+ expect(res.status).toBe(200);
240
+ const body = await readJson(res);
241
+ expect(body.userId).toBe(member.userId);
242
+ expect(body.role).toBe('owner');
243
+ expect(body.isActive).toBe(true);
244
+ expect(body.created).toBe(false);
245
+ });
246
+
247
+ it('claim-ownership CREATES the membership for a membership-less superadmin session', async () => {
248
+ // SECOND joins, then loses their membership row entirely (the orphaned
249
+ // state a lost setup race produces): can sign in, but reaches no
250
+ // workspace — /me is the zero-membership zero state (200, empty list),
251
+ // real workspace endpoints 401. Break-glass rescues exactly this.
252
+ await inviteAndAccept(app, rootCookie, SECOND);
253
+ await app.db.pool.query(
254
+ `DELETE FROM workspace_members WHERE user_id = (SELECT id FROM "user" WHERE email = $1)`,
255
+ [SECOND.email]
256
+ );
257
+ await app.db.pool.query(`UPDATE "user" SET email_verified = true WHERE email = $1`, [SECOND.email]);
258
+ const secondCookie = await signIn(app, SECOND.email, SECOND.password);
259
+ const orphaned = await app.app.request('/api/v1/me', {
260
+ headers: { cookie: secondCookie, 'x-forwarded-for': nextIp() }
261
+ });
262
+ expect(orphaned.status).toBe(200);
263
+ expect((await readJson(orphaned)).workspaces).toEqual([]);
264
+ const noAccess = await app.app.request(host.probeRoute, {
265
+ headers: { cookie: secondCookie, 'x-forwarded-for': nextIp() }
266
+ });
267
+ expect(noAccess.status).toBe(401);
268
+
269
+ const res = await app.app.request(
270
+ '/api/v1/admin/break-glass/claim-ownership',
271
+ json({}, { cookie: secondCookie })
272
+ );
273
+ expect(res.status).toBe(200);
274
+ const body = await readJson(res);
275
+ expect(body.created).toBe(true);
276
+ expect(body.role).toBe('owner');
277
+
278
+ const me = await app.app.request('/api/v1/me', {
279
+ headers: { cookie: secondCookie, 'x-forwarded-for': nextIp() }
280
+ });
281
+ expect(me.status).toBe(200);
282
+ expect((await readJson(me)).role).toBe('owner');
283
+ });
284
+
285
+ it('claim-ownership answers 404 for an unknown named target', async () => {
286
+ const res = await app.app.request(
287
+ '/api/v1/admin/break-glass/claim-ownership',
288
+ json({ userId: 'no-such-user' }, { cookie: rootCookie })
289
+ );
290
+ expect(res.status).toBe(404);
291
+ });
292
+
293
+ it('reset-2fa clears the target factor for a superadmin and 403s everyone else', async () => {
294
+ const member = await memberIdOf(app, rootCookie, MEMBER.email);
295
+ // Enrolled state, set directly: two-factor.test.ts proves enrollment; this
296
+ // suite proves the CLEARING (row + flag).
297
+ await app.db.pool.query(
298
+ `INSERT INTO two_factor (id, secret, backup_codes, user_id) VALUES ('bg-2fa-1', 'sekret', 'codes', $1)`,
299
+ [member.userId]
300
+ );
301
+ await app.db.pool.query(`UPDATE "user" SET two_factor_enabled = true WHERE id = $1`, [member.userId]);
302
+
303
+ // A verified admin who is NOT allowlisted cannot reset anyone's 2FA.
304
+ const adminCookie = await signIn(app, ADMIN.email, ADMIN.password);
305
+ const asAdmin = await app.app.request(
306
+ '/api/v1/admin/break-glass/reset-2fa',
307
+ json({ userId: member.userId }, { cookie: adminCookie })
308
+ );
309
+ expect(asAdmin.status).toBe(403);
310
+
311
+ const res = await app.app.request(
312
+ '/api/v1/admin/break-glass/reset-2fa',
313
+ json({ userId: member.userId }, { cookie: rootCookie })
314
+ );
315
+ expect(res.status).toBe(200);
316
+ expect((await readJson(res)).hadTwoFactor).toBe(true);
317
+
318
+ const rows = await app.db.pool.query(`SELECT count(*)::int AS n FROM two_factor WHERE user_id = $1`, [
319
+ member.userId
320
+ ]);
321
+ expect(rows.rows[0].n).toBe(0);
322
+ const flag = await app.db.pool.query(`SELECT two_factor_enabled FROM "user" WHERE id = $1`, [
323
+ member.userId
324
+ ]);
325
+ expect(flag.rows[0].two_factor_enabled).toBe(false);
326
+
327
+ const audit = await app.db.pool.query(
328
+ `SELECT metadata FROM audit_log WHERE action = 'break_glass.reset_two_factor' ORDER BY id DESC LIMIT 1`
329
+ );
330
+ expect(audit.rows).toHaveLength(1);
331
+ expect(audit.rows[0].metadata.targetUserId).toBe(member.userId);
332
+ expect(audit.rows[0].metadata.hadTwoFactor).toBe(true);
333
+
334
+ // Idempotent: a second reset is a truthful no-op.
335
+ const again = await app.app.request(
336
+ '/api/v1/admin/break-glass/reset-2fa',
337
+ json({ userId: member.userId }, { cookie: rootCookie })
338
+ );
339
+ expect(again.status).toBe(200);
340
+ expect((await readJson(again)).hadTwoFactor).toBe(false);
341
+ });
342
+
343
+ it('claim-ownership stamps origin=local on BOTH the insert and the conflict path (BG-1, PRDCT-1356)', async () => {
344
+ // Conflict path: a hub-projected row (the shape the cloud reconciler
345
+ // sweeps when the hub stops asserting it), seeded raw so the test does
346
+ // not depend on what earlier cases did to the fixture members.
347
+ await app.db.pool.query(
348
+ `INSERT INTO "user" (id, name, email, email_verified, created_at, updated_at)
349
+ VALUES ('bg-hub-user', 'Hub Row', 'hubrow@bg.test', true, now(), now())`
350
+ );
351
+ const ws = (
352
+ await app.db.pool.query<{ id: string }>(`SELECT id FROM workspaces ORDER BY created_at LIMIT 1`)
353
+ ).rows[0]!;
354
+ const seeded = await app.db.pool.query<{ id: string }>(
355
+ `INSERT INTO workspace_members (workspace_id, user_id, role, origin, is_active)
356
+ VALUES ($1, 'bg-hub-user', 'member', 'hub', true) RETURNING id`,
357
+ [ws.id]
358
+ );
359
+ const promoted = await app.app.request(
360
+ '/api/v1/admin/break-glass/claim-ownership',
361
+ json({ userId: 'bg-hub-user', workspaceId: ws.id }, { cookie: rootCookie })
362
+ );
363
+ expect(promoted.status).toBe(200);
364
+ const { rows } = await app.db.pool.query<{ origin: string; role: string }>(
365
+ `SELECT origin, role FROM workspace_members WHERE id = $1`,
366
+ [seeded.rows[0]!.id]
367
+ );
368
+ expect(rows).toEqual([{ origin: 'local', role: 'owner' }]);
369
+ // Insert path: root has no membership row on a fresh workspace — claim
370
+ // it and read the origin back.
371
+ const fresh = await app.db.pool.query<{ id: string }>(
372
+ `INSERT INTO workspaces (name) VALUES ('BG fresh') RETURNING id`
373
+ );
374
+ const claimed = await app.app.request(
375
+ '/api/v1/admin/break-glass/claim-ownership',
376
+ json({ workspaceId: fresh.rows[0]!.id }, { cookie: rootCookie })
377
+ );
378
+ expect(claimed.status).toBe(200);
379
+ const inserted = await app.db.pool.query<{ origin: string }>(
380
+ `SELECT origin FROM workspace_members WHERE id = $1`,
381
+ [(await readJson(claimed)).memberId]
382
+ );
383
+ expect(inserted.rows).toEqual([{ origin: 'local' }]);
384
+ });
385
+ });
386
+
387
+ describe('with SUPERADMIN_EMAILS unset (default)', () => {
388
+ let app: TestApp;
389
+
390
+ beforeAll(async () => {
391
+ app = await createTestApp(await createDatabase(container, 'bg_dormant'));
392
+ await app.app.request(
393
+ '/api/v1/setup',
394
+ json({ setupToken: 'integration-test-setup-token', instanceName: 'BG2', owner: ROOT })
395
+ );
396
+ // Even a VERIFIED owner whose email would be on a typical allowlist gets
397
+ // nothing: no env = no superadmin exists, the capability is dormant.
398
+ await app.db.pool.query(`UPDATE "user" SET email_verified = true WHERE email = $1`, [ROOT.email]);
399
+ }, 60_000);
400
+
401
+ afterAll(async () => {
402
+ await app.stop();
403
+ });
404
+
405
+ it('403s every session on both break-glass endpoints', async () => {
406
+ const cookie = await signIn(app, ROOT.email, ROOT.password);
407
+ const claim = await app.app.request('/api/v1/admin/break-glass/claim-ownership', json({}, { cookie }));
408
+ expect(claim.status).toBe(403);
409
+ const reset = await app.app.request(
410
+ '/api/v1/admin/break-glass/reset-2fa',
411
+ json({ userId: 'anyone' }, { cookie })
412
+ );
413
+ expect(reset.status).toBe(403);
414
+ });
415
+
416
+ it('401s unauthenticated callers (no session, no capability)', async () => {
417
+ const res = await app.app.request('/api/v1/admin/break-glass/claim-ownership', json({}));
418
+ expect(res.status).toBe(401);
419
+ });
420
+ });
421
+
422
+ describe('multi-workspace targeting (ADR 014)', () => {
423
+ const ROOT3 = { email: 'root@bg3.test', name: 'BG3 Root', password: 'bg3-root-password-123' };
424
+ let app: TestApp;
425
+ let rootCookie: string;
426
+ let w2 = '';
427
+
428
+ beforeAll(async () => {
429
+ app = await createTestApp(await createDatabase(container, 'bg_multi'), {
430
+ SUPERADMIN_EMAILS: ROOT3.email
431
+ });
432
+ await app.app.request(
433
+ '/api/v1/setup',
434
+ json({ setupToken: 'integration-test-setup-token', instanceName: 'BG3', owner: ROOT3 })
435
+ );
436
+ await app.db.pool.query(`UPDATE "user" SET email_verified = true WHERE email = $1`, [ROOT3.email]);
437
+ // A second workspace owned by someone else — root has NO standing in it.
438
+ const other = await app.auth.api.signUpEmail({
439
+ body: { email: 'other@bg3.test', password: 'bg3-other-password-12', name: 'BG3 Other' }
440
+ });
441
+ w2 = (await app.registry.workspaces.create('BG3 Second', other.user.id)).workspaceId;
442
+ rootCookie = await signIn(app, ROOT3.email, ROOT3.password);
443
+ }, 60_000);
444
+
445
+ afterAll(async () => {
446
+ await app.stop();
447
+ });
448
+
449
+ it('refuses to guess: several workspaces and no workspaceId → 400 workspace_required', async () => {
450
+ const res = await app.app.request(
451
+ '/api/v1/admin/break-glass/claim-ownership',
452
+ json({}, { cookie: rootCookie })
453
+ );
454
+ expect(res.status).toBe(400);
455
+ expect((await readJson(res)).error.code).toBe('workspace_required');
456
+ });
457
+
458
+ it('claims ownership in the EXPLICIT workspace', async () => {
459
+ const res = await app.app.request(
460
+ '/api/v1/admin/break-glass/claim-ownership',
461
+ json({ workspaceId: w2 }, { cookie: rootCookie })
462
+ );
463
+ expect(res.status).toBe(200);
464
+ const body = await readJson(res);
465
+ expect(body.workspaceId).toBe(w2);
466
+ expect(body.role).toBe('owner');
467
+ expect(body.created).toBe(true);
468
+ const { rows } = await app.db.pool.query(
469
+ `SELECT role, is_active FROM workspace_members wm
470
+ JOIN "user" u ON u.id = wm.user_id
471
+ WHERE u.email = $1 AND wm.workspace_id = $2`,
472
+ [ROOT3.email, w2]
473
+ );
474
+ expect(rows).toEqual([{ role: 'owner', is_active: true }]);
475
+ });
476
+
477
+ it('404s a workspace that does not exist', async () => {
478
+ const res = await app.app.request(
479
+ '/api/v1/admin/break-glass/claim-ownership',
480
+ json({ workspaceId: '00000000-0000-4000-8000-000000000000' }, { cookie: rootCookie })
481
+ );
482
+ expect(res.status).toBe(404);
483
+ });
484
+ });