@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,505 @@
1
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
2
+ import type { StartedPostgreSqlContainer } from '@testcontainers/postgresql';
3
+ import { symmetricDecrypt, symmetricEncrypt } from 'better-auth/crypto';
4
+ import { createDatabase, createTestApp, host, startPostgres, type TestApp } from './helpers.js';
5
+ import { FakeHub, type HubUserFixture } from '@antasphere/chassis-server/testing';
6
+ import * as sso from './sso-helpers.js';
7
+ import {
8
+ DEFAULT_GRANT_DIALS,
9
+ HubGrantService,
10
+ LOCK_WATCHDOG_HEADROOM_MS
11
+ } from '@antasphere/chassis-server/identity';
12
+
13
+ /**
14
+ * The per-user hub grant (identity/hub-grant.ts) against the FakeHub's
15
+ * rotating token endpoint — the security-critical mechanics:
16
+ *
17
+ * - refresh single-flight: N concurrent demands cost ONE token-endpoint
18
+ * POST (a double-refresh would trip RFC 9700 reuse detection and kill
19
+ * the whole grant family);
20
+ * - the advisory-lock re-read: a second "replica" (a second service
21
+ * instance on the same database) waiting on the lock consumes the
22
+ * winner's freshly stored access token instead of presenting the
23
+ * now-rotated-out refresh token;
24
+ * - the rotation round-trip goes THROUGH the Better-Auth-compatible
25
+ * encryption: the service decrypts what better-auth's SSO callback
26
+ * wrote, and what the service writes back is ciphertext better-auth's
27
+ * own primitive opens;
28
+ * - the failure taxonomy: invalid_grant → grant DEAD (row tokens nulled;
29
+ * dead marker = refreshToken IS NULL); invalid_client → transient and
30
+ * LOUD, the row is never touched; network/5xx → transient;
31
+ * - reuse detection end-to-end: presenting a rotated-out token kills the
32
+ * family — every later presentation of ANY of its tokens is dead;
33
+ * - a browser SSO login re-seeds a dead grant (the account row is
34
+ * re-written) and the service serves again.
35
+ *
36
+ * The services here are constructed STANDALONE against the booted app's
37
+ * database — deliberately: two instances = two replicas sharing one
38
+ * Postgres, which is exactly the deployment shape the lock protects.
39
+ */
40
+
41
+ const OWNER = { email: 'owner@grant.test', name: 'Op Grant', password: 'op-grant-password-123' };
42
+ const AUTH_SECRET = 'integration-test-secret-0123456789abcdef0123456789abcdef';
43
+
44
+ const ORG_G = '44444444-aaaa-4bbb-8ccc-000000000001';
45
+
46
+ const alice: HubUserFixture = {
47
+ sub: 'hub-grant-alice',
48
+ email: 'alice@grant.test',
49
+ workspaceId: ORG_G,
50
+ role: 'member',
51
+ workspaceName: 'Grant Org'
52
+ };
53
+
54
+ let container: StartedPostgreSqlContainer;
55
+ let hub: FakeHub;
56
+ let app: TestApp;
57
+ let connectionString: string;
58
+ let aliceId: string;
59
+
60
+ const json = (body: unknown) => ({
61
+ method: 'POST',
62
+ headers: { 'content-type': 'application/json' },
63
+ body: JSON.stringify(body)
64
+ });
65
+
66
+ function makeService(dials: Partial<typeof DEFAULT_GRANT_DIALS> = {}): HubGrantService {
67
+ return new HubGrantService({
68
+ db: app.db.db,
69
+ issuerUrl: hub.issuer,
70
+ clientId: host.hubClientId,
71
+ clientSecret: 'integration-test-hub-secret-0001',
72
+ tokenResource: `${hub.issuer}/mcp`,
73
+ key: async () => AUTH_SECRET,
74
+ connectionString,
75
+ logger: app.logger,
76
+ dials: { ...DEFAULT_GRANT_DIALS, tokenTimeoutMs: 3_000, ...dials }
77
+ });
78
+ }
79
+
80
+ /** The unanswered-presentation record for alice's hub link, or null. */
81
+ async function presentationRow(): Promise<{ refresh_token: string } | null> {
82
+ const { rows } = await app.db.pool.query(
83
+ `SELECT p.refresh_token FROM hub_grant_presentations p JOIN account a ON a.id = p.account_id
84
+ WHERE a.provider_id = 'antasphere' AND a.user_id = $1`,
85
+ [aliceId]
86
+ );
87
+ return (rows[0] as { refresh_token: string } | undefined) ?? null;
88
+ }
89
+
90
+ async function grantRow(): Promise<{ access_token: string | null; refresh_token: string | null }> {
91
+ const { rows } = await app.db.pool.query(
92
+ `SELECT a.access_token, a.refresh_token FROM account a JOIN "user" u ON u.id = a.user_id
93
+ WHERE u.email = $1 AND a.provider_id = 'antasphere'`,
94
+ [alice.email]
95
+ );
96
+ expect(rows).toHaveLength(1);
97
+ return rows[0];
98
+ }
99
+
100
+ /** Expire the stored access token so the next demand takes the refresh path. */
101
+ async function staleStoredAccess(): Promise<void> {
102
+ await app.db.pool.query(
103
+ `UPDATE account SET access_token_expires_at = now()
104
+ WHERE provider_id = 'antasphere' AND user_id = $1`,
105
+ [aliceId]
106
+ );
107
+ }
108
+
109
+ beforeAll(async () => {
110
+ [container, hub] = await Promise.all([startPostgres(), FakeHub.start({ clientId: host.hubClientId })]);
111
+ connectionString = await createDatabase(container, 'hub_grant');
112
+ app = await createTestApp(connectionString, {
113
+ EDITION: 'cloud',
114
+ HUB_ISSUER_URL: hub.issuer,
115
+ HUB_CLIENT_ID: host.hubClientId,
116
+ HUB_CLIENT_SECRET: 'integration-test-hub-secret-0001'
117
+ });
118
+ const res = await app.app.request(
119
+ '/api/v1/setup',
120
+ json({ setupToken: 'integration-test-setup-token', instanceName: 'Grant', owner: OWNER })
121
+ );
122
+ expect(res.status).toBe(201);
123
+ // A real browser SSO login writes the grant onto the account row — the
124
+ // exact rows (and ciphertext) production sees.
125
+ await sso.ssoLogin(app, hub, alice);
126
+ const { rows } = await app.db.pool.query(`SELECT id FROM "user" WHERE email = $1`, [alice.email]);
127
+ aliceId = rows[0].id as string;
128
+ }, 240_000);
129
+
130
+ afterAll(async () => {
131
+ await app?.stop();
132
+ await Promise.all([container?.stop(), hub?.stop()]);
133
+ });
134
+
135
+ describe('reading the grant better-auth wrote', () => {
136
+ it('serves the login-stored access token (decrypted) without any refresh', async () => {
137
+ const service = makeService();
138
+ const before = hub.refreshCount();
139
+ const access = await service.accessToken(aliceId);
140
+ expect(access.kind).toBe('ok');
141
+ if (access.kind !== 'ok') return;
142
+ // A real JWT the hub minted — decrypted from the row's ciphertext.
143
+ expect(access.accessToken.split('.')).toHaveLength(3);
144
+ expect(hub.refreshCount()).toBe(before); // no refresh needed
145
+ });
146
+
147
+ it('a user with no hub identity answers no_link', async () => {
148
+ const { rows } = await app.db.pool.query(`SELECT id FROM "user" WHERE email = $1`, [OWNER.email]);
149
+ const service = makeService();
150
+ expect(await service.accessToken(rows[0].id as string)).toEqual({ kind: 'no_link' });
151
+ });
152
+
153
+ it('prime() serves the login token from memory (zero row reads needed to prove: zero refreshes)', async () => {
154
+ const service = makeService();
155
+ const before = hub.refreshCount();
156
+ service.prime(aliceId, 'primed-token', new Date(Date.now() + 10 * 60_000));
157
+ expect(await service.accessToken(aliceId)).toEqual({ kind: 'ok', accessToken: 'primed-token' });
158
+ expect(hub.refreshCount()).toBe(before);
159
+ });
160
+ });
161
+
162
+ describe('rotation: single-flight, advisory-lock re-read, encrypted write-back', () => {
163
+ it('N concurrent demands on a stale token cost exactly ONE refresh POST', async () => {
164
+ await staleStoredAccess();
165
+ const service = makeService();
166
+ hub.tokenDelayMs = 150; // hold the refresh so all racers pile up
167
+ let results: Awaited<ReturnType<HubGrantService['accessToken']>>[];
168
+ const before = hub.refreshCount();
169
+ try {
170
+ results = await Promise.all(Array.from({ length: 8 }, () => service.accessToken(aliceId)));
171
+ } finally {
172
+ hub.tokenDelayMs = 0;
173
+ }
174
+ expect(hub.refreshCount()).toBe(before + 1);
175
+ const tokens = new Set(results.map((r) => (r.kind === 'ok' ? r.accessToken : r.kind)));
176
+ expect(tokens.size).toBe(1); // everyone got the same fresh token
177
+ });
178
+
179
+ it('the rotation wrote back THROUGH the encryption: new ciphertext, decryptable, raw rt inside', async () => {
180
+ const row = await grantRow();
181
+ expect(row.refresh_token).toBeTruthy();
182
+ // Never the raw secret at rest…
183
+ expect(row.refresh_token!).not.toMatch(/^rt_/);
184
+ // …but the raw secret under Better Auth's own primitive + key.
185
+ const raw = await symmetricDecrypt({ key: AUTH_SECRET, data: row.refresh_token! });
186
+ expect(raw).toMatch(/^rt_/);
187
+ // And the access token round-trips the same way.
188
+ const rawAccess = await symmetricDecrypt({ key: AUTH_SECRET, data: row.access_token! });
189
+ expect(rawAccess.split('.')).toHaveLength(3);
190
+ });
191
+
192
+ it('a second replica waiting on the lock consumes the winner’s token — never a second rotation', async () => {
193
+ await staleStoredAccess();
194
+ const replicaA = makeService();
195
+ const replicaB = makeService();
196
+ hub.tokenDelayMs = 250;
197
+ const before = hub.refreshCount();
198
+ let a: Awaited<ReturnType<HubGrantService['accessToken']>>;
199
+ let b: Awaited<ReturnType<HubGrantService['accessToken']>>;
200
+ try {
201
+ [a, b] = await Promise.all([replicaA.accessToken(aliceId), replicaB.accessToken(aliceId)]);
202
+ } finally {
203
+ hub.tokenDelayMs = 0;
204
+ }
205
+ expect(a.kind).toBe('ok');
206
+ expect(b.kind).toBe('ok');
207
+ // ONE refresh total: the loser re-read the row after the lock and found
208
+ // the winner's fresh access token. A second POST here would present the
209
+ // rotated-out refresh token — the family-killing event.
210
+ expect(hub.refreshCount()).toBe(before + 1);
211
+ });
212
+
213
+ it('presenting a rotated-out refresh token trips reuse detection and kills the FAMILY', async () => {
214
+ // Capture the current ciphertext, rotate once (making it stale), then
215
+ // put the stale ciphertext back — the split-brain/restored-backup shape
216
+ // the advisory lock cannot cover.
217
+ const staleRow = await grantRow();
218
+ const service = makeService();
219
+ await staleStoredAccess();
220
+ const rotated = await service.refresh(aliceId);
221
+ expect(rotated.kind).toBe('ok');
222
+ const freshRow = await grantRow();
223
+ expect(freshRow.refresh_token).not.toBe(staleRow.refresh_token);
224
+
225
+ // The stale replica presents the rotated-out token…
226
+ await app.db.pool.query(
227
+ `UPDATE account SET refresh_token = $1, access_token_expires_at = now()
228
+ WHERE provider_id = 'antasphere' AND user_id = $2`,
229
+ [staleRow.refresh_token, aliceId]
230
+ );
231
+ const staleReplica = makeService();
232
+ expect(await staleReplica.refresh(aliceId)).toEqual({ kind: 'grant_dead' });
233
+ // …the row is dead-marked (refreshToken IS NULL)…
234
+ const deadRow = await grantRow();
235
+ expect(deadRow.refresh_token).toBeNull();
236
+ expect(deadRow.access_token).toBeNull();
237
+
238
+ // …and the WHOLE family died hub-side: even the freshest token is now
239
+ // refused (restore it and try).
240
+ await app.db.pool.query(
241
+ `UPDATE account SET refresh_token = $1 WHERE provider_id = 'antasphere' AND user_id = $2`,
242
+ [freshRow.refresh_token, aliceId]
243
+ );
244
+ expect(await makeService().refresh(aliceId)).toEqual({ kind: 'grant_dead' });
245
+ });
246
+
247
+ it('a browser SSO login re-seeds the dead grant and the service serves again', async () => {
248
+ // (The family was killed above.) The login self-heals: better-auth
249
+ // re-writes both tokens on the SAME account row.
250
+ expect(await makeService().accessToken(aliceId)).toEqual({ kind: 'grant_dead' });
251
+ await sso.ssoLogin(app, hub, alice);
252
+ const row = await grantRow();
253
+ expect(row.refresh_token).toBeTruthy();
254
+ const access = await makeService().accessToken(aliceId);
255
+ expect(access.kind).toBe('ok');
256
+ });
257
+ });
258
+
259
+ describe('the failure taxonomy', () => {
260
+ it('invalid_client is TRANSIENT and loud — a misconfiguration never kills grants', async () => {
261
+ await staleStoredAccess();
262
+ const before = await grantRow();
263
+ hub.tokenMode = 'invalid_client';
264
+ try {
265
+ expect(await makeService().accessToken(aliceId)).toEqual({ kind: 'inconclusive' });
266
+ } finally {
267
+ hub.tokenMode = 'ok';
268
+ }
269
+ const after = await grantRow();
270
+ expect(after.refresh_token).toBe(before.refresh_token); // untouched
271
+ });
272
+
273
+ it('network failures and 5xx are transient — nothing destroyed, recovery is immediate', async () => {
274
+ await staleStoredAccess();
275
+ hub.tokenMode = 'network';
276
+ try {
277
+ expect(await makeService().accessToken(aliceId)).toEqual({ kind: 'inconclusive' });
278
+ } finally {
279
+ hub.tokenMode = 'ok';
280
+ }
281
+ hub.tokenMode = 'http500';
282
+ try {
283
+ expect(await makeService().accessToken(aliceId)).toEqual({ kind: 'inconclusive' });
284
+ } finally {
285
+ hub.tokenMode = 'ok';
286
+ }
287
+ const recovered = await makeService().accessToken(aliceId);
288
+ expect(recovered.kind).toBe('ok');
289
+ });
290
+
291
+ it('invalid_grant is DEFINITIVE: tokens nulled, callers get grant_dead until a re-login', async () => {
292
+ await staleStoredAccess();
293
+ hub.tokenMode = 'invalid_grant';
294
+ try {
295
+ expect(await makeService().accessToken(aliceId)).toEqual({ kind: 'grant_dead' });
296
+ } finally {
297
+ hub.tokenMode = 'ok';
298
+ }
299
+ const row = await grantRow();
300
+ expect(row.refresh_token).toBeNull();
301
+ expect(row.access_token).toBeNull();
302
+ // Dead marker read, no hub round-trip needed.
303
+ const before = hub.refreshCount();
304
+ expect(await makeService().accessToken(aliceId)).toEqual({ kind: 'grant_dead' });
305
+ expect(hub.refreshCount()).toBe(before);
306
+ // Heal for any later suites.
307
+ await sso.ssoLogin(app, hub, alice);
308
+ });
309
+
310
+ it('a legacy PLAINTEXT refresh token still works (decrypt passes it through) and rotates into ciphertext', async () => {
311
+ // Simulate a pre-encryption row: store the raw refresh token plaintext.
312
+ const row = await grantRow();
313
+ const raw = await symmetricDecrypt({ key: AUTH_SECRET, data: row.refresh_token! });
314
+ await app.db.pool.query(
315
+ `UPDATE account SET refresh_token = $1, access_token_expires_at = now()
316
+ WHERE provider_id = 'antasphere' AND user_id = $2`,
317
+ [raw, aliceId]
318
+ );
319
+ const access = await makeService().accessToken(aliceId);
320
+ expect(access.kind).toBe('ok');
321
+ // The write-back upgraded the row to ciphertext.
322
+ const upgraded = await grantRow();
323
+ expect(upgraded.refresh_token).not.toMatch(/^rt_/);
324
+ await expect(symmetricDecrypt({ key: AUTH_SECRET, data: upgraded.refresh_token! })).resolves.toMatch(
325
+ /^rt_/
326
+ );
327
+ // Sanity: our encrypt primitive matches better-auth's (same key path).
328
+ const cipher = await symmetricEncrypt({ key: AUTH_SECRET, data: 'probe' });
329
+ await expect(symmetricDecrypt({ key: AUTH_SECRET, data: cipher })).resolves.toBe('probe');
330
+ });
331
+ });
332
+
333
+ describe('ambiguous outcomes: the presentation record + the RFC 7662 probe (PRDCT-1370)', () => {
334
+ // Short timeouts: these cases wait for the client-side abort on purpose.
335
+ const FAST = { tokenTimeoutMs: 400, introspectTimeoutMs: 400 };
336
+
337
+ it('a timeout AFTER the hub rotated: the next demand probes, marks the grant dead, and never presents the rotated-out token — the family survives', async () => {
338
+ await staleStoredAccess();
339
+ const before = await grantRow();
340
+ const presentations = hub.refreshCount();
341
+ const probes = hub.introspectRequests.length;
342
+
343
+ hub.tokenMode = 'commit_then_hang';
344
+ try {
345
+ expect(await makeService(FAST).accessToken(aliceId)).toEqual({ kind: 'inconclusive' });
346
+ } finally {
347
+ hub.tokenMode = 'ok';
348
+ }
349
+ // The hub consumed the token (one presentation) and the record survived
350
+ // the transient outcome, naming the exact ciphertext presented.
351
+ expect(hub.refreshCount()).toBe(presentations + 1);
352
+ expect((await presentationRow())?.refresh_token).toBe(before.refresh_token);
353
+ expect((await grantRow()).refresh_token).toBe(before.refresh_token); // untouched
354
+
355
+ // Second demand: a probe, NOT a presentation.
356
+ expect(await makeService(FAST).accessToken(aliceId)).toEqual({ kind: 'grant_dead' });
357
+ expect(hub.refreshCount()).toBe(presentations + 1);
358
+ expect(hub.introspectRequests.length).toBe(probes + 1);
359
+ expect(hub.introspectRequests.at(-1)?.hint).toBe('refresh_token');
360
+ // The family was NOT torn down — the whole point (the CLI grant shares it).
361
+ expect(hub.isFamilyDead(alice.sub)).toBe(false);
362
+ // Dead-marked locally, record cleared.
363
+ const dead = await grantRow();
364
+ expect(dead.refresh_token).toBeNull();
365
+ expect(await presentationRow()).toBeNull();
366
+ // A browser login heals.
367
+ await sso.ssoLogin(app, hub, alice);
368
+ expect((await makeService().accessToken(aliceId)).kind).toBe('ok');
369
+ });
370
+
371
+ it('a timeout BEFORE the hub consumed the token: the probe says active and the next demand presents again — nothing destroyed', async () => {
372
+ await staleStoredAccess();
373
+ const before = await grantRow();
374
+ const probes = hub.introspectRequests.length;
375
+ hub.tokenMode = 'hang';
376
+ try {
377
+ expect(await makeService(FAST).accessToken(aliceId)).toEqual({ kind: 'inconclusive' });
378
+ } finally {
379
+ hub.tokenMode = 'ok';
380
+ }
381
+ expect((await presentationRow())?.refresh_token).toBe(before.refresh_token);
382
+
383
+ const access = await makeService(FAST).accessToken(aliceId);
384
+ expect(access.kind).toBe('ok');
385
+ expect(hub.introspectRequests.length).toBe(probes + 1);
386
+ expect((await grantRow()).refresh_token).not.toBe(before.refresh_token); // rotated
387
+ expect(await presentationRow()).toBeNull();
388
+ });
389
+
390
+ it('a confirmed refresh clears the record: ordinary refreshes never probe', async () => {
391
+ await staleStoredAccess();
392
+ const probes = hub.introspectRequests.length;
393
+ expect((await makeService().accessToken(aliceId)).kind).toBe('ok');
394
+ expect(await presentationRow()).toBeNull();
395
+ expect(hub.introspectRequests.length).toBe(probes);
396
+ });
397
+
398
+ it('the probe failing is transient — nothing destroyed, the record survives, recovery is immediate', async () => {
399
+ await staleStoredAccess();
400
+ const before = await grantRow();
401
+ hub.tokenMode = 'hang';
402
+ try {
403
+ expect(await makeService(FAST).accessToken(aliceId)).toEqual({ kind: 'inconclusive' });
404
+ } finally {
405
+ hub.tokenMode = 'ok';
406
+ }
407
+ for (const mode of ['network', 'http500'] as const) {
408
+ hub.introspectMode = mode;
409
+ try {
410
+ expect(await makeService(FAST).accessToken(aliceId)).toEqual({ kind: 'inconclusive' });
411
+ } finally {
412
+ hub.introspectMode = 'ok';
413
+ }
414
+ expect((await grantRow()).refresh_token).toBe(before.refresh_token);
415
+ expect((await presentationRow())?.refresh_token).toBe(before.refresh_token);
416
+ }
417
+ expect((await makeService(FAST).accessToken(aliceId)).kind).toBe('ok');
418
+ expect(await presentationRow()).toBeNull();
419
+ });
420
+
421
+ it('a browser re-login makes a stale record inert by construction (different ciphertext): no probe', async () => {
422
+ await staleStoredAccess();
423
+ const probes = hub.introspectRequests.length;
424
+ hub.tokenMode = 'hang';
425
+ try {
426
+ expect(await makeService(FAST).accessToken(aliceId)).toEqual({ kind: 'inconclusive' });
427
+ } finally {
428
+ hub.tokenMode = 'ok';
429
+ }
430
+ expect(await presentationRow()).not.toBeNull();
431
+ await sso.ssoLogin(app, hub, alice); // re-seeds the row: new ciphertext
432
+ await staleStoredAccess();
433
+ expect((await makeService(FAST).accessToken(aliceId)).kind).toBe('ok');
434
+ expect(hub.introspectRequests.length).toBe(probes);
435
+ expect(await presentationRow()).toBeNull();
436
+ });
437
+
438
+ it('a probe answered 401 or a 200 without a boolean `active` is inconclusive — a probe is never the reason a grant dies', async () => {
439
+ await staleStoredAccess();
440
+ const before = await grantRow();
441
+ hub.tokenMode = 'hang';
442
+ try {
443
+ expect(await makeService(FAST).accessToken(aliceId)).toEqual({ kind: 'inconclusive' });
444
+ } finally {
445
+ hub.tokenMode = 'ok';
446
+ }
447
+ for (const mode of ['invalid_client', 'malformed'] as const) {
448
+ hub.introspectMode = mode;
449
+ try {
450
+ expect(await makeService(FAST).accessToken(aliceId)).toEqual({ kind: 'inconclusive' });
451
+ } finally {
452
+ hub.introspectMode = 'ok';
453
+ }
454
+ expect((await grantRow()).refresh_token).toBe(before.refresh_token);
455
+ expect((await presentationRow())?.refresh_token).toBe(before.refresh_token);
456
+ }
457
+ expect((await makeService(FAST).accessToken(aliceId)).kind).toBe('ok');
458
+ });
459
+
460
+ it('the lock watchdog outlives probe + presentation: a sibling replica never presents the same token', async () => {
461
+ // The verifier's reproduction (PRDCT-1370): a short watchdog, a slow
462
+ // probe and a slow presentation. Without the clamp the watchdog cut the
463
+ // lock mid-refresh and replica B presented the token a second time —
464
+ // reuse detection, family dead. With it, exactly one presentation.
465
+ await staleStoredAccess();
466
+ hub.tokenMode = 'hang';
467
+ try {
468
+ expect(await makeService(FAST).accessToken(aliceId)).toEqual({ kind: 'inconclusive' });
469
+ } finally {
470
+ hub.tokenMode = 'ok';
471
+ }
472
+ const presentations = hub.refreshCount();
473
+ const dials = { tokenTimeoutMs: 3_000, introspectTimeoutMs: 3_000, lockWatchdogMs: 600 };
474
+ const a = makeService(dials);
475
+ const b = makeService(dials);
476
+ expect(a.dials.lockWatchdogMs).toBe(3_000 + 3_000 + LOCK_WATCHDOG_HEADROOM_MS);
477
+ hub.introspectDelayMs = 400;
478
+ hub.tokenDelayMs = 500;
479
+ try {
480
+ const first = a.accessToken(aliceId);
481
+ await new Promise((r) => setTimeout(r, 100));
482
+ const second = b.accessToken(aliceId);
483
+ const [ra, rb] = await Promise.all([first, second]);
484
+ expect(ra.kind).toBe('ok');
485
+ expect(rb.kind).toBe('ok');
486
+ } finally {
487
+ hub.introspectDelayMs = 0;
488
+ hub.tokenDelayMs = 0;
489
+ }
490
+ expect(hub.refreshCount()).toBe(presentations + 1);
491
+ expect(hub.isFamilyDead(alice.sub)).toBe(false);
492
+ });
493
+
494
+ it('invalid_client clears the record — the hub refused the client before consuming the token', async () => {
495
+ await staleStoredAccess();
496
+ hub.tokenMode = 'invalid_client';
497
+ try {
498
+ expect(await makeService(FAST).accessToken(aliceId)).toEqual({ kind: 'inconclusive' });
499
+ } finally {
500
+ hub.tokenMode = 'ok';
501
+ }
502
+ expect(await presentationRow()).toBeNull();
503
+ expect((await makeService(FAST).accessToken(aliceId)).kind).toBe('ok');
504
+ });
505
+ });