tapimo 0.19.0 → 0.20.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 (494) hide show
  1. package/README.md +13 -5
  2. package/dist/ApiKey.d.ts +2 -2
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +2 -2
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +85 -11
  7. package/dist/ApiKeys.d.ts.map +1 -1
  8. package/dist/ApiKeys.js +218 -56
  9. package/dist/ApiKeys.js.map +1 -1
  10. package/dist/App.d.ts +12543 -13912
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +4 -5
  13. package/dist/App.js.map +1 -1
  14. package/dist/Scope.d.ts +12 -20
  15. package/dist/Scope.d.ts.map +1 -1
  16. package/dist/Scope.js +20 -36
  17. package/dist/Scope.js.map +1 -1
  18. package/dist/admin/App.d.ts +871 -828
  19. package/dist/admin/App.d.ts.map +1 -1
  20. package/dist/admin/App.js +14 -26
  21. package/dist/admin/App.js.map +1 -1
  22. package/dist/admin/apps/api-keys.d.ts +93 -186
  23. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  24. package/dist/admin/apps/api-keys.js +3 -5
  25. package/dist/admin/apps/api-keys.js.map +1 -1
  26. package/dist/admin/apps/audit-logs.d.ts +18 -18
  27. package/dist/admin/apps/audit-logs.js +1 -1
  28. package/dist/admin/apps/audit-logs.js.map +1 -1
  29. package/dist/admin/apps/earn-vaults.d.ts +27 -27
  30. package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
  31. package/dist/admin/apps/earn-vaults.js +11 -5
  32. package/dist/admin/apps/earn-vaults.js.map +1 -1
  33. package/dist/admin/apps/organizations.d.ts +17 -17
  34. package/dist/admin/apps/prop-amms.d.ts +4 -4
  35. package/dist/admin/apps/routes-support.js +1 -38
  36. package/dist/admin/apps/routes-support.js.map +1 -1
  37. package/dist/admin/apps/routes.d.ts +153 -153
  38. package/dist/admin/apps/sentinel.d.ts +4 -4
  39. package/dist/admin/apps/verified-tokens.d.ts +24 -24
  40. package/dist/admin/apps/zone-delegation-requests.d.ts +332 -0
  41. package/dist/admin/apps/zone-delegation-requests.d.ts.map +1 -0
  42. package/dist/admin/apps/zone-delegation-requests.js +200 -0
  43. package/dist/admin/apps/zone-delegation-requests.js.map +1 -0
  44. package/dist/admin/apps/zone-delegations.d.ts +512 -52
  45. package/dist/admin/apps/zone-delegations.d.ts.map +1 -1
  46. package/dist/admin/apps/zone-delegations.js +134 -44
  47. package/dist/admin/apps/zone-delegations.js.map +1 -1
  48. package/dist/admin/index.d.ts +1 -1
  49. package/dist/admin/index.d.ts.map +1 -1
  50. package/dist/admin/index.js +1 -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 +341 -397
  55. package/dist/admin/ui.generated.js.map +1 -1
  56. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  57. package/dist/analytics/tables/requestEvents.js +4 -2
  58. package/dist/analytics/tables/requestEvents.js.map +1 -1
  59. package/dist/apps/data/App.d.ts +11932 -13214
  60. package/dist/apps/data/App.d.ts.map +1 -1
  61. package/dist/apps/data/App.js +32 -13
  62. package/dist/apps/data/App.js.map +1 -1
  63. package/dist/apps/data/ZoneSelection.d.ts +1 -1
  64. package/dist/apps/data/ZoneSelection.d.ts.map +1 -1
  65. package/dist/apps/data/ZoneSelection.js +2 -2
  66. package/dist/apps/data/ZoneSelection.js.map +1 -1
  67. package/dist/apps/data/routes/activities.d.ts +60 -27
  68. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  69. package/dist/apps/data/routes/activities.js +91 -16
  70. package/dist/apps/data/routes/activities.js.map +1 -1
  71. package/dist/apps/data/routes/addresses.d.ts +893 -875
  72. package/dist/apps/data/routes/addresses.d.ts.map +1 -1
  73. package/dist/apps/data/routes/balances.d.ts +36 -36
  74. package/dist/apps/data/routes/balances.d.ts.map +1 -1
  75. package/dist/apps/data/routes/balances.js +3 -1
  76. package/dist/apps/data/routes/balances.js.map +1 -1
  77. package/dist/apps/data/routes/blocks.d.ts +40 -40
  78. package/dist/apps/data/routes/blocks.d.ts.map +1 -1
  79. package/dist/apps/data/routes/blocks.js +3 -7
  80. package/dist/apps/data/routes/blocks.js.map +1 -1
  81. package/dist/apps/data/routes/coingecko.d.ts +236 -236
  82. package/dist/apps/data/routes/earn-transactions.d.ts +14 -14
  83. package/dist/apps/data/routes/earn.d.ts +261 -1445
  84. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  85. package/dist/apps/data/routes/earn.js +64 -490
  86. package/dist/apps/data/routes/earn.js.map +1 -1
  87. package/dist/apps/data/routes/exchanges.d.ts +144 -144
  88. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  89. package/dist/apps/data/routes/exchanges.js +5 -15
  90. package/dist/apps/data/routes/exchanges.js.map +1 -1
  91. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  92. package/dist/apps/data/routes/fee-amm.js +3 -9
  93. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  94. package/dist/apps/data/routes/indexer.d.ts +13 -13
  95. package/dist/apps/data/routes/receipts.d.ts +53 -53
  96. package/dist/apps/data/routes/receipts.js +1 -1
  97. package/dist/apps/data/routes/receipts.js.map +1 -1
  98. package/dist/apps/data/routes/rpc.d.ts +52 -0
  99. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  100. package/dist/apps/data/routes/rpc.js +19 -18
  101. package/dist/apps/data/routes/rpc.js.map +1 -1
  102. package/dist/apps/data/routes/tokenlist.d.ts +14 -14
  103. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
  104. package/dist/apps/data/routes/tokenlist.js +1 -3
  105. package/dist/apps/data/routes/tokenlist.js.map +1 -1
  106. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  107. package/dist/apps/data/routes/tokens.js +7 -5
  108. package/dist/apps/data/routes/tokens.js.map +1 -1
  109. package/dist/apps/data/routes/transactions.d.ts +281 -281
  110. package/dist/apps/data/routes/transactions.js +3 -3
  111. package/dist/apps/data/routes/transactions.js.map +1 -1
  112. package/dist/apps/data/routes/transfers.d.ts +12 -12
  113. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  114. package/dist/apps/data/routes/transfers.js +1 -3
  115. package/dist/apps/data/routes/transfers.js.map +1 -1
  116. package/dist/apps/data/routes/valuation.d.ts +30 -30
  117. package/dist/apps/data/routes/valuation.d.ts.map +1 -1
  118. package/dist/apps/data/routes/valuation.js +1 -3
  119. package/dist/apps/data/routes/valuation.js.map +1 -1
  120. package/dist/apps/data/routes/verified-tokens.d.ts +86 -86
  121. package/dist/apps/data/routes/webhooks.d.ts +102 -102
  122. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  123. package/dist/apps/data/routes/webhooks.js +9 -23
  124. package/dist/apps/data/routes/webhooks.js.map +1 -1
  125. package/dist/apps/data/routes/zone-deposits.d.ts +16 -16
  126. package/dist/apps/data/routes/zone-withdrawals.d.ts +31 -31
  127. package/dist/apps/data/routes/zones.d.ts +28 -28
  128. package/dist/apps/management/App.d.ts +18 -102
  129. package/dist/apps/management/App.d.ts.map +1 -1
  130. package/dist/apps/management/App.js +1 -1
  131. package/dist/apps/management/App.js.map +1 -1
  132. package/dist/apps/management/routes/api-keys.d.ts +253 -339
  133. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  134. package/dist/apps/management/routes/api-keys.js +25 -11
  135. package/dist/apps/management/routes/api-keys.js.map +1 -1
  136. package/dist/apps/management/routes/billing.d.ts +92 -92
  137. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  138. package/dist/apps/management/routes/billing.js +5 -13
  139. package/dist/apps/management/routes/billing.js.map +1 -1
  140. package/dist/apps/management/routes/cli.d.ts.map +1 -1
  141. package/dist/apps/management/routes/cli.js +3 -9
  142. package/dist/apps/management/routes/cli.js.map +1 -1
  143. package/dist/apps/management/routes/faucet.d.ts +16 -16
  144. package/dist/apps/management/routes/fee-payer-usage.d.ts +38 -38
  145. package/dist/apps/management/routes/fee-payer-usage.d.ts.map +1 -1
  146. package/dist/apps/management/routes/fee-payer-usage.js +2 -4
  147. package/dist/apps/management/routes/fee-payer-usage.js.map +1 -1
  148. package/dist/apps/management/routes/invitations.d.ts +52 -52
  149. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  150. package/dist/apps/management/routes/invitations.js +4 -12
  151. package/dist/apps/management/routes/invitations.js.map +1 -1
  152. package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
  153. package/dist/apps/management/routes/invite-links.js +6 -14
  154. package/dist/apps/management/routes/invite-links.js.map +1 -1
  155. package/dist/apps/management/routes/me.d.ts +6 -6
  156. package/dist/apps/management/routes/me.d.ts.map +1 -1
  157. package/dist/apps/management/routes/me.js +3 -7
  158. package/dist/apps/management/routes/me.js.map +1 -1
  159. package/dist/apps/management/routes/members.d.ts +84 -84
  160. package/dist/apps/management/routes/members.d.ts.map +1 -1
  161. package/dist/apps/management/routes/members.js +2 -4
  162. package/dist/apps/management/routes/members.js.map +1 -1
  163. package/dist/apps/management/routes/orgs.d.ts +44 -44
  164. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  165. package/dist/apps/management/routes/orgs.js +2 -6
  166. package/dist/apps/management/routes/orgs.js.map +1 -1
  167. package/dist/apps/management/routes/projects.d.ts.map +1 -1
  168. package/dist/apps/management/routes/projects.js +2 -6
  169. package/dist/apps/management/routes/projects.js.map +1 -1
  170. package/dist/apps/management/routes/route-usage.d.ts +34 -34
  171. package/dist/apps/management/routes/route-usage.d.ts.map +1 -1
  172. package/dist/apps/management/routes/route-usage.js +2 -4
  173. package/dist/apps/management/routes/route-usage.js.map +1 -1
  174. package/dist/apps/management/routes/routes-access-requests.d.ts +120 -91
  175. package/dist/apps/management/routes/routes-access-requests.d.ts.map +1 -1
  176. package/dist/apps/management/routes/routes-access-requests.js +49 -9
  177. package/dist/apps/management/routes/routes-access-requests.js.map +1 -1
  178. package/dist/apps/management/routes/usage.d.ts.map +1 -1
  179. package/dist/apps/management/routes/usage.js +8 -16
  180. package/dist/apps/management/routes/usage.js.map +1 -1
  181. package/dist/apps/management/routes/verified-token-requests.d.ts +39 -39
  182. package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -1
  183. package/dist/apps/management/routes/verified-token-requests.js +3 -7
  184. package/dist/apps/management/routes/verified-token-requests.js.map +1 -1
  185. package/dist/apps/management/routes/webhooks.d.ts +2089 -2089
  186. package/dist/apps/management/routes/zone-delegations.d.ts +14 -17
  187. package/dist/apps/management/routes/zone-delegations.d.ts.map +1 -1
  188. package/dist/apps/management/routes/zone-delegations.js +1 -6
  189. package/dist/apps/management/routes/zone-delegations.js.map +1 -1
  190. package/dist/apps/mpp/App.d.ts +44 -44
  191. package/dist/apps/mpp/App.js +3 -3
  192. package/dist/apps/mpp/App.js.map +1 -1
  193. package/dist/apps/mpp/internal/Adapter.js +1 -1
  194. package/dist/apps/mpp/internal/Adapter.js.map +1 -1
  195. package/dist/apps/mpp/internal/Error.d.ts.map +1 -1
  196. package/dist/apps/mpp/internal/Error.js +1 -0
  197. package/dist/apps/mpp/internal/Error.js.map +1 -1
  198. package/dist/apps/mpp/internal/Policy.d.ts +5 -1
  199. package/dist/apps/mpp/internal/Policy.d.ts.map +1 -1
  200. package/dist/apps/mpp/internal/Policy.js +35 -13
  201. package/dist/apps/mpp/internal/Policy.js.map +1 -1
  202. package/dist/apps/relay/App.d.ts +2 -2
  203. package/dist/apps/relay/App.d.ts.map +1 -1
  204. package/dist/apps/relay/App.js +25 -16
  205. package/dist/apps/relay/App.js.map +1 -1
  206. package/dist/apps/routes/App.d.ts +311 -311
  207. package/dist/apps/routes/routes/deposit-addresses.d.ts +1099 -1099
  208. package/dist/apps/routes/routes/list.d.ts +74 -74
  209. package/dist/apps/routes/routes/transfers.d.ts +94 -94
  210. package/dist/cli/ApiKeys.d.ts +19 -26
  211. package/dist/cli/ApiKeys.d.ts.map +1 -1
  212. package/dist/db/Db.d.ts +3 -3
  213. package/dist/db/Db.d.ts.map +1 -1
  214. package/dist/db/Db.js +27 -0
  215. package/dist/db/Db.js.map +1 -1
  216. package/dist/db/Schema.d.ts +28 -62
  217. package/dist/db/Schema.d.ts.map +1 -1
  218. package/dist/db/Schema.js +18 -18
  219. package/dist/db/Schema.js.map +1 -1
  220. package/dist/db/tables/rewardRuns.d.ts +21 -0
  221. package/dist/db/tables/rewardRuns.d.ts.map +1 -1
  222. package/dist/db/tables/rewardRuns.js +19 -0
  223. package/dist/db/tables/rewardRuns.js.map +1 -1
  224. package/dist/db/tables/routesAccessRequests.d.ts +13 -0
  225. package/dist/db/tables/routesAccessRequests.d.ts.map +1 -1
  226. package/dist/db/tables/routesAccessRequests.js +12 -0
  227. package/dist/db/tables/routesAccessRequests.js.map +1 -1
  228. package/dist/db/tables/zoneDelegationRequests.d.ts +165 -0
  229. package/dist/db/tables/zoneDelegationRequests.d.ts.map +1 -0
  230. package/dist/db/tables/{zoneKeyRequests.js → zoneDelegationRequests.js} +43 -42
  231. package/dist/db/tables/zoneDelegationRequests.js.map +1 -0
  232. package/dist/db/tables/zoneDelegations.d.ts +11 -5
  233. package/dist/db/tables/zoneDelegations.d.ts.map +1 -1
  234. package/dist/db/tables/zoneDelegations.js +21 -3
  235. package/dist/db/tables/zoneDelegations.js.map +1 -1
  236. package/dist/handlers/internal/accounts.d.ts +7 -0
  237. package/dist/handlers/internal/accounts.d.ts.map +1 -0
  238. package/dist/handlers/internal/{multisig.js → accounts.js} +10 -10
  239. package/dist/handlers/internal/{multisig.js.map → accounts.js.map} +1 -1
  240. package/dist/handlers/internal/sponsorship.js +17 -17
  241. package/dist/handlers/internal/sponsorship.js.map +1 -1
  242. package/dist/handlers/relay.d.ts +6 -6
  243. package/dist/handlers/relay.d.ts.map +1 -1
  244. package/dist/handlers/relay.js +15 -15
  245. package/dist/handlers/relay.js.map +1 -1
  246. package/dist/internal/Auth.d.ts.map +1 -1
  247. package/dist/internal/Auth.js +4 -8
  248. package/dist/internal/Auth.js.map +1 -1
  249. package/dist/internal/EarnRates.d.ts.map +1 -1
  250. package/dist/internal/EarnRates.js +1 -3
  251. package/dist/internal/EarnRates.js.map +1 -1
  252. package/dist/internal/EarnVaults.js +2 -2
  253. package/dist/internal/Log.d.ts +2 -4
  254. package/dist/internal/Log.d.ts.map +1 -1
  255. package/dist/internal/Log.js +0 -3
  256. package/dist/internal/Log.js.map +1 -1
  257. package/dist/internal/Response.d.ts +1 -1
  258. package/dist/internal/Response.d.ts.map +1 -1
  259. package/dist/internal/Response.js +0 -3
  260. package/dist/internal/Response.js.map +1 -1
  261. package/dist/internal/Schema.d.ts +11 -0
  262. package/dist/internal/Schema.d.ts.map +1 -1
  263. package/dist/internal/Schema.js +24 -1
  264. package/dist/internal/Schema.js.map +1 -1
  265. package/dist/internal/ZoneAccess.d.ts +70 -35
  266. package/dist/internal/ZoneAccess.d.ts.map +1 -1
  267. package/dist/internal/ZoneAccess.js +312 -131
  268. package/dist/internal/ZoneAccess.js.map +1 -1
  269. package/dist/internal/ZoneDelegation.d.ts +7 -19
  270. package/dist/internal/ZoneDelegation.d.ts.map +1 -1
  271. package/dist/internal/ZoneDelegation.js +8 -29
  272. package/dist/internal/ZoneDelegation.js.map +1 -1
  273. package/dist/internal/{ZoneKeyApproval.d.ts → ZoneDelegationApproval.d.ts} +3 -5
  274. package/dist/internal/ZoneDelegationApproval.d.ts.map +1 -0
  275. package/dist/internal/ZoneDelegationApproval.js +16 -0
  276. package/dist/internal/ZoneDelegationApproval.js.map +1 -0
  277. package/dist/internal/ZoneKeyRewrite.d.ts +98 -0
  278. package/dist/internal/ZoneKeyRewrite.d.ts.map +1 -0
  279. package/dist/internal/ZoneKeyRewrite.js +151 -0
  280. package/dist/internal/ZoneKeyRewrite.js.map +1 -0
  281. package/dist/internal/rewards/Campaigns.d.ts +2 -0
  282. package/dist/internal/rewards/Campaigns.d.ts.map +1 -1
  283. package/dist/internal/rewards/Campaigns.js.map +1 -1
  284. package/dist/internal/rewards/Distributions.d.ts.map +1 -1
  285. package/dist/internal/rewards/Distributions.js +2 -6
  286. package/dist/internal/rewards/Distributions.js.map +1 -1
  287. package/dist/internal/rewards/Monitor.d.ts +4 -1
  288. package/dist/internal/rewards/Monitor.d.ts.map +1 -1
  289. package/dist/internal/rewards/Monitor.js +19 -2
  290. package/dist/internal/rewards/Monitor.js.map +1 -1
  291. package/dist/internal/rewards/Runner.d.ts.map +1 -1
  292. package/dist/internal/rewards/Runner.js +9 -1
  293. package/dist/internal/rewards/Runner.js.map +1 -1
  294. package/dist/internal/routes/Action.d.ts.map +1 -1
  295. package/dist/internal/routes/Action.js +2 -3
  296. package/dist/internal/routes/Action.js.map +1 -1
  297. package/dist/internal/routes/Deposit.d.ts.map +1 -1
  298. package/dist/internal/routes/Deposit.js +2 -6
  299. package/dist/internal/routes/Deposit.js.map +1 -1
  300. package/dist/internal/routes/DepositAddress.d.ts.map +1 -1
  301. package/dist/internal/routes/DepositAddress.js +4 -10
  302. package/dist/internal/routes/DepositAddress.js.map +1 -1
  303. package/dist/internal/routes/Policy.d.ts.map +1 -1
  304. package/dist/internal/routes/Policy.js +2 -1
  305. package/dist/internal/routes/Policy.js.map +1 -1
  306. package/dist/internal/routes/Provider.js +8 -8
  307. package/dist/internal/routes/Provider.js.map +1 -1
  308. package/dist/internal/routes/Reconciliation.d.ts.map +1 -1
  309. package/dist/internal/routes/Reconciliation.js +3 -2
  310. package/dist/internal/routes/Reconciliation.js.map +1 -1
  311. package/dist/internal/routes/Transfer.d.ts.map +1 -1
  312. package/dist/internal/routes/Transfer.js +4 -12
  313. package/dist/internal/routes/Transfer.js.map +1 -1
  314. package/dist/internal/routes/UniversalDepositAddress.d.ts.map +1 -1
  315. package/dist/internal/routes/UniversalDepositAddress.js +3 -6
  316. package/dist/internal/routes/UniversalDepositAddress.js.map +1 -1
  317. package/dist/internal/routes/providers/SquidOrder.d.ts.map +1 -1
  318. package/dist/internal/routes/providers/SquidOrder.js +3 -2
  319. package/dist/internal/routes/providers/SquidOrder.js.map +1 -1
  320. package/dist/internal/routes/providers/relay.d.ts.map +1 -1
  321. package/dist/internal/routes/providers/relay.js +4 -3
  322. package/dist/internal/routes/providers/relay.js.map +1 -1
  323. package/package.json +20 -5
  324. package/src/ApiKey.ts +2 -2
  325. package/src/ApiKeys.test.ts +284 -51
  326. package/src/ApiKeys.ts +297 -64
  327. package/src/App.ts +4 -5
  328. package/src/Client.test-d.ts +33 -126
  329. package/src/Scope.test.ts +26 -45
  330. package/src/Scope.ts +24 -37
  331. package/src/admin/App.test.ts +51 -4
  332. package/src/admin/App.ts +25 -33
  333. package/src/admin/Mcp.test.ts +2 -1
  334. package/src/admin/apps/api-keys.test.ts +90 -20
  335. package/src/admin/apps/api-keys.ts +5 -8
  336. package/src/admin/apps/audit-logs.ts +1 -1
  337. package/src/admin/apps/earn-vaults.test.ts +19 -19
  338. package/src/admin/apps/earn-vaults.ts +11 -5
  339. package/src/admin/apps/routes-support.test.ts +254 -3
  340. package/src/admin/apps/routes-support.ts +1 -44
  341. package/src/admin/apps/scopes.test.ts +3 -3
  342. package/src/admin/apps/zone-delegation-requests.test.ts +246 -0
  343. package/src/admin/apps/zone-delegation-requests.ts +240 -0
  344. package/src/admin/apps/zone-delegations.test.ts +218 -58
  345. package/src/admin/apps/zone-delegations.ts +146 -46
  346. package/src/admin/index.ts +1 -1
  347. package/src/admin/ui/src/components/ApiKeyForm.tsx +4 -3
  348. package/src/admin/ui/src/lib/config.ts +3 -3
  349. package/src/admin/ui/src/lib/query-client.ts +1 -1
  350. package/src/admin/ui/src/lib/review-queues.ts +10 -10
  351. package/src/admin/ui/src/lib/routes-setup.test.ts +1 -2
  352. package/src/admin/ui/src/lib/routes-setup.ts +3 -5
  353. package/src/admin/ui/src/lib/scopes.test.ts +3 -6
  354. package/src/admin/ui/src/lib/scopes.ts +2 -9
  355. package/src/admin/ui/src/lib/{zone-key-requests.ts → zone-delegation-requests.ts} +27 -31
  356. package/src/admin/ui/src/routeTree.gen.ts +17 -17
  357. package/src/admin/ui/src/routes/__root.tsx +1 -1
  358. package/src/admin/ui/src/routes/api-keys.tsx +2 -15
  359. package/src/admin/ui/src/routes/audit-logs.tsx +2 -2
  360. package/src/admin/ui/src/routes/index.tsx +11 -11
  361. package/src/admin/ui/src/routes/organizations.tsx +3 -1
  362. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +3 -1
  363. package/src/admin/ui/src/routes/organizations_.$orgId_.setup.tsx +1 -3
  364. package/src/admin/ui/src/routes/{zone-key-requests.tsx → zone-delegation-requests.tsx} +228 -172
  365. package/src/admin/ui/vite.config.ts +14 -1
  366. package/src/admin/ui.generated.ts +412 -583
  367. package/src/analytics/tables/requestEvents.test.ts +13 -3
  368. package/src/analytics/tables/requestEvents.ts +4 -3
  369. package/src/apps/data/App.test.ts +72 -35
  370. package/src/apps/data/App.ts +29 -13
  371. package/src/apps/data/ZoneSelection.test.ts +53 -70
  372. package/src/apps/data/ZoneSelection.ts +2 -2
  373. package/src/apps/data/routes/activities.test.ts +140 -6
  374. package/src/apps/data/routes/activities.ts +124 -18
  375. package/src/apps/data/routes/balances.ts +5 -1
  376. package/src/apps/data/routes/blocks.ts +9 -13
  377. package/src/apps/data/routes/earn-transactions.local.test.ts +6 -1
  378. package/src/apps/data/routes/earn-transactions.test.ts +7 -3
  379. package/src/apps/data/routes/earn.bench.ts +0 -14
  380. package/src/apps/data/routes/earn.discovery.test.ts +4 -39
  381. package/src/apps/data/routes/earn.local.test.ts +27 -34
  382. package/src/apps/data/routes/earn.test.ts +172 -490
  383. package/src/apps/data/routes/earn.ts +103 -652
  384. package/src/apps/data/routes/exchanges.ts +20 -30
  385. package/src/apps/data/routes/fee-amm.ts +12 -18
  386. package/src/apps/data/routes/indexer.test.ts +14 -12
  387. package/src/apps/data/routes/receipts.ts +1 -1
  388. package/src/apps/data/routes/rpc.test.ts +23 -18
  389. package/src/apps/data/routes/rpc.ts +21 -21
  390. package/src/apps/data/routes/rpc.zone.test.ts +60 -6
  391. package/src/apps/data/routes/tokenlist.ts +4 -6
  392. package/src/apps/data/routes/tokens.test.ts +20 -0
  393. package/src/apps/data/routes/tokens.ts +10 -5
  394. package/src/apps/data/routes/transactions.ts +3 -3
  395. package/src/apps/data/routes/transfers.ts +4 -6
  396. package/src/apps/data/routes/valuation.test.ts +2 -4
  397. package/src/apps/data/routes/valuation.ts +4 -6
  398. package/src/apps/data/routes/webhooks.test.ts +1 -5
  399. package/src/apps/data/routes/webhooks.ts +30 -44
  400. package/src/apps/data/routes/zones.test.ts +35 -19
  401. package/src/apps/management/App.ts +3 -1
  402. package/src/apps/management/routes/api-keys.test.ts +112 -22
  403. package/src/apps/management/routes/api-keys.ts +30 -15
  404. package/src/apps/management/routes/billing.ts +17 -25
  405. package/src/apps/management/routes/cli.ts +12 -18
  406. package/src/apps/management/routes/fee-payer-usage.ts +5 -4
  407. package/src/apps/management/routes/invitations.ts +16 -21
  408. package/src/apps/management/routes/invite-links.ts +18 -26
  409. package/src/apps/management/routes/me.ts +9 -13
  410. package/src/apps/management/routes/members.ts +5 -7
  411. package/src/apps/management/routes/orgs.ts +8 -12
  412. package/src/apps/management/routes/projects.ts +8 -12
  413. package/src/apps/management/routes/route-usage.ts +5 -4
  414. package/src/apps/management/routes/routes-access-requests.test.ts +64 -2
  415. package/src/apps/management/routes/routes-access-requests.ts +99 -15
  416. package/src/apps/management/routes/scopes.test.ts +3 -3
  417. package/src/apps/management/routes/usage.ts +20 -28
  418. package/src/apps/management/routes/verified-token-requests.ts +9 -13
  419. package/src/apps/management/routes/zone-delegations.test.ts +3 -5
  420. package/src/apps/management/routes/zone-delegations.ts +4 -9
  421. package/src/apps/mpp/App.ts +3 -3
  422. package/src/apps/mpp/Mpp.zone.test.ts +49 -18
  423. package/src/apps/mpp/internal/Adapter.ts +1 -1
  424. package/src/apps/mpp/internal/Error.test.ts +3 -0
  425. package/src/apps/mpp/internal/Error.ts +1 -0
  426. package/src/apps/mpp/internal/Policy.test.ts +9 -8
  427. package/src/apps/mpp/internal/Policy.ts +37 -14
  428. package/src/apps/relay/App.sandbox.test.ts +15 -14
  429. package/src/apps/relay/App.test.ts +120 -61
  430. package/src/apps/relay/App.ts +28 -18
  431. package/src/apps/relay/App.zone-billing.test.ts +9 -4
  432. package/src/apps/routes/App.test.ts +21 -22
  433. package/src/db/Db.ts +34 -3
  434. package/src/db/Schema.ts +26 -26
  435. package/src/db/tables/rewardRuns.ts +39 -0
  436. package/src/db/tables/routesAccessRequests.test.ts +65 -0
  437. package/src/db/tables/routesAccessRequests.ts +25 -0
  438. package/src/db/tables/{zoneKeyRequests.test.ts → zoneDelegationRequests.test.ts} +36 -77
  439. package/src/db/tables/{zoneKeyRequests.ts → zoneDelegationRequests.ts} +69 -52
  440. package/src/db/tables/zoneDelegations.test.ts +5 -6
  441. package/src/db/tables/zoneDelegations.ts +25 -8
  442. package/src/handlers/internal/{multisig.store.test.ts → accounts.store.test.ts} +16 -16
  443. package/src/handlers/internal/{multisig.test.ts → accounts.test.ts} +85 -82
  444. package/src/handlers/internal/{multisig.ts → accounts.ts} +9 -9
  445. package/src/handlers/internal/sponsorship.ts +20 -20
  446. package/src/handlers/relay.test.ts +48 -46
  447. package/src/handlers/relay.ts +26 -26
  448. package/src/internal/Auth.test.ts +6 -2
  449. package/src/internal/Auth.ts +4 -10
  450. package/src/internal/EarnRates.ts +4 -6
  451. package/src/internal/EarnVaults.ts +2 -2
  452. package/src/internal/Log.test-d.ts +1 -0
  453. package/src/internal/Log.test.ts +4 -6
  454. package/src/internal/Log.ts +2 -6
  455. package/src/internal/Response.ts +0 -3
  456. package/src/internal/Schema.test.ts +42 -0
  457. package/src/internal/Schema.ts +36 -1
  458. package/src/internal/ZoneAccess.test.ts +515 -312
  459. package/src/internal/ZoneAccess.ts +410 -158
  460. package/src/internal/ZoneDelegation.test.ts +1 -41
  461. package/src/internal/ZoneDelegation.ts +10 -48
  462. package/src/internal/ZoneDelegationApproval.test.ts +22 -0
  463. package/src/internal/{ZoneKeyApproval.ts → ZoneDelegationApproval.ts} +4 -11
  464. package/src/internal/ZoneKeyRewrite.ts +231 -0
  465. package/src/internal/rewards/Campaigns.ts +2 -0
  466. package/src/internal/rewards/Distributions.ts +8 -12
  467. package/src/internal/rewards/Monitor.test.ts +64 -0
  468. package/src/internal/rewards/Monitor.ts +27 -4
  469. package/src/internal/rewards/Runner.ts +9 -1
  470. package/src/internal/routes/Action.ts +5 -6
  471. package/src/internal/routes/Deposit.ts +8 -12
  472. package/src/internal/routes/DepositAddress.ts +13 -19
  473. package/src/internal/routes/Policy.ts +2 -1
  474. package/src/internal/routes/Provider.ts +8 -8
  475. package/src/internal/routes/Reconciliation.ts +3 -2
  476. package/src/internal/routes/Transfer.ts +16 -24
  477. package/src/internal/routes/UniversalDepositAddress.ts +9 -12
  478. package/src/internal/routes/providers/SquidOrder.ts +3 -2
  479. package/src/internal/routes/providers/relay.ts +4 -3
  480. package/dist/admin/apps/zone-key-requests.d.ts +0 -406
  481. package/dist/admin/apps/zone-key-requests.d.ts.map +0 -1
  482. package/dist/admin/apps/zone-key-requests.js +0 -183
  483. package/dist/admin/apps/zone-key-requests.js.map +0 -1
  484. package/dist/db/tables/zoneKeyRequests.d.ts +0 -289
  485. package/dist/db/tables/zoneKeyRequests.d.ts.map +0 -1
  486. package/dist/db/tables/zoneKeyRequests.js.map +0 -1
  487. package/dist/handlers/internal/multisig.d.ts +0 -7
  488. package/dist/handlers/internal/multisig.d.ts.map +0 -1
  489. package/dist/internal/ZoneKeyApproval.d.ts.map +0 -1
  490. package/dist/internal/ZoneKeyApproval.js +0 -21
  491. package/dist/internal/ZoneKeyApproval.js.map +0 -1
  492. package/src/admin/apps/zone-key-requests.test.ts +0 -272
  493. package/src/admin/apps/zone-key-requests.ts +0 -231
  494. package/src/internal/ZoneKeyApproval.test.ts +0 -46
