@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,384 @@
1
+ import type { Context, MiddlewareHandler } from 'hono';
2
+ import type { RateLimiterAbstract } from 'rate-limiter-flexible';
3
+ import { ACTIVE_WORKSPACE_HEADER, type Principal } from '@antasphere/chassis-contract';
4
+ import type { PlatformRegistry } from '../platform/registry.js';
5
+ import { WorkspaceMismatchError } from '../apikeys/service.js';
6
+ import { apiError } from '../api/errors.js';
7
+ import {
8
+ quotaHeaderEntries,
9
+ type ClientIpFn,
10
+ type QuotaDecision,
11
+ type RequestQuotaService
12
+ } from './rate-limit.js';
13
+ import { looksLikeJwt } from './scopes.js';
14
+
15
+ declare module 'hono' {
16
+ interface ContextVariableMap {
17
+ principal: Principal | null;
18
+ /** The tool's `guest_forbidden` sentence, set by `authContext` for `requireNonGuest()` to answer with. */
19
+ guestForbiddenMessage: string;
20
+ /** Demo sign-in: the pass that opened this request's session, when one did (the audit mark). */
21
+ demoPassId?: string;
22
+ }
23
+ }
24
+
25
+ /** Paths under /api/v1 that are reachable without any credential. */
26
+ const PUBLIC_API_PATHS = new Set([
27
+ '/api/v1/instance',
28
+ '/api/v1/setup',
29
+ '/api/v1/openapi.json',
30
+ // CLI email-OTP sign-in (api/cli-auth.ts): pre-auth by definition — the
31
+ // flow exists to OBTAIN a credential. Both are rate-limited in api/index.ts.
32
+ '/api/v1/cli/auth/request',
33
+ '/api/v1/cli/auth/complete',
34
+ // CLI cross-tool connect (api/sso-connect.ts, cloud edition): pre-auth by
35
+ // the same definition — the hub-minted exchange JWT in the body IS the
36
+ // credential, verified hard by the handler. Rate-limited in api/index.ts.
37
+ // The entry is inert on oss: the route is never registered there, so the
38
+ // path falls through to the JSON 404 terminator for every caller.
39
+ '/api/v1/sso/cli-connect'
40
+ ]);
41
+
42
+ export function isPublicApiPath(path: string): boolean {
43
+ return PUBLIC_API_PATHS.has(path) || path.startsWith('/api/v1/auth/');
44
+ }
45
+
46
+ /**
47
+ * An edition's post-resolution verdict on an otherwise-valid principal.
48
+ * `ok` with a `role` means "the caller's authoritative role just changed —
49
+ * run THIS request under it"; `ok` with a `workspace` means "the person's
50
+ * DEFAULT workspace just moved (the pass this request ran read it off the
51
+ * hub) — run THIS request there", which the gate says only for a request
52
+ * that selected nothing (`request.selector` null) on a credential that pins
53
+ * nothing (PRDCT-2958, F2: the command that runs the pass used to land in
54
+ * the old default, the one after it in the new); a refusal carries the
55
+ * exact wire error. The seam exists for the cloud edition's hub gates (org
56
+ * suspension + hub membership re-assertion, internal/federation.md P4); oss
57
+ * never wires one. The gate also sees WHAT is being asked (`request`), so a
58
+ * policy can exempt specific surfaces (e.g. suspension keeping GET /me
59
+ * readable — the visible-but-blocked posture); implementations may ignore
60
+ * it.
61
+ */
62
+ export type PrincipalGateResult =
63
+ | { ok: true; role?: Principal['role']; workspace?: GateWorkspace }
64
+ | { ok: false; status: 401 | 403; code: string; message: string };
65
+
66
+ /** The membership a gate re-resolved for the request: the fields of the principal it replaces. */
67
+ export interface GateWorkspace {
68
+ workspaceId: string;
69
+ role: Principal['role'];
70
+ origin: Principal['origin'];
71
+ accountRef: string | null;
72
+ }
73
+
74
+ export interface PrincipalGateRequest {
75
+ path: string;
76
+ method: string;
77
+ /** The request's own workspace selector (the X-Workspace-Id header), null when it named none. */
78
+ selector: string | null;
79
+ }
80
+
81
+ export type PrincipalGate = (
82
+ principal: Principal,
83
+ request: PrincipalGateRequest
84
+ ) => Promise<PrincipalGateResult>;
85
+
86
+ /** The gate's verdict applied: the principal to run the request as, or the exact wire refusal. */
87
+ export type AdmitResult =
88
+ { ok: true; principal: Principal } | { ok: false; status: 401 | 403; code: string; message: string };
89
+
90
+ /**
91
+ * The session half of the credential resolver: the identity provider's live
92
+ * resolve of the request's cookie (membership re-checked, the selection rule
93
+ * applied). One home for the call, shared by `authContext` and the
94
+ * zero-state re-admission of `GET /me`.
95
+ */
96
+ export function resolveSessionPrincipal(c: Context, registry: PlatformRegistry): Promise<Principal | null> {
97
+ return registry.identity.resolve({
98
+ headers: c.req.raw.headers,
99
+ path: c.req.path,
100
+ method: c.req.method,
101
+ requestId: c.get('requestId')
102
+ });
103
+ }
104
+
105
+ /**
106
+ * The edition gate on a resolved principal, with a freshly synced hub role
107
+ * applied to THIS request (D11): a demoted admin loses admin surfaces now, a
108
+ * promoted member gains them now. No gate (oss) admits the principal as it
109
+ * is. One home for the step, shared by `authContext` and the zero-state
110
+ * re-admission of `GET /me`.
111
+ */
112
+ export async function admitPrincipal(
113
+ c: Context,
114
+ principal: Principal,
115
+ principalGate: PrincipalGate | undefined
116
+ ): Promise<AdmitResult> {
117
+ if (!principalGate) return { ok: true, principal };
118
+ const verdict = await principalGate(principal, {
119
+ path: c.req.path,
120
+ method: c.req.method,
121
+ selector: c.req.header(ACTIVE_WORKSPACE_HEADER)?.trim() || null
122
+ });
123
+ if (!verdict.ok) return verdict;
124
+ let admitted = principal;
125
+ if (verdict.workspace && verdict.workspace.workspaceId !== principal.workspaceId) {
126
+ const { accountRef, ...rest } = verdict.workspace;
127
+ const { accountRef: _previous, ...withoutRef } = admitted;
128
+ admitted = { ...withoutRef, ...rest, ...(accountRef ? { accountRef } : {}) };
129
+ }
130
+ if (verdict.role !== undefined && verdict.role !== admitted.role) {
131
+ admitted = { ...admitted, role: verdict.role };
132
+ }
133
+ return { ok: true, principal: admitted };
134
+ }
135
+
136
+ export interface AuthContextDeps {
137
+ registry: PlatformRegistry;
138
+ /**
139
+ * Resolves `Bearer <prefix>_...` API keys under the user-scoped selection
140
+ * rule — `requested` is the request's X-Workspace-Id (or null). May throw
141
+ * WorkspaceMismatchError for a valid PINNED key whose pin differs from the
142
+ * header. Absent = keys rejected.
143
+ */
144
+ resolveApiKey?: (token: string, requested: string | null) => Promise<Principal | null>;
145
+ /** Resolves OAuth Bearer JWTs (local JWKS verify + live membership selection). */
146
+ resolveOauthJwt?: (token: string, requested: string | null) => Promise<Principal | null>;
147
+ /** Distinguishes an API-key credential from other bearers. */
148
+ isApiKeyToken: (token: string) => boolean;
149
+ /** Failed key verifications consume from this bucket (per IP) — brute-force wall. */
150
+ keyFailureLimiter?: RateLimiterAbstract;
151
+ /** Client identity (socket address unless TRUST_PROXY opts into XFF). */
152
+ clientIp: ClientIpFn;
153
+ /** General per-principal request quota (I3). Absent = no general limit. */
154
+ requestQuota?: RequestQuotaService;
155
+ /**
156
+ * Post-resolution principal veto (cloud edition only). Runs here — in the
157
+ * single credential resolver — rather than inside any one identity path,
158
+ * so sessions, API keys, AND OAuth bearers all pass the same gate
159
+ * (internal/decisions/016). Absent (oss) = zero overhead, zero hub surface.
160
+ */
161
+ principalGate?: PrincipalGate | undefined;
162
+ /**
163
+ * Demo sign-in (present only while DEMO_SIGN_IN is on, self-hosted): judges
164
+ * a resolved SESSION against the demo pass that opened it, if one did.
165
+ * `ended` = the pass has expired or was revoked and the session is gone:
166
+ * the request goes on signed out. Here, in the single credential resolver,
167
+ * so no route can be reached with a session its pass no longer backs.
168
+ */
169
+ demoSession?:
170
+ ((headers: Headers) => Promise<{ passId: string; sessionId: string } | 'ended' | null>) | undefined;
171
+ /**
172
+ * The composed fail-closed scope allowlist (`createScopeAllowlist` in
173
+ * scopes.ts: the chassis rules, then the tool's). `null` = the endpoint is
174
+ * not open to machine principals. Required: a tool must state its list.
175
+ */
176
+ requiredScopeFor: (path: string, method: string) => string | null;
177
+ /**
178
+ * The tool's `guest_forbidden` sentence (`copy.guestForbidden`). Carried on
179
+ * the request so `requireNonGuest()` keeps its argument-less call sites, in
180
+ * the chassis and in the tool alike. Required: the chassis has no wording.
181
+ */
182
+ guestForbiddenMessage: string;
183
+ }
184
+
185
+ /**
186
+ * The single credential resolver: every /api/v1 request passes through here
187
+ * and comes out with c.get('principal') set (or a 401/403 for bad machine
188
+ * credentials). Three paths, one output shape:
189
+ *
190
+ * - session cookie → IdentityProvider.resolve (live membership re-check)
191
+ * - Bearer API key → O(1) key lookup (M2)
192
+ * - Bearer JWT → OAuth access token this instance minted: local JWKS
193
+ * verification + live membership re-check (M6)
194
+ *
195
+ * Machine principals additionally pass the fail-closed scope allowlist:
196
+ * endpoints not consciously listed in scopes.ts are unreachable with a key
197
+ * or token, whatever the underlying user's role is.
198
+ */
199
+ export function authContext({
200
+ registry,
201
+ resolveApiKey,
202
+ resolveOauthJwt,
203
+ isApiKeyToken,
204
+ keyFailureLimiter,
205
+ clientIp,
206
+ requestQuota,
207
+ principalGate,
208
+ demoSession,
209
+ requiredScopeFor,
210
+ guestForbiddenMessage
211
+ }: AuthContextDeps): MiddlewareHandler {
212
+ return async (c, next) => {
213
+ c.set('principal', null);
214
+ c.set('guestForbiddenMessage', guestForbiddenMessage);
215
+
216
+ if (isPublicApiPath(c.req.path)) {
217
+ return next();
218
+ }
219
+
220
+ const authHeader = c.req.header('authorization')?.trim() ?? '';
221
+ const bearer = /^Bearer\s+(.+)$/i.exec(authHeader)?.[1]?.trim() ?? null;
222
+
223
+ // The universal per-request workspace selector (user-scoped credential
224
+ // model): EVERY credential kind resolves it through the shared
225
+ // resolveMembership rule — sessions inside the identity provider,
226
+ // machine credentials via the threaded `requested` below. A PINNED API
227
+ // key is the one exception: it always resolves its pin, and a header
228
+ // naming a different workspace is rejected loudly (403 below) instead of
229
+ // being silently served the pinned one.
230
+ const requested = c.req.header(ACTIVE_WORKSPACE_HEADER)?.trim() || null;
231
+
232
+ let principal: Principal | null = null;
233
+
234
+ if (bearer && looksLikeJwt(bearer)) {
235
+ if (!resolveOauthJwt) {
236
+ return apiError(c, 401, 'oauth_not_enabled', 'OAuth bearer tokens are not enabled on this instance');
237
+ }
238
+ principal = await resolveOauthJwt(bearer, requested);
239
+ if (!principal) {
240
+ return apiError(c, 401, 'invalid_token', 'OAuth bearer token is invalid, expired, or revoked');
241
+ }
242
+ } else if (bearer && isApiKeyToken(bearer)) {
243
+ if (!resolveApiKey) {
244
+ return apiError(c, 401, 'invalid_api_key', 'API key not recognized');
245
+ }
246
+ const ip = clientIp(c);
247
+ if (keyFailureLimiter) {
248
+ const bucket = await keyFailureLimiter.get(ip).catch(() => null);
249
+ if (bucket && bucket.remainingPoints <= 0 && bucket.msBeforeNext > 0) {
250
+ return apiError(c, 429, 'rate_limited', 'Too many failed API key attempts');
251
+ }
252
+ }
253
+ try {
254
+ principal = await resolveApiKey(bearer, requested);
255
+ } catch (cause) {
256
+ // A VALID pinned key + a mismatching selector: a client bug or a
257
+ // confused-deputy attempt — loud 403, and no failure-limiter charge
258
+ // (the credential itself verified fine).
259
+ if (cause instanceof WorkspaceMismatchError) {
260
+ return apiError(c, 403, 'workspace_mismatch', 'This credential is pinned to a different workspace');
261
+ }
262
+ throw cause;
263
+ }
264
+ if (!principal) {
265
+ if (keyFailureLimiter) await keyFailureLimiter.consume(ip).catch(() => {});
266
+ return apiError(c, 401, 'invalid_api_key', 'API key not recognized');
267
+ }
268
+ } else if (bearer) {
269
+ return apiError(c, 401, 'unsupported_credential', 'Unrecognized bearer credential format');
270
+ } else {
271
+ principal = await resolveSessionPrincipal(c, registry);
272
+ // Fail-closed: a judge that throws refuses the session for this
273
+ // request rather than letting a dead pass's session through.
274
+ if (principal && demoSession) {
275
+ const verdict = await demoSession(c.req.raw.headers);
276
+ if (verdict === 'ended') principal = null;
277
+ else if (verdict) c.set('demoPassId', verdict.passId);
278
+ }
279
+ }
280
+
281
+ // General per-principal request quota (I3), consumed BEFORE the scope
282
+ // gate so even fail-closed 403 hammering is bounded — once a credential
283
+ // resolves, the quota is the outermost cost control. The bucket key is
284
+ // derived from the principal (see principalBucketKey), never a header.
285
+ // Sessions are included: the default quota is far above any dashboard's
286
+ // request rate, and a hijacked/scripted session is the same cost vector.
287
+ let quota: QuotaDecision | null = null;
288
+ if (principal && requestQuota) {
289
+ quota = await requestQuota.consume(principal);
290
+ if (quota && !quota.ok) {
291
+ for (const [name, value] of quotaHeaderEntries(quota)) c.header(name, value);
292
+ c.header('Retry-After', String(quota.retryAfterSeconds ?? Math.max(1, quota.resetSeconds)));
293
+ return apiError(c, 429, 'rate_limited', 'API request quota exceeded, slow down');
294
+ }
295
+ }
296
+
297
+ // Edition principal gate (cloud: hub org-status + membership
298
+ // re-assertion, internal/federation.md P4). AFTER the quota — hammering a
299
+ // suspended org stays rate-bounded — and BEFORE the scope gate, so a
300
+ // definitive hub refusal wins over any per-endpoint outcome. Cache-first
301
+ // inside; the hub is never a hard round-trip in the hot path.
302
+ if (principal && principalGate) {
303
+ const admitted = await admitPrincipal(c, principal, principalGate);
304
+ if (!admitted.ok) {
305
+ return apiError(c, admitted.status, admitted.code, admitted.message);
306
+ }
307
+ principal = admitted.principal;
308
+ }
309
+
310
+ // Fail-closed scope gate for machine principals only.
311
+ if (principal?.scopes) {
312
+ const needed = requiredScopeFor(c.req.path, c.req.method);
313
+ if (!needed) {
314
+ return apiError(c, 403, 'endpoint_not_allowed', 'This endpoint is not available to this credential');
315
+ }
316
+ if (!principal.scopes.has(needed)) {
317
+ return apiError(c, 403, 'insufficient_scope', `This credential was not granted "${needed}"`);
318
+ }
319
+ }
320
+
321
+ c.set('principal', principal);
322
+ if (!quota) return next();
323
+
324
+ // Success path: stamp the quota headers on whatever response the route
325
+ // produced (set on c.res so streamed/raw Response bodies get them too),
326
+ // letting well-behaved agents self-throttle before hitting 429s.
327
+ await next();
328
+ for (const [name, value] of quotaHeaderEntries(quota)) c.res.headers.set(name, value);
329
+ return;
330
+ };
331
+ }
332
+
333
+ /** Route guard: 401 when unauthenticated. */
334
+ export function requireAuth(): MiddlewareHandler {
335
+ return async (c, next) => {
336
+ if (!c.get('principal')) {
337
+ return apiError(c, 401, 'unauthenticated', 'Authentication required');
338
+ }
339
+ return next();
340
+ };
341
+ }
342
+
343
+ /**
344
+ * Route guard (D2, internal/federation.md P6): an `origin='guest'` membership
345
+ * belongs to an EXTERNAL per-resource collaborator — it exists so the platform
346
+ * can resolve them to a principal at all, not to make them a workspace
347
+ * actor. Guests keep every ADR 013 per-resource surface their grant opens
348
+ * (read, version push, share tokens, annotations on THAT resource) but are
349
+ * refused resource creation and workspace-level surfaces on BOTH editions.
350
+ * Judges the resolved principal, so sessions, API keys, and OAuth bearers
351
+ * all pass the same gate. 403 — the surfaces this guards are flat,
352
+ * documented workspace surfaces, not probeable per-grant resources, so the
353
+ * ADR 013 hide-existence 404 posture does not apply here (same stance as
354
+ * requireRole).
355
+ */
356
+ export function requireNonGuest(): MiddlewareHandler {
357
+ return async (c, next) => {
358
+ const principal = c.get('principal');
359
+ if (!principal) {
360
+ return apiError(c, 401, 'unauthenticated', 'Authentication required');
361
+ }
362
+ if (principal.origin === 'guest') {
363
+ // A principal only ever comes out of `authContext`, which set the sentence first.
364
+ return apiError(c, 403, 'guest_forbidden', c.get('guestForbiddenMessage'));
365
+ }
366
+ return next();
367
+ };
368
+ }
369
+
370
+ const ROLE_RANK = { member: 0, admin: 1, owner: 2 } as const;
371
+
372
+ /** Route guard: 403 below the required role. */
373
+ export function requireRole(min: keyof typeof ROLE_RANK): MiddlewareHandler {
374
+ return async (c, next) => {
375
+ const principal = c.get('principal');
376
+ if (!principal) {
377
+ return apiError(c, 401, 'unauthenticated', 'Authentication required');
378
+ }
379
+ if (ROLE_RANK[principal.role] < ROLE_RANK[min]) {
380
+ return apiError(c, 403, 'forbidden', `Requires ${min} role`);
381
+ }
382
+ return next();
383
+ };
384
+ }
@@ -0,0 +1,33 @@
1
+ import type { Context, MiddlewareHandler } from 'hono';
2
+ import { bodyLimit } from 'hono/body-limit';
3
+ import type { BodyCap } from '../tool-definition.js';
4
+
5
+ /**
6
+ * A body-size cap that DEFERS its declared-size refusal to the entitlement
7
+ * gate (PRDCT-2632). Built once per `BodyCap` from hono's `bodyLimit` (a
8
+ * declared Content-Length over the cap is refused before a byte is read; an
9
+ * undeclared body is counted and cut at the cap), with one difference: when
10
+ * the request's matched route carries a gate that judges a plan limit
11
+ * (`isDeferring`), a declared oversize body is not refused HERE — the
12
+ * refusal is parked on the context (`bodyRefusal`) for the gate to fire
13
+ * after the plan check, and the request is rebuilt WITHOUT a body, so
14
+ * nothing downstream can read a byte of it whatever the middleware order.
15
+ * A metered account thus meets 403 `plan_required` with its upgrade link at
16
+ * any size, and the cap's 413 only when the plan allows the size.
17
+ */
18
+ export function deferrableBodyCap(cap: BodyCap, isDeferring: (c: Context) => boolean): MiddlewareHandler {
19
+ const streamCap = bodyLimit({ maxSize: cap.maxBytes, onError: cap.onError });
20
+ return (c, next) => {
21
+ const declared = Number(c.req.header('content-length') ?? '');
22
+ if (Number.isFinite(declared) && declared > cap.maxBytes && isDeferring(c)) {
23
+ c.set('bodyRefusal', () => cap.onError(c));
24
+ // Rebuilt from the URL, the method and the headers: a `body: null`
25
+ // in a Request init keeps the input's body (the Fetch spec), so the
26
+ // drop must be a request that never had one.
27
+ const raw = c.req.raw;
28
+ c.req.raw = new Request(raw.url, { method: raw.method, headers: raw.headers, signal: raw.signal });
29
+ return next();
30
+ }
31
+ return streamCap(c, next);
32
+ };
33
+ }
@@ -0,0 +1,23 @@
1
+ import type { Context } from 'hono';
2
+
3
+ declare module 'hono' {
4
+ interface ContextVariableMap {
5
+ /**
6
+ * A body-size refusal the size cap DEFERRED (PRDCT-2632): the request
7
+ * declared a Content-Length over the tool's cap on a route whose gate
8
+ * judges a plan limit, so the gate must see the declared size first (a
9
+ * metered account meets 403 `plan_required` with the upgrade link at any
10
+ * size, never the instance-cap 413 in its place). The cap DROPS the
11
+ * request's body when it defers (`c.req.raw` is rebuilt without one), so
12
+ * nothing downstream can read a byte of it by construction; the gate
13
+ * fires the refusal right after the plan check, or before anything else
14
+ * when no plan applies. Undefined otherwise.
15
+ */
16
+ bodyRefusal: (() => Response) | undefined;
17
+ }
18
+ }
19
+
20
+ /** The pending deferred refusal of this request, if any. */
21
+ export function pendingBodyRefusal(c: Context): (() => Response) | undefined {
22
+ return c.get('bodyRefusal');
23
+ }
@@ -0,0 +1,122 @@
1
+ import type { Context, MiddlewareHandler } from 'hono';
2
+ import { apiError } from '../api/errors.js';
3
+ import { isPublicOauthPath } from './oauth-public.js';
4
+
5
+ /**
6
+ * Cross-site request forgery defence for the whole `/api/v1` surface.
7
+ *
8
+ * The session cookie authenticates every business route (`/orgs`, `/members`,
9
+ * `/invitations`, `/me/*`, `/apikeys`, every product route a instantiation
10
+ * adds), and a cookie is attached by the browser on a cross-site POST just as
11
+ * happily as on a same-origin one. Better Auth's own origin check covers only
12
+ * its `/sign-in` paths, so without this middleware a page on any origin could
13
+ * drive a signed-in user's whole API.
14
+ *
15
+ * Two independent signals, both browser-set and unforgeable by page script
16
+ * (both are Forbidden Header Names — `fetch` cannot override them):
17
+ *
18
+ * 1. `Sec-Fetch-Site: cross-site` — a request initiated by a document on a
19
+ * different site. Refused outright.
20
+ * 2. `Origin` — present on every browser request with an unsafe method.
21
+ * Refused unless it is the origin we are being served on, the configured
22
+ * public origin, or an explicitly trusted extra.
23
+ *
24
+ * Deliberately NOT refused:
25
+ *
26
+ * - Safe methods (GET/HEAD/OPTIONS). A cross-site read cannot be seen by the
27
+ * attacker — `/api/v1` sends no `Access-Control-Allow-Origin` except on the
28
+ * OAuth surfaces below — and refusing them would break legitimate
29
+ * cross-origin embeds of authenticated content.
30
+ * - Requests presenting an `Authorization` header. Those are API keys and
31
+ * OAuth bearers: not ambient credentials, so not forgeable cross-site (a
32
+ * page cannot attach the header without a preflight we never approve).
33
+ * Refusing them would break every scripted and browser-based MCP client.
34
+ * - The deliberate wildcard-CORS OAuth endpoints (oauth-public.ts): token,
35
+ * register, introspect, revoke, jwks and discovery are cookie-less by
36
+ * design and MUST answer any origin.
37
+ * - Requests with neither header. Non-browser clients (curl, the CLI, the
38
+ * SDK, server-to-server) send neither, and this middleware must not become
39
+ * a browser-only gate on a self-hosted API. The ambient-credential threat
40
+ * this closes is browser-only, and browsers always send at least one.
41
+ */
42
+ export interface CrossSiteGuardOptions {
43
+ /** The instance's configured public origin (PUBLIC_BASE_URL). */
44
+ publicBaseUrl: string;
45
+ /**
46
+ * Additional origins allowed to drive the API with cookies — a separately
47
+ * hosted first-party dashboard, say. Empty on the template.
48
+ */
49
+ extraTrustedOrigins?: readonly string[];
50
+ /**
51
+ * Origins REFUSED even when they equal the origin this request was served
52
+ * on: the viewer origin (VIEWER_BASE_URL, PRDCT-1352). The host gate keeps
53
+ * `/api/v1` (minus the token-authed `/viewer/*` subset) off that hostname
54
+ * altogether, so in the shipped assembly no request here carries it as the
55
+ * serving origin — this is the second lock, so that a gate regression or a
56
+ * proxy routing the viewer hostname at the app never turns "the origin we
57
+ * are served on" into a trust grant for author-controlled script.
58
+ */
59
+ deniedOrigins?: readonly string[];
60
+ /** Escape hatch for surfaces that must stay open (paths are `/api/v1/...`). */
61
+ isExempt?: (path: string) => boolean;
62
+ }
63
+
64
+ const SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']);
65
+
66
+ function originOf(value: string): string | null {
67
+ try {
68
+ return new URL(value).origin;
69
+ } catch {
70
+ return null;
71
+ }
72
+ }
73
+
74
+ /** The origin this very request was addressed to (works on localhost, previews, any domain). */
75
+ function servingOrigin(c: Context): string | null {
76
+ return originOf(c.req.url);
77
+ }
78
+
79
+ export function crossSiteGuard({
80
+ publicBaseUrl,
81
+ extraTrustedOrigins = [],
82
+ deniedOrigins = [],
83
+ isExempt
84
+ }: CrossSiteGuardOptions): MiddlewareHandler {
85
+ const configured = new Set<string>();
86
+ const publicOrigin = originOf(publicBaseUrl);
87
+ if (publicOrigin) configured.add(publicOrigin);
88
+ for (const extra of extraTrustedOrigins) {
89
+ const origin = originOf(extra);
90
+ if (origin) configured.add(origin);
91
+ }
92
+ const denied = new Set<string>();
93
+ for (const value of deniedOrigins) {
94
+ const origin = originOf(value);
95
+ if (origin) denied.add(origin);
96
+ }
97
+ // A denied origin wins over every trust source, the serving origin included.
98
+ for (const origin of denied) configured.delete(origin);
99
+
100
+ return async (c, next) => {
101
+ if (SAFE_METHODS.has(c.req.method)) return next();
102
+ if (c.req.header('authorization')) return next();
103
+ if (isPublicOauthPath(c.req.path)) return next();
104
+ if (isExempt?.(c.req.path)) return next();
105
+
106
+ const secFetchSite = c.req.header('sec-fetch-site');
107
+ if (secFetchSite === 'cross-site') {
108
+ return apiError(c, 403, 'cross_site_forbidden', 'Cross-site requests are not accepted on this API');
109
+ }
110
+
111
+ const origin = c.req.header('origin');
112
+ if (origin) {
113
+ const serving = servingOrigin(c);
114
+ const trusted = !denied.has(origin) && (origin === serving || configured.has(origin));
115
+ if (!trusted) {
116
+ return apiError(c, 403, 'cross_site_forbidden', 'Cross-site requests are not accepted on this API');
117
+ }
118
+ }
119
+
120
+ return next();
121
+ };
122
+ }
@@ -0,0 +1,61 @@
1
+ import type { MiddlewareHandler } from 'hono';
2
+ import { apiError } from '../api/errors.js';
3
+
4
+ /**
5
+ * P7 (internal/federation.md "Hub-managed membership"): on the cloud edition,
6
+ * membership of a HUB-ORIGIN (projected) workspace is the hub's source of
7
+ * truth — invites, roles, and removals happen at the hub and flow in via
8
+ * SSO login + the P4 re-assertion. A local membership mutation on such a
9
+ * workspace would create divergence the next login/re-assertion fights, so
10
+ * every mutation on the local /members + /invitations surfaces answers this
11
+ * refusal instead. READS stay open: the projected roster is real.
12
+ *
13
+ * Why 403 (not 410): the member rows demonstrably exist (GET /members
14
+ * serves them), so "the resource is gone" would be false — what is denied
15
+ * is the OPERATION's local authority, for every caller, regardless of role
16
+ * or credential. That is RFC 9110 403's "forbidden for reasons unrelated to
17
+ * the credentials", and it keeps the platform's refusal vocabulary in one
18
+ * register (guest_forbidden, endpoint_not_allowed, workspace_mismatch are
19
+ * all 403). The `hub_managed` code + `details.manageUrl` carry the pointer.
20
+ *
21
+ * Fail-closed by construction:
22
+ * - Hub-origin detection keys on `principal.accountRef`, which every
23
+ * credential resolver (session: platform/local-identity.ts; API key:
24
+ * apikeys/service.ts; OAuth bearer: identity/oauth-jwt.ts) populates from
25
+ * a LIVE join on `workspaces.central_account_id` for the request's ONE
26
+ * workspace (ADR 014) — the same signal the live hub gate keys on
27
+ * (identity/hub-live-gate.ts). There is no principal-construction path
28
+ * that skips the join.
29
+ * - The gate is method-keyed, not route-enumerated, and mounted ONCE per
30
+ * subtree wildcard (`/members/*`, `/invitations/*` — in Hono the
31
+ * wildcard also matches the collection root): any FUTURE mutation
32
+ * registered anywhere under those surfaces is refused by default, with
33
+ * no per-path registration to remember.
34
+ * - It is registered only on EDITION=cloud (the `hubConfig` presence
35
+ * switch, like every cloud seam) — oss carries zero behavior change, and
36
+ * on oss `centralAccountId` is never set anyway (double cover).
37
+ *
38
+ * Principal-less requests pass through untouched: the public invitation
39
+ * accept/lookup segments are explicitly SKIPPED where this is mounted (their
40
+ * target workspace is the invitation's, not the caller's — the accept
41
+ * handler runs its own hub-origin check on the invitation's workspace), and
42
+ * every other gated route 401s in its requireAuth/requireRole gate.
43
+ */
44
+ export function hubManagedMembershipGate(
45
+ manageUrl: string,
46
+ message = 'Membership of this workspace is managed at the Antasphere hub — invite, remove, and change roles there'
47
+ ): MiddlewareHandler {
48
+ return async (c, next) => {
49
+ const method = c.req.method;
50
+ if (method === 'GET' || method === 'HEAD' || method === 'OPTIONS') return next();
51
+ const principal = c.get('principal');
52
+ if (principal?.accountRef) {
53
+ return apiError(c, 403, 'hub_managed', message, { manageUrl });
54
+ }
55
+ return next();
56
+ };
57
+ }
58
+
59
+ /** The teams' sentence for the same gate (PRDCT-2813): a projected team is shaped on the account site. */
60
+ export const HUB_MANAGED_TEAMS_MESSAGE =
61
+ 'Teams of this workspace are managed at the Antasphere hub — create, rename, and seat people there';