@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,510 @@
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
+ readJson,
8
+ startPostgres,
9
+ type TestApp,
10
+ host
11
+ } from './helpers.js';
12
+ import { oauthBearer } from './sso-helpers.js';
13
+
14
+ /**
15
+ * POST /api/v1/workspaces on the SELF-HOSTED edition (PRDCT-2444): the happy
16
+ * path, every refusal, the per-user cap under a concurrent burst, the audit
17
+ * placement (the NEW workspace hears of it, the one the person was in does
18
+ * NOT), idempotent replay, and `/me.canCreateWorkspace` agreeing with the
19
+ * route at every step.
20
+ */
21
+ const OWNER = { email: 'owner@wscreate.test', name: 'Olive Owner', password: 'olive-owner-password-123' };
22
+ const MEMBER = { email: 'member@wscreate.test', name: 'Mia Member', password: 'mia-member-password-1234' };
23
+ const GUEST = { email: 'guest@wscreate.test', name: 'Gus Guest', password: 'gus-guest-password-12345' };
24
+ const MIXED = { email: 'mixed@wscreate.test', name: 'Max Mixed', password: 'max-mixed-password-12345' };
25
+
26
+ const WS_HEADER = 'x-workspace-id';
27
+ const CAP = 3;
28
+
29
+ let container: StartedPostgreSqlContainer;
30
+ let app: TestApp;
31
+ let w1 = '';
32
+ let ownerCookie = '';
33
+ let memberCookie = '';
34
+ let guestCookie = '';
35
+ let mixedCookie = '';
36
+
37
+ let ipCounter = 0;
38
+ const nextIp = () => `10.61.${Math.floor(ipCounter / 250)}.${(ipCounter++ % 250) + 1}`;
39
+
40
+ const post = (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(target: TestApp, email: string, password: string): Promise<string> {
47
+ const res = await target.app.request('/api/v1/auth/sign-in/email', post({ email, password }));
48
+ expect(res.status).toBe(200);
49
+ return extractCookie(res);
50
+ }
51
+
52
+ async function userId(email: string): Promise<string> {
53
+ const res = await app.db.pool.query(`SELECT id FROM "user" WHERE email = $1`, [email]);
54
+ return res.rows[0].id as string;
55
+ }
56
+
57
+ async function addMember(
58
+ who: { email: string; name: string; password: string },
59
+ workspaceId: string,
60
+ origin: 'local' | 'guest',
61
+ role: 'member' | 'owner' = 'member'
62
+ ): Promise<void> {
63
+ const existing = await app.db.pool.query(`SELECT id FROM "user" WHERE email = $1`, [who.email]);
64
+ const id =
65
+ (existing.rows[0]?.id as string | undefined) ?? (await app.auth.api.signUpEmail({ body: who })).user.id;
66
+ await app.db.pool.query(
67
+ `INSERT INTO workspace_members (workspace_id, user_id, role, origin, is_active) VALUES ($1, $2, $3, $4, true)`,
68
+ [workspaceId, id, role, origin]
69
+ );
70
+ }
71
+
72
+ const create = (cookie: string, name: unknown, headers: Record<string, string> = {}) =>
73
+ app.app.request('/api/v1/workspaces', post({ name }, { cookie, ...headers }));
74
+
75
+ const canCreate = async (cookie: string, workspace?: string): Promise<boolean> => {
76
+ const res = await app.app.request('/api/v1/me', {
77
+ headers: { cookie, ...(workspace ? { [WS_HEADER]: workspace } : {}) }
78
+ });
79
+ expect(res.status).toBe(200);
80
+ return (await readJson(res)).canCreateWorkspace as boolean;
81
+ };
82
+
83
+ beforeAll(async () => {
84
+ container = await startPostgres();
85
+ app = await createTestApp(await createDatabase(container, 'ws_create'), {
86
+ MAX_WORKSPACES_PER_USER: String(CAP)
87
+ });
88
+ const setup = await app.app.request(
89
+ '/api/v1/setup',
90
+ post({ setupToken: 'integration-test-setup-token', instanceName: 'First', owner: OWNER })
91
+ );
92
+ expect(setup.status).toBe(201);
93
+ w1 = (await readJson(setup)).workspaceId;
94
+ ownerCookie = await signIn(app, OWNER.email, OWNER.password);
95
+
96
+ await addMember(MEMBER, w1, 'local');
97
+ memberCookie = await signIn(app, MEMBER.email, MEMBER.password);
98
+ await addMember(GUEST, w1, 'guest');
99
+ guestCookie = await signIn(app, GUEST.email, GUEST.password);
100
+ }, 240_000);
101
+
102
+ afterAll(async () => {
103
+ await app?.stop();
104
+ await container?.stop();
105
+ });
106
+
107
+ describe('POST /workspaces — self-hosted', () => {
108
+ let created = '';
109
+
110
+ it('a plain member creates a workspace and becomes its ACTIVE LOCAL OWNER', async () => {
111
+ expect(await canCreate(memberCookie)).toBe(true);
112
+ const res = await create(memberCookie, ' Mia’s studio ');
113
+ expect(res.status).toBe(201);
114
+ const body = await readJson(res);
115
+ // trimmed; the look is the dialog's to set, absent here so the row carries none
116
+ expect(body).toEqual({
117
+ workspace: {
118
+ id: expect.any(String),
119
+ name: 'Mia’s studio',
120
+ look: { theme: null, pattern: null, field: null, grain: null }
121
+ }
122
+ });
123
+ created = body.workspace.id;
124
+ expect(created).not.toBe(w1);
125
+
126
+ const rows = await app.db.pool.query(
127
+ `SELECT role, origin, is_active FROM workspace_members WHERE workspace_id = $1`,
128
+ [created]
129
+ );
130
+ expect(rows.rows).toEqual([{ role: 'owner', origin: 'local', is_active: true }]);
131
+ const ws = await app.db.pool.query(`SELECT name, central_account_id FROM workspaces WHERE id = $1`, [
132
+ created
133
+ ]);
134
+ expect(ws.rows[0]).toEqual({ name: 'Mia’s studio', central_account_id: null });
135
+
136
+ // /me lists it, and the header selects it with the owner role.
137
+ const me = await readJson(
138
+ await app.app.request('/api/v1/me', { headers: { cookie: memberCookie, [WS_HEADER]: created } })
139
+ );
140
+ expect(me.activeWorkspaceId).toBe(created);
141
+ expect(me.role).toBe('owner');
142
+ expect(me.workspaces.map((w: { id: string }) => w.id)).toEqual([w1, created]);
143
+ });
144
+
145
+ it('audits workspace.create in the NEW workspace only — the one the person was in hears nothing', async () => {
146
+ const mine = await app.db.pool.query(
147
+ `SELECT action, actor_user_id, actor_via, resource_type, resource_id, metadata
148
+ FROM audit_log WHERE workspace_id = $1`,
149
+ [created]
150
+ );
151
+ expect(mine.rows).toEqual([
152
+ {
153
+ action: 'workspace.create',
154
+ actor_user_id: await userId(MEMBER.email),
155
+ actor_via: 'session',
156
+ resource_type: 'workspace',
157
+ resource_id: created,
158
+ metadata: { name: 'Mia’s studio' }
159
+ }
160
+ ]);
161
+ // Nothing anywhere else mentions it: not by action, not by path, not by id.
162
+ const elsewhere = await app.db.pool.query(
163
+ `SELECT id FROM audit_log
164
+ WHERE (workspace_id IS DISTINCT FROM $1::uuid)
165
+ AND (action ILIKE '%workspace%' OR resource_id = $1::text OR metadata::text ILIKE '%' || $1::text || '%')`,
166
+ [created]
167
+ );
168
+ expect(elsewhere.rows).toEqual([]);
169
+ // …and W1's owner cannot read it through the API either.
170
+ const audit = await readJson(
171
+ await app.app.request('/api/v1/audit', { headers: { cookie: ownerCookie } })
172
+ );
173
+ expect(JSON.stringify(audit)).not.toContain(created);
174
+ expect(JSON.stringify(audit)).not.toContain('workspace.create');
175
+ });
176
+
177
+ it('400 on a name that is blank once trimmed, on a missing name, and on a control character', async () => {
178
+ for (const name of [' ', '', undefined, 'bad\u0000name', 'x'.repeat(121)]) {
179
+ const res = await create(memberCookie, name);
180
+ expect(res.status).toBe(400);
181
+ expect((await readJson(res)).error.code).toBe('validation_error');
182
+ }
183
+ });
184
+
185
+ it('401 without a session', async () => {
186
+ const res = await app.app.request('/api/v1/workspaces', post({ name: 'Nope' }));
187
+ expect(res.status).toBe(401);
188
+ });
189
+
190
+ it('401 for a session whose user holds NO active membership (not a member of this instance)', async () => {
191
+ const stray = { email: 'stray@wscreate.test', name: 'Stray', password: 'stray-user-password-123' };
192
+ await app.auth.api.signUpEmail({ body: stray });
193
+ const cookie = await signIn(app, stray.email, stray.password);
194
+ const res = await create(cookie, 'Squat');
195
+ expect(res.status).toBe(401);
196
+ const count = await app.db.pool.query(`SELECT count(*)::int AS n FROM workspaces WHERE name = 'Squat'`);
197
+ expect(count.rows[0].n).toBe(0);
198
+ });
199
+
200
+ it('403 guest_forbidden for a guest-only user, and /me says so', async () => {
201
+ expect(await canCreate(guestCookie)).toBe(false);
202
+ const res = await create(guestCookie, 'Guest land');
203
+ expect(res.status).toBe(403);
204
+ expect((await readJson(res)).error.code).toBe('guest_forbidden');
205
+ });
206
+
207
+ it('a guest SOMEWHERE who is a real member ELSEWHERE may create — from either workspace', async () => {
208
+ const other = (await app.registry.workspaces.create('Other', await userId(OWNER.email))).workspaceId;
209
+ await addMember(MIXED, w1, 'guest');
210
+ await addMember(MIXED, other, 'local');
211
+ mixedCookie = await signIn(app, MIXED.email, MIXED.password);
212
+ // Active workspace = W1, where they are a guest: still allowed, it is a
213
+ // fact about the person.
214
+ expect(await canCreate(mixedCookie, w1)).toBe(true);
215
+ const res = await create(mixedCookie, 'Mixed’s own', { [WS_HEADER]: w1 });
216
+ expect(res.status).toBe(201);
217
+ });
218
+
219
+ it('machine credentials are refused: API key and OAuth bearer, both 403, nothing created', async () => {
220
+ const minted = await readJson(
221
+ await app.app.request(
222
+ '/api/v1/api-keys',
223
+ post(
224
+ { name: 'all-scopes', scopes: [host.scopes.read, host.scopes.write, host.scopes.dataExport] },
225
+ { cookie: ownerCookie }
226
+ )
227
+ )
228
+ );
229
+ const byKey = await app.app.request(
230
+ '/api/v1/workspaces',
231
+ post({ name: 'By key' }, { authorization: `Bearer ${minted.key}` })
232
+ );
233
+ expect(byKey.status).toBe(403);
234
+ expect((await readJson(byKey)).error.code).toBe('endpoint_not_allowed');
235
+
236
+ const bearer = await oauthBearer(app, ownerCookie);
237
+ const byToken = await app.app.request(
238
+ '/api/v1/workspaces',
239
+ post({ name: 'By token' }, { authorization: `Bearer ${bearer}` })
240
+ );
241
+ expect(byToken.status).toBe(403);
242
+ expect((await readJson(byToken)).error.code).toBe('endpoint_not_allowed');
243
+
244
+ // The flag is false for a machine credential too.
245
+ const me = await readJson(
246
+ await app.app.request('/api/v1/me', { headers: { authorization: `Bearer ${minted.key}` } })
247
+ );
248
+ expect(me.canCreateWorkspace).toBe(false);
249
+
250
+ const count = await app.db.pool.query(
251
+ `SELECT count(*)::int AS n FROM workspaces WHERE name IN ('By key', 'By token')`
252
+ );
253
+ expect(count.rows[0].n).toBe(0);
254
+ });
255
+
256
+ it('is not reachable through /mcp: no tool creates a workspace', async () => {
257
+ const bearer = await oauthBearer(app, ownerCookie);
258
+ const res = await app.app.request('/mcp', {
259
+ method: 'POST',
260
+ headers: {
261
+ 'content-type': 'application/json',
262
+ accept: 'application/json, text/event-stream',
263
+ authorization: `Bearer ${bearer}`
264
+ },
265
+ body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {} })
266
+ });
267
+ expect(res.status).toBe(200);
268
+ const names = ((await readJson(res)).result.tools as Array<{ name: string }>).map((t) => t.name);
269
+ expect(names.length).toBeGreaterThan(0);
270
+ expect(names.filter((n) => /workspace/i.test(n) && /creat|new|add/i.test(n))).toEqual([]);
271
+ });
272
+
273
+ it('replays an Idempotency-Key instead of creating twice; a different body 409s', async () => {
274
+ const before = await app.db.pool.query(`SELECT count(*)::int AS n FROM workspaces`);
275
+ const first = await create(ownerCookie, 'Once', { 'idempotency-key': 'ws-once' });
276
+ expect(first.status).toBe(201);
277
+ const again = await create(ownerCookie, 'Once', { 'idempotency-key': 'ws-once' });
278
+ expect(again.status).toBe(201);
279
+ expect(again.headers.get('idempotency-replayed')).toBe('true');
280
+ expect((await readJson(again)).workspace.id).toBe((await readJson(first)).workspace.id);
281
+ const reuse = await create(ownerCookie, 'Twice', { 'idempotency-key': 'ws-once' });
282
+ expect(reuse.status).toBe(409);
283
+ const after = await app.db.pool.query(`SELECT count(*)::int AS n FROM workspaces`);
284
+ expect(after.rows[0].n).toBe(before.rows[0].n + 1);
285
+ });
286
+
287
+ it('caps OWNED workspaces per user: a concurrent burst never passes the cap, then 403 workspace_limit_reached', async () => {
288
+ // MEMBER owns 1 (created above); the cap is 3.
289
+ const owned = async () =>
290
+ (
291
+ await app.db.pool.query(
292
+ `SELECT count(*)::int AS n FROM workspace_members
293
+ WHERE user_id = $1 AND role = 'owner' AND is_active`,
294
+ [await userId(MEMBER.email)]
295
+ )
296
+ ).rows[0].n as number;
297
+ expect(await owned()).toBe(1);
298
+ // 30-way, on purpose: without the per-user advisory lock every request
299
+ // counts "1 owned" before any insert commits and they ALL pass (measured:
300
+ // 10 created, 11 owned). A small burst is bounded by luck — by the pool
301
+ // and the event loop — and would stay green with the lock removed.
302
+ const BURST = 30;
303
+ const burst = await Promise.all(
304
+ Array.from({ length: BURST }, (_, i) => create(memberCookie, `Burst ${i}`))
305
+ );
306
+ const statuses = burst.map((r) => r.status);
307
+ // EXACTLY the room that was left — not "at most", not "about".
308
+ expect(statuses.filter((st) => st === 201)).toHaveLength(CAP - 1);
309
+ expect(statuses.filter((st) => st === 403)).toHaveLength(BURST - (CAP - 1));
310
+ for (const res of burst.filter((r) => r.status === 403)) {
311
+ expect((await readJson(res)).error.code).toBe('workspace_limit_reached');
312
+ }
313
+ expect(await owned()).toBe(CAP);
314
+ expect(await canCreate(memberCookie)).toBe(false);
315
+
316
+ // The cap counts OWNED workspaces only: memberships elsewhere are free,
317
+ // and another person is unaffected.
318
+ expect(await canCreate(mixedCookie)).toBe(true);
319
+ });
320
+
321
+ it('a deactivated owner membership stops counting; the cap is per person', async () => {
322
+ const id = await userId(MEMBER.email);
323
+ // Hand one of Mia's workspaces a second owner, then deactivate Mia there.
324
+ const mine = await app.db.pool.query(
325
+ `SELECT workspace_id FROM workspace_members WHERE user_id = $1 AND role = 'owner' LIMIT 1`,
326
+ [id]
327
+ );
328
+ const target = mine.rows[0].workspace_id as string;
329
+ await addMember(OWNER, target, 'local', 'owner');
330
+ await app.db.pool.query(
331
+ `UPDATE workspace_members SET is_active = false WHERE workspace_id = $1 AND user_id = $2`,
332
+ [target, id]
333
+ );
334
+ expect(await canCreate(memberCookie)).toBe(true);
335
+ expect((await create(memberCookie, 'Room again')).status).toBe(201);
336
+ expect((await create(memberCookie, 'Over')).status).toBe(403);
337
+ });
338
+ });
339
+
340
+ describe('MAX_WORKSPACES_PER_USER=0 closes creation for everyone', () => {
341
+ let closed: TestApp;
342
+ let cookie = '';
343
+
344
+ beforeAll(async () => {
345
+ closed = await createTestApp(await createDatabase(container, 'ws_create_closed'), {
346
+ MAX_WORKSPACES_PER_USER: '0'
347
+ });
348
+ const setup = await closed.app.request(
349
+ '/api/v1/setup',
350
+ post({ setupToken: 'integration-test-setup-token', instanceName: 'Closed', owner: OWNER })
351
+ );
352
+ expect(setup.status).toBe(201);
353
+ cookie = await signIn(closed, OWNER.email, OWNER.password);
354
+ }, 240_000);
355
+
356
+ afterAll(async () => {
357
+ await closed?.stop();
358
+ });
359
+
360
+ it('403 workspace_creation_disabled even for the instance owner, and /me answers false', async () => {
361
+ const res = await closed.app.request('/api/v1/workspaces', post({ name: 'Nope' }, { cookie }));
362
+ expect(res.status).toBe(403);
363
+ expect((await readJson(res)).error.code).toBe('workspace_creation_disabled');
364
+ const me = await readJson(await closed.app.request('/api/v1/me', { headers: { cookie } }));
365
+ expect(me.canCreateWorkspace).toBe(false);
366
+ const count = await closed.db.pool.query(`SELECT count(*)::int AS n FROM workspaces`);
367
+ expect(count.rows[0].n).toBe(1);
368
+ });
369
+ });
370
+
371
+ describe('the creation rate wall counts identified POSTs only, per person first, then per address', () => {
372
+ let walled: TestApp;
373
+ const people: Array<{ email: string; cookie: string }> = [];
374
+
375
+ const req = (method: string, address: string, cookie?: string, name = 'Walled') =>
376
+ walled.app.request('/api/v1/workspaces', {
377
+ method,
378
+ headers: {
379
+ 'content-type': 'application/json',
380
+ 'x-forwarded-for': address,
381
+ ...(cookie ? { cookie } : {})
382
+ },
383
+ ...(method === 'POST' ? { body: JSON.stringify({ name }) } : {})
384
+ });
385
+ const flag = async (address: string, cookie: string): Promise<boolean> =>
386
+ (
387
+ await readJson(
388
+ await walled.app.request('/api/v1/me', { headers: { cookie, 'x-forwarded-for': address } })
389
+ )
390
+ ).canCreateWorkspace as boolean;
391
+
392
+ beforeAll(async () => {
393
+ walled = await createTestApp(await createDatabase(container, 'ws_create_wall'), {
394
+ MAX_WORKSPACES_PER_USER: '1000' // the cap out of the way: only the wall refuses here
395
+ });
396
+ const setup = await walled.app.request(
397
+ '/api/v1/setup',
398
+ post({ setupToken: 'integration-test-setup-token', instanceName: 'Walled', owner: OWNER })
399
+ );
400
+ expect(setup.status).toBe(201);
401
+ const w = (await readJson(setup)).workspaceId as string;
402
+ for (let i = 0; i < 12; i++) {
403
+ const who = { email: `p${i}@wall.test`, name: `Person ${i}`, password: 'wall-person-password-123' };
404
+ const created = await walled.auth.api.signUpEmail({ body: who });
405
+ await walled.db.pool.query(
406
+ `INSERT INTO workspace_members (workspace_id, user_id, role, origin, is_active) VALUES ($1, $2, 'member', 'local', true)`,
407
+ [w, created.user.id]
408
+ );
409
+ people.push({ email: who.email, cookie: await signIn(walled, who.email, who.password) });
410
+ }
411
+ }, 300_000);
412
+
413
+ afterAll(async () => {
414
+ await walled?.stop();
415
+ });
416
+
417
+ it('unauthenticated requests and other methods spend NOTHING: a person’s first POST from that address is served', async () => {
418
+ const address = '10.64.0.1';
419
+ for (let i = 0; i < 61; i++) {
420
+ expect((await req('OPTIONS', address)).status).not.toBe(429);
421
+ expect((await req('POST', address)).status).toBe(401); // no session
422
+ }
423
+ for (const method of ['HEAD', 'PUT', 'PATCH', 'DELETE', 'GET']) {
424
+ for (let i = 0; i < 13; i++) await req(method, address, people[0]!.cookie);
425
+ }
426
+ // 61 OPTIONS + 61 anonymous POSTs + 65 signed-in non-POSTs later:
427
+ expect(await flag(address, people[0]!.cookie)).toBe(true);
428
+ expect((await req('POST', address, people[0]!.cookie, 'First ever')).status).toBe(201);
429
+ });
430
+
431
+ it('one person gets 60 attempts an hour, then 429 — and /me stops offering it; a colleague at the SAME address is untouched', async () => {
432
+ const address = '10.64.0.2';
433
+ const hammer = people[1]!;
434
+ const statuses: number[] = [];
435
+ for (let i = 0; i < 65; i++) statuses.push((await req('POST', address, hammer.cookie, `H${i}`)).status);
436
+ expect(statuses.slice(0, 60).every((st) => st === 201)).toBe(true);
437
+ expect(statuses.slice(60)).toEqual([429, 429, 429, 429, 429]);
438
+ expect(await flag(address, hammer.cookie)).toBe(false); // honest about the wall, not the cap (61 < 1000)
439
+ // The person bucket follows the PERSON, not the address.
440
+ expect((await req('POST', '10.64.0.99', hammer.cookie)).status).toBe(429);
441
+
442
+ const colleague = people[2]!;
443
+ expect(await flag(address, colleague.cookie)).toBe(true);
444
+ expect((await req('POST', address, colleague.cookie, 'Colleague')).status).toBe(201);
445
+ });
446
+
447
+ it('an address gets ten people’s worth (600): past it everyone there is refused, and is served again from elsewhere', async () => {
448
+ const address = '10.64.0.3';
449
+ // Person 0 has spent 1 point of their own, elsewhere: start from person 3.
450
+ let served = 0;
451
+ for (const person of people.slice(3, 12)) {
452
+ // nine fresh people × 60 = 540
453
+ for (let i = 0; i < 60; i++) {
454
+ const res = await req('POST', address, person.cookie, `A${served}`);
455
+ expect(res.status).toBe(201);
456
+ served++;
457
+ }
458
+ }
459
+ // The colleague of the previous test has 59 left: 59 more = 599; person 0 has 59 left: 1 more = 600.
460
+ for (let i = 0; i < 59; i++)
461
+ expect((await req('POST', address, people[2]!.cookie, `B${i}`)).status).toBe(201);
462
+ expect((await req('POST', address, people[0]!.cookie, 'The 600th')).status).toBe(201);
463
+ // The address is spent. Person 0 still has budget of their own (58) — refused HERE only.
464
+ expect((await req('POST', address, people[0]!.cookie, 'Over')).status).toBe(429);
465
+ expect(await flag(address, people[0]!.cookie)).toBe(false);
466
+ expect(await flag('10.64.0.4', people[0]!.cookie)).toBe(true);
467
+ expect((await req('POST', '10.64.0.4', people[0]!.cookie, 'Elsewhere')).status).toBe(201);
468
+ // …and the refusal at the walled address did NOT charge the person: 58 − 1 (Elsewhere) = 57 left.
469
+ let left = 0;
470
+ while ((await req('POST', '10.64.0.5', people[0]!.cookie, `L${left}`)).status === 201) left++;
471
+ expect(left).toBe(57);
472
+ }, 120_000);
473
+ });
474
+
475
+ describe('TOOL_RESTRICTED does nothing on the self-hosted edition', () => {
476
+ // PRDCT-2947: the switch is the cloud's (organizations granted by
477
+ // Antasphere); a self-hosted workspace is local, so creation stays open
478
+ // and discovery carries no `restricted` key.
479
+ let oss: TestApp;
480
+ let cookie = '';
481
+
482
+ beforeAll(async () => {
483
+ oss = await createTestApp(await createDatabase(container, 'ws_create_restricted_oss'), {
484
+ TOOL_RESTRICTED: 'true'
485
+ });
486
+ const setup = await oss.app.request(
487
+ '/api/v1/setup',
488
+ post({ setupToken: 'integration-test-setup-token', instanceName: 'Restricted OSS', owner: OWNER })
489
+ );
490
+ expect(setup.status).toBe(201);
491
+ cookie = await signIn(oss, OWNER.email, OWNER.password);
492
+ }, 240_000);
493
+
494
+ afterAll(async () => {
495
+ await oss?.stop();
496
+ });
497
+
498
+ it('/me.canCreateWorkspace stays true, POST /workspaces answers 201, /instance carries no restricted key', async () => {
499
+ const meRes = await oss.app.request('/api/v1/me', { headers: { cookie } });
500
+ expect(meRes.status).toBe(200);
501
+ expect((await readJson(meRes)).canCreateWorkspace).toBe(true);
502
+
503
+ const res = await oss.app.request('/api/v1/workspaces', post({ name: 'Still open' }, { cookie }));
504
+ expect(res.status).toBe(201);
505
+ expect((await readJson(res)).workspace.name).toBe('Still open');
506
+
507
+ const info = await readJson(await oss.app.request('/api/v1/instance'));
508
+ expect('restricted' in info).toBe(false);
509
+ });
510
+ });