@@ -643,12 +643,10 @@ export namespace schema {
643
643
  context: z
644
644
  .optional(Context)
645
645
  .check(z.meta({ examples: [{ title: 'Prod USDC large transfers' }] })),
646
- createdAt: z.iso
647
- .datetime()
648
- .check(
649
- z.describe('When the subscription was created, as an ISO 8601 timestamp.'),
650
- z.meta({ examples: ['2024-01-01T00:00:00Z'] }),
651
- ),
646
+ createdAt: Schema.dateTime().check(
647
+ z.describe('When the subscription was created, as an ISO 8601 timestamp.'),
648
+ z.meta({ examples: ['2024-01-01T00:00:00Z'] }),
649
+ ),
652
650
  destination: PublicDestination,
653
651
  environment: z
654
652
  .optional(z.enum(['production', 'sandbox']))
@@ -658,7 +656,7 @@ export namespace schema {
658
656
  ),
659
657
  eventType: EventType.check(z.meta({ examples: ['token:transfer'] })),
660
658
  expiresAt: z
661
- .optional(z.iso.datetime())
659
+ .optional(Schema.dateTime())
662
660
  .check(
663
661
  z.describe(
664
662
  'When the subscription expires, as an ISO 8601 timestamp. Only MPP-paid subscriptions expire.',
@@ -686,7 +684,7 @@ export namespace schema {
686
684
  z.meta({ examples: ['wh_001718668800000_2ZPE2gvateYEQ0dQslgvkhjx'] }),
687
685
  ),
688
686
  lastDeliveryAt: z
689
- .optional(z.iso.datetime())
687
+ .optional(Schema.dateTime())
690
688
  .check(
691
689
  z.describe(
692
690
  'When Tempo last delivered an event successfully, as an ISO 8601 timestamp. Refreshed at most once per minute during sustained delivery.',
@@ -694,12 +692,10 @@ export namespace schema {
694
692
  z.meta({ examples: ['2024-01-01T00:00:00Z'] }),
695
693
  ),
696
694
  status: Status.check(z.meta({ examples: ['active'] })),
697
- updatedAt: z.iso
698
- .datetime()
699
- .check(
700
- z.describe('When the subscription was last changed, as an ISO 8601 timestamp.'),
701
- z.meta({ examples: ['2024-01-01T00:00:00Z'] }),
702
- ),
695
+ updatedAt: Schema.dateTime().check(
696
+ z.describe('When the subscription was last changed, as an ISO 8601 timestamp.'),
697
+ z.meta({ examples: ['2024-01-01T00:00:00Z'] }),
698
+ ),
703
699
  })
704
700
  .check(
705
701
  z.describe(
@@ -729,12 +725,10 @@ export namespace schema {
729
725
  z.describe('Retry attempt number for this delivery, starting at 1.'),
730
726
  z.meta({ examples: [1] }),
731
727
  ),
732
- createdAt: z.iso
733
- .datetime()
734
- .check(
735
- z.describe('When this delivery attempt was created, as an ISO 8601 timestamp.'),
736
- z.meta({ examples: ['2024-01-01T00:00:00Z'] }),
737
- ),
728
+ createdAt: Schema.dateTime().check(
729
+ z.describe('When this delivery attempt was created, as an ISO 8601 timestamp.'),
730
+ z.meta({ examples: ['2024-01-01T00:00:00Z'] }),
731
+ ),
738
732
  error: z
739
733
  .optional(z.string())
740
734
  .check(
@@ -822,12 +816,10 @@ export namespace schema {
822
816
  z.describe('Address that sent the transfer.'),
823
817
  z.meta({ examples: ['0xe7687128b0a808c2831ff94d4f7b2fb35c65af38'] }),
824
818
  ),
825
- timestamp: z.iso
826
- .datetime()
827
- .check(
828
- z.describe('Block timestamp as an ISO 8601 string.'),
829
- z.meta({ examples: ['2026-01-14T18:38:03.685Z'] }),
830
- ),
819
+ timestamp: Schema.dateTime().check(
820
+ z.describe('Block timestamp as an ISO 8601 string.'),
821
+ z.meta({ examples: ['2026-01-14T18:38:03.685Z'] }),
822
+ ),
831
823
  token: z.optional(
832
824
  z
833
825
  .object({
@@ -954,12 +946,10 @@ export namespace schema {
954
946
  z.describe('Index of the log within its block.'),
955
947
  z.meta({ examples: [0] }),
956
948
  ),
957
- timestamp: z.iso
958
- .datetime()
959
- .check(
960
- z.describe('When the block was produced, as an ISO 8601 timestamp.'),
961
- z.meta({ examples: ['2026-01-14T18:38:03.685Z'] }),
962
- ),
949
+ timestamp: Schema.dateTime().check(
950
+ z.describe('When the block was produced, as an ISO 8601 timestamp.'),
951
+ z.meta({ examples: ['2026-01-14T18:38:03.685Z'] }),
952
+ ),
963
953
  topics: z.array(Schema.Hash).check(
964
954
  z.describe(
965
955
  'Zero to four log topics; non-anonymous events start with the event signature hash.',
@@ -1051,12 +1041,10 @@ export namespace schema {
1051
1041
  z.describe('Number of transactions included in this block.'),
1052
1042
  z.meta({ examples: [3] }),
1053
1043
  ),
1054
- timestamp: z.iso
1055
- .datetime()
1056
- .check(
1057
- z.describe('When the block was produced, as an ISO 8601 timestamp.'),
1058
- z.meta({ examples: ['2026-01-14T18:38:03.685Z'] }),
1059
- ),
1044
+ timestamp: Schema.dateTime().check(
1045
+ z.describe('When the block was produced, as an ISO 8601 timestamp.'),
1046
+ z.meta({ examples: ['2026-01-14T18:38:03.685Z'] }),
1047
+ ),
1060
1048
  })
1061
1049
  .check(z.describe('Payload for a `block:created` event (a new block header).')),
1062
1050
  'WebhookBlockEvent',
@@ -1089,12 +1077,10 @@ export namespace schema {
1089
1077
  context: z
1090
1078
  .optional(Context)
1091
1079
  .check(z.meta({ examples: [{ title: 'Prod USDC large transfers' }] })),
1092
- createdAt: z.iso
1093
- .datetime()
1094
- .check(
1095
- z.describe('When Tempo built this delivery envelope, as an ISO 8601 timestamp.'),
1096
- z.meta({ examples: ['2026-01-14T18:38:03.000Z'] }),
1097
- ),
1080
+ createdAt: Schema.dateTime().check(
1081
+ z.describe('When Tempo built this delivery envelope, as an ISO 8601 timestamp.'),
1082
+ z.meta({ examples: ['2026-01-14T18:38:03.000Z'] }),
1083
+ ),
1098
1084
  id: z
1099
1085
  .string()
1100
1086
  .check(
@@ -95,16 +95,23 @@ function errorCodes(
95
95
  return resolveSchema(document, envelope?.properties?.['error'])?.properties?.['code']?.enum
96
96
  }
97
97
 
98
- const zoneReader = {
99
- id: 'key_zone_withdrawal',
100
- orgId: 'org_test',
101
- scopes: ['data:read', `zone:${runtime.zone.chainId}:read`],
102
- token: 'secret_zone_withdrawal',
103
- } satisfies TestApp.kvStore.Key
98
+ const zoneReader = TestApp.zoneReader(
99
+ {
100
+ id: 'key_zone_withdrawal',
101
+ orgId: 'org_test',
102
+ scopes: ['data:read'],
103
+ token: 'secret_zone_withdrawal',
104
+ },
105
+ [runtime.zone.chainId],
106
+ )
107
+ /** Holds the readers' organization delegation for the Zone. */
108
+ const db = TestApp.database()
109
+ await TestApp.delegateZone(db, { chainIds: [runtime.zone.chainId, runtime.zone.chainId + 1] })
104
110
 
105
111
  function client(options: { keys?: readonly TestApp.kvStore.Key[] | undefined } = {}) {
106
112
  return TestApp.client({
107
113
  auth: { keys: options.keys ?? [zoneReader] },
114
+ db,
108
115
  zones: [
109
116
  TestApp.zone({
110
117
  chainId: runtime.zone.chainId,
@@ -330,12 +337,15 @@ describe('GET /v1/zones/withdrawals/:senderTag', () => {
330
337
  )
331
338
 
332
339
  test('requires the matching Zone read scope', async () => {
333
- const otherReader = {
334
- id: 'key_other_zone_withdrawal',
335
- orgId: 'org_test',
336
- scopes: ['data:read', `zone:${runtime.zone.chainId + 1}:read`],
337
- token: 'secret_other_zone_withdrawal',
338
- } satisfies TestApp.kvStore.Key
340
+ const otherReader = TestApp.zoneReader(
341
+ {
342
+ id: 'key_other_zone_withdrawal',
343
+ orgId: 'org_test',
344
+ scopes: ['data:read'],
345
+ token: 'secret_other_zone_withdrawal',
346
+ },
347
+ [runtime.zone.chainId + 1],
348
+ )
339
349
  const response = await client({ keys: [otherReader] }).v1.zones.withdrawals[':senderTag'].$get(
340
350
  {
341
351
  param: { senderTag },
@@ -348,7 +358,7 @@ describe('GET /v1/zones/withdrawals/:senderTag', () => {
348
358
  expect(response.status).toBe(403)
349
359
  expect(body.error.code).toBe('api_key_forbidden')
350
360
  expect(body.error.message).toBe(
351
- `Chain ID ${runtime.zone.chainId} is a Zone. Use an API key granting \`zone:${runtime.zone.chainId}:read\`.`,
361
+ `Chain ID ${runtime.zone.chainId} is a Zone. Use an API key granting \`zone:read\` and tagged for Zone ${runtime.zone.chainId}, issued to an organization delegated that Zone.`,
352
362
  )
353
363
  })
354
364
 
@@ -369,6 +379,7 @@ describe('GET /v1/zones/withdrawals/:senderTag', () => {
369
379
  test('rejects invalid sender tags and chain ids', async () => {
370
380
  const app = TestApp.create({
371
381
  auth: { keys: [zoneReader] },
382
+ db,
372
383
  zones: [
373
384
  TestApp.zone({
374
385
  chainId: runtime.zone.chainId,
@@ -408,6 +419,7 @@ describe('GET /v1/zones/withdrawals/:senderTag', () => {
408
419
  test('maps parent indexer failures to the upstream error contract', async () => {
409
420
  const app = TestApp.create({
410
421
  auth: { keys: [zoneReader] },
422
+ db,
411
423
  tidx: { baseUrl: 'http://127.0.0.1:1' },
412
424
  zones: [
413
425
  TestApp.zone({
@@ -571,12 +583,15 @@ describe('GET /v1/zones/deposits/:depositHash', () => {
571
583
  expect(response.status).toBe(401)
572
584
  expect(body.error.code).toBe('api_key_invalid')
573
585
 
574
- const otherReader = {
575
- id: 'key_other_zone_deposit',
576
- orgId: 'org_test',
577
- scopes: ['data:read', `zone:${runtime.zone.chainId + 1}:read`],
578
- token: 'secret_other_zone_deposit',
579
- } satisfies TestApp.kvStore.Key
586
+ const otherReader = TestApp.zoneReader(
587
+ {
588
+ id: 'key_other_zone_deposit',
589
+ orgId: 'org_test',
590
+ scopes: ['data:read'],
591
+ token: 'secret_other_zone_deposit',
592
+ },
593
+ [runtime.zone.chainId + 1],
594
+ )
580
595
  const denied = await client({ keys: [otherReader] }).v1.zones.deposits[':depositHash'].$get(
581
596
  { param: { depositHash }, query: { chainId: String(runtime.zone.chainId) } },
582
597
  auth(otherReader),
@@ -589,6 +604,7 @@ describe('GET /v1/zones/deposits/:depositHash', () => {
589
604
  test('rejects invalid hashes and unsupported Zones', async () => {
590
605
  const app = TestApp.create({
591
606
  auth: { keys: [zoneReader] },
607
+ db,
592
608
  zones: [
593
609
  TestApp.zone({
594
610
  chainId: runtime.zone.chainId,
@@ -90,7 +90,7 @@ export function management(options: management.Options = {}) {
90
90
  .route('/', orgs())
91
91
  .route('/', projects())
92
92
  .route('/', routeUsage())
93
- .route('/', routesAccessRequests())
93
+ .route('/', routesAccessRequests(options.routesAccessRequests))
94
94
  .route('/', scopes())
95
95
  .route('/', usage())
96
96
  .route('/', verifiedTokenRequests())
@@ -110,6 +110,8 @@ export declare namespace management {
110
110
  consoleUrl?: string | undefined
111
111
  /** Legacy group-level transactional email sender. Prefer `App.create({ email })`. */
112
112
  email?: Email.Sender | undefined
113
+ /** Routes access request options, such as a reviewer notifier; omit to send no notices. */
114
+ routesAccessRequests?: routesAccessRequests.Options | undefined
113
115
  /** Webhook capability shared with the data group and host poller; omit or pass false to disable management. */
114
116
  webhook?: App.Webhook | false | undefined
115
117
  }
@@ -9,6 +9,7 @@ import * as TestApp from '../../../../test/App.js'
9
9
  import * as Containers from '../../../../test/containers.js'
10
10
  import * as ApiKeyAdmissions from '../../../db/tables/apiKeyAdmissions.js'
11
11
  import * as Memberships from '../../../db/tables/memberships.js'
12
+ import * as ZoneDelegations from '../../../db/tables/zoneDelegations.js'
12
13
  import * as Keys from './api-keys.js'
13
14
  import * as Me from './me.js'
14
15
  import * as Orgs from './orgs.js'
@@ -244,7 +245,7 @@ test('publishes generator-ready OpenAPI contracts', async () => {
244
245
  "$ref": "#/components/schemas/ApiKeyMalformedOrBodyInvalidOrParamInvalidError",
245
246
  },
246
247
  "403": {
247
- "$ref": "#/components/schemas/ApiKeyForbiddenOrApiKeyIpForbiddenOrForbiddenOrScopeNotIssuableOrZoneKeyApprovalRequiredError",
248
+ "$ref": "#/components/schemas/ApiKeyForbiddenOrApiKeyIpForbiddenOrForbiddenOrScopeNotIssuableError",
248
249
  },
249
250
  "404": {
250
251
  "$ref": "#/components/schemas/ApiKeysNotEnabledOrOrganizationNotFoundOrProjectNotFoundError",
@@ -337,7 +338,7 @@ test('publishes generator-ready OpenAPI contracts', async () => {
337
338
  "$ref": "#/components/schemas/ApiKeyMalformedOrBodyInvalidOrParamInvalidError",
338
339
  },
339
340
  "403": {
340
- "$ref": "#/components/schemas/ApiKeyForbiddenOrApiKeyIpForbiddenOrForbiddenOrZoneKeyApprovalRequiredError",
341
+ "$ref": "#/components/schemas/ForbiddenError",
341
342
  },
342
343
  "404": {
343
344
  "$ref": "#/components/schemas/ApiKeyNotFoundOrApiKeysNotEnabledOrOrganizationNotFoundOrProjectNotFoundError",
@@ -554,6 +555,7 @@ describe('keys', () => {
554
555
  await Memberships.create(db, { orgId: org.id, role: 'admin', userId: user.id })
555
556
  const base = `/v1/orgs/${org.id}/projects/${project.id}/api-keys`
556
557
  const expiresAt = new Date(Date.now() + 86_400_000).toISOString()
558
+ await TestApp.delegateZone(db, { chainIds: [421700001, 421700002], orgId: org.id })
557
559
 
558
560
  // Tags and expiry only narrow a key an admin could mint anyway, so they need no owner.
559
561
  const created = await TestApp.json(
@@ -617,8 +619,9 @@ describe('keys', () => {
617
619
  )
618
620
  await Memberships.create(db, { orgId: org.id, role: 'admin', userId: user.id })
619
621
  const base = `/v1/orgs/${org.id}/projects/${project.id}/api-keys`
622
+ await TestApp.delegateZone(db, { chainIds: [421700001], orgId: org.id })
620
623
 
621
- // Tags name the Zones the key reads; the organization's delegations bound them at request time.
624
+ // Tags name the Zones the key reads, and must stay within the organization's delegations.
622
625
  const response = await app.request(
623
626
  base,
624
627
  json('POST', admin, { scopes: ['zone:read'], zoneTags: [421700001] }),
@@ -630,6 +633,80 @@ describe('keys', () => {
630
633
  })
631
634
  })
632
635
 
636
+ test('generic Zone scopes are self-serve and bounded by delegated tags', async () => {
637
+ const db = TestApp.database()
638
+ const store = TestApp.kvStore({ keys: [] })
639
+ const app = createApp({ db, kv: { store } })
640
+ const owner = await session(app)
641
+ const { org, project } = await fixture(app, owner)
642
+ const admin = await session(app)
643
+ const user = await TestApp.json(
644
+ await app.request('/v1/me', { headers: { cookie: admin } }),
645
+ Me.schema.User,
646
+ )
647
+ await Memberships.create(db, { orgId: org.id, role: 'admin', userId: user.id })
648
+ const base = `/v1/orgs/${org.id}/projects/${project.id}/api-keys`
649
+ await TestApp.delegateZone(db, { chainIds: [421700001], orgId: org.id })
650
+ const revoked = await ZoneDelegations.create(db, { accountsAll: true, chainId: 421700002, environment: 'production', orgId: org.id, tokensAll: true }) // prettier-ignore
651
+ await ZoneDelegations.revoke(db, revoked.id)
652
+ await TestApp.delegateZone(db, { chainIds: [421700003], environment: 'sandbox', orgId: org.id })
653
+ const mint = async (body: unknown) => {
654
+ const response = await app.request(base, json('POST', admin, body))
655
+ return { body: (await response.json()) as { error?: { code: string; message: string } }, status: response.status } // prettier-ignore
656
+ }
657
+
658
+ // Broadcasts need no delegation coverage at request time, so the delegation's existence bounds the tags at mint.
659
+ expect(await mint({ scopes: ['zone:write'] })).toMatchObject({
660
+ body: {
661
+ error: {
662
+ code: 'body_invalid',
663
+ message:
664
+ 'invalid API key input: Zone scopes require `zoneTags` naming Zones the organization is delegated',
665
+ },
666
+ },
667
+ status: 400,
668
+ })
669
+ // Revoked, other-environment, and unknown Zones are all undelegated, for reads and writes alike.
670
+ for (const scope of ['zone:read', 'zone:write'])
671
+ for (const zoneTags of [[421700002], [421700003], [421700001, 421700009]])
672
+ expect(await mint({ scopes: [scope], zoneTags })).toMatchObject({
673
+ body: {
674
+ error: { code: 'body_invalid', message: expect.stringContaining('not delegated') },
675
+ },
676
+ status: 400,
677
+ })
678
+ expect((await mint({ scopes: ['zone:read'] })).status).toBe(400)
679
+ expect((await mint({ scopes: ['zone:read'], zoneTags: [421700001] })).status).toBe(200)
680
+
681
+ const created = await TestApp.json(
682
+ await app.request(
683
+ base,
684
+ json('POST', admin, { scopes: ['zone:read', 'zone:write'], zoneTags: [421700001] }),
685
+ ),
686
+ Keys.schema.createApiKey.Response,
687
+ )
688
+ expect(created).toMatchObject({ scopes: ['zone:read', 'zone:write'], zoneTags: [421700001] })
689
+ expect(await ApiKeys.resolve(store, created.token)).toMatchObject({ zoneTags: [421700001] })
690
+
691
+ // Edits keep a Zone key inside its delegations; clearing the tags leaves a broadcasting key nowhere to broadcast.
692
+ const widened = await app.request(`${base}/${created.id}`, json('PATCH', admin, { zoneTags: [421700001, 421700002] })) // prettier-ignore
693
+ expect(widened.status).toBe(400)
694
+ expect(await widened.json()).toMatchObject({ error: { code: 'body_invalid' } })
695
+ const cleared = await app.request(`${base}/${created.id}`, json('PATCH', admin, { zoneTags: null })) // prettier-ignore
696
+ expect(cleared.status).toBe(400)
697
+ expect(await ApiKeys.resolve(store, created.token)).toMatchObject({ zoneTags: [421700001] })
698
+ const reader = await TestApp.json(
699
+ await app.request(base, json('POST', admin, { scopes: ['zone:read'], zoneTags: [421700001] })), // prettier-ignore
700
+ Keys.schema.createApiKey.Response,
701
+ )
702
+ const readerWidened = await app.request(`${base}/${reader.id}`, json('PATCH', admin, { zoneTags: [421700002] })) // prettier-ignore
703
+ expect(readerWidened.status).toBe(400)
704
+ // A read key may drop its tags: it then reads no Zone.
705
+ const readerCleared = await app.request(`${base}/${reader.id}`, json('PATCH', admin, { zoneTags: null })) // prettier-ignore
706
+ expect(readerCleared.status).toBe(200)
707
+ expect(await ApiKeys.resolve(store, reader.token)).not.toHaveProperty('zoneTags')
708
+ })
709
+
633
710
  test('derives CLI keys from the source tags and keeps their expiry within the session', async () => {
634
711
  const db = TestApp.database()
635
712
  const store = TestApp.kvStore({ keys: [] })
@@ -637,6 +714,7 @@ describe('keys', () => {
637
714
  const owner = await session(app)
638
715
  const { org, project } = await fixture(app, owner)
639
716
  const base = `/v1/orgs/${org.id}/projects/${project.id}/api-keys`
717
+ await TestApp.delegateZone(db, { chainIds: [421700002], orgId: org.id })
640
718
  const cli = await cliSession(app, owner, ['data:read', 'zone:read'])
641
719
  const mint = (body: unknown) =>
642
720
  app.request(base, { body: JSON.stringify(body), headers: cli.bearer, method: 'POST' })
@@ -675,7 +753,7 @@ describe('keys', () => {
675
753
  expect(shortened.expiresAt).toBe(sooner)
676
754
  })
677
755
 
678
- test('protects Zone read issuance and updates while leaving rotation and revocation open', async () => {
756
+ test('refuses retired per-Zone read scopes and rotates tagged Zone read keys', async () => {
679
757
  const db = TestApp.database()
680
758
  const store = TestApp.kvStore({ keys: [] })
681
759
  const app = createApp({ db, kv: { store } })
@@ -686,15 +764,16 @@ describe('keys', () => {
686
764
  base,
687
765
  superAdminJson('POST', { scopes: ['zone:421700001:read'] }),
688
766
  )
689
- expect(response.status).toBe(403)
690
- expect(await response.json()).toMatchObject({ error: { code: 'zone_key_approval_required' } })
767
+ expect(response.status).toBe(400)
768
+ expect(await response.json()).toMatchObject({ error: { code: 'body_invalid' } })
691
769
  for (const projectId of [project.id, undefined]) {
692
770
  const { record, token } = await ApiKeys.mint(store, {
693
771
  orgId: org.id,
694
772
  ...(projectId ? { projectId } : {}),
695
- scopes: ['zone:421700001:read'],
773
+ scopes: ['zone:read'],
774
+ zoneTags: [421700001],
696
775
  })
697
- // Approved issuance records an admission; rotation copies policies only from admitted keys.
776
+ // Rotation copies policies only from admitted keys.
698
777
  await db.transaction((tx) =>
699
778
  ApiKeyAdmissions.admit(tx, {
700
779
  expiresAt: null,
@@ -707,18 +786,17 @@ describe('keys', () => {
707
786
  }),
708
787
  )
709
788
  const path = projectId ? `${base}/${record.id}` : `/v1/orgs/${org.id}/api-keys/${record.id}`
710
- // Rotation copies the approved scopes unchanged, so it needs no second approval.
711
789
  const rotation = await app.request(`${path}/rotate`, {
712
790
  headers: { cookie: owner },
713
791
  method: 'POST',
714
792
  })
715
793
  expect(rotation.status).toBe(200)
716
794
  const rotated = await TestApp.json(rotation, Keys.schema.rotateApiKey.Response)
717
- expect(rotated.scopes).toEqual(['zone:421700001:read'])
795
+ expect([rotated.scopes, rotated.zoneTags]).toEqual([['zone:read'], [421700001]])
718
796
  expect(await ApiKeys.resolve(store, rotated.token)).not.toBeNull()
719
797
  if (projectId) {
720
798
  const update = await app.request(path, json('PATCH', owner, { allowedIps: [] }))
721
- expect(update.status).toBe(403)
799
+ expect(update.status).toBe(200)
722
800
  }
723
801
  expect(await ApiKeys.resolve(store, token)).not.toBeNull()
724
802
  expect(
@@ -729,11 +807,13 @@ describe('keys', () => {
729
807
  })
730
808
 
731
809
  test('rotates a super-admin-issued Zone write key without delegating scope issuance', async () => {
810
+ const db = TestApp.database()
732
811
  const store = TestApp.kvStore({ keys: [] })
733
- const app = createApp({ kv: { store } })
812
+ const app = createApp({ db, kv: { store } })
734
813
  const owner = await session(app)
735
814
  const { org, project } = await fixture(app, owner)
736
815
  const base = `/v1/orgs/${org.id}/projects/${project.id}/api-keys`
816
+ await TestApp.delegateZone(db, { chainIds: [421700001], orgId: org.id })
737
817
  const source = await TestApp.json(
738
818
  await app.request(
739
819
  base,
@@ -741,7 +821,8 @@ describe('keys', () => {
741
821
  allowedIps: ['203.0.113.0/24'],
742
822
  environment: 'production',
743
823
  name: 'Privy',
744
- scopes: ['zone:421700001:write'],
824
+ scopes: ['zone:write'],
825
+ zoneTags: [421700001],
745
826
  }),
746
827
  ),
747
828
  Keys.schema.createApiKey.Response,
@@ -759,7 +840,8 @@ describe('keys', () => {
759
840
  name: 'Privy',
760
841
  orgId: org.id,
761
842
  projectId: project.id,
762
- scopes: ['zone:421700001:write'],
843
+ scopes: ['zone:write'],
844
+ zoneTags: [421700001],
763
845
  })
764
846
  expect(replacement.createdBy).not.toBe('super_admin')
765
847
  expect(replacement.id).not.toBe(source.id)
@@ -834,10 +916,12 @@ describe('keys', () => {
834
916
  )
835
917
 
836
918
  test('rotates an organization-level super-admin-issued zone key', async () => {
919
+ const db = TestApp.database()
837
920
  const store = TestApp.kvStore({ keys: [] })
838
- const app = createApp({ kv: { store } })
921
+ const app = createApp({ db, kv: { store } })
839
922
  const owner = await session(app)
840
923
  const { org, project } = await fixture(app, owner)
924
+ await TestApp.delegateZone(db, { chainIds: [421700001], orgId: org.id })
841
925
  const { record: projectSource } = await ApiKeys.mint(store, {
842
926
  orgId: org.id,
843
927
  projectId: project.id,
@@ -859,7 +943,8 @@ describe('keys', () => {
859
943
  environment: 'production',
860
944
  name: 'Privy',
861
945
  orgId: org.id,
862
- scopes: ['zone:421700001:write'],
946
+ scopes: ['zone:write'],
947
+ zoneTags: [421700001],
863
948
  })
864
949
 
865
950
  const response = await app.request(`/v1/orgs/${org.id}/api-keys/${source.id}/rotate`, {
@@ -872,7 +957,8 @@ describe('keys', () => {
872
957
  environment: 'production',
873
958
  name: 'Privy',
874
959
  orgId: org.id,
875
- scopes: ['zone:421700001:write'],
960
+ scopes: ['zone:write'],
961
+ zoneTags: [421700001],
876
962
  })
877
963
  expect(replacement.projectId).toBeUndefined()
878
964
  expect(replacement.createdBy).not.toBe('super_admin')
@@ -913,10 +999,12 @@ describe('keys', () => {
913
999
  projectId: project.id,
914
1000
  scopes: ['data:read', 'webhooks:write'],
915
1001
  })
1002
+ await TestApp.delegateZone(db, { chainIds: [421700001], orgId: org.id })
916
1003
  const { record: zone } = await ApiKeys.mintBounded(db, store, {
917
1004
  createdBy: 'super_admin',
918
1005
  orgId: org.id,
919
- scopes: ['zone:421700001:write'],
1006
+ scopes: ['zone:write'],
1007
+ zoneTags: [421700001],
920
1008
  })
921
1009
  const paths = [`${base}/${webhooks.id}`, `/v1/orgs/${org.id}/api-keys/${zone.id}`]
922
1010
 
@@ -950,8 +1038,10 @@ describe('keys', () => {
950
1038
  },
951
1039
  {
952
1040
  "caller": "admin",
953
- "code": "scope_not_issuable",
954
- "status": 403,
1041
+ "scopes": [
1042
+ "zone:write",
1043
+ ],
1044
+ "status": 200,
955
1045
  },
956
1046
  {
957
1047
  "caller": "owner",
@@ -964,7 +1054,7 @@ describe('keys', () => {
964
1054
  {
965
1055
  "caller": "owner",
966
1056
  "scopes": [
967
- "zone:421700001:write",
1057
+ "zone:write",
968
1058
  ],
969
1059
  "status": 200,
970
1060
  },
@@ -1047,7 +1137,7 @@ describe('keys', () => {
1047
1137
  const source = await TestApp.json(
1048
1138
  await app.request(
1049
1139
  `/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
1050
- superAdminJson('POST', { scopes: ['zone:421700001:write'] }),
1140
+ superAdminJson('POST', { scopes: ['webhooks:write'] }),
1051
1141
  ),
1052
1142
  Keys.schema.createApiKey.Response,
1053
1143
  )
@@ -37,12 +37,10 @@ export namespace schema {
37
37
  ),
38
38
  z.meta({ examples: [['203.0.113.0/24', '2001:db8::1']] }),
39
39
  ),
40
- createdAt: z.iso
41
- .datetime()
42
- .check(
43
- z.describe('When the key was minted (ISO 8601).'),
44
- z.meta({ examples: ['2026-01-01T00:00:00.000Z'] }),
45
- ),
40
+ createdAt: Schema.dateTime().check(
41
+ z.describe('When the key was minted (ISO 8601).'),
42
+ z.meta({ examples: ['2026-01-01T00:00:00.000Z'] }),
43
+ ),
46
44
  createdBy: z
47
45
  .optional(z.string())
48
46
  .check(
@@ -53,7 +51,7 @@ export namespace schema {
53
51
  .enum(['production', 'sandbox'])
54
52
  .check(z.describe('Key environment.'), z.meta({ examples: ['sandbox'] })),
55
53
  expiresAt: z
56
- .optional(z.iso.datetime())
54
+ .optional(Schema.dateTime())
57
55
  .check(
58
56
  z.describe('When the key expires (ISO 8601). Absent for a non-expiring key.'),
59
57
  z.meta({ examples: ['2027-01-01T00:00:00.000Z'] }),
@@ -64,7 +62,7 @@ export namespace schema {
64
62
  z.describe('Opaque key id (`key_…`).'),
65
63
  z.meta({ examples: ['key_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
66
64
  ),
67
- lastUsedAt: z.optional(z.iso.datetime()).check(
65
+ lastUsedAt: z.optional(Schema.dateTime()).check(
68
66
  z.describe('When the key last authenticated a request (ISO 8601). Omitted with `include=` or when no usage is available.'), // prettier-ignore
69
67
  z.meta({ examples: ['2026-07-21T12:34:56.000Z'] }),
70
68
  ),
@@ -220,7 +218,7 @@ export namespace schema {
220
218
  ),
221
219
  zoneTags: ApiKey.schema.ApiKey.shape.zoneTags.check(
222
220
  z.describe(
223
- "Zone chain ids the key may read among the organization's delegations. Omit for a key that reads no Zone.",
221
+ "Zone chain ids the key may read or broadcast to among the organization's delegations. Required with `zone:read` or `zone:write`, naming only delegated Zones. Omit for a key that uses no Zone.",
224
222
  ),
225
223
  ),
226
224
  })
@@ -342,7 +340,9 @@ export namespace schema {
342
340
  zoneTags: z
343
341
  .optional(z.nullable(ApiKey.schema.ZoneTags))
344
342
  .check(
345
- z.describe('Replacement Zone chain id tags, or `null` to read no Zone.'),
343
+ z.describe(
344
+ 'Replacement Zone chain id tags, or `null` to use no Zone. Tags stay within delegated Zones.',
345
+ ),
346
346
  z.meta({ examples: [[421700001]] }),
347
347
  ),
348
348
  })
@@ -382,7 +382,7 @@ export function apiKeys(options: apiKeys.Options = {}) {
382
382
  description: 'Malformed API key, invalid path, or invalid body.',
383
383
  },
384
384
  403: {
385
- codes: ['scope_not_issuable', 'zone_key_approval_required'],
385
+ codes: ['scope_not_issuable'],
386
386
  description: 'The requested scopes are not issuable.',
387
387
  },
388
388
  404: {
@@ -454,6 +454,14 @@ export function apiKeys(options: apiKeys.Options = {}) {
454
454
  })
455
455
  try {
456
456
  const orgId = Auth.org(c).id
457
+ // Self-serve Zone read keys name only delegated Zones; derived keys copy their
458
+ // source's already bounded tags, and `zone:write` is checked inside the mint.
459
+ if (!source && body.scopes.includes('zone:read'))
460
+ await ApiKeys.ensureDelegatedTags(Db.get(c.get('db')), {
461
+ environment: body.environment,
462
+ orgId,
463
+ zoneTags: body.zoneTags,
464
+ })
457
465
  // Snapshot billing onto the record so the auth path throttles a
458
466
  // sandbox key to the public quota without a per-request billing read.
459
467
  // Only sandbox is gated; production keys carry no snapshot.
@@ -796,9 +804,8 @@ export function apiKeys(options: apiKeys.Options = {}) {
796
804
  description: 'Malformed API key, invalid path, or invalid body.',
797
805
  },
798
806
  403: {
799
- codes: ['forbidden', 'zone_key_approval_required'],
800
- description:
801
- 'Widening a reviewed Zone key, or changing anything but its tags and expiry, needs a new approved issuance request.',
807
+ codes: ['forbidden'],
808
+ description: 'The caller may not change this key.',
802
809
  },
803
810
  404: {
804
811
  codes: [
@@ -855,6 +862,14 @@ export function apiKeys(options: apiKeys.Options = {}) {
855
862
  message: 'An expiry can only move earlier. Mint a new key to extend it.',
856
863
  status: 400,
857
864
  })
865
+ // Replacement tags on a Zone key stay within the organization's delegations; `null`
866
+ // leaves it using no Zone, which narrows (and a write key refuses it inside the update).
867
+ if (body.zoneTags && record.scopes.some((scope) => scope.startsWith('zone:')))
868
+ await ApiKeys.ensureDelegatedTags(Db.get(c.get('db')), {
869
+ environment: record.environment,
870
+ orgId: record.orgId,
871
+ zoneTags: body.zoneTags,
872
+ })
858
873
  const updated = await ApiKeys.updateBounded(Db.get(c.get('db')), kv.store, keyId, body, {
859
874
  expectedOwner: { orgId: Auth.org(c).id, projectId: Auth.project(c).id },
860
875
  scopeCatalog: c.get('scopeCatalog'),
@@ -1091,7 +1106,7 @@ function canRotate(c: Context<App.Environment>, scopes: readonly Scope.Id[]) {
1091
1106
  if (principal?.type === 'super_admin') return true
1092
1107
  if (principal?.type !== 'session') return false
1093
1108
  const selfServe = Scope.selfServeFrom(c.get('scopeCatalog'))
1094
- // Rotation returns a new token, so copying restricted grants (Routes, webhooks, Zone, wildcard) stays owner-only.
1109
+ // Rotation returns a new token, so copying restricted grants (Routes, webhooks, per-Zone, wildcard) stays owner-only.
1095
1110
  return (
1096
1111
  (!principal.cli || scopes.every((scope) => principal.cli!.scopes.includes(scope))) &&
1097
1112
  ((scopes.every((scope) => selfServe.includes(scope)) && !scopes.includes('management:write')) ||