tapimo 0.15.0 → 0.18.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 (734) hide show
  1. package/README.md +137 -0
  2. package/dist/ApiKey.d.ts +1 -0
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +4 -2
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +37 -11
  7. package/dist/ApiKeys.d.ts.map +1 -1
  8. package/dist/ApiKeys.js +163 -10
  9. package/dist/ApiKeys.js.map +1 -1
  10. package/dist/App.d.ts +13085 -11974
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +46 -15
  13. package/dist/App.js.map +1 -1
  14. package/dist/Scope.d.ts +1 -1
  15. package/dist/Scope.d.ts.map +1 -1
  16. package/dist/Scope.js +1 -1
  17. package/dist/Scope.js.map +1 -1
  18. package/dist/admin/App.d.ts +896 -191
  19. package/dist/admin/App.d.ts.map +1 -1
  20. package/dist/admin/App.js +5 -1
  21. package/dist/admin/App.js.map +1 -1
  22. package/dist/admin/apps/api-keys.d.ts +33 -33
  23. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  24. package/dist/admin/apps/api-keys.js +8 -3
  25. package/dist/admin/apps/api-keys.js.map +1 -1
  26. package/dist/admin/apps/earn-vaults.d.ts +66 -27
  27. package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
  28. package/dist/admin/apps/earn-vaults.js +36 -7
  29. package/dist/admin/apps/earn-vaults.js.map +1 -1
  30. package/dist/admin/apps/organizations.d.ts +25 -25
  31. package/dist/admin/apps/organizations.d.ts.map +1 -1
  32. package/dist/admin/apps/organizations.js +5 -4
  33. package/dist/admin/apps/organizations.js.map +1 -1
  34. package/dist/admin/apps/route-policies.d.ts +632 -0
  35. package/dist/admin/apps/route-policies.d.ts.map +1 -0
  36. package/dist/admin/apps/route-policies.js +257 -0
  37. package/dist/admin/apps/route-policies.js.map +1 -0
  38. package/dist/admin/apps/routes-support.js +39 -3
  39. package/dist/admin/apps/routes-support.js.map +1 -1
  40. package/dist/admin/apps/routes.d.ts +454 -1
  41. package/dist/admin/apps/routes.d.ts.map +1 -1
  42. package/dist/admin/apps/routes.js +82 -1
  43. package/dist/admin/apps/routes.js.map +1 -1
  44. package/dist/admin/apps/verified-tokens.d.ts +10 -4
  45. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  46. package/dist/admin/apps/verified-tokens.js +24 -4
  47. package/dist/admin/apps/verified-tokens.js.map +1 -1
  48. package/dist/admin/index.d.ts +0 -1
  49. package/dist/admin/index.d.ts.map +1 -1
  50. package/dist/admin/index.js +0 -1
  51. package/dist/admin/index.js.map +1 -1
  52. package/dist/admin/ui.generated.d.ts +1 -1
  53. package/dist/admin/ui.generated.d.ts.map +1 -1
  54. package/dist/admin/ui.generated.js +99 -103
  55. package/dist/admin/ui.generated.js.map +1 -1
  56. package/dist/analytics/Analytics.d.ts.map +1 -1
  57. package/dist/analytics/Analytics.js +65 -1
  58. package/dist/analytics/Analytics.js.map +1 -1
  59. package/dist/analytics/tables/requestEvents.d.ts +1 -1
  60. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  61. package/dist/analytics/tables/requestEvents.js +58 -59
  62. package/dist/analytics/tables/requestEvents.js.map +1 -1
  63. package/dist/apps/data/App.d.ts +2709 -2670
  64. package/dist/apps/data/App.d.ts.map +1 -1
  65. package/dist/apps/data/routes/activities.d.ts +5 -5
  66. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  67. package/dist/apps/data/routes/activities.js +50 -0
  68. package/dist/apps/data/routes/activities.js.map +1 -1
  69. package/dist/apps/data/routes/addresses.d.ts +248 -248
  70. package/dist/apps/data/routes/balances.d.ts +22 -22
  71. package/dist/apps/data/routes/balances.d.ts.map +1 -1
  72. package/dist/apps/data/routes/balances.js +5 -7
  73. package/dist/apps/data/routes/balances.js.map +1 -1
  74. package/dist/apps/data/routes/coingecko.d.ts +54 -54
  75. package/dist/apps/data/routes/earn.d.ts +103 -12
  76. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  77. package/dist/apps/data/routes/earn.js +456 -220
  78. package/dist/apps/data/routes/earn.js.map +1 -1
  79. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  80. package/dist/apps/data/routes/exchanges.js +267 -97
  81. package/dist/apps/data/routes/exchanges.js.map +1 -1
  82. package/dist/apps/data/routes/fee-amm.d.ts +1 -19
  83. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  84. package/dist/apps/data/routes/fee-amm.js +93 -56
  85. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  86. package/dist/apps/data/routes/indexer.d.ts +10 -10
  87. package/dist/apps/data/routes/rpc.d.ts +30 -30
  88. package/dist/apps/data/routes/rpc.js +45 -11
  89. package/dist/apps/data/routes/rpc.js.map +1 -1
  90. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  91. package/dist/apps/data/routes/tokens.js +11 -6
  92. package/dist/apps/data/routes/tokens.js.map +1 -1
  93. package/dist/apps/data/routes/transactions.d.ts +2 -0
  94. package/dist/apps/data/routes/transactions.d.ts.map +1 -1
  95. package/dist/apps/data/routes/transactions.js +30 -22
  96. package/dist/apps/data/routes/transactions.js.map +1 -1
  97. package/dist/apps/data/routes/transfers.d.ts +12 -12
  98. package/dist/apps/data/routes/valuation.d.ts +30 -30
  99. package/dist/apps/data/routes/verified-tokens.d.ts +5 -5
  100. package/dist/apps/data/routes/webhooks.d.ts +1 -1
  101. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  102. package/dist/apps/data/routes/webhooks.js +18 -11
  103. package/dist/apps/data/routes/webhooks.js.map +1 -1
  104. package/dist/apps/data/routes/zones.d.ts +3 -3
  105. package/dist/apps/management/App.d.ts +3043 -2198
  106. package/dist/apps/management/App.d.ts.map +1 -1
  107. package/dist/apps/management/App.js +2 -0
  108. package/dist/apps/management/App.js.map +1 -1
  109. package/dist/apps/management/Billing.d.ts +2 -4
  110. package/dist/apps/management/Billing.d.ts.map +1 -1
  111. package/dist/apps/management/Billing.js +65 -26
  112. package/dist/apps/management/Billing.js.map +1 -1
  113. package/dist/apps/management/routes/api-keys.d.ts +16 -4
  114. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  115. package/dist/apps/management/routes/api-keys.js +70 -11
  116. package/dist/apps/management/routes/api-keys.js.map +1 -1
  117. package/dist/apps/management/routes/billing.d.ts +84 -84
  118. package/dist/apps/management/routes/cli.d.ts +912 -0
  119. package/dist/apps/management/routes/cli.d.ts.map +1 -0
  120. package/dist/apps/management/routes/cli.js +416 -0
  121. package/dist/apps/management/routes/cli.js.map +1 -0
  122. package/dist/apps/management/routes/faucet.d.ts +8 -8
  123. package/dist/apps/management/routes/invitations.d.ts +24 -24
  124. package/dist/apps/management/routes/invite-links.d.ts +66 -66
  125. package/dist/apps/management/routes/members.d.ts +16 -16
  126. package/dist/apps/management/routes/orgs.d.ts +20 -20
  127. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  128. package/dist/apps/management/routes/orgs.js +8 -2
  129. package/dist/apps/management/routes/orgs.js.map +1 -1
  130. package/dist/apps/management/routes/projects.d.ts +76 -76
  131. package/dist/apps/management/routes/usage.d.ts +8 -8
  132. package/dist/apps/management/routes/verified-token-requests.d.ts +42 -42
  133. package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -1
  134. package/dist/apps/management/routes/verified-token-requests.js +7 -9
  135. package/dist/apps/management/routes/verified-token-requests.js.map +1 -1
  136. package/dist/apps/management/routes/webhooks.d.ts +119 -119
  137. package/dist/apps/mcp.d.ts.map +1 -1
  138. package/dist/apps/mcp.js +16 -3
  139. package/dist/apps/mcp.js.map +1 -1
  140. package/dist/apps/mpp/internal/Policy.d.ts.map +1 -1
  141. package/dist/apps/mpp/internal/Policy.js +4 -2
  142. package/dist/apps/mpp/internal/Policy.js.map +1 -1
  143. package/dist/apps/mpp/internal/Sponsorship.d.ts.map +1 -1
  144. package/dist/apps/mpp/internal/Sponsorship.js +6 -4
  145. package/dist/apps/mpp/internal/Sponsorship.js.map +1 -1
  146. package/dist/apps/relay/App.d.ts +8 -0
  147. package/dist/apps/relay/App.d.ts.map +1 -1
  148. package/dist/apps/relay/App.js +16 -4
  149. package/dist/apps/relay/App.js.map +1 -1
  150. package/dist/apps/relay/Sponsorships.d.ts +4 -6
  151. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  152. package/dist/apps/relay/Sponsorships.js +71 -60
  153. package/dist/apps/relay/Sponsorships.js.map +1 -1
  154. package/dist/apps/routes/Access.d.ts +53 -0
  155. package/dist/apps/routes/Access.d.ts.map +1 -1
  156. package/dist/apps/routes/Access.js +121 -0
  157. package/dist/apps/routes/Access.js.map +1 -1
  158. package/dist/apps/routes/App.d.ts +397 -158
  159. package/dist/apps/routes/App.d.ts.map +1 -1
  160. package/dist/apps/routes/App.js.map +1 -1
  161. package/dist/apps/routes/routes/deposit-addresses.d.ts +169 -61
  162. package/dist/apps/routes/routes/deposit-addresses.d.ts.map +1 -1
  163. package/dist/apps/routes/routes/deposit-addresses.js +217 -32
  164. package/dist/apps/routes/routes/deposit-addresses.js.map +1 -1
  165. package/dist/apps/routes/routes/quotes.d.ts +90 -0
  166. package/dist/apps/routes/routes/quotes.d.ts.map +1 -1
  167. package/dist/apps/routes/routes/quotes.js +101 -27
  168. package/dist/apps/routes/routes/quotes.js.map +1 -1
  169. package/dist/apps/routes/routes/transfers.d.ts +187 -135
  170. package/dist/apps/routes/routes/transfers.d.ts.map +1 -1
  171. package/dist/apps/routes/routes/transfers.js +182 -37
  172. package/dist/apps/routes/routes/transfers.js.map +1 -1
  173. package/dist/cli/ApiKeys.d.ts +120 -0
  174. package/dist/cli/ApiKeys.d.ts.map +1 -0
  175. package/dist/cli/ApiKeys.js +138 -0
  176. package/dist/cli/ApiKeys.js.map +1 -0
  177. package/dist/cli/Cli.d.ts +92 -34
  178. package/dist/cli/Cli.d.ts.map +1 -1
  179. package/dist/cli/Cli.js +188 -11
  180. package/dist/cli/Cli.js.map +1 -1
  181. package/dist/cli/Credentials.d.ts +95 -0
  182. package/dist/cli/Credentials.d.ts.map +1 -0
  183. package/dist/cli/Credentials.js +110 -0
  184. package/dist/cli/Credentials.js.map +1 -0
  185. package/dist/cli/Spec.d.ts +59 -0
  186. package/dist/cli/Spec.d.ts.map +1 -0
  187. package/dist/cli/Spec.js +77 -0
  188. package/dist/cli/Spec.js.map +1 -0
  189. package/dist/cli/bin.js +5 -1
  190. package/dist/cli/bin.js.map +1 -1
  191. package/dist/cli/commands/admin.d.ts +5 -5
  192. package/dist/cli/commands/admin.d.ts.map +1 -1
  193. package/dist/cli/commands/auth.d.ts +73 -0
  194. package/dist/cli/commands/auth.d.ts.map +1 -0
  195. package/dist/cli/commands/auth.js +204 -0
  196. package/dist/cli/commands/auth.js.map +1 -0
  197. package/dist/cli/commands/keys.d.ts +17 -0
  198. package/dist/cli/commands/keys.d.ts.map +1 -0
  199. package/dist/cli/commands/keys.js +109 -0
  200. package/dist/cli/commands/keys.js.map +1 -0
  201. package/dist/cloudflare.d.ts +27 -2
  202. package/dist/cloudflare.d.ts.map +1 -1
  203. package/dist/cloudflare.js +144 -17
  204. package/dist/cloudflare.js.map +1 -1
  205. package/dist/db/Db.d.ts +23 -0
  206. package/dist/db/Db.d.ts.map +1 -1
  207. package/dist/db/Db.js +418 -1
  208. package/dist/db/Db.js.map +1 -1
  209. package/dist/db/Schema.d.ts +44 -1
  210. package/dist/db/Schema.d.ts.map +1 -1
  211. package/dist/db/Schema.js +56 -3
  212. package/dist/db/Schema.js.map +1 -1
  213. package/dist/db/tables/apiKeyAdmissions.d.ts +28 -2
  214. package/dist/db/tables/apiKeyAdmissions.d.ts.map +1 -1
  215. package/dist/db/tables/apiKeyAdmissions.js +65 -9
  216. package/dist/db/tables/apiKeyAdmissions.js.map +1 -1
  217. package/dist/db/tables/apiKeyOwnerTombstones.d.ts +2 -0
  218. package/dist/db/tables/apiKeyOwnerTombstones.d.ts.map +1 -1
  219. package/dist/db/tables/apiKeyOwnerTombstones.js +2 -2
  220. package/dist/db/tables/apiKeyOwnerTombstones.js.map +1 -1
  221. package/dist/db/tables/authAccounts.d.ts.map +1 -1
  222. package/dist/db/tables/authAccounts.js +1 -0
  223. package/dist/db/tables/authAccounts.js.map +1 -1
  224. package/dist/db/tables/earnVaults.d.ts +5 -1
  225. package/dist/db/tables/earnVaults.d.ts.map +1 -1
  226. package/dist/db/tables/earnVaults.js +12 -1
  227. package/dist/db/tables/earnVaults.js.map +1 -1
  228. package/dist/db/tables/organizations.d.ts +4 -2
  229. package/dist/db/tables/organizations.d.ts.map +1 -1
  230. package/dist/db/tables/organizations.js +8 -34
  231. package/dist/db/tables/organizations.js.map +1 -1
  232. package/dist/db/tables/requestUsage.d.ts +5 -1
  233. package/dist/db/tables/requestUsage.d.ts.map +1 -1
  234. package/dist/db/tables/requestUsage.js +10 -2
  235. package/dist/db/tables/requestUsage.js.map +1 -1
  236. package/dist/db/tables/rewardEligibilityAssociations.d.ts +13 -0
  237. package/dist/db/tables/rewardEligibilityAssociations.d.ts.map +1 -1
  238. package/dist/db/tables/rewardEligibilityAssociations.js +11 -0
  239. package/dist/db/tables/rewardEligibilityAssociations.js.map +1 -1
  240. package/dist/db/tables/rewardRuns.d.ts.map +1 -1
  241. package/dist/db/tables/rewardRuns.js +4 -0
  242. package/dist/db/tables/rewardRuns.js.map +1 -1
  243. package/dist/db/tables/rewardTransactionAttempts.d.ts.map +1 -1
  244. package/dist/db/tables/rewardTransactionAttempts.js +2 -0
  245. package/dist/db/tables/rewardTransactionAttempts.js.map +1 -1
  246. package/dist/db/tables/routesDepositAddresses.d.ts +29 -0
  247. package/dist/db/tables/routesDepositAddresses.d.ts.map +1 -1
  248. package/dist/db/tables/routesDepositAddresses.js +84 -31
  249. package/dist/db/tables/routesDepositAddresses.js.map +1 -1
  250. package/dist/db/tables/routesDeposits.d.ts.map +1 -1
  251. package/dist/db/tables/routesDeposits.js +31 -21
  252. package/dist/db/tables/routesDeposits.js.map +1 -1
  253. package/dist/db/tables/routesIdempotency.d.ts.map +1 -1
  254. package/dist/db/tables/routesIdempotency.js +2 -0
  255. package/dist/db/tables/routesIdempotency.js.map +1 -1
  256. package/dist/db/tables/routesPolicies.d.ts +58 -0
  257. package/dist/db/tables/routesPolicies.d.ts.map +1 -0
  258. package/dist/db/tables/routesPolicies.js +198 -0
  259. package/dist/db/tables/routesPolicies.js.map +1 -0
  260. package/dist/db/tables/routesTransferSubsidies.d.ts +22 -0
  261. package/dist/db/tables/routesTransferSubsidies.d.ts.map +1 -1
  262. package/dist/db/tables/routesTransferSubsidies.js +41 -0
  263. package/dist/db/tables/routesTransferSubsidies.js.map +1 -1
  264. package/dist/db/tables/routesTransferSubsidyNonces.d.ts.map +1 -1
  265. package/dist/db/tables/routesTransferSubsidyNonces.js +12 -4
  266. package/dist/db/tables/routesTransferSubsidyNonces.js.map +1 -1
  267. package/dist/db/tables/routesTransfers.d.ts +26 -1
  268. package/dist/db/tables/routesTransfers.d.ts.map +1 -1
  269. package/dist/db/tables/routesTransfers.js +36 -6
  270. package/dist/db/tables/routesTransfers.js.map +1 -1
  271. package/dist/db/tables/sponsoredTransactions.d.ts +45 -13
  272. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  273. package/dist/db/tables/sponsoredTransactions.js +144 -33
  274. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  275. package/dist/db/tables/users.d.ts +2 -0
  276. package/dist/db/tables/users.d.ts.map +1 -1
  277. package/dist/db/tables/users.js +11 -0
  278. package/dist/db/tables/users.js.map +1 -1
  279. package/dist/db/tables/verifiedTokenRequests.d.ts +21 -4
  280. package/dist/db/tables/verifiedTokenRequests.d.ts.map +1 -1
  281. package/dist/db/tables/verifiedTokenRequests.js +65 -44
  282. package/dist/db/tables/verifiedTokenRequests.js.map +1 -1
  283. package/dist/handlers/internal/multisig.d.ts +1 -1
  284. package/dist/handlers/internal/multisig.d.ts.map +1 -1
  285. package/dist/handlers/internal/multisig.js +3 -2
  286. package/dist/handlers/internal/multisig.js.map +1 -1
  287. package/dist/handlers/relay.d.ts +2 -0
  288. package/dist/handlers/relay.d.ts.map +1 -1
  289. package/dist/handlers/relay.js +34 -15
  290. package/dist/handlers/relay.js.map +1 -1
  291. package/dist/internal/Auth.d.ts +73 -6
  292. package/dist/internal/Auth.d.ts.map +1 -1
  293. package/dist/internal/Auth.js +212 -85
  294. package/dist/internal/Auth.js.map +1 -1
  295. package/dist/internal/BetterAuth.d.ts +22 -0
  296. package/dist/internal/BetterAuth.d.ts.map +1 -1
  297. package/dist/internal/BetterAuth.js +17 -14
  298. package/dist/internal/BetterAuth.js.map +1 -1
  299. package/dist/internal/Cache.d.ts +5 -0
  300. package/dist/internal/Cache.d.ts.map +1 -1
  301. package/dist/internal/Cache.js +3 -1
  302. package/dist/internal/Cache.js.map +1 -1
  303. package/dist/internal/CliAuth.d.ts +243 -0
  304. package/dist/internal/CliAuth.d.ts.map +1 -0
  305. package/dist/internal/CliAuth.js +249 -0
  306. package/dist/internal/CliAuth.js.map +1 -0
  307. package/dist/internal/EarnSharePrices.d.ts +1 -1
  308. package/dist/internal/EarnSharePrices.d.ts.map +1 -1
  309. package/dist/internal/EarnSharePrices.js +7 -3
  310. package/dist/internal/EarnSharePrices.js.map +1 -1
  311. package/dist/internal/EarnVaults.d.ts +47 -0
  312. package/dist/internal/EarnVaults.d.ts.map +1 -1
  313. package/dist/internal/EarnVaults.js +188 -4
  314. package/dist/internal/EarnVaults.js.map +1 -1
  315. package/dist/internal/EarnWithdrawals.d.ts +73 -0
  316. package/dist/internal/EarnWithdrawals.d.ts.map +1 -0
  317. package/dist/internal/EarnWithdrawals.js +189 -0
  318. package/dist/internal/EarnWithdrawals.js.map +1 -0
  319. package/dist/internal/ExecutionContext.d.ts +21 -0
  320. package/dist/internal/ExecutionContext.d.ts.map +1 -0
  321. package/dist/internal/ExecutionContext.js +16 -0
  322. package/dist/internal/ExecutionContext.js.map +1 -0
  323. package/dist/internal/Log.d.ts +7 -3
  324. package/dist/internal/Log.d.ts.map +1 -1
  325. package/dist/internal/Log.js +163 -130
  326. package/dist/internal/Log.js.map +1 -1
  327. package/dist/internal/MetricSink.d.ts.map +1 -1
  328. package/dist/internal/MetricSink.js +26 -1
  329. package/dist/internal/MetricSink.js.map +1 -1
  330. package/dist/internal/RateLimit.d.ts +67 -0
  331. package/dist/internal/RateLimit.d.ts.map +1 -1
  332. package/dist/internal/RateLimit.js +174 -27
  333. package/dist/internal/RateLimit.js.map +1 -1
  334. package/dist/internal/Store.d.ts +6 -0
  335. package/dist/internal/Store.d.ts.map +1 -1
  336. package/dist/internal/Store.js +4 -5
  337. package/dist/internal/Store.js.map +1 -1
  338. package/dist/internal/Timing.d.ts +6 -0
  339. package/dist/internal/Timing.d.ts.map +1 -1
  340. package/dist/internal/Timing.js +21 -1
  341. package/dist/internal/Timing.js.map +1 -1
  342. package/dist/internal/Tron.d.ts +124 -0
  343. package/dist/internal/Tron.d.ts.map +1 -0
  344. package/dist/internal/Tron.js +202 -0
  345. package/dist/internal/Tron.js.map +1 -0
  346. package/dist/internal/Viem.d.ts +4 -0
  347. package/dist/internal/Viem.d.ts.map +1 -1
  348. package/dist/internal/Viem.js +25 -6
  349. package/dist/internal/Viem.js.map +1 -1
  350. package/dist/internal/rewards/Read.d.ts +2 -2
  351. package/dist/internal/rewards/Read.d.ts.map +1 -1
  352. package/dist/internal/rewards/Read.js +11 -2
  353. package/dist/internal/rewards/Read.js.map +1 -1
  354. package/dist/internal/routes/Action.d.ts +27 -0
  355. package/dist/internal/routes/Action.d.ts.map +1 -1
  356. package/dist/internal/routes/Action.js +49 -1
  357. package/dist/internal/routes/Action.js.map +1 -1
  358. package/dist/internal/routes/Chain.d.ts +2 -0
  359. package/dist/internal/routes/Chain.d.ts.map +1 -1
  360. package/dist/internal/routes/Chain.js +4 -0
  361. package/dist/internal/routes/Chain.js.map +1 -1
  362. package/dist/internal/routes/DepositAddress.d.ts.map +1 -1
  363. package/dist/internal/routes/DepositAddress.js +5 -0
  364. package/dist/internal/routes/DepositAddress.js.map +1 -1
  365. package/dist/internal/routes/Evidence.d.ts.map +1 -1
  366. package/dist/internal/routes/Evidence.js +6 -2
  367. package/dist/internal/routes/Evidence.js.map +1 -1
  368. package/dist/internal/routes/EvmTransferSettlement.d.ts +17 -0
  369. package/dist/internal/routes/EvmTransferSettlement.d.ts.map +1 -0
  370. package/dist/internal/routes/EvmTransferSettlement.js +196 -0
  371. package/dist/internal/routes/EvmTransferSettlement.js.map +1 -0
  372. package/dist/internal/routes/Policy.d.ts +161 -0
  373. package/dist/internal/routes/Policy.d.ts.map +1 -0
  374. package/dist/internal/routes/Policy.js +249 -0
  375. package/dist/internal/routes/Policy.js.map +1 -0
  376. package/dist/internal/routes/Provider.d.ts +66 -3
  377. package/dist/internal/routes/Provider.d.ts.map +1 -1
  378. package/dist/internal/routes/Provider.js +106 -27
  379. package/dist/internal/routes/Provider.js.map +1 -1
  380. package/dist/internal/routes/Quota.d.ts +57 -0
  381. package/dist/internal/routes/Quota.d.ts.map +1 -0
  382. package/dist/internal/routes/Quota.js +144 -0
  383. package/dist/internal/routes/Quota.js.map +1 -0
  384. package/dist/internal/routes/Reconciliation.d.ts.map +1 -1
  385. package/dist/internal/routes/Reconciliation.js +163 -47
  386. package/dist/internal/routes/Reconciliation.js.map +1 -1
  387. package/dist/internal/routes/SolanaTransferSettlement.d.ts +12 -0
  388. package/dist/internal/routes/SolanaTransferSettlement.d.ts.map +1 -0
  389. package/dist/internal/routes/SolanaTransferSettlement.js +220 -0
  390. package/dist/internal/routes/SolanaTransferSettlement.js.map +1 -0
  391. package/dist/internal/routes/SolanaTransferSubsidy.d.ts +172 -0
  392. package/dist/internal/routes/SolanaTransferSubsidy.d.ts.map +1 -0
  393. package/dist/internal/routes/SolanaTransferSubsidy.js +564 -0
  394. package/dist/internal/routes/SolanaTransferSubsidy.js.map +1 -0
  395. package/dist/internal/routes/Subsidy.d.ts +2 -2
  396. package/dist/internal/routes/Subsidy.d.ts.map +1 -1
  397. package/dist/internal/routes/Subsidy.js +2 -2
  398. package/dist/internal/routes/Subsidy.js.map +1 -1
  399. package/dist/internal/routes/Transfer.d.ts +229 -1
  400. package/dist/internal/routes/Transfer.d.ts.map +1 -1
  401. package/dist/internal/routes/Transfer.js +644 -7
  402. package/dist/internal/routes/Transfer.js.map +1 -1
  403. package/dist/internal/routes/TransferReconciliation.d.ts +6 -0
  404. package/dist/internal/routes/TransferReconciliation.d.ts.map +1 -1
  405. package/dist/internal/routes/TransferReconciliation.js +83 -159
  406. package/dist/internal/routes/TransferReconciliation.js.map +1 -1
  407. package/dist/internal/routes/TransferSettlement.d.ts +45 -0
  408. package/dist/internal/routes/TransferSettlement.d.ts.map +1 -0
  409. package/dist/internal/routes/TransferSettlement.js +2 -0
  410. package/dist/internal/routes/TransferSettlement.js.map +1 -0
  411. package/dist/internal/routes/TransferSubsidy.d.ts +23 -8
  412. package/dist/internal/routes/TransferSubsidy.d.ts.map +1 -1
  413. package/dist/internal/routes/TransferSubsidy.js +64 -5
  414. package/dist/internal/routes/TransferSubsidy.js.map +1 -1
  415. package/dist/internal/routes/TronTransferSettlement.d.ts +12 -0
  416. package/dist/internal/routes/TronTransferSettlement.d.ts.map +1 -0
  417. package/dist/internal/routes/TronTransferSettlement.js +201 -0
  418. package/dist/internal/routes/TronTransferSettlement.js.map +1 -0
  419. package/dist/internal/routes/TronTransferSubsidy.d.ts +174 -0
  420. package/dist/internal/routes/TronTransferSubsidy.d.ts.map +1 -0
  421. package/dist/internal/routes/TronTransferSubsidy.js +439 -0
  422. package/dist/internal/routes/TronTransferSubsidy.js.map +1 -0
  423. package/dist/internal/routes/index.d.ts +3 -0
  424. package/dist/internal/routes/index.d.ts.map +1 -1
  425. package/dist/internal/routes/index.js +3 -0
  426. package/dist/internal/routes/index.js.map +1 -1
  427. package/dist/internal/routes/providers/across.d.ts +3 -0
  428. package/dist/internal/routes/providers/across.d.ts.map +1 -1
  429. package/dist/internal/routes/providers/across.js +11 -0
  430. package/dist/internal/routes/providers/across.js.map +1 -1
  431. package/dist/internal/routes/providers/legacyMesh.d.ts +29 -0
  432. package/dist/internal/routes/providers/legacyMesh.d.ts.map +1 -0
  433. package/dist/internal/routes/providers/legacyMesh.js +474 -0
  434. package/dist/internal/routes/providers/legacyMesh.js.map +1 -0
  435. package/dist/internal/routes/providers/legacyMeshDelivery.d.ts +59 -0
  436. package/dist/internal/routes/providers/legacyMeshDelivery.d.ts.map +1 -0
  437. package/dist/internal/routes/providers/legacyMeshDelivery.js +229 -0
  438. package/dist/internal/routes/providers/legacyMeshDelivery.js.map +1 -0
  439. package/dist/internal/routes/providers/legacyMeshOutbound.d.ts +19 -0
  440. package/dist/internal/routes/providers/legacyMeshOutbound.d.ts.map +1 -0
  441. package/dist/internal/routes/providers/legacyMeshOutbound.js +457 -0
  442. package/dist/internal/routes/providers/legacyMeshOutbound.js.map +1 -0
  443. package/dist/internal/routes/providers/legacyMeshOutboundDelivery.d.ts +123 -0
  444. package/dist/internal/routes/providers/legacyMeshOutboundDelivery.d.ts.map +1 -0
  445. package/dist/internal/routes/providers/legacyMeshOutboundDelivery.js +333 -0
  446. package/dist/internal/routes/providers/legacyMeshOutboundDelivery.js.map +1 -0
  447. package/dist/internal/routes/providers/propAmm.d.ts +70 -0
  448. package/dist/internal/routes/providers/propAmm.d.ts.map +1 -0
  449. package/dist/internal/routes/providers/propAmm.js +524 -0
  450. package/dist/internal/routes/providers/propAmm.js.map +1 -0
  451. package/dist/internal/routes/providers/relay.d.ts +4 -1
  452. package/dist/internal/routes/providers/relay.d.ts.map +1 -1
  453. package/dist/internal/routes/providers/relay.js +353 -42
  454. package/dist/internal/routes/providers/relay.js.map +1 -1
  455. package/dist/internal/routes/providers/rhino.d.ts +3 -0
  456. package/dist/internal/routes/providers/rhino.d.ts.map +1 -1
  457. package/dist/internal/routes/providers/rhino.js +11 -0
  458. package/dist/internal/routes/providers/rhino.js.map +1 -1
  459. package/dist/internal/routes/providers/squid.d.ts +3 -0
  460. package/dist/internal/routes/providers/squid.d.ts.map +1 -1
  461. package/dist/internal/routes/providers/squid.js +19 -0
  462. package/dist/internal/routes/providers/squid.js.map +1 -1
  463. package/dist/internal/routes/providers/stargate.d.ts.map +1 -1
  464. package/dist/internal/routes/providers/stargate.js +22 -9
  465. package/dist/internal/routes/providers/stargate.js.map +1 -1
  466. package/dist/internal/routes/providers/symbiosis.d.ts.map +1 -1
  467. package/dist/internal/routes/providers/symbiosis.js +13 -17
  468. package/dist/internal/routes/providers/symbiosis.js.map +1 -1
  469. package/dist/providers/index.d.ts +14 -0
  470. package/dist/providers/index.d.ts.map +1 -1
  471. package/dist/providers/index.js +6 -0
  472. package/dist/providers/index.js.map +1 -1
  473. package/package.json +16 -6
  474. package/src/ApiKey.ts +7 -3
  475. package/src/ApiKeys.test.ts +652 -8
  476. package/src/ApiKeys.ts +191 -11
  477. package/src/App.test.ts +405 -18
  478. package/src/App.ts +51 -15
  479. package/src/Client.test-d.ts +157 -10
  480. package/src/Client.test.ts +4 -4
  481. package/src/Scope.ts +1 -2
  482. package/src/admin/App.ts +11 -5
  483. package/src/admin/apps/api-keys.test.ts +98 -2
  484. package/src/admin/apps/api-keys.ts +21 -12
  485. package/src/admin/apps/earn-vaults.test.ts +76 -7
  486. package/src/admin/apps/earn-vaults.ts +47 -8
  487. package/src/admin/apps/organizations.test.ts +147 -31
  488. package/src/admin/apps/organizations.ts +5 -4
  489. package/src/admin/apps/route-policies.test.ts +220 -0
  490. package/src/admin/apps/route-policies.ts +322 -0
  491. package/src/admin/apps/routes-support.test.ts +107 -2
  492. package/src/admin/apps/routes-support.ts +43 -3
  493. package/src/admin/apps/routes.test.ts +150 -0
  494. package/src/admin/apps/routes.ts +97 -1
  495. package/src/admin/apps/scopes.test.ts +1 -1
  496. package/src/admin/apps/verified-tokens.test.ts +32 -0
  497. package/src/admin/apps/verified-tokens.ts +30 -4
  498. package/src/admin/index.ts +0 -1
  499. package/src/admin/ui/plugin.ts +2 -1
  500. package/src/admin/ui/src/components/Dialog.tsx +1 -1
  501. package/src/admin/ui/src/components/EarnVaultForm.tsx +45 -0
  502. package/src/admin/ui/src/components/RoutePicker.tsx +79 -0
  503. package/src/admin/ui/src/lib/earn-vaults.ts +1 -1
  504. package/src/admin/ui/src/lib/route-policies.test.ts +452 -0
  505. package/src/admin/ui/src/lib/route-policies.ts +232 -0
  506. package/src/admin/ui/src/lib/routes.ts +34 -0
  507. package/src/admin/ui/src/lib/verified-tokens.ts +24 -9
  508. package/src/admin/ui/src/routeTree.gen.ts +21 -0
  509. package/src/admin/ui/src/routes/__root.tsx +1 -1
  510. package/src/admin/ui/src/routes/api-keys.tsx +21 -2
  511. package/src/admin/ui/src/routes/early-access.tsx +1 -0
  512. package/src/admin/ui/src/routes/earn.vaults.tsx +1 -0
  513. package/src/admin/ui/src/routes/earn.vaults_.$vaultId.edit.tsx +22 -0
  514. package/src/admin/ui/src/routes/earn.vaults_.new.tsx +10 -0
  515. package/src/admin/ui/src/routes/organizations.tsx +35 -4
  516. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +18 -3
  517. package/src/admin/ui/src/routes/route-policies.tsx +678 -0
  518. package/src/admin/ui/src/routes/routes.tsx +85 -2
  519. package/src/admin/ui/src/routes/routes_.support.tsx +6 -22
  520. package/src/admin/ui/src/routes/subsidies.tsx +1 -0
  521. package/src/admin/ui/src/routes/verified-tokens.tsx +65 -20
  522. package/src/admin/ui.generated.ts +99 -104
  523. package/src/analytics/Analytics.test.ts +4 -50
  524. package/src/analytics/Analytics.ts +65 -1
  525. package/src/analytics/tables/requestEvents.test.ts +402 -1
  526. package/src/analytics/tables/requestEvents.ts +62 -93
  527. package/src/apps/data/App.test.ts +26 -0
  528. package/src/apps/data/routes/activities.test.ts +315 -14
  529. package/src/apps/data/routes/activities.ts +60 -0
  530. package/src/apps/data/routes/balances.ts +5 -7
  531. package/src/apps/data/routes/earn.bench.ts +18 -0
  532. package/src/apps/data/routes/earn.local.test.ts +507 -0
  533. package/src/apps/data/routes/earn.test.ts +405 -127
  534. package/src/apps/data/routes/earn.ts +660 -270
  535. package/src/apps/data/routes/exchanges.bench.ts +3 -1
  536. package/src/apps/data/routes/exchanges.test.ts +166 -2
  537. package/src/apps/data/routes/exchanges.ts +308 -101
  538. package/src/apps/data/routes/fee-amm.test.ts +153 -2
  539. package/src/apps/data/routes/fee-amm.ts +132 -62
  540. package/src/apps/data/routes/receipts.test.ts +21 -18
  541. package/src/apps/data/routes/rpc.test.ts +34 -2
  542. package/src/apps/data/routes/rpc.ts +47 -10
  543. package/src/apps/data/routes/tokens.provenance.test.ts +157 -0
  544. package/src/apps/data/routes/tokens.test.ts +4 -4
  545. package/src/apps/data/routes/tokens.ts +11 -6
  546. package/src/apps/data/routes/transactions.test.ts +29 -0
  547. package/src/apps/data/routes/transactions.ts +33 -22
  548. package/src/apps/data/routes/webhooks.scan.test.ts +29 -0
  549. package/src/apps/data/routes/webhooks.test.ts +18 -3
  550. package/src/apps/data/routes/webhooks.ts +27 -13
  551. package/src/apps/management/App.ts +4 -0
  552. package/src/apps/management/Billing.test.ts +253 -27
  553. package/src/apps/management/Billing.ts +83 -29
  554. package/src/apps/management/routes/api-keys.test.ts +1 -1
  555. package/src/apps/management/routes/api-keys.ts +85 -11
  556. package/src/apps/management/routes/billing.test.ts +68 -4
  557. package/src/apps/management/routes/cli.test.ts +370 -0
  558. package/src/apps/management/routes/cli.ts +533 -0
  559. package/src/apps/management/routes/me.test.ts +14 -4
  560. package/src/apps/management/routes/orgs.test.ts +108 -4
  561. package/src/apps/management/routes/orgs.ts +7 -2
  562. package/src/apps/management/routes/usage.test.ts +2 -2
  563. package/src/apps/management/routes/verified-token-requests.test.ts +16 -2
  564. package/src/apps/management/routes/verified-token-requests.ts +7 -10
  565. package/src/apps/mcp.test.ts +108 -25
  566. package/src/apps/mcp.ts +15 -3
  567. package/src/apps/mpp/internal/Policy.test.ts +51 -4
  568. package/src/apps/mpp/internal/Policy.ts +6 -2
  569. package/src/apps/mpp/internal/Sponsorship.test.ts +29 -0
  570. package/src/apps/mpp/internal/Sponsorship.ts +6 -4
  571. package/src/apps/relay/App.test.ts +85 -1
  572. package/src/apps/relay/App.ts +28 -4
  573. package/src/apps/relay/Sponsorships.test.ts +97 -1
  574. package/src/apps/relay/Sponsorships.ts +77 -66
  575. package/src/apps/routes/Access.test.ts +319 -0
  576. package/src/apps/routes/Access.ts +147 -0
  577. package/src/apps/routes/App.ts +10 -0
  578. package/src/apps/routes/routes/chains.test.ts +3 -2
  579. package/src/apps/routes/routes/deposit-addresses.test.ts +978 -34
  580. package/src/apps/routes/routes/deposit-addresses.ts +243 -32
  581. package/src/apps/routes/routes/propAmm.local.test.ts +951 -0
  582. package/src/apps/routes/routes/quotes.test.ts +81 -8
  583. package/src/apps/routes/routes/quotes.ts +109 -27
  584. package/src/apps/routes/routes/transfers.test.ts +252 -15
  585. package/src/apps/routes/routes/transfers.ts +212 -33
  586. package/src/cli/ApiKeys.test.ts +265 -0
  587. package/src/cli/ApiKeys.ts +189 -0
  588. package/src/cli/Cli.test.ts +188 -0
  589. package/src/cli/Cli.ts +280 -12
  590. package/src/cli/Credentials.test.ts +122 -0
  591. package/src/cli/Credentials.ts +144 -0
  592. package/src/cli/Spec.test.ts +133 -0
  593. package/src/cli/Spec.ts +132 -0
  594. package/src/cli/bin.test.ts +49 -0
  595. package/src/cli/bin.ts +5 -1
  596. package/src/cli/commands/auth.test.ts +138 -0
  597. package/src/cli/commands/auth.ts +254 -0
  598. package/src/cli/commands/keys.test.ts +68 -0
  599. package/src/cli/commands/keys.ts +125 -0
  600. package/src/cloudflare.ts +184 -36
  601. package/src/db/Db.test.ts +877 -0
  602. package/src/db/Db.ts +507 -1
  603. package/src/db/Schema.ts +75 -4
  604. package/src/db/tables/apiKeyAdmissions.test.ts +57 -0
  605. package/src/db/tables/apiKeyAdmissions.ts +101 -11
  606. package/src/db/tables/apiKeyOwnerTombstones.ts +2 -2
  607. package/src/db/tables/authAccounts.test.ts +30 -0
  608. package/src/db/tables/authAccounts.ts +1 -0
  609. package/src/db/tables/earnVaults.test.ts +34 -0
  610. package/src/db/tables/earnVaults.ts +17 -2
  611. package/src/db/tables/organizations.test.ts +148 -84
  612. package/src/db/tables/organizations.ts +11 -43
  613. package/src/db/tables/requestUsage.ts +13 -2
  614. package/src/db/tables/rewardCampaigns.test.ts +163 -0
  615. package/src/db/tables/rewardEligibilityAssociations.test.ts +14 -0
  616. package/src/db/tables/rewardEligibilityAssociations.ts +24 -0
  617. package/src/db/tables/rewardRuns.ts +4 -0
  618. package/src/db/tables/rewardTransactionAttempts.ts +2 -0
  619. package/src/db/tables/routesDepositAddresses.test.ts +263 -0
  620. package/src/db/tables/routesDepositAddresses.ts +118 -34
  621. package/src/db/tables/routesDeposits.test.ts +133 -0
  622. package/src/db/tables/routesDeposits.ts +47 -26
  623. package/src/db/tables/routesIdempotency.test.ts +80 -0
  624. package/src/db/tables/routesIdempotency.ts +2 -0
  625. package/src/db/tables/routesPolicies.test.ts +143 -0
  626. package/src/db/tables/routesPolicies.ts +251 -0
  627. package/src/db/tables/routesTransferSubsidies.ts +64 -0
  628. package/src/db/tables/routesTransferSubsidyNonces.ts +16 -4
  629. package/src/db/tables/routesTransfers.test.ts +183 -2
  630. package/src/db/tables/routesTransfers.ts +65 -6
  631. package/src/db/tables/sponsoredTransactions.test.ts +351 -76
  632. package/src/db/tables/sponsoredTransactions.ts +182 -38
  633. package/src/db/tables/users.test.ts +51 -0
  634. package/src/db/tables/users.ts +12 -0
  635. package/src/db/tables/verifiedTokenRequests.test.ts +39 -20
  636. package/src/db/tables/verifiedTokenRequests.ts +80 -44
  637. package/src/handlers/internal/multisig.store.test.ts +120 -0
  638. package/src/handlers/internal/multisig.test.ts +389 -0
  639. package/src/handlers/internal/multisig.ts +13 -10
  640. package/src/handlers/relay.test.ts +259 -83
  641. package/src/handlers/relay.ts +36 -17
  642. package/src/internal/Auth.test.ts +383 -8
  643. package/src/internal/Auth.ts +272 -99
  644. package/src/internal/BetterAuth.test.ts +58 -22
  645. package/src/internal/BetterAuth.ts +23 -17
  646. package/src/internal/Cache.ts +3 -1
  647. package/src/internal/CliAuth.test.ts +279 -0
  648. package/src/internal/CliAuth.ts +319 -0
  649. package/src/internal/EarnSharePrices.test.ts +25 -0
  650. package/src/internal/EarnSharePrices.ts +9 -2
  651. package/src/internal/EarnVaults.test.ts +67 -0
  652. package/src/internal/EarnVaults.ts +299 -4
  653. package/src/internal/EarnWithdrawals.test.ts +315 -0
  654. package/src/internal/EarnWithdrawals.ts +266 -0
  655. package/src/internal/ExecutionContext.ts +31 -0
  656. package/src/internal/Log.test.ts +210 -0
  657. package/src/internal/Log.ts +180 -142
  658. package/src/internal/MetricSink.test.ts +190 -15
  659. package/src/internal/MetricSink.ts +27 -1
  660. package/src/internal/RateLimit.test.ts +144 -8
  661. package/src/internal/RateLimit.ts +254 -26
  662. package/src/internal/Store.ts +4 -5
  663. package/src/internal/Timing.test.ts +32 -0
  664. package/src/internal/Timing.ts +20 -1
  665. package/src/internal/Tron.test-d.ts +22 -0
  666. package/src/internal/Tron.test.ts +94 -0
  667. package/src/internal/Tron.ts +289 -0
  668. package/src/internal/Viem.rpc.test.ts +76 -0
  669. package/src/internal/Viem.ts +29 -6
  670. package/src/internal/rewards/Read.test.ts +109 -0
  671. package/src/internal/rewards/Read.ts +13 -4
  672. package/src/internal/routes/Action.test.ts +70 -0
  673. package/src/internal/routes/Action.ts +76 -1
  674. package/src/internal/routes/Chain.test.ts +18 -0
  675. package/src/internal/routes/Chain.ts +5 -0
  676. package/src/internal/routes/DepositAddress.ts +5 -0
  677. package/src/internal/routes/Evidence.test.ts +64 -0
  678. package/src/internal/routes/Evidence.ts +11 -3
  679. package/src/internal/routes/EvmTransferSettlement.ts +213 -0
  680. package/src/internal/routes/Policy.test.ts +316 -0
  681. package/src/internal/routes/Policy.ts +399 -0
  682. package/src/internal/routes/Provider.test.ts +103 -2
  683. package/src/internal/routes/Provider.ts +149 -27
  684. package/src/internal/routes/Quota.test.ts +149 -0
  685. package/src/internal/routes/Quota.ts +226 -0
  686. package/src/internal/routes/Reconciliation.local.test.ts +285 -0
  687. package/src/internal/routes/Reconciliation.test.ts +371 -13
  688. package/src/internal/routes/Reconciliation.ts +193 -42
  689. package/src/internal/routes/SolanaTransferSettlement.ts +232 -0
  690. package/src/internal/routes/SolanaTransferSubsidy.test.ts +471 -0
  691. package/src/internal/routes/SolanaTransferSubsidy.ts +760 -0
  692. package/src/internal/routes/SourceObservation.test.ts +221 -0
  693. package/src/internal/routes/Subsidy.local.test.ts +46 -0
  694. package/src/internal/routes/Subsidy.ts +4 -2
  695. package/src/internal/routes/Transfer.solana.test.ts +389 -0
  696. package/src/internal/routes/Transfer.test.ts +130 -1
  697. package/src/internal/routes/Transfer.tron.test.ts +172 -0
  698. package/src/internal/routes/Transfer.ts +866 -8
  699. package/src/internal/routes/TransferReconciliation.test.ts +26 -4
  700. package/src/internal/routes/TransferReconciliation.ts +91 -166
  701. package/src/internal/routes/TransferSettlement.ts +48 -0
  702. package/src/internal/routes/TransferSubsidy.local.test.ts +143 -0
  703. package/src/internal/routes/TransferSubsidy.test.ts +34 -4
  704. package/src/internal/routes/TransferSubsidy.ts +87 -12
  705. package/src/internal/routes/TronTransferSettlement.ts +212 -0
  706. package/src/internal/routes/TronTransferSubsidy.test.ts +109 -0
  707. package/src/internal/routes/TronTransferSubsidy.ts +619 -0
  708. package/src/internal/routes/index.test.ts +3 -0
  709. package/src/internal/routes/index.ts +3 -0
  710. package/src/internal/routes/providers/across.ts +13 -0
  711. package/src/internal/routes/providers/index.test.ts +141 -0
  712. package/src/internal/routes/providers/legacyMesh.test.ts +484 -0
  713. package/src/internal/routes/providers/legacyMesh.ts +633 -0
  714. package/src/internal/routes/providers/legacyMeshDelivery.test.ts +306 -0
  715. package/src/internal/routes/providers/legacyMeshDelivery.ts +320 -0
  716. package/src/internal/routes/providers/legacyMeshOutbound.test.ts +401 -0
  717. package/src/internal/routes/providers/legacyMeshOutbound.ts +566 -0
  718. package/src/internal/routes/providers/legacyMeshOutboundDelivery.ts +473 -0
  719. package/src/internal/routes/providers/propAmm.ts +640 -0
  720. package/src/internal/routes/providers/relay.test.ts +1299 -18
  721. package/src/internal/routes/providers/relay.ts +424 -39
  722. package/src/internal/routes/providers/rhino.ts +14 -1
  723. package/src/internal/routes/providers/squid.ts +21 -0
  724. package/src/internal/routes/providers/stargate.test.ts +105 -1
  725. package/src/internal/routes/providers/stargate.ts +22 -9
  726. package/src/internal/routes/providers/symbiosis.ts +13 -47
  727. package/src/providers/index.test.ts +2 -0
  728. package/src/providers/index.ts +18 -0
  729. package/dist/admin/Access.d.ts +0 -39
  730. package/dist/admin/Access.d.ts.map +0 -1
  731. package/dist/admin/Access.js +0 -73
  732. package/dist/admin/Access.js.map +0 -1
  733. package/src/admin/Access.test.ts +0 -130
  734. package/src/admin/Access.ts +0 -85
