@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
package/dist/boot.js ADDED
@@ -0,0 +1,822 @@
1
+ import { migrationStatus, runMigrations } from '@antasphere/chassis-db/migrate';
2
+ import { and, count, eq, isNull } from 'drizzle-orm';
3
+ import { join } from 'node:path';
4
+ import { APIError } from 'better-auth/api';
5
+ import { AccountDeletionService, LastOwnerError } from './accounts/index.js';
6
+ import { ErasureLog } from './accounts/index.js';
7
+ import { createApiApp } from './api/create-api.js';
8
+ import { buildPepperRegistry } from './apikeys/index.js';
9
+ import { ApiKeyService } from './apikeys/index.js';
10
+ import { createApp } from './app.js';
11
+ import { hubSubjectResolver } from './entitlements/hub-subject.js';
12
+ import { AuditService } from './audit/index.js';
13
+ import { createEmailDriver, emailAssetsAt, setEmailAssets } from './email/index.js';
14
+ import { buildChangeEmailConfirmEmail, buildOtpEmail, buildPasswordResetEmail, buildVerifyEmailEmail } from './email/index.js';
15
+ import { hubConfig, parseEnv } from './env.js';
16
+ import { createAuth, mcpResourceUrl } from './identity/index.js';
17
+ import { HubSsoService } from './identity/index.js';
18
+ import { HubGrantService } from './identity/index.js';
19
+ import { HubLogoutService } from './identity/index.js';
20
+ import { hubApiResource, HubUserClient } from './identity/index.js';
21
+ import { DEFAULT_FEDERATION_DIALS, HubOrgReconciler } from './identity/index.js';
22
+ import { OauthJwtVerifier } from './identity/index.js';
23
+ import { preflightSigningKey } from './identity/index.js';
24
+ import { mcpRoutes } from './mcp/index.js';
25
+ import { wellKnownRoutes } from './routes/index.js';
26
+ import { instanceSettings, user as userTable, workspaceMembers, workspaces } from '@antasphere/chassis-db';
27
+ import { FileService } from './files/index.js';
28
+ import { createJobs, DEFAULT_USAGE_RETRY, PgBossUsageSink } from './jobs/index.js';
29
+ import { assertToolEntitlements, describeHubClockSkew, EMPTY_TOOL_ENTITLEMENTS, EntitlementProfiles, hubClockSkewMs, HubCreditCheck, HubMachineToken, HubUsagePoster } from './entitlements/index.js';
30
+ import { createLogger } from './logger.js';
31
+ import { createStorageDriver } from './storage/index.js';
32
+ import { createRateLimiters, makeClientIp, rateLimit } from './middleware/index.js';
33
+ import { hstsValue } from './middleware/index.js';
34
+ import { createMetrics } from './observability/index.js';
35
+ import { createOtel } from './observability/index.js';
36
+ import { bindEditionSeams } from './platform/index.js';
37
+ import { AllowAllEntitlements } from './platform/index.js';
38
+ import { EventBus } from './platform/index.js';
39
+ import { LocalIdentityProvider } from './platform/index.js';
40
+ import { createRegistry } from './platform/index.js';
41
+ import { NoopUsageSink } from './platform/index.js';
42
+ import { WorkspaceService } from './platform/index.js';
43
+ import { clearGeneratedSetupToken, resolveAuthSecret, resolveSetupToken } from './util/index.js';
44
+ import { createRuntimeState } from './util/index.js';
45
+ import { assertToolCopy, assertToolIdentity } from './tool-definition.js';
46
+ /**
47
+ * The boot sequence, in its locked order: env → logger → db probe → secret →
48
+ * migrations under advisory lock → app assembly → ready.
49
+ */
50
+ export async function bootPlatform(tool, source = process.env, overrides = {}) {
51
+ assertToolIdentity(tool.identity);
52
+ assertToolCopy(tool.copy);
53
+ const env = parseEnv(source, {
54
+ version: tool.runtime.version,
55
+ ...(tool.env ? { extension: tool.env } : {})
56
+ });
57
+ const logger = createLogger(env);
58
+ const state = createRuntimeState();
59
+ logger.info({ serviceRole: env.SERVICE_ROLE, nodeEnv: env.NODE_ENV }, 'booting');
60
+ state.reason = 'connecting to database';
61
+ const db = tool.db(env.DATABASE_URL);
62
+ // createDb already survives an idle connection ended by the server (a
63
+ // Postgres restart); this listener is only what makes it visible in the log.
64
+ db.pool.on('error', (err) => logger.warn({ err }, 'postgres ended an idle pooled connection; the pool opens a new one on the next query'));
65
+ await db.pool.query('SELECT 1');
66
+ logger.info('database reachable');
67
+ const authSecret = await resolveAuthSecret(env.AUTH_SECRET, env.DATA_DIR, logger);
68
+ const migrationsFolder = tool.runtime.findMigrationsDir();
69
+ let migrationsPending = false;
70
+ /** The migration-stage reason /readyz must keep reporting (later stages overwrite state.reason). */
71
+ let readinessRefusal = null;
72
+ // Hash-based status FIRST, on every boot (OPS-6, PRDCT-1357): a database
73
+ // carrying migrations this image does not ship was migrated by a NEWER
74
+ // image — a rollback / `:next` behind `:latest`. drizzle's migrator would
75
+ // happily consider it up to date (it compares timestamps), and a
76
+ // count-based status called it "current". Refuse readiness instead: the
77
+ // schema is ahead of the code, and the newer version's share links and
78
+ // rows are exactly what the older code would corrupt.
79
+ const preStatus = await migrationStatus({ connectionString: env.DATABASE_URL, migrationsFolder });
80
+ if (preStatus.downgrade) {
81
+ migrationsPending = true;
82
+ state.reason =
83
+ `database is AHEAD of this image: ${preStatus.unknownApplied} applied migration(s) this ` +
84
+ `version does not ship (a downgrade, or edited migration files) — deploy the version that applied them`;
85
+ readinessRefusal = state.reason;
86
+ logger.error({ status: preStatus }, 'refusing readiness: ' + state.reason);
87
+ }
88
+ else if (env.AUTO_MIGRATE) {
89
+ state.reason = 'applying migrations';
90
+ await runMigrations({
91
+ connectionString: env.DATABASE_URL,
92
+ migrationsFolder,
93
+ log: (msg) => logger.info({ scope: 'migrate' }, msg)
94
+ });
95
+ }
96
+ else {
97
+ const status = preStatus;
98
+ migrationsPending = status.pending;
99
+ if (status.pending) {
100
+ state.reason = `migrations pending (${status.applied}/${status.onDisk} applied) and AUTO_MIGRATE=false`;
101
+ readinessRefusal = state.reason;
102
+ logger.error({ status }, 'refusing readiness: run migrations manually or set AUTO_MIGRATE=true');
103
+ // The process stays up (healthz green) but /readyz keeps failing.
104
+ }
105
+ else {
106
+ logger.info({ status }, 'migrations current (AUTO_MIGRATE=false)');
107
+ }
108
+ }
109
+ // R7 edition-flip guard (internal/federation.md): EDITION selects the identity
110
+ // binding, so flipping it under an already-set-up instance silently changes
111
+ // who can log in and where workspace ownership is asserted. Setup stamps
112
+ // the instance's edition; a boot whose env EDITION differs refuses to start
113
+ // unless the operator explicitly acknowledges with EDITION_CHANGE_ALLOWED=true
114
+ // (which re-stamps and proceeds). A fresh database (no instance row yet)
115
+ // boots under any edition — cloud instances start from a fresh DB. The
116
+ // stamp is compared whenever it is READABLE — including while migrations
117
+ // are pending (readiness is red then, but the API still serves, so a
118
+ // skipped guard would be one whole mis-bound boot). Only a schema that
119
+ // predates the stamp column (migration 0020, AUTO_MIGRATE=false) has no
120
+ // stamp to compare; the first migrated boot enforces the guard.
121
+ let stampedEdition;
122
+ try {
123
+ const [instanceRow] = await db.db
124
+ .select({ edition: instanceSettings.edition })
125
+ .from(instanceSettings)
126
+ .limit(1);
127
+ stampedEdition = instanceRow?.edition;
128
+ }
129
+ catch (cause) {
130
+ if (!migrationsPending)
131
+ throw cause;
132
+ // Pending migrations + unreadable stamp: the edition column (or the
133
+ // table) is not migrated in yet on this database — nothing to compare.
134
+ }
135
+ if (stampedEdition !== undefined && stampedEdition !== env.EDITION) {
136
+ if (!env.EDITION_CHANGE_ALLOWED) {
137
+ const message = `refusing to boot: EDITION=${env.EDITION} but this instance was set up as ` +
138
+ `'${stampedEdition}'. An edition flip on a populated instance changes identity ` +
139
+ `semantics for existing users and workspaces. If this is intentional, set ` +
140
+ `EDITION_CHANGE_ALLOWED=true for ONE boot to re-stamp it (internal/federation.md).`;
141
+ logger.error({ stamped: stampedEdition, env: env.EDITION }, message);
142
+ await db.pool.end(); // clean refusal — no leaked pool for the caller
143
+ throw new Error(message);
144
+ }
145
+ // Pre-flight (EDIT-1/3/4, PRDCT-1356): the acknowledgement alone is not
146
+ // enough — a flip that migrates nothing leaves the cloud enforcement
147
+ // model not applying to pre-flip data, so the flip is REFUSED unless the
148
+ // instance is already in the shape the target edition requires.
149
+ const refuseFlip = async (reason) => {
150
+ const message = `refusing the edition flip '${stampedEdition}' → '${env.EDITION}' (EDITION_CHANGE_ALLOWED=true): ` +
151
+ reason;
152
+ logger.error({ stamped: stampedEdition, env: env.EDITION }, message);
153
+ await db.pool.end();
154
+ throw new Error(message);
155
+ };
156
+ if (stampedEdition === 'cloud' && env.EDITION === 'oss') {
157
+ // The reverse flip would leave every hub-minted session, OAuth grant
158
+ // and hub-origin membership behind as unrevocable LOCAL credentials
159
+ // (EDIT-4). Rebuild from a fresh database instead.
160
+ await refuseFlip('a cloud instance cannot be flipped back to oss — hub-minted credentials would become ' +
161
+ 'unrevocable local ones (internal/federation.md). Set up a fresh oss instance and restore data into it.');
162
+ }
163
+ if (env.EDITION === 'cloud') {
164
+ const [legacy] = await db.db
165
+ .select({ n: count() })
166
+ .from(workspaces)
167
+ .where(isNull(workspaces.centralAccountId));
168
+ if ((legacy?.n ?? 0) > 0) {
169
+ // EDIT-1: a workspace without a hub projection is outside every
170
+ // hub gate (HubLiveGate returns ok before any hub read) — the whole
171
+ // cloud enforcement model would never apply to it.
172
+ await refuseFlip(`${legacy?.n} workspace(s) carry no hub projection (central_account_id IS NULL). ` +
173
+ 'Project each one at the hub (or delete it) before flipping — cloud enforcement cannot cover an unprojected workspace.');
174
+ }
175
+ const [unverified] = await db.db
176
+ .select({ n: count() })
177
+ .from(userTable)
178
+ .where(eq(userTable.emailVerified, false));
179
+ if ((unverified?.n ?? 0) > 0) {
180
+ // EDIT-3: hub-only login links the trusted provider onto a VERIFIED
181
+ // local email only; every unverified user would be locked out.
182
+ await refuseFlip(`${unverified?.n} user(s) have an unverified email and would be locked out under hub-only login. ` +
183
+ 'Have them verify (or remove them) before flipping.');
184
+ }
185
+ }
186
+ await db.db.update(instanceSettings).set({ edition: env.EDITION });
187
+ logger.warn({ from: stampedEdition, to: env.EDITION }, 'edition re-stamped (EDITION_CHANGE_ALLOWED=true) — unset the flag again after this boot');
188
+ }
189
+ else if (env.EDITION_CHANGE_ALLOWED) {
190
+ // A leftover acknowledgement must never rot silently: while the flag
191
+ // stays set, the guard above is disarmed — any future EDITION change
192
+ // would re-stamp without refusing. Say so on every boot.
193
+ logger.warn('EDITION_CHANGE_ALLOWED=true but no edition change is pending — unset it: while it stays ' +
194
+ 'set, the R7 edition-flip guard is disarmed and a future EDITION change re-stamps ' +
195
+ 'without refusal (internal/federation.md)');
196
+ }
197
+ // The first-boot claim credential (PRDCT-1347): SETUP_TOKEN, or a token
198
+ // generated into the data volume while the instance is unclaimed. A boot
199
+ // that cannot read the stamp (pending migrations, pre-stamp schema) is
200
+ // treated as unclaimed — generating a token an already-set-up instance
201
+ // never needs is harmless; skipping it on an unclaimed one is the hole.
202
+ const setupToken = await resolveSetupToken(env.SETUP_TOKEN, env.DATA_DIR, stampedEdition !== undefined, logger);
203
+ // OAuth signing-key preflight (ADR 023): one decrypt of the key the jwt
204
+ // plugin would sign with. A rotated AUTH_SECRET leaves the stored JWKS
205
+ // private key undecryptable and — with no rotationInterval — never
206
+ // replaced, so every token mint would 500 while authorize still 302s.
207
+ // Refuse to boot instead, with the remedies in the message. Skipped only
208
+ // while migrations are pending (the jwks table may not exist yet, and the
209
+ // instance already refuses readiness in that state).
210
+ if (!migrationsPending) {
211
+ state.reason = 'verifying OAuth signing key';
212
+ const preflight = await preflightSigningKey(db.db, authSecret);
213
+ if (!preflight.ok) {
214
+ logger.fatal({ kid: preflight.kid }, preflight.reason);
215
+ // Fail-fast means clean-fast: release the pool this boot opened so the
216
+ // refusing process (and the integration test that pins it) never
217
+ // strands an open connection.
218
+ await db.pool.end().catch(() => { });
219
+ throw new Error('OAuth signing-key preflight failed — see the preceding log line');
220
+ }
221
+ }
222
+ // Email first: whether it delivers decides whether email-OTP login,
223
+ // self-serve password reset, and self-serve email change exist.
224
+ const email = overrides.email ?? createEmailDriver(env, logger);
225
+ // the mails' band and mark are served from the dashboard's static folder
226
+ setEmailAssets(emailAssetsAt(env.PUBLIC_BASE_URL));
227
+ // Audit is built before auth so credential events on Better-Auth-native
228
+ // routes (password reset/change) can be recorded through it.
229
+ const audit = new AuditService(db.db, logger);
230
+ /**
231
+ * Best-effort audit for a Better-Auth credential event. Credential events
232
+ * are USER-level, so the row lands in every workspace the user is an
233
+ * ACTIVE member of (ADR 014) — each workspace's trail records its own
234
+ * members' password/2FA changes, and a workspace the user was deactivated
235
+ * from records nothing; a single-workspace instance gets exactly one row,
236
+ * as before.
237
+ */
238
+ const auditAccountEvent = async (event, userId) => {
239
+ const rows = await db.db
240
+ .select({ workspaceId: workspaceMembers.workspaceId })
241
+ .from(workspaceMembers)
242
+ .where(and(eq(workspaceMembers.userId, userId), eq(workspaceMembers.isActive, true)));
243
+ for (const row of rows) {
244
+ await audit.write({
245
+ workspaceId: row.workspaceId,
246
+ principal: { userId, via: 'session' },
247
+ action: `user.${event}`,
248
+ resourceType: 'user',
249
+ resourceId: userId
250
+ });
251
+ }
252
+ };
253
+ // Account deletion: shared by the self-service /delete-user hooks below
254
+ // and the admin DELETE /members/{id} route. No storage dep — files are
255
+ // workspace data and survive the delete (ADR 006). The connection string
256
+ // feeds the dedicated advisory-lock clients that serialize last-owner
257
+ // removals (the migration-lock pattern).
258
+ const erasureLog = new ErasureLog(env.DATA_DIR, logger, authSecret);
259
+ const accountDeletion = new AccountDeletionService(db.db, audit, logger, env.DATABASE_URL, erasureLog);
260
+ // The event bus exists before auth so identity-layer hooks can publish:
261
+ // `user.created` fires from Better Auth's databaseHooks.user.create.after,
262
+ // covering EVERY account entrance by construction (setup, invitation
263
+ // accept, collaborator claim, future SSO JIT) — never from individual
264
+ // call sites.
265
+ const events = new EventBus(logger);
266
+ // The cloud edition's hub SSO binding (internal/federation.md): constructed
267
+ // iff EDITION=cloud — hubConfig() is the single switch, so an oss boot
268
+ // provably instantiates no SSO surface. Everything hub-SSO hangs off this
269
+ // one object: the genericOAuth provider inside createAuth, the per-login
270
+ // projection after-hook, and the login-scope wrap around the auth mount.
271
+ const hub = hubConfig(env);
272
+ // THE seam constant: the RFC 8707 `resource` for BOTH the SSO code
273
+ // exchange and every refresh — `<hub>/mcp`, so the tokens the tool holds
274
+ // are HUB-audienced and callable at hub /api/v1 as the user. Flipping it
275
+ // to null (if the hub ever accepts opaque tokens) is this one line.
276
+ const hubTokenResource = hub ? hubApiResource(hub.issuerUrl) : null;
277
+ const hubSso = hub
278
+ ? new HubSsoService({
279
+ db: db.db,
280
+ logger,
281
+ issuerUrl: hub.issuerUrl,
282
+ clientId: hub.clientId,
283
+ clientSecret: hub.clientSecret,
284
+ // Own resource URL — always derived from PUBLIC_BASE_URL, never
285
+ // configured. Pins H3 connect tokens only; login tokens are
286
+ // hub-audienced via tokenResource.
287
+ resourceUrl: mcpResourceUrl(env.PUBLIC_BASE_URL),
288
+ tokenResource: hubTokenResource,
289
+ publicBaseUrl: env.PUBLIC_BASE_URL
290
+ })
291
+ : undefined;
292
+ // ONE source for the never-trusted origins: Better Auth here, the cross-site
293
+ // guard of the API app below.
294
+ const untrustedOrigins = tool.api.untrustedOrigins?.(env) ?? [];
295
+ // Identity + seams: local defaults, swappable at this one point.
296
+ // The product's name and footer line in the four mails Better Auth sends (the fifth, the invitation, is api/invitations.ts).
297
+ const brand = { name: tool.identity.displayName, tagline: tool.copy.mail.tagline };
298
+ const auth = createAuth({
299
+ db: db.db,
300
+ env,
301
+ authSecret,
302
+ // The tool's values for the two generic-by-parameter points of the
303
+ // identity module: the published OAuth scope list (order included) and
304
+ // the origins that are never trusted (e.g. a tool's viewer origin,
305
+ // where author-controlled script runs, PRDCT-1352). None = nothing installed.
306
+ oauthScopes: tool.scopes.oauth,
307
+ untrustedOrigins,
308
+ hubSso,
309
+ // Demo sign-in (the demo pass spec): the redeem endpoint's deps, only while the switch is on.
310
+ ...(env.DEMO_SIGN_IN
311
+ ? { demoPass: { db: db.db, emailDomains: env.DEMO_SIGN_IN_EMAIL_DOMAINS, audit } }
312
+ : {}),
313
+ onAccountEvent: auditAccountEvent,
314
+ onUserCreated: (user) => events.chassisView().emit('user.created', { userId: user.id, email: user.email }),
315
+ beforeUserDelete: async (userId) => {
316
+ try {
317
+ await accountDeletion.beforeUserDelete(userId);
318
+ }
319
+ catch (cause) {
320
+ // Better Auth turns APIError into the route's 400 response.
321
+ if (cause instanceof LastOwnerError) {
322
+ throw new APIError('BAD_REQUEST', { message: cause.message });
323
+ }
324
+ throw cause;
325
+ }
326
+ },
327
+ afterUserDelete: (user) => accountDeletion.afterUserDelete(user),
328
+ ...(email.delivers
329
+ ? {
330
+ sendOtp: async ({ email: to, otp, type }) => {
331
+ const msg = buildOtpEmail({ brand, otp, type });
332
+ await email.send({ to, ...msg });
333
+ },
334
+ sendResetPassword: async ({ email: to, url }) => {
335
+ // Better Auth builds `url` as the API callback that 302s to the
336
+ // dashboard reset page with ?token — mail it verbatim.
337
+ const msg = buildPasswordResetEmail({
338
+ brand,
339
+ resetUrl: url,
340
+ expiresAt: new Date(Date.now() + 3600_000)
341
+ });
342
+ await email.send({ to, ...msg });
343
+ },
344
+ // Two-leg email change, leg 1 (verified users only): confirmation
345
+ // to the OLD address. Mail Better Auth's `url` verbatim.
346
+ sendChangeEmailConfirmation: async ({ email: to, newEmail, url }) => {
347
+ const msg = buildChangeEmailConfirmEmail({
348
+ brand,
349
+ newEmail,
350
+ confirmUrl: url,
351
+ expiresAt: new Date(Date.now() + 3600_000)
352
+ });
353
+ await email.send({ to, ...msg });
354
+ },
355
+ // Final leg of every email change: verification to the NEW address.
356
+ sendVerificationEmail: async ({ email: to, url }) => {
357
+ const msg = buildVerifyEmailEmail({
358
+ brand,
359
+ verifyUrl: url,
360
+ expiresAt: new Date(Date.now() + 3600_000)
361
+ });
362
+ await email.send({ to, ...msg });
363
+ }
364
+ }
365
+ : {})
366
+ });
367
+ // OPS-3 (PRDCT-1357): replay the erasure tombstone before the app serves.
368
+ // A restore from an older dump brings erased users back; the tombstone
369
+ // (kept outside the dump, carried forward by restore.sh) says who must
370
+ // stay gone. Runs through the same Better Auth cascade every GDPR surface
371
+ // uses (memberships, sessions, accounts go with the user) and lands an
372
+ // instance-level audit row per replay. Skipped while migrations are
373
+ // pending (the tables may not exist; readiness is red anyway).
374
+ //
375
+ // PRDCT-1809: the replay runs under EVERY last-owner guard the subject is
376
+ // subject to (the same locks + re-check the HTTP surfaces use; the 0009
377
+ // trigger backstops), so a sole-owner subject is refused BEFORE any row is
378
+ // touched — Better Auth's cascade drops the account rows before the user
379
+ // row, and an unguarded refusal left a half-erased owner. A refused
380
+ // tombstone is a FAIL-CLOSED verdict: the subject asked to be forgotten
381
+ // and the product cannot decide who inherits their workspace, so the boot
382
+ // audits the refusal, keeps /readyz red with the reason, and CLOSES the
383
+ // service surface (state.closed → 503 everywhere but the probes) until an
384
+ // operator promotes another owner and restarts (docs/operations/
385
+ // backup-restore.md "Erasures hold across a restore"). Readiness alone is
386
+ // not a closure: the API keeps serving under a red /readyz.
387
+ if (!migrationsPending) {
388
+ state.reason = 'replaying erasure tombstones';
389
+ const authCtx = await auth.$context;
390
+ const { replayed, refused } = await erasureLog.replay(async (userId) => {
391
+ const [row] = await db.db
392
+ .select({ id: userTable.id })
393
+ .from(userTable)
394
+ .where(eq(userTable.id, userId))
395
+ .limit(1);
396
+ return row !== undefined;
397
+ }, (userId) => accountDeletion.withAllLastOwnerGuards(userId, async () => {
398
+ await authCtx.internalAdapter.deleteUser(userId);
399
+ await authCtx.internalAdapter.deleteUserSessions(userId);
400
+ }));
401
+ for (const userId of replayed) {
402
+ await audit.write({
403
+ workspaceId: null,
404
+ principal: null, // → actorVia 'system'
405
+ action: 'user.erasure_replayed',
406
+ resourceType: 'user',
407
+ resourceId: userId,
408
+ metadata: { reason: 'tombstoned user present after boot (restore from an older backup)' }
409
+ });
410
+ }
411
+ for (const r of refused) {
412
+ const lastOwner = r.cause instanceof LastOwnerError;
413
+ await audit.write({
414
+ workspaceId: null,
415
+ principal: null, // → actorVia 'system'
416
+ action: 'user.erasure_replay_refused',
417
+ resourceType: 'user',
418
+ resourceId: r.userId,
419
+ metadata: {
420
+ reason: lastOwner
421
+ ? 'tombstoned user is the last active owner of a workspace; the instance refuses to serve until another owner is promoted'
422
+ : 'tombstoned user could not be re-erased; the instance refuses to serve',
423
+ erasedAt: r.erasedAt
424
+ }
425
+ });
426
+ }
427
+ if (refused.length > 0) {
428
+ // The public reason stays COARSE: /readyz is unauthenticated and the
429
+ // subject asked to be forgotten, so their id lives in the log line and
430
+ // the audit row only. The remedy names the cause the operator faces.
431
+ const lastOwner = refused.filter((r) => r.cause instanceof LastOwnerError).length;
432
+ const other = refused.length - lastOwner;
433
+ const remedy = [
434
+ lastOwner > 0
435
+ ? `${lastOwner} subject(s) present again after a restore as the last active owner of a workspace — promote another member to owner, then restart`
436
+ : null,
437
+ other > 0
438
+ ? `${other} re-erasure(s) failed — see the container log, fix the cause, then restart`
439
+ : null
440
+ ]
441
+ .filter((s) => s !== null)
442
+ .join('; ');
443
+ state.reason =
444
+ `refusing to serve: ${refused.length} erasure tombstone(s) could not be replayed: ${remedy} ` +
445
+ '(docs/operations/backup-restore.md, audit action user.erasure_replay_refused)';
446
+ readinessRefusal = state.reason;
447
+ state.closed = state.reason;
448
+ logger.error({ refused: refused.map((r) => ({ userId: r.userId, lastOwner: r.cause instanceof LastOwnerError })) }, state.reason);
449
+ }
450
+ }
451
+ // The live user-scoped federation stack (cloud only, internal/federation.md):
452
+ // each user's OWN hub grant (offline_access refresh token on the account
453
+ // row) → as-the-user `GET /orgs` reads → the org reconciler that the
454
+ // login pass, the live gate, and the unknown-workspace miss hook all
455
+ // share. Constructed AFTER createAuth because the grant encrypts tokens
456
+ // with Better Auth's own key material ((await auth.$context).secretConfig
457
+ // — equal to authSecret under our string-secret config; the fallback
458
+ // keeps the read total if a future Better Auth reshapes the context).
459
+ const hubDials = { ...DEFAULT_FEDERATION_DIALS, ...overrides.hubDials };
460
+ const hubGrant = hub
461
+ ? new HubGrantService({
462
+ db: db.db,
463
+ issuerUrl: hub.issuerUrl,
464
+ clientId: hub.clientId,
465
+ clientSecret: hub.clientSecret,
466
+ tokenResource: hubTokenResource,
467
+ key: async () => (await auth.$context).secretConfig ?? authSecret,
468
+ connectionString: env.DATABASE_URL,
469
+ logger,
470
+ dials: {
471
+ accessSkewMs: hubDials.accessSkewMs,
472
+ tokenTimeoutMs: hubDials.tokenTimeoutMs,
473
+ // The RFC 7662 probe rides the token-endpoint budget: same hub,
474
+ // same hop, and it only runs after an unanswered presentation.
475
+ introspectTimeoutMs: hubDials.tokenTimeoutMs,
476
+ lockWatchdogMs: hubDials.lockWatchdogMs
477
+ }
478
+ })
479
+ : undefined;
480
+ // RP-initiated logout against the hub (SL-2, cloud only): builds the
481
+ // end-session URL /sso/logout returns. Same key seam as the grant store
482
+ // (it only decrypts if a future Better Auth starts encrypting id_tokens);
483
+ // discovery fetches ride the tight orgs timeout dial.
484
+ const hubLogout = hub
485
+ ? new HubLogoutService({
486
+ db: db.db,
487
+ issuerUrl: hub.issuerUrl,
488
+ publicBaseUrl: env.PUBLIC_BASE_URL,
489
+ key: async () => (await auth.$context).secretConfig ?? authSecret,
490
+ logger,
491
+ discoveryTimeoutMs: hubDials.orgsTimeoutMs
492
+ })
493
+ : undefined;
494
+ // The ONE as-the-user hub client: the reconciler's GET /orgs reads and
495
+ // POST /workspaces' organization creation both present the user's own
496
+ // grant through it (never a second, hand-rolled token path).
497
+ const hubUserClient = hub && hubGrant
498
+ ? new HubUserClient({
499
+ grant: hubGrant,
500
+ issuerUrl: hub.issuerUrl,
501
+ logger,
502
+ timeoutMs: hubDials.orgsTimeoutMs,
503
+ // A creation is one deliberate human act, not an identity-path
504
+ // read: it rides the token-endpoint budget, not the tight one.
505
+ createTimeoutMs: hubDials.tokenTimeoutMs
506
+ })
507
+ : undefined;
508
+ const hubReconciler = hub && hubGrant && hubUserClient
509
+ ? new HubOrgReconciler({
510
+ db: db.db,
511
+ client: hubUserClient,
512
+ audit,
513
+ logger,
514
+ dials: hubDials,
515
+ onMembershipRemoval: tool.membershipRemoval,
516
+ ...(overrides.hubNow ? { now: overrides.hubNow } : {})
517
+ })
518
+ : undefined;
519
+ // The SSO login's fail-closed step 3 (assertLogin) runs THIS reconciler.
520
+ if (hubSso && hubReconciler)
521
+ hubSso.bindReconciler(hubReconciler);
522
+ // The reconciler's CACHED pass (its TTL + retry throttle, so no caller can
523
+ // hammer the hub), run by two callers: the unknown-workspace retry of all
524
+ // three credential kinds (one pass + one re-lookup; the requested id is
525
+ // ignored), and the zero-membership /me (the live gate never reaches a
526
+ // principal-less session). undefined on oss.
527
+ const cachedReconcile = hubReconciler
528
+ ? async (userId) => {
529
+ await hubReconciler.reconcile(userId);
530
+ }
531
+ : undefined;
532
+ // Jobs: pg-boss (durable queue). The registry's UsageSink emits into it;
533
+ // the worker side hands batches to the downstream sink (no-op locally).
534
+ // A tool job may need a service built further down (the storage driver is
535
+ // probed later); it reads the domain through this holder at RUN time (nightly).
536
+ const domainRef = { current: null };
537
+ // The billing rail's tool → hub machine channel (the spec, §6), cloud
538
+ // only: one client_credentials token on the instance's own hub client
539
+ // (scope usage:write), shared by the usage poster (the queue's downstream)
540
+ // and the entitlement profile reads. An oss boot constructs none of it.
541
+ const hubMachineToken = hub && hubTokenResource
542
+ ? new HubMachineToken({
543
+ issuerUrl: hub.issuerUrl,
544
+ clientId: hub.clientId,
545
+ clientSecret: hub.clientSecret,
546
+ resource: hubTokenResource,
547
+ logger,
548
+ timeoutMs: hubDials.tokenTimeoutMs
549
+ })
550
+ : undefined;
551
+ const hubUsagePoster = hubMachineToken
552
+ ? new HubUsagePoster({ issuerUrl: hub.issuerUrl, token: hubMachineToken, logger })
553
+ : undefined;
554
+ // The usage queue's retry budget and its hold (PRDCT-2635): stated once,
555
+ // shared by the sink that sends and the held queue that re-drives.
556
+ const usageRetry = { ...DEFAULT_USAGE_RETRY, ...overrides.usageRetry };
557
+ const jobs = await createJobs(env, db.db, db.pool, logger, overrides.usageDownstream ?? hubUsagePoster ?? new NoopUsageSink(), auth, audit, tool.jobs?.({ env, db: db.db, logger, getTool: () => domainRef.current }) ?? [], usageRetry);
558
+ // The tool's billing-rail declarations (slot 22), asserted once here: a
559
+ // route that meters an action the price book does not know, or a limit no
560
+ // tier values, stops the boot naming the route.
561
+ // The slot runs before `services`; an actor hook of an anonymous surface
562
+ // reads the domain lazily through `getTool()` at request time (PRDCT-2634).
563
+ const entitlements = tool.entitlements?.(env, { db: db.db, getTool: () => domainRef.current }) ?? EMPTY_TOOL_ENTITLEMENTS;
564
+ assertToolEntitlements(entitlements);
565
+ // The credit check at the hub (§7 steps 3 and 4), cloud only: the price
566
+ // of a metered request against the organization's balance, before the
567
+ // handler runs. Its metrics join /metrics beside the poster's.
568
+ const hubCreditCheck = hub && hubMachineToken
569
+ ? new HubCreditCheck({
570
+ issuerUrl: hub.issuerUrl,
571
+ token: hubMachineToken,
572
+ logger,
573
+ dials: overrides.entitlementCheckDials
574
+ })
575
+ : undefined;
576
+ const entitlementCloud = hub && hubMachineToken && hubCreditCheck
577
+ ? {
578
+ profiles: new EntitlementProfiles({
579
+ issuerUrl: hub.issuerUrl,
580
+ token: hubMachineToken,
581
+ logger,
582
+ dials: overrides.entitlementDials
583
+ }),
584
+ // The upgrade link of a plan refusal when the hub's answer carries
585
+ // no upgrade page of its own (an older hub): the hub root.
586
+ upgradeUrl: hub.issuerUrl,
587
+ credits: hubCreditCheck,
588
+ // The reported user of an event is the HUB user (the SSO `sub` on
589
+ // the account row), never the tool's local id; a user with no hub
590
+ // link (the operator) reports null. Cached per user for five
591
+ // minutes: an upload burst is one read, not one per event.
592
+ hubSubject: hubSubjectResolver(db.db)
593
+ }
594
+ : undefined;
595
+ // The clock check (PRDCT-2644), cloud only and off the boot's critical
596
+ // path: the hub judges every usage event's date against ITS clock and the
597
+ // poster against this instance's, so a clock more than five minutes ahead
598
+ // of the hub's stamps events the hub would refuse and the poster cannot
599
+ // see it. One read of the hub's Date header, a warning when they disagree
600
+ // beyond the forward bound, nothing refused.
601
+ if (hub) {
602
+ void hubClockSkewMs(hub.issuerUrl).then((skewMs) => {
603
+ if (skewMs === null)
604
+ return;
605
+ const sentence = describeHubClockSkew(skewMs);
606
+ if (sentence)
607
+ logger.warn({ skewMs }, `hub clock: ${sentence}`);
608
+ });
609
+ }
610
+ // The edition split (internal/federation.md): the local defaults below are the
611
+ // oss binding, passed through bindEditionSeams — the ONE place EDITION
612
+ // decides what the registry gets. oss returns them untouched; cloud
613
+ // rebinds the identity descriptor and wires the live principal gate off
614
+ // the reconciler built above. Entitlements stay LOCAL on both editions.
615
+ const seams = bindEditionSeams(hub, {
616
+ identity: new LocalIdentityProvider(auth, db.db, Boolean(env.GOOGLE_CLIENT_ID), email.delivers, email.delivers, // self-serve password reset needs a delivering email driver
617
+ email.delivers, // self-serve email change needs one too
618
+ cachedReconcile),
619
+ entitlements: new AllowAllEntitlements({
620
+ maxFileSizeMb: env.MAX_FILE_SIZE_MB,
621
+ apiRequestsPerMinute: env.API_RATE_LIMIT_PER_MINUTE,
622
+ apiRequestsBurstPerSecond: env.API_RATE_LIMIT_BURST
623
+ }),
624
+ usage: new PgBossUsageSink(jobs.boss, logger, usageRetry)
625
+ }, logger, { db: db.db, reconciler: hubReconciler });
626
+ const registry = createRegistry({
627
+ identity: seams.identity,
628
+ entitlements: seams.entitlements,
629
+ usage: seams.usage,
630
+ events,
631
+ workspaces: new WorkspaceService(db.db)
632
+ });
633
+ // API-key pepper registry (ADR 008): version 1 is the AUTH_SECRET-derived
634
+ // pepper; API_KEY_PEPPERS adds/pins versions so keys survive rotations.
635
+ const pepperRegistry = buildPepperRegistry(authSecret, env.API_KEY_PEPPERS);
636
+ if (env.API_KEY_PEPPERS !== undefined && !pepperRegistry.v1Pinned) {
637
+ logger.warn('API_KEY_PEPPERS is set but does not pin version 1 — version-1 API keys still ' +
638
+ 'depend on the live AUTH_SECRET; pin `1:<historical secret>` before rotating it ' +
639
+ '(internal/security-runbooks.md).');
640
+ }
641
+ const apiKeys = new ApiKeyService(db.db, pepperRegistry, tool.identity.apiKeyPrefix, cachedReconcile);
642
+ // Storage: probed at boot — /readyz stays red on an unwritable volume.
643
+ state.reason = 'probing storage';
644
+ const storage = createStorageDriver(env);
645
+ await storage.healthcheck();
646
+ logger.info({ driver: storage.name }, 'storage writable');
647
+ const fileService = new FileService(db.db, storage, join(env.DATA_DIR, 'tmp'), logger);
648
+ // The tool's domain services, built ONCE, here: after the storage probe
649
+ // and BEFORE the rate limiters, which is where the first service a job
650
+ // reads lazily was always built (a nightly purge firing during boot finds
651
+ // it from this line on). A tool subscribes to the event bus inside this
652
+ // call (no request can arrive before boot returns, so a subscription made
653
+ // here precedes every emission).
654
+ //
655
+ // ONE step swapped sides in the move, and this is it. At base
656
+ // (`apps/server/src/boot.ts@461db68`) the tool's services STRADDLED
657
+ // `createRateLimiters`: the form and form-upload services were built before
658
+ // it (and the purge cell filled), the collaborator service after it. Here
659
+ // `createRateLimiters` runs after the whole `tool.services` slot. The one
660
+ // consequence: a Redis connect failure from the limiters now surfaces after
661
+ // the domain services exist rather than between them; what moved ahead of
662
+ // the connect is the collaborator service's constructor, pure field
663
+ // assignment with no I/O. The limiters are not put back first because the
664
+ // job pollers are live from `createJobs` above, well before this line (at
665
+ // base too): with the limiters first, the form-upload purge cell would be
666
+ // empty during the limiter connect, where at base it was already filled,
667
+ // and a purge job picked up in that window would run as a no-op and
668
+ // complete. One slot cannot sit on both sides of the limiters; this side is
669
+ // the unobservable one.
670
+ const serviceCore = {
671
+ db: db.db,
672
+ env,
673
+ logger,
674
+ storage,
675
+ fileService,
676
+ pepperRegistry,
677
+ email,
678
+ events,
679
+ authSecret,
680
+ timers: jobs.timers,
681
+ overrides: overrides.tool
682
+ };
683
+ const domain = tool.services(serviceCore);
684
+ domainRef.current = domain;
685
+ const limiters = await createRateLimiters(env, logger, tool.rateLimiters);
686
+ const core = { ...serviceCore, limiters };
687
+ // OAuth-bearer verification: local JWKS (we minted the token) + live
688
+ // membership re-check. One instance, shared by the API and /mcp gates.
689
+ const oauthJwt = new OauthJwtVerifier(auth, db.db, env.PUBLIC_BASE_URL, cachedReconcile);
690
+ const api = createApiApp({
691
+ db: db.db,
692
+ env,
693
+ auth,
694
+ registry,
695
+ logger,
696
+ apiKeys,
697
+ audit,
698
+ email,
699
+ limiters,
700
+ storage,
701
+ fileService,
702
+ oauthJwt,
703
+ authSecret,
704
+ setupToken,
705
+ clearGeneratedSetupToken: () => clearGeneratedSetupToken(env.DATA_DIR),
706
+ accountDeletion,
707
+ tool,
708
+ domain,
709
+ untrustedOrigins,
710
+ hubSso,
711
+ // Cloud only: /sso/cli-connect stores the H3 offline grant through it.
712
+ hubGrant,
713
+ // Cloud only: /sso/logout builds the hub end-session leg through it.
714
+ hubLogout,
715
+ // Cloud only (internal/federation.md): the post-resolution LIVE hub gate —
716
+ // reconcile-as-the-user + suspension/revocation/grant-death verdicts —
717
+ // run by authContext on every authenticated request. undefined on oss.
718
+ principalGate: seams.principalGate,
719
+ entitlements,
720
+ entitlementCloud,
721
+ // Cloud only (PRDCT-2443): POST /workspaces creates the organization at
722
+ // the hub AS THE CALLER, then forces their reconcile. undefined on oss —
723
+ // the route creates locally there.
724
+ workspaceCloud: hub && hubGrant && hubUserClient && hubReconciler
725
+ ? {
726
+ createOrg: overrides.hubCreateOrg ?? ((userId, name) => hubUserClient.createOrg(userId, name)),
727
+ forceReconcile: (userId) => hubReconciler.forceReconcile(userId),
728
+ hasHubLink: (userId) => hubGrant.hasStoredGrant(userId),
729
+ manageUrl: hub.issuerUrl
730
+ }
731
+ : undefined,
732
+ // Cloud only: the refusal page's hint (/me.hubDenied), read from the
733
+ // reconciler's memory of the last definitive hub list. undefined on oss.
734
+ hubDeniedOrgs: hubReconciler ? (userId) => hubReconciler.deniedOrgs(userId) : undefined,
735
+ // Cloud only: a person re-seated at the hub is re-admitted on a bare
736
+ // zero-state read. undefined on oss.
737
+ reconcileForZeroState: cachedReconcile
738
+ });
739
+ // The tool's public routes (e.g. a share-link viewer, Phase 4,
740
+ // ADR 012): anonymous, mounted in app.ts's public-route slot.
741
+ const publicRoutes = tool.app?.publicRoutes?.({ ...core, overrides: undefined }, domain);
742
+ // Observability: tracing (exporterless = zero phone-home) + Prometheus.
743
+ const otel = await createOtel(env, logger, { serviceName: tool.identity.otelServiceName });
744
+ const metrics = createMetrics(db.db, jobs.boss);
745
+ // The counters of the seams that talk to the hub join the app registry so
746
+ // a degraded hub, dying grants, a rail that rejects everything or usage
747
+ // held past its retry budget is visible on /metrics (the poster's are
748
+ // cloud only: oss never builds one).
749
+ for (const metric of [
750
+ ...(hubReconciler?.promMetrics ?? []),
751
+ ...(hubGrant?.promMetrics ?? []),
752
+ ...(hubUsagePoster?.promMetrics ?? []),
753
+ ...(hubCreditCheck?.promMetrics ?? []),
754
+ ...jobs.promMetrics
755
+ ]) {
756
+ metrics.registry.registerMetric(metric);
757
+ }
758
+ // MCP tools call the instance's own API in-process, forwarding the caller's
759
+ // bearer — MCP is just another API client. The root app does not exist yet
760
+ // at this point, so the fetcher closes over a holder filled right after
761
+ // createApp (before any request can arrive).
762
+ const rootApp = { current: null };
763
+ let cachedName = null;
764
+ const mcp = mcpRoutes({
765
+ publicBaseUrl: env.PUBLIC_BASE_URL,
766
+ version: env.APP_VERSION,
767
+ fetchApi: async (path, init) => {
768
+ if (!rootApp.current)
769
+ throw new Error('mcp fetchApi called before app assembly');
770
+ return rootApp.current.request(path, init);
771
+ },
772
+ // The /mcp transport gate resolves the credential with NO workspace
773
+ // selector (default-org perspective): the per-tool-call `workspace`
774
+ // argument is mapped to the header at the in-process /api/v1 re-entry,
775
+ // where the real per-request selection happens.
776
+ resolveOauthJwt: (token) => oauthJwt.resolve(token, null),
777
+ resolveApiKey: (token) => apiKeys.resolve(token, null),
778
+ isApiKeyToken: apiKeys.isToken,
779
+ limiter: rateLimit(limiters.mcp, makeClientIp(env.TRUST_PROXY)),
780
+ tool: tool.mcp,
781
+ identity: tool.identity.mcp,
782
+ instanceName: async () => {
783
+ if (cachedName)
784
+ return cachedName;
785
+ const [row] = await db.db.select({ name: instanceSettings.name }).from(instanceSettings).limit(1);
786
+ cachedName = row?.name ?? tool.identity.displayName;
787
+ return cachedName;
788
+ }
789
+ });
790
+ const app = await createApp({
791
+ logger,
792
+ state,
793
+ publicDir: tool.runtime.publicDir,
794
+ displayName: tool.identity.displayName,
795
+ api,
796
+ mcp,
797
+ wellKnown: wellKnownRoutes({ auth, publicBaseUrl: env.PUBLIC_BASE_URL, oauthScopes: tool.scopes.oauth }),
798
+ publicRoutes,
799
+ metricsMiddleware: metrics.middleware,
800
+ metricsRoutes: metrics.routes(env.METRICS_TOKEN),
801
+ otelMiddleware: otel.middleware,
802
+ // The boot probe above runs once. /readyz re-runs this one so a store
803
+ // that goes away later (unmounted volume, wiped /data after a failed
804
+ // restore, revoked S3 credentials) stops reporting ready.
805
+ probeStorage: () => storage.healthcheck(),
806
+ hsts: hstsValue(env),
807
+ rootMiddleware: tool.app?.rootMiddleware?.(env),
808
+ cspFrameSrc: tool.app?.cspFrameSrc?.(env) ?? [],
809
+ cspImgSrc: tool.app?.cspImgSrc?.(env) ?? []
810
+ });
811
+ rootApp.current = app;
812
+ if (!migrationsPending && !readinessRefusal) {
813
+ state.ready = true;
814
+ state.reason = '';
815
+ }
816
+ else if (readinessRefusal) {
817
+ // Keep the migration-stage verdict on /readyz, not the last probe's label.
818
+ state.reason = readinessRefusal;
819
+ }
820
+ return { app, env, logger, state, db, auth, registry, jobs, email, otel, authSecret, tool: domain };
821
+ }
822
+ //# sourceMappingURL=boot.js.map