@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,178 @@
1
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
2
+ import type { StartedPostgreSqlContainer } from '@testcontainers/postgresql';
3
+ import {
4
+ createDatabase,
5
+ createTestApp,
6
+ extractCookie,
7
+ readJson,
8
+ startPostgres,
9
+ type TestApp,
10
+ host
11
+ } from './helpers.js';
12
+
13
+ /**
14
+ * API-key expiry, end to end: the TTL at mint becomes an absolute expires_at,
15
+ * resolution rejects an expired key with EXACTLY the revoked key's 401 (one
16
+ * fail-closed path, no behavioural fork), and the nightly sweep normalizes
17
+ * expired keys to revoked_at without touching live ones.
18
+ */
19
+
20
+ const OWNER = { email: 'owner@expiry.test', name: 'Expiry Owner', password: 'expiry-owner-pass-123' };
21
+
22
+ let container: StartedPostgreSqlContainer;
23
+ let app: TestApp;
24
+ let ownerCookie: string;
25
+
26
+ const json = (body: unknown) => ({
27
+ method: 'POST',
28
+ headers: { 'content-type': 'application/json' },
29
+ body: JSON.stringify(body)
30
+ });
31
+
32
+ async function mint(body: Record<string, unknown>) {
33
+ return app.app.request('/api/v1/api-keys', {
34
+ ...json(body),
35
+ headers: { 'content-type': 'application/json', cookie: ownerCookie }
36
+ });
37
+ }
38
+
39
+ async function me(key: string) {
40
+ return app.app.request('/api/v1/me', { headers: { authorization: `Bearer ${key}` } });
41
+ }
42
+
43
+ async function expiresAtOf(keyId: string): Promise<Date | null> {
44
+ const res = await app.db.pool.query<{ expires_at: Date | null }>(
45
+ 'SELECT expires_at FROM api_keys WHERE key_id = $1',
46
+ [keyId]
47
+ );
48
+ return res.rows[0]?.expires_at ?? null;
49
+ }
50
+
51
+ async function revokedAtOf(keyId: string): Promise<Date | null> {
52
+ const res = await app.db.pool.query<{ revoked_at: Date | null }>(
53
+ 'SELECT revoked_at FROM api_keys WHERE key_id = $1',
54
+ [keyId]
55
+ );
56
+ return res.rows[0]?.revoked_at ?? null;
57
+ }
58
+
59
+ beforeAll(async () => {
60
+ container = await startPostgres();
61
+ app = await createTestApp(await createDatabase(container, 'apikeyexpiry'));
62
+ await app.app.request(
63
+ '/api/v1/setup',
64
+ json({ setupToken: 'integration-test-setup-token', instanceName: 'Expiry', owner: OWNER })
65
+ );
66
+ const signIn = await app.app.request(
67
+ '/api/v1/auth/sign-in/email',
68
+ json({ email: OWNER.email, password: OWNER.password })
69
+ );
70
+ ownerCookie = extractCookie(signIn);
71
+ }, 240_000);
72
+
73
+ afterAll(async () => {
74
+ await app?.stop();
75
+ await container?.stop();
76
+ });
77
+
78
+ describe('minting with a TTL', () => {
79
+ it('computes an absolute expiry ~expiresInDays out and surfaces it in the list', async () => {
80
+ const res = await mint({ name: 'seven-days', scopes: [host.scopes.read], expiresInDays: 7 });
81
+ expect(res.status).toBe(201);
82
+ const body = await readJson(res);
83
+ expect(body.apiKey.expiresAt).toBeTruthy();
84
+ const expected = Date.now() + 7 * 86_400_000;
85
+ expect(Math.abs(new Date(body.apiKey.expiresAt).getTime() - expected)).toBeLessThan(5 * 60_000);
86
+
87
+ const list = await readJson(
88
+ await app.app.request('/api/v1/api-keys', { headers: { cookie: ownerCookie } })
89
+ );
90
+ const listed = list.apiKeys.find((k: { id: string }) => k.id === body.apiKey.id);
91
+ expect(listed?.expiresAt).toBe(body.apiKey.expiresAt);
92
+ });
93
+
94
+ it('omitting the TTL mints a never-expiring key', async () => {
95
+ const res = await mint({ name: 'forever', scopes: [host.scopes.read] });
96
+ expect(res.status).toBe(201);
97
+ const body = await readJson(res);
98
+ expect(body.apiKey.expiresAt).toBeNull();
99
+ });
100
+
101
+ it('rejects expiresInDays: 0 as a validation error', async () => {
102
+ const res = await mint({ name: 'zero-days', scopes: [host.scopes.read], expiresInDays: 0 });
103
+ expect(res.status).toBe(400);
104
+ const body = await readJson(res);
105
+ expect(body.error.code).toBe('validation_error');
106
+ });
107
+ });
108
+
109
+ describe('resolution treats expired exactly like revoked', () => {
110
+ it('answers an expired key with the identical 401 body a revoked key gets', async () => {
111
+ const expired = await readJson(
112
+ await mint({ name: 'to-expire', scopes: [host.scopes.read], expiresInDays: 7 })
113
+ );
114
+ const revoked = await readJson(await mint({ name: 'to-revoke', scopes: [host.scopes.read] }));
115
+
116
+ // Both keys are live before their terminal states.
117
+ expect((await me(expired.key)).status).toBe(200);
118
+ expect((await me(revoked.key)).status).toBe(200);
119
+
120
+ await app.db.pool.query(`UPDATE api_keys SET expires_at = now() - interval '1 hour' WHERE key_id = $1`, [
121
+ expired.apiKey.keyId
122
+ ]);
123
+ const revokeRes = await app.app.request(`/api/v1/api-keys/${revoked.apiKey.id}`, {
124
+ method: 'DELETE',
125
+ headers: { cookie: ownerCookie }
126
+ });
127
+ expect(revokeRes.status).toBe(200);
128
+
129
+ const expiredRes = await me(expired.key);
130
+ const revokedRes = await me(revoked.key);
131
+ expect(expiredRes.status).toBe(401);
132
+ expect(revokedRes.status).toBe(401);
133
+ // Same fail-closed path: the bodies must be indistinguishable.
134
+ expect(await readJson(expiredRes)).toEqual(await readJson(revokedRes));
135
+ });
136
+
137
+ it('a key expiring in the future still resolves and reports its expiry on /me', async () => {
138
+ const minted = await readJson(await mint({ name: 'boundary', scopes: [host.scopes.read] }));
139
+ await app.db.pool.query(`UPDATE api_keys SET expires_at = now() + interval '1 hour' WHERE key_id = $1`, [
140
+ minted.apiKey.keyId
141
+ ]);
142
+ const res = await me(minted.key);
143
+ expect(res.status).toBe(200);
144
+ const body = await readJson(res);
145
+ expect(body.apiKeyExpiresAt).toBeTruthy();
146
+ expect(new Date(body.apiKeyExpiresAt).toISOString()).toBe(body.apiKeyExpiresAt);
147
+ });
148
+ });
149
+
150
+ describe('nightly expiry sweep', () => {
151
+ it('normalizes expired keys to revoked_at = expires_at and leaves live keys alone', async () => {
152
+ const doomed = await readJson(
153
+ await mint({ name: 'sweep-doomed', scopes: [host.scopes.read], expiresInDays: 7 })
154
+ );
155
+ const survivor = await readJson(
156
+ await mint({ name: 'sweep-survivor', scopes: [host.scopes.read], expiresInDays: 7 })
157
+ );
158
+ await app.db.pool.query(`UPDATE api_keys SET expires_at = now() - interval '1 hour' WHERE key_id = $1`, [
159
+ doomed.apiKey.keyId
160
+ ]);
161
+
162
+ // Trigger the worker's sweep handler out of band (the schedule is 03:00).
163
+ await app.jobs.boss.send('apikey-expiry-sweep', {});
164
+
165
+ const deadline = Date.now() + 15_000;
166
+ let revokedAt: Date | null = null;
167
+ while (!revokedAt && Date.now() < deadline) {
168
+ await new Promise((r) => setTimeout(r, 250));
169
+ revokedAt = await revokedAtOf(doomed.apiKey.keyId);
170
+ }
171
+ const expiresAt = await expiresAtOf(doomed.apiKey.keyId);
172
+ expect(revokedAt).not.toBeNull();
173
+ expect(revokedAt!.getTime()).toBe(expiresAt!.getTime());
174
+
175
+ // The unexpired key is untouched.
176
+ expect(await revokedAtOf(survivor.apiKey.keyId)).toBeNull();
177
+ }, 30_000);
178
+ });
@@ -0,0 +1,190 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
3
+ import type { StartedPostgreSqlContainer } from '@testcontainers/postgresql';
4
+ import {
5
+ createDatabase,
6
+ createTestApp,
7
+ extractCookie,
8
+ readJson,
9
+ startPostgres,
10
+ type TestApp,
11
+ host
12
+ } from './helpers.js';
13
+
14
+ /**
15
+ * API-key pepper versioning (ADR 008), proven end to end across real reboots
16
+ * of the app against ONE database:
17
+ *
18
+ * 1. Zero-config backward compat — version 1 reproduces the historical
19
+ * computation byte-identically: sha256(secret + AUTH_SECRET) hex.
20
+ * 2. Rotation survivability — pin v1, add v2, then ROTATE AUTH_SECRET; keys
21
+ * minted under both versions keep authenticating.
22
+ * 3. Fail-closed — an unknown pepper_version answers the byte-identical 401
23
+ * a revoked key gets, and a v1 hash is never accepted against v2's
24
+ * pepper (no cross-version acceptance).
25
+ *
26
+ * Migration 0010 applies in this harness on every boot (backfill default 1).
27
+ */
28
+
29
+ // Test-only throwaway values (same convention as the shared helper's secret).
30
+ const SECRET_OLD = 'rotation-old-auth-secret-0123456789abcdef01234567';
31
+ const SECRET_NEW = 'rotation-new-auth-secret-fedcba9876543210fedcba98';
32
+ const PEPPER_V2 = 'rotation-pepper-v2-abcdef0123456789abcdef012345';
33
+
34
+ const OWNER = { email: 'owner@pepper.test', name: 'Pepper Owner', password: 'pepper-owner-pass-123' };
35
+
36
+ let container: StartedPostgreSqlContainer;
37
+ let connectionString: string;
38
+ let app: TestApp;
39
+
40
+ const json = (body: unknown) => ({
41
+ method: 'POST',
42
+ headers: { 'content-type': 'application/json' },
43
+ body: JSON.stringify(body)
44
+ });
45
+
46
+ async function signIn(): Promise<string> {
47
+ const res = await app.app.request(
48
+ '/api/v1/auth/sign-in/email',
49
+ json({ email: OWNER.email, password: OWNER.password })
50
+ );
51
+ expect(res.status).toBe(200);
52
+ return extractCookie(res);
53
+ }
54
+
55
+ async function mint(cookie: string, name: string) {
56
+ const res = await app.app.request('/api/v1/api-keys', {
57
+ ...json({ name, scopes: [host.scopes.read] }),
58
+ headers: { 'content-type': 'application/json', cookie }
59
+ });
60
+ expect(res.status).toBe(201);
61
+ return readJson(res);
62
+ }
63
+
64
+ async function me(key: string) {
65
+ return app.app.request('/api/v1/me', { headers: { authorization: `Bearer ${key}` } });
66
+ }
67
+
68
+ /**
69
+ * The secret is everything after `<prefix>_<keyId>_` — sliced by length, NEVER by
70
+ * splitting on `_` (base64url contains `_`; see LESSONS.md). keyId comes from
71
+ * the mint response.
72
+ */
73
+ function secretOf(minted: { key: string; apiKey: { keyId: string } }): string {
74
+ return minted.key.slice(`${host.identity.apiKeyPrefix}_${minted.apiKey.keyId}_`.length);
75
+ }
76
+
77
+ /** Today's (pre-versioning) computation, reproduced independently. */
78
+ function legacyHash(secret: string, pepper: string): string {
79
+ return createHash('sha256')
80
+ .update(secret + pepper)
81
+ .digest('hex');
82
+ }
83
+
84
+ async function keyRow(keyId: string): Promise<{ secret_hash: string; pepper_version: number }> {
85
+ const res = await app.db.pool.query<{ secret_hash: string; pepper_version: number }>(
86
+ 'SELECT secret_hash, pepper_version FROM api_keys WHERE key_id = $1',
87
+ [keyId]
88
+ );
89
+ expect(res.rows).toHaveLength(1);
90
+ return res.rows[0]!;
91
+ }
92
+
93
+ /** Stop the current app and boot a fresh one against the SAME database. */
94
+ async function reboot(extraEnv: Record<string, string>): Promise<void> {
95
+ await app.stop();
96
+ app = await createTestApp(connectionString, extraEnv);
97
+ }
98
+
99
+ beforeAll(async () => {
100
+ container = await startPostgres();
101
+ connectionString = await createDatabase(container, 'pepperrotation');
102
+ app = await createTestApp(connectionString, { AUTH_SECRET: SECRET_OLD });
103
+ await app.app.request(
104
+ '/api/v1/setup',
105
+ json({ setupToken: 'integration-test-setup-token', instanceName: 'Pepper', owner: OWNER })
106
+ );
107
+ }, 240_000);
108
+
109
+ afterAll(async () => {
110
+ await app?.stop();
111
+ await container?.stop();
112
+ });
113
+
114
+ // Shared across the sequential phases below (vitest runs this file in order).
115
+ let k1: { key: string; apiKey: { keyId: string } };
116
+ let k2: { key: string; apiKey: { keyId: string } };
117
+
118
+ describe('phase 1 — zero-config backward compatibility (no API_KEY_PEPPERS)', () => {
119
+ it('mints at pepper_version 1 and the hash reproduces sha256(secret + AUTH_SECRET) byte-identically', async () => {
120
+ k1 = await mint(await signIn(), 'k1-version-1');
121
+ const row = await keyRow(k1.apiKey.keyId);
122
+ expect(row.pepper_version).toBe(1);
123
+ // The backward-compat proof: version 1 IS the historical computation.
124
+ expect(row.secret_hash).toBe(legacyHash(secretOf(k1), SECRET_OLD));
125
+ });
126
+
127
+ it('the freshly minted key resolves', async () => {
128
+ expect((await me(k1.key)).status).toBe(200);
129
+ });
130
+ });
131
+
132
+ describe('phase 2 — introduce pepper v2 as current (AUTH_SECRET unchanged)', () => {
133
+ it('boots with API_KEY_PEPPERS=2:<pepper>; the v1 key still resolves', async () => {
134
+ await reboot({ AUTH_SECRET: SECRET_OLD, API_KEY_PEPPERS: `2:${PEPPER_V2}` });
135
+ expect((await me(k1.key)).status).toBe(200);
136
+ }, 120_000);
137
+
138
+ it('new keys mint at version 2, hashed under the v2 pepper', async () => {
139
+ k2 = await mint(await signIn(), 'k2-version-2');
140
+ const row = await keyRow(k2.apiKey.keyId);
141
+ expect(row.pepper_version).toBe(2);
142
+ expect(row.secret_hash).toBe(legacyHash(secretOf(k2), PEPPER_V2));
143
+ expect((await me(k2.key)).status).toBe(200);
144
+ });
145
+
146
+ it('no cross-version acceptance: a v1 hash presented against v2 is rejected', async () => {
147
+ // Point k1's row at v2 — its hash was computed under v1's pepper, so
148
+ // resolution (which uses the STORED version) must reject, not fall back.
149
+ await app.db.pool.query('UPDATE api_keys SET pepper_version = 2 WHERE key_id = $1', [k1.apiKey.keyId]);
150
+ expect((await me(k1.key)).status).toBe(401);
151
+ await app.db.pool.query('UPDATE api_keys SET pepper_version = 1 WHERE key_id = $1', [k1.apiKey.keyId]);
152
+ expect((await me(k1.key)).status).toBe(200);
153
+ });
154
+ });
155
+
156
+ describe('phase 3 — rotate AUTH_SECRET with v1 pinned (the runbook)', () => {
157
+ it('BOTH keys survive the rotation, each via its stored version', async () => {
158
+ await reboot({
159
+ AUTH_SECRET: SECRET_NEW, // rotated!
160
+ API_KEY_PEPPERS: `1:${SECRET_OLD};2:${PEPPER_V2}` // v1 pinned to the historical pepper
161
+ });
162
+ expect((await me(k1.key)).status).toBe(200); // v1 via the pinned pepper
163
+ expect((await me(k2.key)).status).toBe(200); // v2 via its own pepper
164
+ }, 120_000);
165
+
166
+ it('an unknown pepper_version fails closed with the byte-identical 401 of a revoked key', async () => {
167
+ // Sessions died with the AUTH_SECRET rotation (expected, out of scope) —
168
+ // a fresh password sign-in works and mints under the current version (2).
169
+ const cookie = await signIn();
170
+ const orphan = await mint(cookie, 'orphan-unknown-version');
171
+ const revoked = await mint(cookie, 'revoked-control');
172
+ expect((await keyRow(orphan.apiKey.keyId)).pepper_version).toBe(2);
173
+
174
+ await app.db.pool.query('UPDATE api_keys SET pepper_version = 9 WHERE key_id = $1', [
175
+ orphan.apiKey.keyId
176
+ ]);
177
+ const revokeRes = await app.app.request(`/api/v1/api-keys/${revoked.apiKey.id}`, {
178
+ method: 'DELETE',
179
+ headers: { cookie }
180
+ });
181
+ expect(revokeRes.status).toBe(200);
182
+
183
+ const orphanRes = await me(orphan.key);
184
+ const revokedRes = await me(revoked.key);
185
+ expect(orphanRes.status).toBe(401); // never a crash, never another pepper
186
+ expect(revokedRes.status).toBe(401);
187
+ // One fail-closed path: the raw bodies must be byte-identical.
188
+ expect(await orphanRes.text()).toBe(await revokedRes.text());
189
+ });
190
+ });
@@ -0,0 +1,169 @@
1
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
2
+ import type { StartedPostgreSqlContainer } from '@testcontainers/postgresql';
3
+ import { existsSync, readFileSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import {
6
+ createDatabase,
7
+ createTestApp,
8
+ extractCookie,
9
+ readJson,
10
+ startPostgres,
11
+ SETUP_TOKEN,
12
+ type TestApp
13
+ } from './helpers.js';
14
+
15
+ /**
16
+ * PRDCT-1357, the two members this lane added:
17
+ *
18
+ * - OPS-3: a GDPR erasure lands an append-only tombstone in the data
19
+ * volume (outside the dump); a boot that finds a tombstoned user present
20
+ * again — the restore-from-an-older-dump shape — re-erases them and
21
+ * audits the replay;
22
+ * - OPS-6: migration status is hash-based, so a database migrated by a
23
+ * NEWER image (a rollback, `:next` behind `:latest`) refuses readiness
24
+ * instead of reporting "current".
25
+ */
26
+
27
+ const OWNER = { email: 'owner@posture.test', name: 'Owner', password: 'owner-password-12345' };
28
+
29
+ const json = (body: unknown, headers: Record<string, string> = {}) => ({
30
+ method: 'POST',
31
+ headers: { 'content-type': 'application/json', ...headers },
32
+ body: JSON.stringify(body)
33
+ });
34
+
35
+ let container: StartedPostgreSqlContainer;
36
+
37
+ beforeAll(async () => {
38
+ container = await startPostgres();
39
+ }, 180_000);
40
+
41
+ afterAll(async () => {
42
+ await container?.stop();
43
+ });
44
+
45
+ describe('erasure tombstone (OPS-3)', () => {
46
+ let url: string;
47
+ let app: TestApp;
48
+ let dataDir: string;
49
+ let ownerCookie: string;
50
+ const VICTIM_ID = 'u-erased';
51
+
52
+ async function seedMember(a: TestApp, id: string, email: string): Promise<string> {
53
+ await a.db.pool.query(
54
+ `INSERT INTO "user" (id, name, email, email_verified, created_at, updated_at)
55
+ VALUES ($1, $2, $3, true, now(), now())`,
56
+ [id, email, email]
57
+ );
58
+ await a.db.pool.query(
59
+ `INSERT INTO account (id, account_id, provider_id, user_id, password, created_at, updated_at)
60
+ VALUES ($1, $2, 'credential', $3, 'hash', now(), now())`,
61
+ [`acc-${id}`, id, id]
62
+ );
63
+ const ws = await a.db.pool.query<{ id: string }>(`SELECT id FROM workspaces LIMIT 1`);
64
+ const m = await a.db.pool.query<{ id: string }>(
65
+ `INSERT INTO workspace_members (workspace_id, user_id, role, origin, is_active)
66
+ VALUES ($1, $2, 'member', 'local', true) RETURNING id`,
67
+ [ws.rows[0]!.id, id]
68
+ );
69
+ return m.rows[0]!.id;
70
+ }
71
+
72
+ beforeAll(async () => {
73
+ url = await createDatabase(container, 'posture_erasure');
74
+ app = await createTestApp(url);
75
+ dataDir = app.env.DATA_DIR;
76
+ const setup = await app.app.request(
77
+ '/api/v1/setup',
78
+ json({ setupToken: SETUP_TOKEN, instanceName: 'Posture', owner: OWNER })
79
+ );
80
+ expect(setup.status).toBe(201);
81
+ ownerCookie = extractCookie(
82
+ await app.app.request(
83
+ '/api/v1/auth/sign-in/email',
84
+ json({ email: OWNER.email, password: OWNER.password })
85
+ )
86
+ );
87
+ });
88
+
89
+ afterAll(async () => {
90
+ await app.stop();
91
+ });
92
+
93
+ it('an admin erasure appends a tombstone that names the user id and hashes (never carries) the email', async () => {
94
+ const memberId = await seedMember(app, VICTIM_ID, 'victim@posture.test');
95
+ const res = await app.app.request(`/api/v1/members/${memberId}`, {
96
+ method: 'DELETE',
97
+ headers: { cookie: ownerCookie }
98
+ });
99
+ expect(res.status).toBe(200);
100
+ const path = join(dataDir, 'erasures.jsonl');
101
+ expect(existsSync(path)).toBe(true);
102
+ const raw = readFileSync(path, 'utf8');
103
+ const lines = raw
104
+ .trim()
105
+ .split('\n')
106
+ .map((l) => JSON.parse(l) as { userId: string; emailHash: string; at: string });
107
+ expect(lines).toHaveLength(1);
108
+ expect(lines[0]!.userId).toBe(VICTIM_ID);
109
+ expect(lines[0]!.emailHash).toMatch(/^[0-9a-f]{64}$/);
110
+ expect(raw).not.toContain('victim@posture.test');
111
+ });
112
+
113
+ it('a boot that finds the erased user PRESENT again re-erases them and audits the replay', async () => {
114
+ // The restore-from-an-older-dump shape: the row (and their password) is back.
115
+ await seedMember(app, VICTIM_ID, 'victim@posture.test');
116
+ await app.stop();
117
+ app = await createTestApp(url, { DATA_DIR: dataDir });
118
+ const users = await app.db.pool.query(`SELECT 1 FROM "user" WHERE id = $1`, [VICTIM_ID]);
119
+ expect(users.rows).toHaveLength(0);
120
+ const members = await app.db.pool.query(`SELECT 1 FROM workspace_members WHERE user_id = $1`, [
121
+ VICTIM_ID
122
+ ]);
123
+ expect(members.rows).toHaveLength(0);
124
+ const audit = await app.db.pool.query<{ action: string; actor_via: string | null; resource_id: string }>(
125
+ `SELECT action, actor_via, resource_id FROM audit_log WHERE action = 'user.erasure_replayed'`
126
+ );
127
+ expect(audit.rows).toEqual([
128
+ { action: 'user.erasure_replayed', actor_via: 'system', resource_id: VICTIM_ID }
129
+ ]);
130
+ });
131
+
132
+ it('a boot with nothing to replay replays nothing (idempotent) and the login still works', async () => {
133
+ await app.stop();
134
+ app = await createTestApp(url, { DATA_DIR: dataDir });
135
+ const audit = await app.db.pool.query(`SELECT 1 FROM audit_log WHERE action = 'user.erasure_replayed'`);
136
+ expect(audit.rows).toHaveLength(1);
137
+ const signIn = await app.app.request(
138
+ '/api/v1/auth/sign-in/email',
139
+ json({ email: OWNER.email, password: OWNER.password })
140
+ );
141
+ expect(signIn.status).toBe(200);
142
+ });
143
+ });
144
+
145
+ describe('hash-based migration status (OPS-6)', () => {
146
+ it('a database carrying a migration this image does not ship refuses readiness (a downgrade), instead of "current"', async () => {
147
+ const url = await createDatabase(container, 'posture_downgrade');
148
+ const app = await createTestApp(url);
149
+ // What a NEWER image leaves behind: an applied hash no file here produces.
150
+ await app.db.pool.query(`INSERT INTO drizzle.__drizzle_migrations (hash, created_at) VALUES ($1, $2)`, [
151
+ 'f'.repeat(64),
152
+ Date.now()
153
+ ]);
154
+ await app.stop();
155
+
156
+ const older = await createTestApp(url);
157
+ try {
158
+ expect(older.state.ready).toBe(false);
159
+ expect(older.state.reason).toMatch(/AHEAD of this image/);
160
+ const res = await older.app.request('/readyz');
161
+ expect(res.status).toBe(503);
162
+ const body = await readJson(res);
163
+ expect(body.status).toBe('unavailable');
164
+ expect(body.reason).toMatch(/AHEAD of this image/);
165
+ } finally {
166
+ await older.stop();
167
+ }
168
+ });
169
+ });