@@ -1,9 +1,12 @@
1
+ import { sql } from 'kysely'
2
+
1
3
  import * as ApiKeys from './ApiKeys.js'
2
4
  import * as ApiKeyAdmissions from './db/tables/apiKeyAdmissions.js'
3
5
  import * as ApiKeyOwnerTombstones from './db/tables/apiKeyOwnerTombstones.js'
4
6
  import * as ApiKeyRevocations from './db/tables/apiKeyRevocations.js'
5
7
  import * as Organizations from './db/tables/organizations.js'
6
8
  import * as Projects from './db/tables/projects.js'
9
+ import * as RoutesPolicies from './db/tables/routesPolicies.js'
7
10
  import * as Store from './internal/Store.js'
8
11
  import * as TestApp from '../test/App.js'
9
12
 
@@ -214,6 +217,234 @@ describe('markOwnerDeleting', () => {
214
217
  })
215
218
 
216
219
  describe('mintBounded', () => {
220
+ test.each(['routes:write', '*'])(
221
+ 'rejects delegation after the locked source loses %s',
222
+ async (scope) => {
223
+ const db = TestApp.database()
224
+ const state = Store.memory()
225
+ const org = await Organizations.create(db, { name: 'Delegation race' })
226
+ const project = await Projects.create(db, { name: 'Delegation', orgId: org.id })
227
+ const { record: source } = await ApiKeys.mintBounded(db, state, {
228
+ orgId: org.id,
229
+ projectId: project.id,
230
+ scopes: [scope],
231
+ })
232
+ // The handler authorized this snapshot before a restrictive edit committed.
233
+ await ApiKeys.updateBounded(db, state, source.id, { scopes: ['routes:read'] })
234
+ await expect(
235
+ ApiKeys.mintBounded(
236
+ db,
237
+ state,
238
+ {
239
+ environment: source.environment,
240
+ orgId: source.orgId,
241
+ projectId: source.projectId,
242
+ scopes: ['routes:write'],
243
+ },
244
+ {
245
+ policySourceApiKeyId: source.id,
246
+ sourceScopes: ['routes:write'],
247
+ },
248
+ ),
249
+ ).rejects.toBeInstanceOf(RoutesPolicies.InvalidScopeError)
250
+ expect((await ApiKeys.listByOrg(state, org.id)).map(({ id }) => id)).toEqual([source.id])
251
+ },
252
+ )
253
+
254
+ test('copies current locked source restrictions instead of the earlier handler snapshot', async () => {
255
+ const db = TestApp.database()
256
+ const state = Store.memory()
257
+ const org = await Organizations.create(db, { name: 'Restriction race' })
258
+ const project = await Projects.create(db, { name: 'Delegation', orgId: org.id })
259
+ const { record: source } = await ApiKeys.mintBounded(db, state, {
260
+ allowedIps: [],
261
+ expiresAt: new Date(Date.now() + 120_000).toISOString(),
262
+ orgId: org.id,
263
+ projectId: project.id,
264
+ rateLimits: { '*': { limit: 10_000, period: 'minute' } },
265
+ scopes: ['routes:write'],
266
+ })
267
+ const updated = await ApiKeys.updateBounded(db, state, source.id, {
268
+ allowedIps: ['203.0.113.7'],
269
+ rateLimits: { '*': { limit: 10, period: 'minute' } },
270
+ })
271
+ const { record } = await ApiKeys.mintBounded(
272
+ db,
273
+ state,
274
+ {
275
+ allowedIps: source.allowedIps,
276
+ environment: source.environment,
277
+ expiresAt: source.expiresAt,
278
+ orgId: source.orgId,
279
+ projectId: source.projectId,
280
+ rateLimits: source.rateLimits,
281
+ scopes: ['routes:write'],
282
+ },
283
+ {
284
+ policySourceApiKeyId: source.id,
285
+ sourceScopes: ['routes:write'],
286
+ },
287
+ )
288
+ expect(record.allowedIps).toEqual(updated!.allowedIps)
289
+ expect(record.expiresAt).toBe(updated!.expiresAt)
290
+ expect(record.rateLimits).toEqual(updated!.rateLimits)
291
+ })
292
+
293
+ test('releases an unpublished key admission when its KV index disappears before publication', async () => {
294
+ const database = TestApp.databaseFactory()
295
+ const db = database()
296
+ const state = Store.memory()
297
+ const organization = await Organizations.create(db, { name: 'Publication failure' })
298
+ await sql
299
+ .raw(`
300
+ CREATE FUNCTION pause_admission_commit() RETURNS trigger AS $$
301
+ BEGIN
302
+ PERFORM pg_advisory_xact_lock(12813337);
303
+ RETURN NEW;
304
+ END;
305
+ $$ LANGUAGE plpgsql;
306
+ CREATE CONSTRAINT TRIGGER pause_admission_commit
307
+ AFTER INSERT ON api_key_admissions DEFERRABLE INITIALLY DEFERRED
308
+ FOR EACH ROW EXECUTE FUNCTION pause_admission_commit();
309
+ `)
310
+ .execute(db.kysely)
311
+ const locked = Promise.withResolvers<void>()
312
+ const release = Promise.withResolvers<void>()
313
+ const blocker = database().transaction(async (tx) => {
314
+ await sql`SELECT pg_advisory_xact_lock(12813337)`.execute(tx.kysely)
315
+ locked.resolve()
316
+ await release.promise
317
+ })
318
+ await locked.promise
319
+ const minted = ApiKeys.mintBounded(database(), state, {
320
+ orgId: organization.id,
321
+ scopes: ['routes:write'],
322
+ })
323
+ const outcome = Promise.allSettled([minted])
324
+ try {
325
+ await expect
326
+ .poll(async () => {
327
+ const { rows } = await sql<{ waiting: boolean }>`select exists (
328
+ select 1 from pg_locks where locktype = 'advisory' and objid = 12813337 and not granted
329
+ ) as waiting`.execute(db.kysely)
330
+ return rows[0]!.waiting
331
+ })
332
+ .toBe(true)
333
+ const [record] = await ApiKeys.listByOrg(state, organization.id)
334
+ expect(record?.scopes).toEqual([])
335
+ await state.delete(`apikey_id:${record!.id}`)
336
+ release.resolve()
337
+ expect(await outcome).toMatchObject([{ status: 'rejected' }])
338
+ expect(await ApiKeyAdmissions.exists(db, record!.id)).toBe(false)
339
+ expect(await ApiKeyRevocations.exists(db, record!.id)).toBe(true)
340
+ expect(await ApiKeyAdmissions.matches(db, record!)).toBe(false)
341
+ } finally {
342
+ release.resolve()
343
+ await blocker
344
+ await outcome
345
+ }
346
+ })
347
+
348
+ test.each(['current', 'stale'])(
349
+ 'security: rejects a moved rotation source with %s KV',
350
+ async (kv) => {
351
+ const db = TestApp.database()
352
+ const state = Store.memory()
353
+ const org = await Organizations.create(db, { name: 'Rotation race' })
354
+ const first = await Projects.create(db, { name: 'Original', orgId: org.id })
355
+ const second = await Projects.create(db, { name: 'Reassigned', orgId: org.id })
356
+ const { record: source } = await ApiKeys.mintBounded(db, state, {
357
+ orgId: org.id,
358
+ projectId: first.id,
359
+ scopes: ['routes:write'],
360
+ })
361
+ const scope = {
362
+ apiKeyId: source.id,
363
+ environment: source.environment,
364
+ orgId: source.orgId,
365
+ projectId: source.projectId,
366
+ }
367
+ await RoutesPolicies.save(db, { scope, version: 0, policy: { access: [] } })
368
+
369
+ // The HTTP rotation handler already read and authorized `source`. A
370
+ // reassignment can finish before mintBounded takes its policy-copy lock.
371
+ await ApiKeys.updateBounded(db, state, source.id, { projectId: second.id })
372
+ expect(await RoutesPolicies.get(db, scope)).toMatchObject({ policy: {}, version: 2 })
373
+ expect((await ApiKeys.get(state, source.id))?.projectId).toBe(second.id)
374
+ // KV can lag SQL. Re-create the stale attribution through the KV write
375
+ // boundary; the authoritative admission must still reject the rotation.
376
+ if (kv === 'stale') await ApiKeys.update(state, source.id, { projectId: first.id })
377
+
378
+ // Copying the now-empty old scope would mint a usable key in the former
379
+ // project with unrestricted legacy route access instead of the old denial.
380
+ await expect(
381
+ ApiKeys.mintBounded(
382
+ db,
383
+ state,
384
+ {
385
+ environment: source.environment,
386
+ orgId: source.orgId,
387
+ projectId: source.projectId,
388
+ scopes: source.scopes,
389
+ },
390
+ { policySourceApiKeyId: source.id },
391
+ ).then(({ record }) => ({
392
+ inOriginalProject: record.projectId === first.id,
393
+ scopes: record.scopes,
394
+ })),
395
+ ).rejects.toBeInstanceOf(RoutesPolicies.InvalidScopeError)
396
+ },
397
+ )
398
+
399
+ test('leaves a replacement unprivileged when policy copy rolls back at SQL commit', async () => {
400
+ const db = TestApp.database()
401
+ const state = Store.memory()
402
+ const organization = await Organizations.create(db, { name: 'Rotation boundary' })
403
+ const source = await ApiKeys.mintBounded(db, state, {
404
+ environment: 'sandbox',
405
+ orgId: organization.id,
406
+ scopes: ['data:read'],
407
+ })
408
+ const sourceScope = {
409
+ apiKeyId: source.record.id,
410
+ environment: 'sandbox',
411
+ orgId: organization.id,
412
+ } as const
413
+ await RoutesPolicies.save(db, {
414
+ policy: { maxAmount: '17' },
415
+ scope: sourceScope,
416
+ version: 0,
417
+ })
418
+ await installDeferredAdmissionFailure(db, organization.id)
419
+
420
+ await expect(
421
+ ApiKeys.mintBounded(
422
+ db,
423
+ state,
424
+ {
425
+ environment: 'sandbox',
426
+ orgId: organization.id,
427
+ scopes: ['routes:write'],
428
+ },
429
+ { policySourceApiKeyId: source.record.id },
430
+ ),
431
+ ).rejects.toThrow('deferred admission failure')
432
+
433
+ const records = await ApiKeys.listByOrg(state, organization.id)
434
+ const replacement = records.find(({ id }) => id !== source.record.id)!
435
+ expect(records.find(({ id }) => id === source.record.id)?.scopes).toEqual(['data:read'])
436
+ expect(replacement.scopes).toEqual([])
437
+ await expect(ApiKeyAdmissions.matches(db, replacement)).resolves.toBe(false)
438
+ await expect(ApiKeyAdmissions.matches(db, source.record)).resolves.toBe(true)
439
+ expect(await RoutesPolicies.get(db, sourceScope)).toMatchObject({
440
+ policy: { maxAmount: '17' },
441
+ version: 1,
442
+ })
443
+ expect(
444
+ await RoutesPolicies.get(db, { ...sourceScope, apiKeyId: replacement.id }),
445
+ ).toBeUndefined()
446
+ })
447
+
217
448
  test('rejects an authoritative owner tombstone absent from KV', async () => {
218
449
  const db = TestApp.database()
219
450
  const state = Store.memory()
@@ -269,8 +500,8 @@ describe('mintBounded', () => {
269
500
  await state.put(recordKey, raw)
270
501
  await state.put(`apikey_org:${revoked.record.orgId}:${revoked.record.id}`, recordKey)
271
502
 
272
- const stale = await ApiKeys.resolve(state, revoked.token)
273
- expect(stale).not.toBeNull()
503
+ const stale = revoked.record
504
+ expect(await ApiKeys.resolve(state, revoked.token)).toBeNull()
274
505
  await expect(ApiKeyAdmissions.matches(db, stale!)).resolves.toBe(false)
275
506
  await ApiKeys.mintBounded(db, state, {
276
507
  orgId: revoked.record.orgId,
@@ -324,8 +555,8 @@ describe('mintBounded', () => {
324
555
  await state.put(recordKey, raw)
325
556
  await state.put(`apikey_org:${target.record.orgId}:${target.record.id}`, recordKey)
326
557
 
327
- const stale = await ApiKeys.resolve(state, target.token)
328
- expect(stale).not.toBeNull()
558
+ const stale = target.record
559
+ expect(await ApiKeys.resolve(state, target.token)).toBeNull()
329
560
  await expect(ApiKeyAdmissions.matches(db, stale!)).resolves.toBe(false)
330
561
  })
331
562
 
@@ -354,11 +585,12 @@ describe('mintBounded', () => {
354
585
  await state.delete(`apikey_id:${target.record.id}`)
355
586
 
356
587
  await expect(ApiKeys.revokeBounded(db, state, target.record.id)).resolves.toBe(true)
357
- const stale = await ApiKeys.resolve(state, target.token)
358
- expect(stale).not.toBeNull()
588
+ const stale = target.record
589
+ expect(await ApiKeys.resolve(state, target.token)).toBeNull()
359
590
  await expect(ApiKeyAdmissions.matches(db, stale!)).resolves.toBe(false)
360
591
  vi.advanceTimersByTime(ApiKeyRevocations.retentionMs + 1)
361
592
  await expect(ApiKeyRevocations.prune(db)).resolves.toBe(0)
593
+ expect(await ApiKeys.resolve(state, target.token)).toBeNull()
362
594
  await expect(ApiKeyAdmissions.matches(db, stale!)).resolves.toBe(false)
363
595
  await expect(ApiKeyAdmissions.exists(db, target.record.id)).resolves.toBe(true)
364
596
  await expect(
@@ -725,6 +957,249 @@ describe('mintBounded', () => {
725
957
  })
726
958
 
727
959
  describe('updateBounded', () => {
960
+ test('keeps post-commit scope publication serialized with another reassignment', async () => {
961
+ const database = TestApp.databaseFactory()
962
+ const inner = Store.memory()
963
+ const publishing = Promise.withResolvers<void>()
964
+ const release = Promise.withResolvers<void>()
965
+ const state = Store.from({
966
+ ...inner,
967
+ async put(key, value, options = {}) {
968
+ if (key.startsWith('apikey:')) {
969
+ const record = JSON.parse(value) as ApiKeys.Record
970
+ if (record.orgId === 'org_first' && record.scopes.includes('*')) {
971
+ publishing.resolve()
972
+ await release.promise
973
+ }
974
+ }
975
+ await inner.put(key, value, options)
976
+ },
977
+ })
978
+ const { record, token } = await ApiKeys.mintBounded(database(), state, {
979
+ orgId: 'org_source',
980
+ scopes: ['*'],
981
+ })
982
+ const first = ApiKeys.updateBounded(database(), state, record.id, { orgId: 'org_first' })
983
+ const firstResult = Promise.allSettled([first])
984
+ await publishing.promise
985
+ let secondFinished = false
986
+ const second = ApiKeys.updateBounded(database(), state, record.id, {
987
+ orgId: 'org_second',
988
+ scopes: ['routes:read'],
989
+ })
990
+ const secondResult = Promise.allSettled([second]).then((result) => {
991
+ secondFinished = true
992
+ return result
993
+ })
994
+ try {
995
+ // Either the competing move finishes (the bug), or waits for the publishing key lock.
996
+ await expect
997
+ .poll(async () => {
998
+ const { rows } = await sql<{ waiting: boolean }>`select exists (
999
+ select 1 from pg_locks where locktype = 'advisory' and not granted
1000
+ and classid::bigint = ((hashtextextended(${`api-key-admission-id:${record.id}`}, 0) >> 32) & 4294967295)
1001
+ and objid::bigint = (hashtextextended(${`api-key-admission-id:${record.id}`}, 0) & 4294967295)
1002
+ ) as waiting`.execute(database().kysely)
1003
+ return secondFinished || rows[0]!.waiting
1004
+ })
1005
+ .toBe(true)
1006
+ } finally {
1007
+ release.resolve()
1008
+ await Promise.all([firstResult, secondResult])
1009
+ }
1010
+ expect(await firstResult).toMatchObject([{ status: 'fulfilled' }])
1011
+ expect(await secondResult).toMatchObject([{ status: 'fulfilled' }])
1012
+ const current = await ApiKeys.resolve(inner, token)
1013
+ expect(current).toMatchObject({ orgId: 'org_second', scopes: ['routes:read'] })
1014
+ expect(await ApiKeyAdmissions.matches(database(), current!)).toBe(true)
1015
+ })
1016
+
1017
+ test.each([
1018
+ { scopes: undefined, expected: ['*'] },
1019
+ { scopes: [], expected: [] },
1020
+ { scopes: ['routes:read'], expected: ['routes:read'] },
1021
+ ] as const)(
1022
+ 'publishes the latest committed scope intent after another move: %j',
1023
+ async ({ scopes, expected }) => {
1024
+ const db = TestApp.database()
1025
+ const state = Store.memory()
1026
+ const { record, token } = await ApiKeys.mintBounded(db, state, {
1027
+ orgId: 'org_source',
1028
+ scopes: [],
1029
+ })
1030
+ // Seed the durable boundary after a move commits but before its KV publication.
1031
+ await db.transaction(async (tx) => {
1032
+ await ApiKeyAdmissions.lock(tx, record.id)
1033
+ await ApiKeyAdmissions.setPendingScopes(tx, record.id, ['*'])
1034
+ })
1035
+ if (scopes !== undefined) {
1036
+ await ApiKeys.updateBounded(db, state, record.id, { scopes })
1037
+ expect((await ApiKeyAdmissions.get(db, record.id))?.pendingScopes).toBeNull()
1038
+ }
1039
+ await ApiKeys.updateBounded(db, state, record.id, { orgId: 'org_second' })
1040
+ await ApiKeys.updateBounded(db, state, record.id, { orgId: 'org_source' })
1041
+ expect(await ApiKeys.resolve(state, token)).toMatchObject({
1042
+ orgId: 'org_source',
1043
+ scopes: expected,
1044
+ })
1045
+ expect((await ApiKeyAdmissions.get(db, record.id))?.pendingScopes).toBeNull()
1046
+ },
1047
+ )
1048
+
1049
+ test.each(['reassignment', 'rotation'] as const)(
1050
+ 'serializes %s with organization deletion without deadlock',
1051
+ async (operation) => {
1052
+ const database = TestApp.databaseFactory()
1053
+ const db = database()
1054
+ const state = Store.memory()
1055
+ const source = await Organizations.create(db, { name: 'Deleting owner' })
1056
+ const target = await Organizations.create(db, { name: 'New owner' })
1057
+ const { record } = await ApiKeys.mintBounded(db, state, {
1058
+ orgId: source.id,
1059
+ scopes: ['routes:read'],
1060
+ })
1061
+ await RoutesPolicies.save(db, {
1062
+ policy: { maxAmount: '0.07' },
1063
+ scope: { apiKeyId: record.id, environment: record.environment, orgId: source.id },
1064
+ version: 0,
1065
+ })
1066
+ const locked = Promise.withResolvers<void>()
1067
+ const release = Promise.withResolvers<void>()
1068
+ const blocker = database().transaction(async (tx) => {
1069
+ await sql`LOCK TABLE sponsored_transactions IN ACCESS EXCLUSIVE MODE`.execute(tx.kysely)
1070
+ locked.resolve()
1071
+ await release.promise
1072
+ })
1073
+ await locked.promise
1074
+ const deletion = Organizations.deleteOrganization(database(), source.id)
1075
+ const deletionResult = Promise.allSettled([deletion])
1076
+ try {
1077
+ // The deletion holds the subsidy lock while its first liability query waits on the table.
1078
+ await expect
1079
+ .poll(async () => {
1080
+ const { rows } = await sql<{ waiting: boolean }>`select exists (
1081
+ select 1 from pg_locks where relation = 'sponsored_transactions'::regclass and not granted
1082
+ ) as waiting`.execute(db.kysely)
1083
+ return rows[0]!.waiting
1084
+ })
1085
+ .toBe(true)
1086
+ const mutation =
1087
+ operation === 'reassignment'
1088
+ ? ApiKeys.updateBounded(database(), state, record.id, { orgId: target.id })
1089
+ : ApiKeys.mintBounded(
1090
+ database(),
1091
+ state,
1092
+ {
1093
+ orgId: source.id,
1094
+ scopes: ['routes:read'],
1095
+ },
1096
+ { policySourceApiKeyId: record.id },
1097
+ )
1098
+ const mutationResult = Promise.allSettled([mutation])
1099
+ try {
1100
+ await expect
1101
+ .poll(async () => {
1102
+ const { rows } = await sql<{ waiting: boolean }>`select exists (
1103
+ select 1 from pg_locks where locktype = 'advisory' and not granted
1104
+ and classid::bigint = ((hashtextextended(${`routes-subsidy:${source.id}`}, 0) >> 32) & 4294967295)
1105
+ and objid::bigint = (hashtextextended(${`routes-subsidy:${source.id}`}, 0) & 4294967295)
1106
+ ) as waiting`.execute(db.kysely)
1107
+ return rows[0]!.waiting
1108
+ })
1109
+ .toBe(true)
1110
+ } finally {
1111
+ release.resolve()
1112
+ }
1113
+ expect(await deletionResult).toMatchObject([{ status: 'fulfilled', value: true }])
1114
+ expect(await mutationResult).toMatchObject([
1115
+ { status: 'rejected', reason: { name: 'ApiKeys.OwnerDeletedError' } },
1116
+ ])
1117
+ expect(await ApiKeys.get(state, record.id)).toMatchObject({
1118
+ orgId: source.id,
1119
+ scopes: ['routes:read'],
1120
+ })
1121
+ expect(await ApiKeyAdmissions.get(db, record.id)).toBeUndefined()
1122
+ expect(await ApiKeyOwnerTombstones.isDeleted(db, { orgId: source.id })).toBe(true)
1123
+ } finally {
1124
+ release.resolve()
1125
+ await blocker
1126
+ await deletionResult
1127
+ }
1128
+ },
1129
+ )
1130
+
1131
+ test('preserves scopes on repeated reassignment after a former project is deleted', async () => {
1132
+ const db = TestApp.database()
1133
+ const state = Store.memory()
1134
+ const org = await Organizations.create(db, { name: 'Reassignment' })
1135
+ const first = await Projects.create(db, { name: 'First', orgId: org.id })
1136
+ const second = await Projects.create(db, { name: 'Second', orgId: org.id })
1137
+ const third = await Projects.create(db, { name: 'Third', orgId: org.id })
1138
+ const { record, token } = await ApiKeys.mintBounded(db, state, {
1139
+ orgId: org.id,
1140
+ projectId: first.id,
1141
+ scopes: ['routes:read'],
1142
+ })
1143
+ const scope = {
1144
+ apiKeyId: record.id,
1145
+ environment: record.environment,
1146
+ orgId: org.id,
1147
+ projectId: first.id,
1148
+ }
1149
+ await RoutesPolicies.save(db, { policy: { maxAmount: '7' }, scope, version: 0 })
1150
+ await ApiKeys.updateBounded(db, state, record.id, { projectId: second.id })
1151
+ expect(await RoutesPolicies.get(db, scope)).toMatchObject({ policy: {}, version: 2 })
1152
+ await Projects.deleteProject(db, first.id)
1153
+
1154
+ await expect(
1155
+ ApiKeys.updateBounded(db, state, record.id, { projectId: third.id }),
1156
+ ).resolves.toMatchObject({ projectId: third.id, scopes: ['routes:read'] })
1157
+ expect(await ApiKeys.resolve(state, token)).toMatchObject({
1158
+ projectId: third.id,
1159
+ scopes: ['routes:read'],
1160
+ })
1161
+ expect(await RoutesPolicies.get(db, scope)).toMatchObject({ policy: {}, version: 3 })
1162
+ expect(await ApiKeyAdmissions.get(db, record.id)).toMatchObject({ projectId: third.id })
1163
+ })
1164
+
1165
+ test('leaves a reassigned key unprivileged when policy clear rolls back at SQL commit', async () => {
1166
+ const db = TestApp.database()
1167
+ const state = Store.memory()
1168
+ const sourceOwner = await Organizations.create(db, { name: 'Source boundary' })
1169
+ const destinationOwner = await Organizations.create(db, { name: 'Destination boundary' })
1170
+ const minted = await ApiKeys.mintBounded(db, state, {
1171
+ orgId: sourceOwner.id,
1172
+ scopes: ['data:read'],
1173
+ })
1174
+ const sourceScope = {
1175
+ apiKeyId: minted.record.id,
1176
+ environment: minted.record.environment,
1177
+ orgId: sourceOwner.id,
1178
+ } as const
1179
+ await RoutesPolicies.save(db, {
1180
+ policy: { maxAmount: '29' },
1181
+ scope: sourceScope,
1182
+ version: 0,
1183
+ })
1184
+ await installDeferredAdmissionFailure(db, destinationOwner.id)
1185
+
1186
+ await expect(
1187
+ ApiKeys.updateBounded(db, state, minted.record.id, { orgId: destinationOwner.id }),
1188
+ ).rejects.toThrow('deferred admission failure')
1189
+
1190
+ const moved = (await ApiKeys.get(state, minted.record.id))!
1191
+ expect(moved).toMatchObject({ orgId: destinationOwner.id, scopes: [] })
1192
+ await expect(ApiKeyAdmissions.matches(db, moved)).resolves.toBe(false)
1193
+ expect(await ApiKeyAdmissions.get(db, minted.record.id)).toMatchObject({
1194
+ orgId: sourceOwner.id,
1195
+ projectId: null,
1196
+ })
1197
+ expect(await RoutesPolicies.get(db, sourceScope)).toMatchObject({
1198
+ policy: { maxAmount: '29' },
1199
+ version: 1,
1200
+ })
1201
+ })
1202
+
728
1203
  test('rejects an update when the locked key no longer has the expected owner', async () => {
729
1204
  const db = TestApp.database()
730
1205
  const state = Store.memory()
@@ -1032,9 +1507,156 @@ describe('revoke', () => {
1032
1507
 
1033
1508
  // The id index entry maps id -> record key; revoke uses it directly.
1034
1509
  expect(await ApiKeys.revoke(state, record.id)).toBe(true)
1035
- // The record and both index entries are gone.
1510
+ // Only the durable revocation marker remains.
1036
1511
  const { keys } = await state.list({ prefix: 'apikey' })
1037
- expect(keys).toMatchInlineSnapshot(`[]`)
1512
+ expect(keys.map(({ name }) => name.replace(record.id, '<id>'))).toMatchInlineSnapshot(`
1513
+ [
1514
+ "apikey_revoked:<id>",
1515
+ ]
1516
+ `)
1517
+ })
1518
+ })
1519
+
1520
+ describe('reconcileOwnerDeletion', () => {
1521
+ test('removes a fence only when SQL deletion did not commit', async () => {
1522
+ const db = TestApp.database()
1523
+ const state = Store.memory()
1524
+ const owner = { orgId: 'org_retained' }
1525
+ await ApiKeys.markOwnerDeleting(state, owner)
1526
+
1527
+ await ApiKeys.reconcileOwnerDeletion(db, state, owner)
1528
+
1529
+ expect(await ApiKeys.isOwnerDeleted(state, owner)).toBe(false)
1530
+ await ApiKeys.markOwnerDeleting(state, owner)
1531
+ await db.transaction((tx) => ApiKeyOwnerTombstones.markDeleted(tx, owner))
1532
+ await ApiKeys.reconcileOwnerDeletion(db, state, owner)
1533
+ expect(await ApiKeys.isOwnerDeleted(state, owner)).toBe(true)
1534
+ })
1535
+
1536
+ test('waits for a concurrent deletion before reconciling its fence', async () => {
1537
+ const database = TestApp.databaseFactory()
1538
+ const state = Store.memory()
1539
+ const owner = { orgId: 'org_concurrent_deletion' }
1540
+ const locked = Promise.withResolvers<void>()
1541
+ const commit = Promise.withResolvers<void>()
1542
+ const deletion = database().transaction(async (tx) => {
1543
+ await ApiKeyOwnerTombstones.markDeleted(tx, owner)
1544
+ await ApiKeys.markOwnerDeleting(state, owner)
1545
+ locked.resolve()
1546
+ await commit.promise
1547
+ })
1548
+ await locked.promise
1549
+
1550
+ const reconciliation = ApiKeys.reconcileOwnerDeletion(database(), state, owner)
1551
+ try {
1552
+ await vi.waitFor(async () => {
1553
+ const result = await sql<{ waiting: boolean }>`
1554
+ SELECT EXISTS (
1555
+ SELECT 1 FROM pg_locks WHERE locktype = 'advisory' AND NOT granted
1556
+ AND objid::bigint = (hashtextextended(${'organization:' + owner.orgId}, 0) & 4294967295)
1557
+ ) AS waiting
1558
+ `.execute(database().kysely)
1559
+ expect(result.rows[0]?.waiting).toBe(true)
1560
+ })
1561
+ } finally {
1562
+ commit.resolve()
1563
+ }
1564
+ await Promise.all([deletion, reconciliation])
1565
+
1566
+ expect(await ApiKeys.isOwnerDeleted(state, owner)).toBe(true)
1567
+ })
1568
+ })
1569
+
1570
+ describe('isFenced', () => {
1571
+ test('reports revocation and organization or project deletion in one read', async () => {
1572
+ const state = Store.memory()
1573
+ const key = { id: 'key_fenced', orgId: 'org_fenced', projectId: 'prj_fenced' }
1574
+ const sibling = { id: 'key_sibling', orgId: 'org_fenced', projectId: 'prj_sibling' }
1575
+
1576
+ expect(await ApiKeys.isFenced(state, key)).toBe(false)
1577
+
1578
+ await ApiKeys.markOwnerDeleting(state, { orgId: key.orgId, projectId: key.projectId })
1579
+ expect([await ApiKeys.isFenced(state, key), await ApiKeys.isFenced(state, sibling)]).toEqual([
1580
+ true,
1581
+ false,
1582
+ ])
1583
+
1584
+ await ApiKeys.markOwnerDeleting(state, { orgId: key.orgId })
1585
+ expect(await ApiKeys.isFenced(state, sibling)).toBe(true)
1586
+ })
1587
+
1588
+ test('reports a revoked key without an owner fence', async () => {
1589
+ const state = Store.memory()
1590
+ const key = { id: 'key_revoked_only', orgId: 'org_live' }
1591
+
1592
+ await ApiKeys.revoke(state, key.id)
1593
+
1594
+ expect(await ApiKeys.isFenced(state, key)).toBe(true)
1595
+ expect(await ApiKeys.isFenced(state, { ...key, id: 'key_other' })).toBe(false)
1596
+ })
1597
+ })
1598
+
1599
+ describe('revokeBounded', () => {
1600
+ test.each(['missing', 'stale'])('fences a key with a %s ID index', async (index) => {
1601
+ const db = TestApp.database()
1602
+ const state = Store.memory()
1603
+ const { record, token } = await ApiKeys.mintBounded(db, state, {
1604
+ orgId: 'org_revocation',
1605
+ scopes: ['data:read'],
1606
+ })
1607
+ const recordKey = (await state.get(`apikey_id:${record.id}`))!
1608
+ if (index === 'missing') await state.delete(`apikey_id:${record.id}`)
1609
+ else await state.put(`apikey_id:${record.id}`, 'apikey:missing')
1610
+
1611
+ await expect(ApiKeys.revokeBounded(db, state, record.id)).resolves.toBe(true)
1612
+
1613
+ expect(await state.get(recordKey)).not.toBeNull()
1614
+ expect(await ApiKeys.resolve(state, token)).toBeNull()
1615
+ })
1616
+
1617
+ test('rejects a failed KV fence write and permits retry', async () => {
1618
+ const db = TestApp.database()
1619
+ const backing = Store.memory()
1620
+ const { record, token } = await ApiKeys.mintBounded(db, backing, {
1621
+ orgId: 'org_revocation',
1622
+ scopes: ['data:read'],
1623
+ })
1624
+ await backing.delete(`apikey_id:${record.id}`)
1625
+ const state = Store.from({
1626
+ ...backing,
1627
+ async put(key, value, options) {
1628
+ if (key === `apikey_revoked:${record.id}`) throw new Error('KV write failed')
1629
+ await backing.put(key, value, options)
1630
+ },
1631
+ })
1632
+
1633
+ await expect(ApiKeys.revokeBounded(db, state, record.id)).rejects.toThrow('KV write failed')
1634
+ await expect(ApiKeyAdmissions.exists(db, record.id)).resolves.toBe(true)
1635
+ expect(await ApiKeys.resolve(backing, token)).not.toBeNull()
1636
+
1637
+ await expect(ApiKeys.revokeBounded(db, backing, record.id)).resolves.toBe(true)
1638
+ expect(await ApiKeys.resolve(backing, token)).toBeNull()
1639
+ })
1640
+
1641
+ test('keeps the KV fence when record cleanup fails', async () => {
1642
+ const db = TestApp.database()
1643
+ const backing = Store.memory()
1644
+ const { record, token } = await ApiKeys.mintBounded(db, backing, {
1645
+ orgId: 'org_revocation',
1646
+ scopes: ['data:read'],
1647
+ })
1648
+ const recordKey = (await backing.get(`apikey_id:${record.id}`))!
1649
+ const state = Store.from({
1650
+ ...backing,
1651
+ async delete(key) {
1652
+ if (key === recordKey) throw new Error('KV delete failed')
1653
+ await backing.delete(key)
1654
+ },
1655
+ })
1656
+
1657
+ await expect(ApiKeys.revokeBounded(db, state, record.id)).rejects.toThrow('KV delete failed')
1658
+ expect(await backing.get(recordKey)).not.toBeNull()
1659
+ expect(await ApiKeys.resolve(backing, token)).toBeNull()
1038
1660
  })
1039
1661
  })
1040
1662
 
@@ -1435,3 +2057,25 @@ describe('backfill', () => {
1435
2057
  expect(await ApiKeys.revoke(state, record.id)).toBe(true)
1436
2058
  })
1437
2059
  })
2060
+
2061
+ async function installDeferredAdmissionFailure(
2062
+ db: ReturnType<typeof TestApp.database>,
2063
+ orgId: string,
2064
+ ) {
2065
+ await sql
2066
+ .raw(`
2067
+ CREATE FUNCTION fail_admission_at_commit() RETURNS trigger AS $$
2068
+ BEGIN
2069
+ IF NEW.org_id = '${orgId}' THEN
2070
+ RAISE EXCEPTION 'deferred admission failure';
2071
+ END IF;
2072
+ RETURN NEW;
2073
+ END;
2074
+ $$ LANGUAGE plpgsql;
2075
+ CREATE CONSTRAINT TRIGGER fail_admission_at_commit
2076
+ AFTER INSERT OR UPDATE ON api_key_admissions
2077
+ DEFERRABLE INITIALLY DEFERRED
2078
+ FOR EACH ROW EXECUTE FUNCTION fail_admission_at_commit();
2079
+ `)
2080
+ .execute(db.kysely)
2081
+ }