@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,896 @@
1
+ import { betterAuth } from 'better-auth';
2
+ import { APIError } from 'better-auth/api';
3
+ import { createAuthMiddleware } from 'better-auth/api';
4
+ import { drizzleAdapter } from 'better-auth/adapters/drizzle';
5
+ import { emailOTP, genericOAuth, jwt, twoFactor } from 'better-auth/plugins';
6
+ import { deleteSessionCookie } from 'better-auth/cookies';
7
+ import { generateRandomString } from 'better-auth/crypto';
8
+ import { oauthProvider } from '@better-auth/oauth-provider';
9
+ import { and, eq } from 'drizzle-orm';
10
+ import { account, instanceSettings, jwks, oauthAccessToken, oauthClient, oauthConsent, oauthRefreshToken, session, twoFactor as twoFactorTable, user, verification, workspaceMembers } from '@antasphere/chassis-db';
11
+ import { HUB_SSO_PROVIDER_ID, HubSsoLoginError } from './hub-sso.js';
12
+ import { parseSuperadminEmails } from '../accounts/superadmin.js';
13
+ import { demoPassPlugin } from './demo-pass-plugin.js';
14
+ export const AUTH_BASE_PATH = '/api/v1/auth';
15
+ /**
16
+ * The canonical OAuth resource identifier (JWT `aud`, RFC 8707) — the bundled
17
+ * MCP endpoint. Derived from PUBLIC_BASE_URL at boot, never from its own env
18
+ * var: AS and resource live on one origin, so the byte-identical-URL footgun
19
+ * of the split dashboard+MCP deployment cannot exist here.
20
+ */
21
+ export function mcpResourceUrl(publicBaseUrl) {
22
+ return publicBaseUrl.replace(/\/+$/, '') + '/mcp';
23
+ }
24
+ /** Metadata URI fields a registering client may present to users (RFC 7591). */
25
+ const CLIENT_METADATA_URI_FIELDS = ['client_uri', 'logo_uri', 'tos_uri', 'policy_uri'];
26
+ /**
27
+ * Every route that can CHANGE the account's email address from inside the
28
+ * tool, enumerated against the pinned 1.6.22 surface (re-verify on ANY Better
29
+ * Auth bump):
30
+ *
31
+ * - POST /change-email: the core self-serve change (`user.changeEmail`);
32
+ * - POST /email-otp/request-email-change + POST /email-otp/change-email: the
33
+ * emailOTP plugin's pair. Both answer 400 today (the plugin's own
34
+ * `changeEmail.enabled` is never set); closed as config insurance, the
35
+ * stance /set-password has in isPasswordResetPath.
36
+ *
37
+ * On EDITION=cloud the address is the hub's: the sign-in re-syncs it at every
38
+ * login (identity/hub-sso.ts), so a change made here is overwritten at the
39
+ * next one, and until then the person's address here differs from the one
40
+ * their hub identity carries. The dashboard hides the form
41
+ * (`emailChange: false`); the route itself refuses too (PRDCT-2818), like the
42
+ * owner's change-email link (api/members.ts, `email_change_disabled`).
43
+ * Ruled OUT on purpose: the tokened GET /verify-email stays open, since it
44
+ * also lands the address verification the instance sends, and with both
45
+ * mints closed no change token can be issued on cloud.
46
+ */
47
+ function isEmailChangePath(path) {
48
+ return (path.startsWith('/change-email') ||
49
+ path.startsWith('/email-otp/request-email-change') ||
50
+ path.startsWith('/email-otp/change-email'));
51
+ }
52
+ /**
53
+ * Every Better Auth route that can SET or RESET a local password with no
54
+ * current-password proof, enumerated against the pinned 1.6.22 surface
55
+ * (re-enumerated on the 1.6.15 → 1.6.22 bump, 2026-08-29, ADR 001; the
56
+ * dist-wide createAuthEndpoint path+method diff was one added route in the
57
+ * unregistered oauth-popup plugin, nothing removed)
58
+ * (re-verify on ANY Better Auth bump):
59
+ *
60
+ * - core emailAndPassword: POST /request-password-reset,
61
+ * POST /reset-password, GET /reset-password/:token (the mailed callback);
62
+ * - emailOTP plugin (active whenever a mailer delivers):
63
+ * POST /email-otp/request-password-reset, POST /email-otp/reset-password,
64
+ * and the deprecated POST /forget-password/email-otp alias;
65
+ * - core /set-password (the passwordless-ADD route): included as bump
66
+ * insurance. In 1.6.22 its endpoint is `createAuthEndpoint.serverOnly`;
67
+ * in 1.6.15 it was registered PATHLESS
68
+ * (update-user.mjs — `createAuthEndpoint({ method, body, use })` with no
69
+ * path arg), so better-call's router SKIPS it (`!endpoint.path` →
70
+ * `continue`, router.mjs) and it is unreachable over HTTP today — the one
71
+ * route that could otherwise let a passwordless hub-JIT user ADD a
72
+ * credential and then /sign-in/email. A future bump could give it a path;
73
+ * a security allowlist must not depend on that accident, so guard it now
74
+ * (denying an unreachable path is a no-op until it isn't).
75
+ *
76
+ * Ruled OUT of this predicate on purpose, against the same 1.6.22 surface:
77
+ * - /change-password proves the CURRENT password AND requires an existing
78
+ * credential account (CREDENTIAL_ACCOUNT_NOT_FOUND otherwise), so a
79
+ * hub-JIT user — who has neither — cannot set a password through it;
80
+ * - admin /admin/set-user-password and the phoneNumber reset routes live in
81
+ * the admin / phoneNumber plugins, neither of which this instance
82
+ * registers (only genericOAuth, emailOTP, twoFactor, jwt, oauthProvider),
83
+ * so they are never mounted;
84
+ * - /sign-up/email is closed by the sign-up switches + the before-hook.
85
+ *
86
+ * On EDITION=cloud these are the SSO-bypass entrance the D1 hub-only posture
87
+ * closes (internal/federation.md, ADR 017): a hub-JIT user (no credential
88
+ * account) could otherwise mail themselves a reset, SET a local password,
89
+ * and mint sessions via /sign-in/email that skip the per-login hub re-sync.
90
+ * P4's re-assertion still gates every such session, so this is posture, not
91
+ * an attacker hole — but the entrance must not exist. /sign-in/email itself
92
+ * stays wired (the break-glass operator door).
93
+ * `ctx.path` here is the ROUTE PATTERN (e.g. '/reset-password/:token'), so
94
+ * prefix matching covers the tokened callback.
95
+ */
96
+ function isPasswordResetPath(path) {
97
+ return (path.startsWith('/request-password-reset') ||
98
+ path.startsWith('/reset-password') ||
99
+ path.startsWith('/forget-password') ||
100
+ path.startsWith('/email-otp/request-password-reset') ||
101
+ path.startsWith('/email-otp/reset-password') ||
102
+ path.startsWith('/set-password'));
103
+ }
104
+ /**
105
+ * Every emailOTP route that can MINT A SESSION from an emailed code — or
106
+ * mail such a code — enumerated against the pinned 1.6.22 surface
107
+ * (re-enumerated on the 1.6.15 → 1.6.22 bump, 2026-08-29: the plugin's nine
108
+ * routes are byte-identical in path and method)
109
+ * (plugins/email-otp/routes.mjs; re-verify on ANY Better Auth bump):
110
+ *
111
+ * - POST /sign-in/email-otp: the sign-in mint — verifies the code, then
112
+ * UNCONDITIONALLY createSession + setSessionCookie;
113
+ * - POST /email-otp/verify-email: flips emailVerified, and mints a session
114
+ * ONLY under emailVerification.autoSignInAfterVerification — UNSET on
115
+ * this instance, so it mints nothing today. Guarded as config insurance
116
+ * (the same stance as /set-password in isPasswordResetPath): a future
117
+ * config change must not silently open an SSO-bypassing session mint,
118
+ * and no client of this instance calls the route (the email-change
119
+ * landing is the core tokened GET /verify-email, untouched);
120
+ * - POST /email-otp/send-verification-otp: the mail leg. With this close,
121
+ * EVERY redemption route for every OTP type refuses on cloud (sign-in +
122
+ * verify-email here, the reset trio in isPasswordResetPath), so a sent
123
+ * code could only ever be a dead letter — refuse the send rather than
124
+ * mail codes that cannot work.
125
+ *
126
+ * Ruled OUT on purpose, against the same 1.6.22 surface:
127
+ * - POST /email-otp/check-verification-otp verifies WITHOUT consuming and
128
+ * mints nothing (no createSession in its handler); with the send leg
129
+ * closed there is nothing to check anyway;
130
+ * - POST /email-otp/request-email-change + POST /email-otp/change-email
131
+ * (the plugin's two remaining routes, unchanged at 1.6.22): both run behind
132
+ * sensitiveSessionMiddleware — an EXISTING fresh session is their entry
133
+ * condition, so neither is a session ENTRANCE (change-email's
134
+ * setSessionCookie only re-sets the cookie of the session it already
135
+ * required) — and both answer 400 unless the plugin option
136
+ * `changeEmail.enabled` is set, which this instance never sets (the
137
+ * core tokened /change-email flow is the email-change surface; on cloud
138
+ * it is hidden and D10 re-syncs email from the hub anyway). If
139
+ * changeEmail is ever enabled, re-rule them here;
140
+ * - createVerificationOTP / getVerificationOTP are registered PATHLESS in
141
+ * 1.6.15 and `createAuthEndpoint.serverOnly` in 1.6.22 (routes.mjs), so
142
+ * better-call's router skips them: server-side only, unreachable over
143
+ * HTTP (same accident /set-password documents — do not rely on it);
144
+ * - /sign-in/email-otp is the plugin's ONLY /sign-in/* route; the twoFactor
145
+ * plugin mints only from a pending first factor, magic-link/phone are not
146
+ * registered here.
147
+ *
148
+ * On EDITION=cloud this pair is the last non-SSO HUMAN session entrance
149
+ * under the D1 hub-only posture (the reset surface closed at P8; ADR 017 §7
150
+ * recorded the OTP sign-in KNOWN-OPEN pending a charter call — now taken):
151
+ * every cloud credential must trace through "Sign in with Antasphere" so
152
+ * the hub's audit log is the complete access record. P4's re-assertion
153
+ * already gated every OTP session, so this is posture, not an attacker
154
+ * hole. The CLI counterpart (/cli/auth/*) closes in api/cli-auth.ts;
155
+ * /sign-in/email stays wired (the break-glass operator door, which the
156
+ * string '/sign-in/email-otp' does not prefix-match); oss keeps the full
157
+ * OTP login unchanged.
158
+ */
159
+ function isOtpSignInPath(path) {
160
+ return (path.startsWith('/sign-in/email-otp') ||
161
+ path.startsWith('/email-otp/verify-email') ||
162
+ path.startsWith('/email-otp/send-verification-otp'));
163
+ }
164
+ /**
165
+ * The two Better Auth core routes that hand a caller the PROVIDER GRANT
166
+ * stored on their own `account` row, in PLAINTEXT (PRDCT-1354, findings
167
+ * AUTH-3 + AUTH-7; better-auth 1.6.22 `api/routes/account.mjs` (both routes
168
+ * present unchanged at the 1.6.15 → 1.6.22 bump, 2026-08-29) — re-verify
169
+ * the enumeration on ANY bump):
170
+ *
171
+ * - POST /get-access-token → `{ accessToken, idToken, accessTokenExpiresAt,
172
+ * scopes }`, refreshing first when the stored token is within 5 s of
173
+ * expiry;
174
+ * - POST /refresh-token → the same PLUS `refreshToken`, and it ALWAYS
175
+ * refreshes.
176
+ *
177
+ * Both are closed here, on BOTH editions, because both are pure subtraction:
178
+ * nothing in this repo, the SDK, the CLI, the dashboard, or the MCP server
179
+ * calls either one (grep `get-access-token` / `refresh-token`), and no
180
+ * server-side caller exists either — the hub grant's own refresh is
181
+ * `HubGrantService.postRefresh`, which posts to the hub's token endpoint
182
+ * directly and never goes through this route. The plugin-owned OAuth
183
+ * endpoints this instance DOES serve (`/oauth2/token`, `/oauth2/authorize`,
184
+ * …) are separate paths and untouched.
185
+ *
186
+ * Why they must not exist, even though `resolveUserId` requires a session
187
+ * for any HTTP caller (so they are not a cross-USER surface):
188
+ *
189
+ * 1. The `/auth/*` mount is registered BEFORE `authContext`
190
+ * (`api/index.ts`), so neither route sees the fail-closed scope
191
+ * allowlist, the per-principal quota, the idempotency claim, or the
192
+ * audit middleware. Any session-bearing caller — including a browser
193
+ * tricked into a same-site POST, or anything that reaches the cookie —
194
+ * reads the grant with no record left behind, which defeats the whole
195
+ * point of `encryptOAuthTokens: true` (encryption at rest is worthless
196
+ * next to an unlogged plaintext read endpoint).
197
+ * 2. On cloud the grant in question is the HUB grant, i.e. the credential
198
+ * the user-scoped federation is built on (ADR 019). Handing it out in
199
+ * plaintext hands out the ability to read that user's hub orgs directly.
200
+ * 3. `/refresh-token` additionally ROTATES that grant OUTSIDE the
201
+ * `pg_advisory_lock(7432004, hashtext(userId))` single-flight ADR 019
202
+ * requires. The hub's RFC 9700 reuse detection makes an unserialized
203
+ * double-refresh a grant-family-KILLING event, so this route is a
204
+ * denial-of-service any logged-in user can trigger from a browser tab.
205
+ * (The hub-side half of that hardening is PRDCT-1370.)
206
+ *
207
+ * `ctx.path` is the route pattern, and neither route has path params, so an
208
+ * exact match is enough; startsWith would also swallow future siblings, but
209
+ * an allowlist-shaped closure should deny exactly what it has verified.
210
+ */
211
+ function isProviderGrantPath(path) {
212
+ return path === '/get-access-token' || path === '/refresh-token';
213
+ }
214
+ function isHttpUrl(value) {
215
+ if (typeof value !== 'string')
216
+ return false;
217
+ try {
218
+ const url = new URL(value);
219
+ return url.protocol === 'http:' || url.protocol === 'https:';
220
+ }
221
+ catch {
222
+ return false;
223
+ }
224
+ }
225
+ /**
226
+ * Better Auth `trustedOrigins` (PRDCT-1352): the configured public origin plus
227
+ * the origin the request was served on (localhost, previews, any domain behind
228
+ * a TLS-terminating proxy) — MINUS the viewer origin, which is author-
229
+ * controlled script's origin and is never trusted, serving origin or
230
+ * not. The host gate keeps the auth surface off that hostname; this is the
231
+ * second lock behind it. Exported pure so a unit test can pin the exclusion.
232
+ */
233
+ export function trustedOriginsFor(publicBaseUrl, untrustedOrigins) {
234
+ return (request) => {
235
+ const origins = [publicBaseUrl];
236
+ try {
237
+ if (request)
238
+ origins.push(new URL(request.url).origin);
239
+ }
240
+ catch {
241
+ // unparseable request URL — explicit origin only
242
+ }
243
+ return untrustedOrigins.length > 0
244
+ ? origins.filter((origin) => !untrustedOrigins.includes(origin))
245
+ : origins;
246
+ };
247
+ }
248
+ /**
249
+ * The sign-in Origin lock (M9 login-CSRF hardening + PRDCT-1352): a sign-in
250
+ * that presents an Origin must present a trusted one — the serving origin or
251
+ * Better Auth's own trusted list — and the viewer origin is refused OUTRIGHT,
252
+ * even as the serving origin: author script must never mint a session on the
253
+ * hostname it runs on. Exported pure so a unit test can pin both arms.
254
+ */
255
+ export function signInOriginRefused(input) {
256
+ const { origin, servingOrigin, untrustedOrigins, isTrustedOrigin } = input;
257
+ if (untrustedOrigins.includes(origin))
258
+ return true;
259
+ return origin !== servingOrigin && !isTrustedOrigin(origin);
260
+ }
261
+ export function createAuth({ db, env, authSecret, oauthScopes, untrustedOrigins, sendOtp, sendResetPassword, sendChangeEmailConfirmation, sendVerificationEmail, onAccountEvent, onUserCreated, beforeUserDelete, afterUserDelete, hubSso, demoPass }) {
262
+ const isHttps = env.PUBLIC_BASE_URL.startsWith('https://');
263
+ const resource = mcpResourceUrl(env.PUBLIC_BASE_URL);
264
+ /**
265
+ * Cloud only: may this email take the local password door? The setup
266
+ * operator (instance_settings.operator_user_id — the durable record,
267
+ * CLOUD-3) and the SUPERADMIN_EMAILS allowlist; nobody else.
268
+ */
269
+ const isLocalSignInAllowed = async (email) => {
270
+ if (parseSuperadminEmails(env.SUPERADMIN_EMAILS).has(email))
271
+ return true;
272
+ const [row] = await db
273
+ .select({ email: user.email })
274
+ .from(instanceSettings)
275
+ .innerJoin(user, eq(user.id, instanceSettings.operatorUserId))
276
+ .limit(1);
277
+ return row?.email?.toLowerCase() === email;
278
+ };
279
+ /**
280
+ * The JWT issuance gate (user-scoped credential model): a grant is the
281
+ * USER — "consent to act as you" — so issuance requires only that the
282
+ * user holds AT LEAST ONE live active membership on this instance, and
283
+ * the minted claims carry NO workspace. The request-time workspace comes
284
+ * from the X-Workspace-Id selector against the user's live memberships
285
+ * (identity/oauth-jwt.ts); a claim could only ever go stale against that.
286
+ *
287
+ * Runs on the authorization_code AND refresh_token grants; throwing
288
+ * aborts issuance with an RFC 6749 error body. It is NOT what revokes a
289
+ * deactivated member's access, though: a refresh without the RFC 8707
290
+ * `resource` param still mints an OPAQUE token that never passes through
291
+ * here. The real enforcement is resource-side — every request re-checks
292
+ * the live membership (and an opaque token fails the bearer gate's
293
+ * looksLikeJwt outright), so whatever a refresh mints is rejected at
294
+ * every resource. Verified live (M9).
295
+ */
296
+ async function userAccessClaims(user) {
297
+ if (!user?.id) {
298
+ throw new APIError('FORBIDDEN', {
299
+ error: 'access_denied',
300
+ error_description: 'Token issuance requires a user-bound grant'
301
+ });
302
+ }
303
+ const [row] = await db
304
+ .select({ id: workspaceMembers.id })
305
+ .from(workspaceMembers)
306
+ .where(and(eq(workspaceMembers.userId, user.id), eq(workspaceMembers.isActive, true)))
307
+ .limit(1);
308
+ if (!row) {
309
+ throw new APIError('FORBIDDEN', {
310
+ error: 'access_denied',
311
+ error_description: 'This account has no active membership on this instance'
312
+ });
313
+ }
314
+ return { email: user.email };
315
+ }
316
+ return betterAuth({
317
+ plugins: [
318
+ // Cloud edition only (hubSso is constructed iff EDITION=cloud): the
319
+ // "Sign in with Antasphere" relying party. `disableSignUp` is
320
+ // DELIBERATELY unset on this provider — hub SSO is the sanctioned
321
+ // account entrance on cloud, the conscious FOURTH switch next to the
322
+ // three closed-signup switches (internal/federation.md). Discovery, token
323
+ // exchange (with RFC 8707 `resource`), and token verification all
324
+ // live in the provider config hub-sso.ts builds.
325
+ ...(hubSso ? [genericOAuth({ config: [hubSso.providerConfig()] })] : []),
326
+ // Demo sign-in, self-hosted only: POST /demo/redeem signs a demo pass's
327
+ // person in (identity/demo-pass-plugin.ts). Never on cloud, where every
328
+ // session entrance goes through the hub.
329
+ ...(demoPass && !hubSso ? [demoPassPlugin(demoPass)] : []),
330
+ ...(sendOtp
331
+ ? [
332
+ emailOTP({
333
+ // Closed instance: OTP signs in EXISTING accounts only — without
334
+ // this, an OTP send to any unknown email would mint a user.
335
+ disableSignUp: true,
336
+ async sendVerificationOTP({ email, otp, type }) {
337
+ await sendOtp({ email, otp, type });
338
+ }
339
+ })
340
+ ]
341
+ : []),
342
+ // Optional per-user 2FA: TOTP + one-time backup codes (ADR 009). NO
343
+ // otpOptions.sendOTP — an emailed code as "second factor" would collapse
344
+ // into the first factor for anyone who controls the mailbox. Enrollment
345
+ // is password-gated and pending until the user proves a working
346
+ // authenticator by verifying one TOTP code; disable is password-gated.
347
+ // Sign-up stays closed: the plugin adds no account-creating route.
348
+ twoFactor({
349
+ // Label shown in authenticator apps — the instance's own host.
350
+ issuer: new URL(env.PUBLIC_BASE_URL).host
351
+ }),
352
+ jwt({
353
+ // JWTs exist ONLY as OAuth access tokens — never attach a signed JWT
354
+ // to plain session responses (recommended with an oauth provider).
355
+ disableSettingJwtHeader: true,
356
+ // RS256 over the default EdDSA: broadest verifier compatibility.
357
+ jwks: { keyPairConfig: { alg: 'RS256' } },
358
+ // Clean origin (no /api/v1/auth path) as the OAuth issuer — the exact
359
+ // `iss` oauth-jwt.ts pins, and the root of the RFC 8414 discovery
360
+ // documents re-served by routes/wellknown.ts.
361
+ jwt: { issuer: env.PUBLIC_BASE_URL }
362
+ }),
363
+ oauthProvider({
364
+ // SPA routes; the API-side validation is Better Auth's signed query.
365
+ loginPage: '/login',
366
+ consentPage: '/oauth/consent',
367
+ scopes: [...oauthScopes],
368
+ // `resource` (RFC 8707) must be the bundled MCP endpoint — its exact `aud`.
369
+ validAudiences: [resource],
370
+ // No M2M clients: every token is bound to a consenting user.
371
+ grantTypes: ['authorization_code', 'refresh_token'],
372
+ // MCP clients (claude.ai, Claude Desktop) register themselves via
373
+ // RFC 7591 without a session. Registration alone grants nothing —
374
+ // tokens still require login + consent + an active membership — and
375
+ // the register endpoint is rate-limited in api/index.ts.
376
+ //
377
+ // PLT-29: it is still an anonymous write to the authorization server's
378
+ // client table, so an operator gets a switch rather than a hardcoded
379
+ // posture. OAUTH_DYNAMIC_CLIENT_REGISTRATION=false turns BOTH flags
380
+ // off and /oauth2/register refuses everyone.
381
+ //
382
+ // THE DEFAULT IS DELIBERATELY UNCHANGED (true): the MCP connector
383
+ // story depends on open registration, and the fleet-wide decision on
384
+ // whether that posture should change belongs to the OIDC audit, not to
385
+ // this hardening pass. This adds the control and nothing else.
386
+ allowDynamicClientRegistration: env.OAUTH_DYNAMIC_CLIENT_REGISTRATION,
387
+ allowUnauthenticatedClientRegistration: env.OAUTH_DYNAMIC_CLIENT_REGISTRATION,
388
+ clientRegistrationDefaultScopes: [...oauthScopes],
389
+ clientRegistrationAllowedScopes: [...oauthScopes],
390
+ // Short access tokens: stateless JWTs can't be revoked, so revocation
391
+ // latency == remaining lifetime. Refresh tokens rotate (reuse detected).
392
+ accessTokenExpiresIn: 900, // 15 min
393
+ refreshTokenExpiresIn: 60 * 60 * 24 * 365, // 365 days (sliding; see the session note)
394
+ customAccessTokenClaims: async ({ user }) => userAccessClaims(user),
395
+ // Grants are user-scoped ("act as you"): consents bind NOTHING —
396
+ // the explicit `undefined` referenceId is the no-binding wiring
397
+ // (the plugin type requires the key whenever postLogin exists).
398
+ // `page` + `shouldRedirect` are required by the type too; the
399
+ // constant false keeps the flow on the consent page itself.
400
+ postLogin: {
401
+ page: '/oauth/consent',
402
+ shouldRedirect: () => false,
403
+ consentReferenceId: async () => undefined
404
+ },
405
+ // Root discovery documents are re-served by routes/wellknown.ts.
406
+ silenceWarnings: { oauthAuthServerConfig: true, openidConfig: true }
407
+ })
408
+ ],
409
+ // Google social sign-in is a SELF-HOST option only. On EDITION=cloud
410
+ // (hubSso present) the provider is NOT registered at all — even with
411
+ // GOOGLE_CLIENT_ID/SECRET set — so /sign-in/social and
412
+ // /oauth2/callback/google mint nothing there. Under the D1 hub-only
413
+ // posture the sole sanctioned human entrance is "Sign in with
414
+ // Antasphere" (the `antasphere` genericOAuth provider), with
415
+ // /sign-in/email kept as the deliberate break-glass door; a live Google
416
+ // provider would be a NON-hub session entrance that defeats the
417
+ // audit-completeness guarantee (every cloud login must trace through the
418
+ // hub). Gating on !hubSso — not on "operator didn't set the env var" —
419
+ // makes the guarantee hold by construction (defense in depth against
420
+ // misconfiguration), mirroring the OTP closures. oss is unchanged: with
421
+ // the credentials set, Google signs in accounts that already exist
422
+ // (disableSignUp — the callback never creates), one of the three
423
+ // closed-signup switches.
424
+ socialProviders: !hubSso && env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET
425
+ ? {
426
+ google: {
427
+ clientId: env.GOOGLE_CLIENT_ID,
428
+ clientSecret: env.GOOGLE_CLIENT_SECRET,
429
+ disableSignUp: true
430
+ }
431
+ }
432
+ : {},
433
+ account: {
434
+ // OAuth provider tokens on the `account` row are encrypted at rest —
435
+ // on cloud that row IS the user's hub grant (offline_access refresh
436
+ // token, identity/hub-grant.ts), which must never sit plaintext in a
437
+ // dump. Enabled on BOTH editions (oss: Google tokens, when
438
+ // configured). CONFIG-ONLY: no schema change (drift:check proves),
439
+ // and retroactively safe — the decrypt passes legacy PLAINTEXT values
440
+ // through untouched (dist/oauth2/utils.mjs isLikelyEncrypted, verified
441
+ // on 1.6.15 and again on 1.6.22;
442
+ // hub-grant.ts mirrors the same rule). The key is the auth context's
443
+ // secretConfig (= this instance's AUTH_SECRET under our string-secret
444
+ // config). Re-verify both facts on any Better Auth bump.
445
+ encryptOAuthTokens: true,
446
+ // D9 (cloud only): the hub is a TRUSTED provider — its verified email
447
+ // assertion may link onto an existing local account of the same
448
+ // address (the setup operator's entrance under hub-only login).
449
+ // Keeping requireLocalEmailVerified true (stated, not defaulted)
450
+ // means a parked UNVERIFIED local account can never be taken over via
451
+ // SSO; the cloud setup flow mints the operator emailVerified=true for
452
+ // exactly this reason (internal/federation.md, pinned by edition tests).
453
+ ...(hubSso
454
+ ? {
455
+ accountLinking: {
456
+ trustedProviders: [HUB_SSO_PROVIDER_ID],
457
+ requireLocalEmailVerified: true
458
+ }
459
+ }
460
+ : {})
461
+ },
462
+ baseURL: env.PUBLIC_BASE_URL,
463
+ basePath: AUTH_BASE_PATH,
464
+ secret: authSecret,
465
+ // Cloud only: OAuth-callback error redirects land on the LOGIN PAGE.
466
+ // Verified on 1.6.15 and 1.6.22 (re-verify on ANY bump): the genericOAuth callback
467
+ // resolves `onAPIError.errorURL || <authBase>/error` and redirects an
468
+ // AS error (`?error=login_required` from a prompt=none authorize,
469
+ // `?error=access_denied`, …) there BEFORE parseState runs
470
+ // (generic-oauth/routes.mjs) — errorCallbackURL never sees these, so
471
+ // without this the silent-connect bounce would dead-end on better-auth's
472
+ // own /error page. The dashboard maps the ?error=… code to copy (and the
473
+ // silent-connect lattice treats the login_required family as a quiet
474
+ // hint-clear, Stage E). oss stays byte-identical: key absent, the
475
+ // pre-change default error landing untouched.
476
+ ...(hubSso ? { onAPIError: { errorURL: env.PUBLIC_BASE_URL.replace(/\/+$/, '') + '/login' } } : {}),
477
+ database: drizzleAdapter(db, {
478
+ provider: 'pg',
479
+ schema: {
480
+ user,
481
+ session,
482
+ account,
483
+ verification,
484
+ jwks,
485
+ oauthClient,
486
+ oauthAccessToken,
487
+ oauthRefreshToken,
488
+ oauthConsent,
489
+ twoFactor: twoFactorTable
490
+ }
491
+ }),
492
+ user: {
493
+ // Self-serve email change. Enabled unconditionally: without a
494
+ // delivering email driver POST /change-email answers 400 BEFORE the
495
+ // email lookup (no enumeration oracle), so the switch is safe. Tokens
496
+ // are stateless HS256 JWTs signed with the auth secret (1h expiry,
497
+ // not revocable). Template users are typically emailVerified=false, so
498
+ // the COMMON flow is single-leg: one verification mail to the NEW
499
+ // address; the confirmation-to-old-address leg only runs for verified
500
+ // users.
501
+ changeEmail: {
502
+ enabled: true,
503
+ ...(sendChangeEmailConfirmation
504
+ ? {
505
+ sendChangeEmailConfirmation: async ({ user, newEmail, url, token }) => {
506
+ await sendChangeEmailConfirmation({ email: user.email, newEmail, url, token });
507
+ }
508
+ }
509
+ : {})
510
+ },
511
+ // Self-service account deletion (GDPR erasure). NO
512
+ // sendDeleteAccountVerification: the password path works with zero
513
+ // SMTP. Known Better Auth default, relied on rather than fought: a
514
+ // session younger than freshAge (24h) may delete WITHOUT a password
515
+ // server-side. The dashboard collects the password where the instance
516
+ // offers one (`/instance` auth.methods has `password`); a hub-JIT user
517
+ // on cloud has NO credential account, so there the dashboard sends the
518
+ // person through a fresh hub sign-in first and the new session is the
519
+ // proof (PRDCT-2640). Deletion is local either way: it erases the
520
+ // instance's user row, never the hub identity (accounts/deletion.ts).
521
+ // See ADR 006.
522
+ deleteUser: {
523
+ enabled: true,
524
+ beforeDelete: async (user) => {
525
+ await beforeUserDelete?.(user.id);
526
+ },
527
+ afterDelete: async (user) => {
528
+ await afterUserDelete?.({ id: user.id, email: user.email });
529
+ }
530
+ }
531
+ },
532
+ emailVerification: {
533
+ expiresIn: 3600,
534
+ ...(sendVerificationEmail
535
+ ? {
536
+ sendVerificationEmail: async ({ user, url, token }) => {
537
+ // `user.email` is already the NEW address on the change-email leg.
538
+ await sendVerificationEmail({ email: user.email, url, token });
539
+ }
540
+ }
541
+ : {}),
542
+ // Fires when GET /verify-email flips a user to verified — the email
543
+ // change landing. Also fires on a hypothetical plain verify-email
544
+ // (acceptable: the audit row records a verification event either way).
545
+ afterEmailVerification: async (user) => {
546
+ if (user?.id)
547
+ await onAccountEvent?.('email_change', user.id);
548
+ }
549
+ },
550
+ emailAndPassword: {
551
+ enabled: true,
552
+ minPasswordLength: 12,
553
+ // Recovery/change implies a possibly-compromised account: drop the
554
+ // other sessions on a reset. Applies to self-serve AND admin-minted
555
+ // reset links (both land on POST /reset-password).
556
+ revokeSessionsOnPasswordReset: true,
557
+ onPasswordReset: async ({ user }) => {
558
+ await onAccountEvent?.('password_reset', user.id);
559
+ },
560
+ // Self-serve reset exists only with a delivering email driver; without
561
+ // it, /request-password-reset returns 400 (no user-enumeration oracle).
562
+ // Cloud never wires it (defense in depth under the before-hook refusal
563
+ // above): no reset mail can even be BUILT on that edition.
564
+ ...(sendResetPassword && !hubSso
565
+ ? {
566
+ resetPasswordTokenExpiresIn: 3600,
567
+ sendResetPassword: async ({ user, url, token }) => {
568
+ await sendResetPassword({ email: user.email, url, token });
569
+ }
570
+ }
571
+ : {})
572
+ },
573
+ hooks: {
574
+ // Accounts enter through setup or invitations only. HTTP sign-up is
575
+ // closed: ctx.request exists exactly when the call arrived over HTTP;
576
+ // server-side auth.api.signUpEmail calls (setup, invitation accept)
577
+ // carry no request and pass.
578
+ before: createAuthMiddleware(async (ctx) => {
579
+ if (ctx.path.startsWith('/sign-up') && ctx.request) {
580
+ throw new APIError('FORBIDDEN', {
581
+ message: 'Sign-up is closed on this instance — ask an admin for an invitation'
582
+ });
583
+ }
584
+ // Cloud edition (D1, hub-only login): the ENTIRE local password-reset
585
+ // surface refuses — see isPasswordResetPath for the enumerated routes
586
+ // and why. Unconditional (no ctx.request escape hatch like sign-up's):
587
+ // nothing server-side consumes these paths, so a server-side caller
588
+ // appearing would itself be a posture regression. oss is untouched
589
+ // (hubSso exists only on EDITION=cloud boots).
590
+ if (hubSso && isPasswordResetPath(ctx.path)) {
591
+ throw new APIError('FORBIDDEN', {
592
+ message: 'Password reset is disabled on this edition — credentials are managed at the Antasphere hub'
593
+ });
594
+ }
595
+ // Cloud edition (PRDCT-2818): the email address is the hub's — see
596
+ // isEmailChangePath. Unconditional like the reset closure: nothing
597
+ // server-side calls these paths.
598
+ if (hubSso && isEmailChangePath(ctx.path)) {
599
+ throw new APIError('FORBIDDEN', {
600
+ code: 'email_change_disabled',
601
+ message: `Email changes are disabled on this edition — change your address on your Antasphere account (${hubSso.accountUrl()})`
602
+ });
603
+ }
604
+ // Cloud edition (D1, hub-only login): the emailOTP SIGN-IN surface
605
+ // refuses too — see isOtpSignInPath for the enumerated routes and
606
+ // why. Same unconditional stance as the reset closure (no
607
+ // ctx.request escape hatch): the one server-side consumer of these
608
+ // paths, /cli/auth/request's sendVerificationOTP delegate, refuses
609
+ // at its own route first on cloud (cli_otp_disabled), so a
610
+ // server-side caller reaching this would itself be a posture
611
+ // regression. oss is untouched (hubSso exists only on cloud boots).
612
+ if (hubSso && isOtpSignInPath(ctx.path)) {
613
+ throw new APIError('FORBIDDEN', {
614
+ code: 'otp_signin_disabled',
615
+ message: 'Email-code sign-in is disabled on this edition — use "Sign in with Antasphere"'
616
+ });
617
+ }
618
+ // BOTH editions: the provider-grant read/rotate routes are closed —
619
+ // see isProviderGrantPath for the enumeration and the three reasons.
620
+ // Unconditional (no `ctx.request` escape hatch): nothing server-side
621
+ // calls them either, so a server-side caller appearing would itself
622
+ // be the regression this guard exists to catch.
623
+ // Cloud edition (CLOUD-1 / AUTH-13, PRDCT-1356): unlinking the
624
+ // `antasphere` provider is refused. The /auth/* mount sits BEFORE
625
+ // authContext (isPublicApiPath), so the principal gate never saw
626
+ // this route; a hub user who unlinked kept their origin='hub'
627
+ // memberships while every later reconcile took the `no_link` branch
628
+ // — the one fail-open outcome — cached like a success. Enforcement
629
+ // must not be switchable by its subject. (The reconciler now also
630
+ // fails CLOSED on no_link for a principal holding hub-origin rows.)
631
+ if (hubSso && ctx.path === '/unlink-account') {
632
+ const providerId = ctx.body?.providerId;
633
+ if (providerId === 'antasphere') {
634
+ throw new APIError('FORBIDDEN', {
635
+ code: 'hub_unlink_forbidden',
636
+ message: 'The Antasphere link cannot be removed on this edition — it is the identity itself'
637
+ });
638
+ }
639
+ }
640
+ // Cloud edition (EDIT-2, PRDCT-1356): /sign-in/email stays WIRED as
641
+ // the break-glass door, but it is a door for the OPERATOR and the
642
+ // SUPERADMIN_EMAILS allowlist only. Every other local credential on
643
+ // a cloud instance (pre-flip users, a leftover invitation account)
644
+ // would otherwise be a permanent non-SSO entrance that never sees
645
+ // the hub's audit log. Decided on the presented email; unknown
646
+ // addresses answer the same 403 (no account oracle).
647
+ if (hubSso && ctx.path === '/sign-in/email' && ctx.request) {
648
+ const email = String(ctx.body?.email ?? '')
649
+ .trim()
650
+ .toLowerCase();
651
+ if (!email || !(await isLocalSignInAllowed(email))) {
652
+ throw new APIError('FORBIDDEN', {
653
+ code: 'local_signin_disabled',
654
+ message: 'Password sign-in is reserved for the instance operator on this edition — use "Sign in with Antasphere"'
655
+ });
656
+ }
657
+ }
658
+ if (isProviderGrantPath(ctx.path)) {
659
+ throw new APIError('FORBIDDEN', {
660
+ code: 'provider_grant_forbidden',
661
+ message: 'This endpoint is disabled — provider tokens are never handed to callers'
662
+ });
663
+ }
664
+ // Login-CSRF (session fixation) hardening: Better Auth's own origin
665
+ // check deliberately SKIPS requests carrying neither cookies nor
666
+ // Sec-Fetch metadata (progressive enhancement for non-browser
667
+ // clients), which leaves legacy browsers open to a cross-site login
668
+ // POST. Close the gap on the credential-issuing sign-in surface: a
669
+ // sign-in that DOES present an Origin must present a trusted one —
670
+ // trust evaluation is Better Auth's own isTrustedOrigin plus the
671
+ // serving origin (mirroring the trustedOrigins function below).
672
+ // CLI/SDK/MCP flows send no Origin header and are untouched; the
673
+ // OAuth token/register endpoints keep their deliberate public
674
+ // wildcard-CORS posture (middleware/oauth-public.ts).
675
+ if (ctx.path.startsWith('/sign-in') && ctx.request) {
676
+ const origin = ctx.request.headers.get('origin');
677
+ if (origin) {
678
+ let servingOrigin = null;
679
+ try {
680
+ servingOrigin = new URL(ctx.request.url).origin;
681
+ }
682
+ catch {
683
+ // unparseable request URL — judge by the trusted list only
684
+ }
685
+ if (signInOriginRefused({
686
+ origin,
687
+ servingOrigin,
688
+ untrustedOrigins,
689
+ isTrustedOrigin: (o) => ctx.context.isTrustedOrigin(o)
690
+ })) {
691
+ throw new APIError('FORBIDDEN', { message: 'Invalid origin' });
692
+ }
693
+ }
694
+ }
695
+ // PRDCT-1437 door 1 (the change-email consume existence oracle) is
696
+ // closed at the mount wrapper in api/index.ts, not here: it needs to
697
+ // rewrite the REQUEST before Better Auth reads the token, which a
698
+ // before-hook's `ctx.query` mutation does not reach (the endpoint reads
699
+ // its own parsed query). See `rewriteChangeEmailCollision` there.
700
+ // The plugin scheme-checks only redirect_uris — reject javascript:/data:
701
+ // metadata URIs on every client write path (unauthenticated DCR AND the
702
+ // session-gated create/update-client endpoints) so a stored javascript:
703
+ // URI can never reach a render site. The consent page also validates at
704
+ // render (defense in depth).
705
+ if (ctx.path === '/oauth2/register' ||
706
+ ctx.path === '/oauth2/create-client' ||
707
+ ctx.path === '/oauth2/update-client') {
708
+ const raw = ctx.body;
709
+ // register/create-client carry the fields at the top level;
710
+ // update-client nests them under `update`.
711
+ const meta = (raw?.update ?? raw);
712
+ for (const field of CLIENT_METADATA_URI_FIELDS) {
713
+ const value = meta?.[field];
714
+ if (value != null && value !== '' && !isHttpUrl(value)) {
715
+ throw new APIError('BAD_REQUEST', {
716
+ error: 'invalid_client_metadata',
717
+ error_description: `${field} must be an http(s) URL`
718
+ });
719
+ }
720
+ }
721
+ }
722
+ }),
723
+ // A successful /change-password or /change-email reaches the after-hook
724
+ // (an error throws before it) — audit them here since Better Auth
725
+ // routes bypass the /api/v1 audit middleware. Reset is audited via
726
+ // onPasswordReset above; the email-change LANDING (the verify-email
727
+ // consumption) via afterEmailVerification.
728
+ after: createAuthMiddleware(async (ctx) => {
729
+ // Hub SSO per-login work (cloud only) — the callback after-hook.
730
+ // Fires for JIT AND returning users; `ctx.context.newSession` is the
731
+ // success discriminator (set by setSessionCookie before the redirect
732
+ // throw, never on the plugin's error exits — S1(b)). The verified
733
+ // assertion crosses from getUserInfo via the request-scoped login
734
+ // scope (ADR 015); everything here FAILS CLOSED: no assertion or a
735
+ // failed projection revokes the just-minted session — a cloud login
736
+ // without its hub-asserted workspace projection must not exist.
737
+ if (hubSso && ctx.path === '/oauth2/callback/:providerId') {
738
+ const providerId = ctx.params?.providerId;
739
+ if (providerId === HUB_SSO_PROVIDER_ID) {
740
+ const data = ctx.context.newSession;
741
+ if (!data?.user)
742
+ return; // error exits mint no session — nothing to assert
743
+ const assertion = hubSso.takeAssertion();
744
+ try {
745
+ if (!assertion) {
746
+ throw new HubSsoLoginError('sso_assertion_missing', 'callback succeeded but no verified hub assertion reached the after-hook');
747
+ }
748
+ await hubSso.assertLogin(data.user.id, assertion);
749
+ }
750
+ catch (err) {
751
+ hubSso.logLoginFailure(err, data.user.id);
752
+ // Same undo dance as the 2FA interstitial below: drop the
753
+ // session row, expire the cookie, clear newSession, then
754
+ // replace the success redirect with the error redirect.
755
+ await ctx.context.internalAdapter.deleteSession(data.session.token);
756
+ deleteSessionCookie(ctx, true);
757
+ ctx.context.setNewSession(null);
758
+ const code = err instanceof HubSsoLoginError ? err.code : 'sso_login_failed';
759
+ throw ctx.redirect(hubSso.loginErrorUrl(code));
760
+ }
761
+ return;
762
+ }
763
+ }
764
+ if (ctx.path === '/change-password') {
765
+ const userId = ctx.context.session?.user?.id;
766
+ if (userId)
767
+ await onAccountEvent?.('password_change', userId);
768
+ }
769
+ if (ctx.path === '/change-email') {
770
+ const userId = ctx.context.session?.user?.id;
771
+ if (userId)
772
+ await onAccountEvent?.('email_change_request', userId);
773
+ }
774
+ // 2FA credential-material events (both routes are session-gated).
775
+ // Enroll = secret + backup codes issued (activation completes on the
776
+ // first verified TOTP); disable = secret + backup codes destroyed.
777
+ if (ctx.path === '/two-factor/enable') {
778
+ const userId = ctx.context.session?.user?.id;
779
+ if (userId)
780
+ await onAccountEvent?.('two_factor_enroll', userId);
781
+ }
782
+ if (ctx.path === '/two-factor/disable') {
783
+ const userId = ctx.context.session?.user?.id;
784
+ if (userId)
785
+ await onAccountEvent?.('two_factor_disable', userId);
786
+ }
787
+ // Close the email-OTP second-factor bypass: the twoFactor plugin's
788
+ // own sign-in hook covers /sign-in/email (password) but NOT
789
+ // /sign-in/email-otp, so an emailed one-time code would sign a
790
+ // 2FA-enabled user straight in — collapsing 2FA to mailbox control.
791
+ // Mirror the plugin's dance (verified against 1.6.22
792
+ // plugins/two-factor/index.mjs; re-verify on ANY Better Auth bump):
793
+ // drop the just-minted session, park the pending sign-in in the
794
+ // verification table behind the signed `two_factor` cookie, park the
795
+ // per-challenge attempt counter next to it, and answer
796
+ // { twoFactorRedirect } so /two-factor/verify-totp (or a backup code)
797
+ // completes it. A 2FA-enabled user always has a verified TOTP secret
798
+ // — activation is the only path that sets user.twoFactorEnabled.
799
+ // The `2fa-attempts-<identifier>` row is NOT optional since 1.6.22:
800
+ // verifyTwoFactor's beginAttempt CONSUMES it on every verify and
801
+ // throws INVALID_TWO_FACTOR_COOKIE (401) when it is missing, so a
802
+ // mirror that parks only the `2fa-` row hands the user a pending
803
+ // state no factor can ever complete (PRDCT-1344; the hub hit the
804
+ // same trap on its own bump).
805
+ if (ctx.path === '/sign-in/email-otp') {
806
+ const data = ctx.context.newSession;
807
+ if (!data?.user || !data.user.twoFactorEnabled)
808
+ return;
809
+ deleteSessionCookie(ctx, true);
810
+ await ctx.context.internalAdapter.deleteSession(data.session.token);
811
+ ctx.context.setNewSession(null);
812
+ const maxAge = 600; // the plugin's twoFactorCookieMaxAge default
813
+ const twoFactorCookie = ctx.context.createAuthCookie('two_factor', { maxAge });
814
+ const identifier = `2fa-${generateRandomString(20)}`;
815
+ const expiresAt = new Date(Date.now() + maxAge * 1000);
816
+ await ctx.context.internalAdapter.createVerificationValue({
817
+ value: data.user.id,
818
+ identifier,
819
+ expiresAt
820
+ });
821
+ await ctx.context.internalAdapter.createVerificationValue({
822
+ value: '0',
823
+ identifier: `2fa-attempts-${identifier}`,
824
+ expiresAt
825
+ });
826
+ await ctx.setSignedCookie(twoFactorCookie.name, identifier, ctx.context.secret, twoFactorCookie.attributes);
827
+ return ctx.json({ twoFactorRedirect: true, twoFactorMethods: ['totp'] });
828
+ }
829
+ })
830
+ },
831
+ // Adapter-level hooks: config only, NOT schema — the drift guard's
832
+ // generated auth-schema is unaffected (verified via drift:check).
833
+ databaseHooks: {
834
+ user: {
835
+ create: {
836
+ // Every entrance (setup's signUpEmail, invitation accept, the
837
+ // collaborator claim endpoint, a future SSO JIT) creates the row
838
+ // through the internal adapter, so this is the single trustworthy
839
+ // `user.created` seam. Synchronous dispatch onto the event bus;
840
+ // subscriber failures are isolated there and never fail account
841
+ // creation.
842
+ after: async (user) => {
843
+ onUserCreated?.({ id: user.id, email: user.email });
844
+ }
845
+ }
846
+ }
847
+ },
848
+ // Sessions, split by edition (SL-5, "one concept of being logged in"):
849
+ //
850
+ // - oss keeps the template's long-lived SLIDING sessions, byte-identical
851
+ // (safe because the auth-context middleware re-checks workspace
852
+ // membership on every request — instant revocation).
853
+ // - cloud runs 30-day FIXED (non-sliding) sessions: the tool session is
854
+ // a PROJECTION of the hub anchor session, and a sliding projection
855
+ // would never re-dance — it would let the anchor die under an active
856
+ // user. A fixed expiry forces a silent prompt=none re-derivation from
857
+ // the anchor at most every 30 days, and each re-derivation's authorize
858
+ // touch slides the HUB session forward (the anchor stays alive off
859
+ // activity on ANY tool). The knob is `disableSessionRefresh` — pinned
860
+ // on 1.6.15 and 1.6.22 (dist/api/routes/session.mjs is its ONLY reader:
861
+ // `needsRefresh` goes false, so expiresAt never advances on use;
862
+ // re-verify on ANY bump). `updateAge` is left at its default there —
863
+ // it is dead config once refresh is disabled. CONFIG-ONLY: no schema
864
+ // change (drift:check proves).
865
+ session: hubSso
866
+ ? {
867
+ expiresIn: 60 * 60 * 24 * 30,
868
+ disableSessionRefresh: true
869
+ }
870
+ : {
871
+ expiresIn: 60 * 60 * 24 * 365,
872
+ updateAge: 60 * 60 * 24
873
+ },
874
+ advanced: {
875
+ useSecureCookies: isHttps
876
+ },
877
+ // The library's own limiter, on in production, keyed per client ADDRESS. Its defaults
878
+ // (100 requests per 10 s per path, and 3 per 10 s on every /sign-in/*) let a room behind one
879
+ // venue address sign in at about 18 people a minute and share one session-check budget
880
+ // (proven on the cloud instances, 2026-10-01: the fourth sign-in start in 10 s answered 429).
881
+ // Sized for an event: the general budget at ten times the default, the SSO start at 500.
882
+ // The per-person walls (login, otp, the API quota) stay what they are.
883
+ rateLimit: {
884
+ window: 10,
885
+ max: 1000,
886
+ customRules: {
887
+ '/sign-in/oauth2': { window: 10, max: 500 }
888
+ }
889
+ },
890
+ // Trust the serving origin (works on localhost, previews, any domain)
891
+ // plus the configured public origin behind a TLS-terminating proxy.
892
+ // Serving origin + public origin, never the viewer origin (see trustedOriginsFor).
893
+ trustedOrigins: trustedOriginsFor(env.PUBLIC_BASE_URL, untrustedOrigins)
894
+ });
895
+ }
896
+ //# sourceMappingURL=better-auth.js.map