tapimo 0.1.0 → 0.2.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 (385) hide show
  1. package/README.md +28 -15
  2. package/dist/ApiKey.d.ts +3 -1
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +1 -0
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +71 -3
  7. package/dist/ApiKeys.d.ts.map +1 -1
  8. package/dist/ApiKeys.js +117 -1
  9. package/dist/ApiKeys.js.map +1 -1
  10. package/dist/App.d.ts +7139 -4197
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +133 -329
  13. package/dist/App.js.map +1 -1
  14. package/dist/Scope.d.ts +11 -4
  15. package/dist/Scope.d.ts.map +1 -1
  16. package/dist/Scope.js +12 -4
  17. package/dist/Scope.js.map +1 -1
  18. package/dist/Webhooks.d.ts +2 -2
  19. package/dist/Webhooks.d.ts.map +1 -1
  20. package/dist/Webhooks.js +2 -2
  21. package/dist/Webhooks.js.map +1 -1
  22. package/dist/admin/App.d.ts +126 -53
  23. package/dist/admin/App.d.ts.map +1 -1
  24. package/dist/admin/apps/api-keys.d.ts +104 -6
  25. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  26. package/dist/admin/apps/api-keys.js +67 -0
  27. package/dist/admin/apps/api-keys.js.map +1 -1
  28. package/dist/admin/apps/scopes.d.ts +4 -1
  29. package/dist/admin/apps/scopes.d.ts.map +1 -1
  30. package/dist/admin/apps/verified-tokens.d.ts +115 -115
  31. package/dist/admin/apps/verified-tokens.js +1 -1
  32. package/dist/admin/apps/verified-tokens.js.map +1 -1
  33. package/dist/admin/ui.generated.d.ts +1 -1
  34. package/dist/admin/ui.generated.js +1 -1
  35. package/dist/apps/data/App.d.ts +19176 -0
  36. package/dist/apps/data/App.d.ts.map +1 -0
  37. package/dist/apps/data/App.js +236 -0
  38. package/dist/apps/data/App.js.map +1 -0
  39. package/dist/apps/data/index.d.ts +3 -0
  40. package/dist/apps/data/index.d.ts.map +1 -0
  41. package/dist/apps/data/index.js +3 -0
  42. package/dist/apps/data/index.js.map +1 -0
  43. package/dist/apps/{activities.d.ts → data/routes/activities.d.ts} +32 -32
  44. package/dist/apps/{activities.d.ts.map → data/routes/activities.d.ts.map} +1 -1
  45. package/dist/apps/{activities.js → data/routes/activities.js} +13 -13
  46. package/dist/apps/data/routes/activities.js.map +1 -0
  47. package/dist/apps/{addresses.d.ts → data/routes/addresses.d.ts} +22 -22
  48. package/dist/apps/{addresses.d.ts.map → data/routes/addresses.d.ts.map} +1 -1
  49. package/dist/apps/{addresses.js → data/routes/addresses.js} +5 -5
  50. package/dist/apps/data/routes/addresses.js.map +1 -0
  51. package/dist/apps/{balances.d.ts → data/routes/balances.d.ts} +49 -49
  52. package/dist/apps/data/routes/balances.d.ts.map +1 -0
  53. package/dist/apps/{balances.js → data/routes/balances.js} +42 -11
  54. package/dist/apps/data/routes/balances.js.map +1 -0
  55. package/dist/apps/{blocks.d.ts → data/routes/blocks.d.ts} +47 -47
  56. package/dist/apps/data/routes/blocks.d.ts.map +1 -0
  57. package/dist/apps/{blocks.js → data/routes/blocks.js} +13 -13
  58. package/dist/apps/data/routes/blocks.js.map +1 -0
  59. package/dist/apps/{coingecko.d.ts → data/routes/coingecko.d.ts} +183 -183
  60. package/dist/apps/data/routes/coingecko.d.ts.map +1 -0
  61. package/dist/apps/{coingecko.js → data/routes/coingecko.js} +13 -13
  62. package/dist/apps/data/routes/coingecko.js.map +1 -0
  63. package/dist/apps/{exchanges.d.ts → data/routes/exchanges.d.ts} +460 -460
  64. package/dist/apps/data/routes/exchanges.d.ts.map +1 -0
  65. package/dist/apps/{exchanges.js → data/routes/exchanges.js} +19 -19
  66. package/dist/apps/data/routes/exchanges.js.map +1 -0
  67. package/dist/apps/{fee-amm.d.ts → data/routes/fee-amm.d.ts} +94 -94
  68. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -0
  69. package/dist/apps/{fee-amm.js → data/routes/fee-amm.js} +17 -17
  70. package/dist/apps/data/routes/fee-amm.js.map +1 -0
  71. package/dist/apps/{indexer.d.ts → data/routes/indexer.d.ts} +4 -4
  72. package/dist/apps/data/routes/indexer.d.ts.map +1 -0
  73. package/dist/apps/{indexer.js → data/routes/indexer.js} +13 -9
  74. package/dist/apps/data/routes/indexer.js.map +1 -0
  75. package/dist/apps/{receipts.d.ts → data/routes/receipts.d.ts} +49 -49
  76. package/dist/apps/data/routes/receipts.d.ts.map +1 -0
  77. package/dist/apps/{receipts.js → data/routes/receipts.js} +12 -12
  78. package/dist/apps/data/routes/receipts.js.map +1 -0
  79. package/dist/apps/{rpc.d.ts → data/routes/rpc.d.ts} +4 -4
  80. package/dist/apps/data/routes/rpc.d.ts.map +1 -0
  81. package/dist/apps/{rpc.js → data/routes/rpc.js} +6 -6
  82. package/dist/apps/data/routes/rpc.js.map +1 -0
  83. package/dist/apps/{tokenlist.d.ts → data/routes/tokenlist.d.ts} +4 -4
  84. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -0
  85. package/dist/apps/{tokenlist.js → data/routes/tokenlist.js} +8 -8
  86. package/dist/apps/data/routes/tokenlist.js.map +1 -0
  87. package/dist/apps/{tokens.d.ts → data/routes/tokens.d.ts} +468 -468
  88. package/dist/apps/data/routes/tokens.d.ts.map +1 -0
  89. package/dist/apps/{tokens.js → data/routes/tokens.js} +19 -19
  90. package/dist/apps/data/routes/tokens.js.map +1 -0
  91. package/dist/apps/{transactions.d.ts → data/routes/transactions.d.ts} +304 -304
  92. package/dist/apps/data/routes/transactions.d.ts.map +1 -0
  93. package/dist/apps/{transactions.js → data/routes/transactions.js} +13 -13
  94. package/dist/apps/data/routes/transactions.js.map +1 -0
  95. package/dist/apps/{transfers.d.ts → data/routes/transfers.d.ts} +6 -6
  96. package/dist/apps/data/routes/transfers.d.ts.map +1 -0
  97. package/dist/apps/{transfers.js → data/routes/transfers.js} +13 -13
  98. package/dist/apps/data/routes/transfers.js.map +1 -0
  99. package/dist/apps/{verified-tokens.d.ts → data/routes/verified-tokens.d.ts} +42 -42
  100. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -0
  101. package/dist/apps/{verified-tokens.js → data/routes/verified-tokens.js} +9 -9
  102. package/dist/apps/data/routes/verified-tokens.js.map +1 -0
  103. package/dist/apps/{webhooks.d.ts → data/routes/webhooks.d.ts} +62 -62
  104. package/dist/apps/{webhooks.d.ts.map → data/routes/webhooks.d.ts.map} +1 -1
  105. package/dist/apps/{webhooks.js → data/routes/webhooks.js} +30 -26
  106. package/dist/apps/data/routes/webhooks.js.map +1 -0
  107. package/dist/apps/docs/App.d.ts +27 -0
  108. package/dist/apps/docs/App.d.ts.map +1 -0
  109. package/dist/apps/docs/App.js +122 -0
  110. package/dist/apps/docs/App.js.map +1 -0
  111. package/dist/{internal/OpenApi.fonts.d.ts → apps/docs/fonts.d.ts} +1 -1
  112. package/dist/apps/docs/fonts.d.ts.map +1 -0
  113. package/dist/{internal/OpenApi.fonts.js → apps/docs/fonts.js} +1 -1
  114. package/dist/apps/docs/fonts.js.map +1 -0
  115. package/dist/apps/docs/index.d.ts +2 -0
  116. package/dist/apps/docs/index.d.ts.map +1 -0
  117. package/dist/apps/docs/index.js +2 -0
  118. package/dist/apps/docs/index.js.map +1 -0
  119. package/dist/{internal/OpenApi.logo.d.ts → apps/docs/logo.d.ts} +1 -1
  120. package/dist/apps/docs/logo.d.ts.map +1 -0
  121. package/dist/{internal/OpenApi.logo.js → apps/docs/logo.js} +1 -1
  122. package/dist/apps/docs/logo.js.map +1 -0
  123. package/dist/apps/index.d.ts +4 -0
  124. package/dist/apps/index.d.ts.map +1 -0
  125. package/dist/apps/index.js +4 -0
  126. package/dist/apps/index.js.map +1 -0
  127. package/dist/apps/metadata.d.ts +7 -0
  128. package/dist/apps/metadata.d.ts.map +1 -0
  129. package/dist/apps/metadata.js +15 -0
  130. package/dist/apps/metadata.js.map +1 -0
  131. package/dist/apps/platform/App.d.ts +2985 -0
  132. package/dist/apps/platform/App.d.ts.map +1 -0
  133. package/dist/apps/platform/App.js +74 -0
  134. package/dist/apps/platform/App.js.map +1 -0
  135. package/dist/apps/platform/index.d.ts +2 -0
  136. package/dist/apps/platform/index.d.ts.map +1 -0
  137. package/dist/apps/platform/index.js +2 -0
  138. package/dist/apps/platform/index.js.map +1 -0
  139. package/dist/apps/platform/routes/api-keys.d.ts +681 -0
  140. package/dist/apps/platform/routes/api-keys.d.ts.map +1 -0
  141. package/dist/apps/platform/routes/api-keys.js +328 -0
  142. package/dist/apps/platform/routes/api-keys.js.map +1 -0
  143. package/dist/apps/platform/routes/invitations.d.ts +733 -0
  144. package/dist/apps/platform/routes/invitations.d.ts.map +1 -0
  145. package/dist/apps/platform/routes/invitations.js +413 -0
  146. package/dist/apps/platform/routes/invitations.js.map +1 -0
  147. package/dist/apps/platform/routes/me.d.ts +95 -0
  148. package/dist/apps/platform/routes/me.d.ts.map +1 -0
  149. package/dist/apps/platform/routes/me.js +81 -0
  150. package/dist/apps/platform/routes/me.js.map +1 -0
  151. package/dist/apps/platform/routes/members.d.ts +554 -0
  152. package/dist/apps/platform/routes/members.d.ts.map +1 -0
  153. package/dist/apps/platform/routes/members.js +251 -0
  154. package/dist/apps/platform/routes/members.js.map +1 -0
  155. package/dist/apps/platform/routes/orgs.d.ts +582 -0
  156. package/dist/apps/platform/routes/orgs.d.ts.map +1 -0
  157. package/dist/apps/platform/routes/orgs.js +265 -0
  158. package/dist/apps/platform/routes/orgs.js.map +1 -0
  159. package/dist/apps/platform/routes/projects.d.ts +740 -0
  160. package/dist/apps/platform/routes/projects.d.ts.map +1 -0
  161. package/dist/apps/platform/routes/projects.js +286 -0
  162. package/dist/apps/platform/routes/projects.js.map +1 -0
  163. package/dist/apps/relay/App.d.ts +41 -0
  164. package/dist/apps/relay/App.d.ts.map +1 -0
  165. package/dist/apps/relay/App.js +103 -0
  166. package/dist/apps/relay/App.js.map +1 -0
  167. package/dist/apps/relay/Sponsorships.d.ts +107 -0
  168. package/dist/apps/relay/Sponsorships.d.ts.map +1 -0
  169. package/dist/apps/relay/Sponsorships.js +193 -0
  170. package/dist/apps/relay/Sponsorships.js.map +1 -0
  171. package/dist/apps/relay/index.d.ts +3 -0
  172. package/dist/apps/relay/index.d.ts.map +1 -0
  173. package/dist/apps/relay/index.js +3 -0
  174. package/dist/apps/relay/index.js.map +1 -0
  175. package/dist/cli/Cli.d.ts +1 -1
  176. package/dist/cli/Cli.js +1 -1
  177. package/dist/cli/commands/admin.d.ts +1 -1
  178. package/dist/cli/commands/admin.js +2 -2
  179. package/dist/cli/commands/admin.js.map +1 -1
  180. package/dist/db/Db.d.ts +10 -0
  181. package/dist/db/Db.d.ts.map +1 -1
  182. package/dist/db/Db.js +128 -0
  183. package/dist/db/Db.js.map +1 -1
  184. package/dist/db/Schema.d.ts +80 -0
  185. package/dist/db/Schema.d.ts.map +1 -1
  186. package/dist/db/Schema.js +99 -0
  187. package/dist/db/Schema.js.map +1 -1
  188. package/dist/db/tables/invitations.d.ts +84 -0
  189. package/dist/db/tables/invitations.d.ts.map +1 -0
  190. package/dist/db/tables/invitations.js +165 -0
  191. package/dist/db/tables/invitations.js.map +1 -0
  192. package/dist/db/tables/memberships.d.ts +108 -0
  193. package/dist/db/tables/memberships.d.ts.map +1 -0
  194. package/dist/db/tables/memberships.js +153 -0
  195. package/dist/db/tables/memberships.js.map +1 -0
  196. package/dist/db/tables/organizations.d.ts +44 -0
  197. package/dist/db/tables/organizations.d.ts.map +1 -1
  198. package/dist/db/tables/organizations.js +89 -0
  199. package/dist/db/tables/organizations.js.map +1 -1
  200. package/dist/db/tables/projects.d.ts +65 -0
  201. package/dist/db/tables/projects.d.ts.map +1 -0
  202. package/dist/db/tables/projects.js +75 -0
  203. package/dist/db/tables/projects.js.map +1 -0
  204. package/dist/db/tables/sponsoredTransactions.d.ts +120 -0
  205. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -0
  206. package/dist/db/tables/sponsoredTransactions.js +161 -0
  207. package/dist/db/tables/sponsoredTransactions.js.map +1 -0
  208. package/dist/db/tables/users.d.ts +48 -0
  209. package/dist/db/tables/users.d.ts.map +1 -0
  210. package/dist/db/tables/users.js +65 -0
  211. package/dist/db/tables/users.js.map +1 -0
  212. package/dist/handlers/internal/executionError.d.ts.map +1 -1
  213. package/dist/handlers/internal/executionError.js +2 -0
  214. package/dist/handlers/internal/executionError.js.map +1 -1
  215. package/dist/handlers/internal/multisig.d.ts +2 -6
  216. package/dist/handlers/internal/multisig.d.ts.map +1 -1
  217. package/dist/handlers/internal/multisig.js +11 -36
  218. package/dist/handlers/internal/multisig.js.map +1 -1
  219. package/dist/handlers/internal/sponsorship.d.ts +32 -2
  220. package/dist/handlers/internal/sponsorship.d.ts.map +1 -1
  221. package/dist/handlers/internal/sponsorship.js +34 -9
  222. package/dist/handlers/internal/sponsorship.js.map +1 -1
  223. package/dist/handlers/relay.d.ts +6 -2
  224. package/dist/handlers/relay.d.ts.map +1 -1
  225. package/dist/handlers/relay.js +16 -2
  226. package/dist/handlers/relay.js.map +1 -1
  227. package/dist/internal/Auth.d.ts +208 -6
  228. package/dist/internal/Auth.d.ts.map +1 -1
  229. package/dist/internal/Auth.js +500 -21
  230. package/dist/internal/Auth.js.map +1 -1
  231. package/dist/internal/Log.d.ts +7 -3
  232. package/dist/internal/Log.d.ts.map +1 -1
  233. package/dist/internal/Log.js +4 -0
  234. package/dist/internal/Log.js.map +1 -1
  235. package/dist/internal/OpenApi.d.ts +0 -19
  236. package/dist/internal/OpenApi.d.ts.map +1 -1
  237. package/dist/internal/OpenApi.js +0 -57
  238. package/dist/internal/OpenApi.js.map +1 -1
  239. package/dist/internal/Store.d.ts +7 -1
  240. package/dist/internal/Store.d.ts.map +1 -1
  241. package/dist/internal/Store.js +16 -2
  242. package/dist/internal/Store.js.map +1 -1
  243. package/dist/internal/Viem.d.ts +84 -0
  244. package/dist/internal/Viem.d.ts.map +1 -1
  245. package/dist/internal/Webhooks.d.ts +1 -1
  246. package/dist/internal/Webhooks.d.ts.map +1 -1
  247. package/package.json +31 -5
  248. package/src/ApiKey.ts +1 -0
  249. package/src/ApiKeys.test.ts +104 -1
  250. package/src/ApiKeys.ts +154 -1
  251. package/src/App.test.ts +11187 -179
  252. package/src/App.ts +200 -420
  253. package/src/Client.test-d.ts +306 -15
  254. package/src/Scope.ts +13 -4
  255. package/src/Webhooks.ts +2 -2
  256. package/src/admin/apps/api-keys.test.ts +51 -0
  257. package/src/admin/apps/api-keys.ts +84 -0
  258. package/src/admin/apps/scopes.test.ts +5 -1
  259. package/src/admin/apps/verified-tokens.ts +1 -1
  260. package/src/admin/ui.generated.ts +1 -1
  261. package/src/apps/composition.test.ts +66 -0
  262. package/src/apps/data/App.ts +295 -0
  263. package/src/apps/data/index.ts +2 -0
  264. package/src/apps/{activities.test.ts → data/routes/activities.test.ts} +3 -3
  265. package/src/apps/{activities.ts → data/routes/activities.ts} +15 -15
  266. package/src/apps/{addresses.ts → data/routes/addresses.ts} +6 -6
  267. package/src/apps/{balances.test.ts → data/routes/balances.test.ts} +2 -2
  268. package/src/apps/{balances.ts → data/routes/balances.ts} +46 -12
  269. package/src/apps/{blocks.test.ts → data/routes/blocks.test.ts} +3 -3
  270. package/src/apps/{blocks.ts → data/routes/blocks.ts} +14 -14
  271. package/src/apps/{coingecko.test.ts → data/routes/coingecko.test.ts} +2 -2
  272. package/src/apps/{coingecko.ts → data/routes/coingecko.ts} +14 -14
  273. package/src/apps/{exchanges.test.ts → data/routes/exchanges.test.ts} +1 -1
  274. package/src/apps/{exchanges.ts → data/routes/exchanges.ts} +20 -20
  275. package/src/apps/{fee-amm.test.ts → data/routes/fee-amm.test.ts} +3 -3
  276. package/src/apps/{fee-amm.ts → data/routes/fee-amm.ts} +18 -18
  277. package/src/apps/{indexer.test.ts → data/routes/indexer.test.ts} +26 -5
  278. package/src/apps/{indexer.ts → data/routes/indexer.ts} +13 -10
  279. package/src/apps/{receipts.test.ts → data/routes/receipts.test.ts} +3 -3
  280. package/src/apps/{receipts.ts → data/routes/receipts.ts} +13 -13
  281. package/src/apps/{rpc.test.ts → data/routes/rpc.test.ts} +44 -5
  282. package/src/apps/{rpc.ts → data/routes/rpc.ts} +7 -7
  283. package/src/apps/{tokenlist.test.ts → data/routes/tokenlist.test.ts} +3 -3
  284. package/src/apps/{tokenlist.ts → data/routes/tokenlist.ts} +9 -9
  285. package/src/apps/{tokens.test.ts → data/routes/tokens.test.ts} +2 -2
  286. package/src/apps/{tokens.ts → data/routes/tokens.ts} +20 -20
  287. package/src/apps/{transactions.test.ts → data/routes/transactions.test.ts} +3 -3
  288. package/src/apps/{transactions.ts → data/routes/transactions.ts} +15 -15
  289. package/src/apps/{transfers.test.ts → data/routes/transfers.test.ts} +2 -2
  290. package/src/apps/{transfers.ts → data/routes/transfers.ts} +15 -15
  291. package/src/apps/{verified-tokens.test.ts → data/routes/verified-tokens.test.ts} +3 -3
  292. package/src/apps/{verified-tokens.ts → data/routes/verified-tokens.ts} +10 -10
  293. package/src/apps/{webhooks.test.ts → data/routes/webhooks.test.ts} +6 -6
  294. package/src/apps/{webhooks.ts → data/routes/webhooks.ts} +39 -35
  295. package/src/apps/docs/App.ts +136 -0
  296. package/src/apps/docs/index.ts +1 -0
  297. package/src/apps/index.ts +3 -0
  298. package/src/apps/metadata.ts +23 -0
  299. package/src/apps/platform/App.ts +130 -0
  300. package/src/apps/platform/index.ts +1 -0
  301. package/src/apps/platform/routes/api-keys.test.ts +330 -0
  302. package/src/apps/platform/routes/api-keys.ts +427 -0
  303. package/src/apps/platform/routes/invitations.test.ts +361 -0
  304. package/src/apps/platform/routes/invitations.ts +519 -0
  305. package/src/apps/platform/routes/me.test.ts +121 -0
  306. package/src/apps/platform/routes/me.ts +106 -0
  307. package/src/apps/platform/routes/members.test.ts +195 -0
  308. package/src/apps/platform/routes/members.ts +313 -0
  309. package/src/apps/platform/routes/orgs.test.ts +313 -0
  310. package/src/apps/platform/routes/orgs.ts +343 -0
  311. package/src/apps/platform/routes/projects.test.ts +181 -0
  312. package/src/apps/platform/routes/projects.ts +367 -0
  313. package/src/apps/relay/App.test.ts +484 -0
  314. package/src/apps/relay/App.ts +128 -0
  315. package/src/apps/relay/Sponsorships.test.ts +375 -0
  316. package/src/apps/relay/Sponsorships.ts +305 -0
  317. package/src/apps/relay/index.ts +2 -0
  318. package/src/cli/Cli.ts +1 -1
  319. package/src/cli/commands/admin.ts +2 -2
  320. package/src/db/Db.ts +159 -0
  321. package/src/db/Schema.ts +137 -0
  322. package/src/db/tables/invitations.ts +198 -0
  323. package/src/db/tables/memberships.ts +208 -0
  324. package/src/db/tables/organizations.test.ts +37 -1
  325. package/src/db/tables/organizations.ts +105 -0
  326. package/src/db/tables/projects.ts +107 -0
  327. package/src/db/tables/sponsoredTransactions.test.ts +218 -0
  328. package/src/db/tables/sponsoredTransactions.ts +236 -0
  329. package/src/db/tables/users.ts +85 -0
  330. package/src/handlers/internal/executionError.ts +2 -0
  331. package/src/handlers/internal/multisig.test.ts +84 -10
  332. package/src/handlers/internal/multisig.ts +15 -50
  333. package/src/handlers/internal/sponsorship.ts +74 -15
  334. package/src/handlers/{internal/relay.localnet.test.ts → relay.test.ts} +2 -2
  335. package/src/handlers/relay.ts +23 -5
  336. package/src/internal/Auth.test.ts +316 -0
  337. package/src/internal/Auth.ts +691 -25
  338. package/src/internal/Cursor.test.ts +3 -2
  339. package/src/internal/Log.test.ts +1 -1
  340. package/src/internal/Log.ts +11 -3
  341. package/src/internal/MetricSink.ts +1 -1
  342. package/src/internal/OpenApi.ts +0 -59
  343. package/src/internal/Store.test.ts +52 -0
  344. package/src/internal/Store.ts +24 -5
  345. package/src/internal/Tidx.local.test.ts +1 -1
  346. package/src/internal/VerifiedTokens.test.ts +1 -1
  347. package/src/internal/WebhookDestination.test.ts +1 -1
  348. package/src/internal/Webhooks.test.ts +4 -4
  349. package/src/internal/Webhooks.ts +1 -1
  350. package/dist/apps/activities.js.map +0 -1
  351. package/dist/apps/addresses.js.map +0 -1
  352. package/dist/apps/balances.d.ts.map +0 -1
  353. package/dist/apps/balances.js.map +0 -1
  354. package/dist/apps/blocks.d.ts.map +0 -1
  355. package/dist/apps/blocks.js.map +0 -1
  356. package/dist/apps/coingecko.d.ts.map +0 -1
  357. package/dist/apps/coingecko.js.map +0 -1
  358. package/dist/apps/exchanges.d.ts.map +0 -1
  359. package/dist/apps/exchanges.js.map +0 -1
  360. package/dist/apps/fee-amm.d.ts.map +0 -1
  361. package/dist/apps/fee-amm.js.map +0 -1
  362. package/dist/apps/indexer.d.ts.map +0 -1
  363. package/dist/apps/indexer.js.map +0 -1
  364. package/dist/apps/receipts.d.ts.map +0 -1
  365. package/dist/apps/receipts.js.map +0 -1
  366. package/dist/apps/rpc.d.ts.map +0 -1
  367. package/dist/apps/rpc.js.map +0 -1
  368. package/dist/apps/tokenlist.d.ts.map +0 -1
  369. package/dist/apps/tokenlist.js.map +0 -1
  370. package/dist/apps/tokens.d.ts.map +0 -1
  371. package/dist/apps/tokens.js.map +0 -1
  372. package/dist/apps/transactions.d.ts.map +0 -1
  373. package/dist/apps/transactions.js.map +0 -1
  374. package/dist/apps/transfers.d.ts.map +0 -1
  375. package/dist/apps/transfers.js.map +0 -1
  376. package/dist/apps/verified-tokens.d.ts.map +0 -1
  377. package/dist/apps/verified-tokens.js.map +0 -1
  378. package/dist/apps/webhooks.js.map +0 -1
  379. package/dist/internal/OpenApi.fonts.d.ts.map +0 -1
  380. package/dist/internal/OpenApi.fonts.js.map +0 -1
  381. package/dist/internal/OpenApi.logo.d.ts.map +0 -1
  382. package/dist/internal/OpenApi.logo.js.map +0 -1
  383. package/src/handlers/internal/multisig.localnet.test.ts +0 -146
  384. /package/src/{internal/OpenApi.fonts.ts → apps/docs/fonts.ts} +0 -0
  385. /package/src/{internal/OpenApi.logo.ts → apps/docs/logo.ts} +0 -0
@@ -1,4 +1,6 @@
1
- import type { Context as hono_Context, Hono, MiddlewareHandler } from 'hono'
1
+ import * as Accounts from 'accounts/server'
2
+ import { Hono } from 'hono'
3
+ import type { Context as hono_Context, MiddlewareHandler } from 'hono'
2
4
  import type { HonoBase } from 'hono/hono-base'
3
5
  import { createMiddleware } from 'hono/factory'
4
6
  import { matchedRoutes } from 'hono/route'
@@ -6,15 +8,23 @@ import type { MergePath, RouterRoute, Schema as core_Schema } from 'hono/types'
6
8
  import { findTargetHandler } from 'hono/utils/handler'
7
9
  import { Credential, type Method } from 'mppx'
8
10
  import { Mppx, tempo } from 'mppx/hono'
11
+ import * as z from 'zod/mini'
9
12
 
13
+ import type * as App from '../App.js'
10
14
  import * as ApiKey from '../ApiKey.js'
11
15
  import * as ApiKeys from '../ApiKeys.js'
16
+ import * as Db from '../db/Db.js'
12
17
  import type * as Store from './Store.js'
18
+ import * as Memberships from '../db/tables/memberships.js'
19
+ import * as Metadata from '../apps/metadata.js'
13
20
  import * as OpenApi from './OpenApi.js'
21
+ import * as Organizations from '../db/tables/organizations.js'
22
+ import * as Projects from '../db/tables/projects.js'
14
23
  import * as RateLimit from './RateLimit.js'
15
24
  import * as Response from './Response.js'
16
25
  import * as Schema from './Schema.js'
17
26
  import * as Scope from '../Scope.js'
27
+ import * as Users from '../db/tables/users.js'
18
28
  import * as Viem from './Viem.js'
19
29
 
20
30
  const policySymbol = Symbol('tempo-api.auth.policy')
@@ -38,6 +48,10 @@ export type Context = {
38
48
  mpp?: Mpp | undefined
39
49
  /** Rate-limit store used by API handlers. */
40
50
  rateLimit?: RateLimit.Store | undefined
51
+ /** Session lane resolution (the session surface's capability), or undefined when sessions are unmounted. */
52
+ session?: Session | undefined
53
+ /** Super-admin machine credential (sha256 of the configured secret), or undefined when disabled. */
54
+ superAdmin?: { tokenHash: string } | undefined
41
55
  }
42
56
 
43
57
  /** Hono environment variables set by auth middleware. */
@@ -87,9 +101,11 @@ type Mpp = {
87
101
  /**
88
102
  * Route-level auth policy override. Each lane is independent: `true` enables it
89
103
  * with the middleware defaults, `false` disables it, an object enables it with
90
- * the given overrides, and omitting it inherits the resolved policy. Lanes are
91
- * default-closed except `apiKey` (see {@link middleware}), so a route that
92
- * declares nothing — or declares only `apiKey` — is API-key-only.
104
+ * the given overrides, and omitting it inherits the resolved policy. All lanes
105
+ * are default-closed (see {@link middleware}).
106
+ *
107
+ * Routes grant API-key, public, or paid access explicitly via {@link policy};
108
+ * otherwise only `super_admin` reaches them.
93
109
  */
94
110
  export type PolicyOverride = {
95
111
  /** API-key lane: pass quota/scopes overrides, or a boolean to enable/disable. */
@@ -118,6 +134,8 @@ export type PolicyOverride = {
118
134
  }
119
135
  | boolean
120
136
  | undefined
137
+ /** Session lane: `true` allows signed-in wallet sessions, `false` disables. */
138
+ session?: boolean | undefined
121
139
  }
122
140
 
123
141
  /** Authenticated or anonymous caller. */
@@ -133,6 +151,8 @@ export type Principal =
133
151
  orgId: string
134
152
  /** Payment metadata for paid overflow requests. */
135
153
  payment?: Payment | undefined
154
+ /** Owning project id, when the key carries project attribution. */
155
+ projectId?: string | undefined
136
156
  /** Principal kind. */
137
157
  type: 'api_key'
138
158
  }
@@ -144,13 +164,46 @@ export type Principal =
144
164
  /** Principal kind. */
145
165
  type: 'public'
146
166
  }
167
+ | {
168
+ /** Verified email bound to the session, when present. */
169
+ email?: string | undefined
170
+ /** Principal id: the user id (`usr_…`). */
171
+ id: string
172
+ /** Identity that established the session, as a provider/subject pair (OIDC `iss`/`sub` semantics). */
173
+ identity: {
174
+ /** Identity provider: `wallet` for SIWE sign-in, an OIDC issuer otherwise. */
175
+ provider: string
176
+ /** Provider-scoped subject — the wallet address for `wallet`. */
177
+ subject: string
178
+ }
179
+ /** Payment metadata for paid overflow requests. */
180
+ payment?: Payment | undefined
181
+ /** Principal kind. */
182
+ type: 'session'
183
+ }
184
+ | {
185
+ /** Attributed super admin identity (`'super_admin'` for the configured machine secret). */
186
+ actor: string
187
+ /** Principal id. */
188
+ id: string
189
+ /** Payment metadata for paid overflow requests. */
190
+ payment?: Payment | undefined
191
+ /** Principal kind. */
192
+ type: 'super_admin'
193
+ }
147
194
 
148
195
  /** Hono variables set by auth middleware. */
149
196
  export type Variables = {
150
197
  /** Auth context consumed by protected handlers. */
151
198
  auth: Context
199
+ /** Caller's membership resolved by {@link ensureOrg}; absent for `super_admin`. */
200
+ membership?: Memberships.Record | undefined
201
+ /** Organization resolved by {@link ensureOrg} for the current request. */
202
+ org?: Organizations.Record | undefined
152
203
  /** Authenticated or anonymous caller. */
153
204
  principal?: Principal | undefined
205
+ /** Project resolved by {@link ensureProject} for the current request. */
206
+ project?: Projects.Record | undefined
154
207
  }
155
208
 
156
209
  /** Reads the authenticated principal from a Hono context. */
@@ -158,6 +211,147 @@ export function getPrincipal<environment extends Environment>(c: hono_Context<en
158
211
  return c.get('principal') ?? null
159
212
  }
160
213
 
214
+ /**
215
+ * Guards org-scoped routes (`:orgId`). Sessions reach only organizations they
216
+ * are members of; the super admin reaches any. Missing or foreign
217
+ * organizations return `404`, so foreign org ids are indistinguishable from
218
+ * absent ones; a member below the required role gets `403` (the resource is
219
+ * visible to them, the action is not). The resolved organization and
220
+ * membership land on context — read them with {@link org} and
221
+ * {@link membership}.
222
+ */
223
+ export function ensureOrg(options: ensureOrg.Options = {}): MiddlewareHandler {
224
+ const { role: minimum = 'member' } = options
225
+ // Typed loosely (like {@link policy}) so route chains keep their inferred
226
+ // schema; a concrete environment parameter collapses Hono's inference.
227
+ return createMiddleware<Environment>(async (c, next) => {
228
+ const notFound = () =>
229
+ Response.error(c, {
230
+ code: 'organization_not_found',
231
+ message: 'Organization not found',
232
+ status: 404,
233
+ })
234
+ const orgId = c.req.param('orgId' as never) as string | undefined
235
+ if (!orgId) return notFound()
236
+ const principal = getPrincipal(c)
237
+ if (principal?.type !== 'session' && principal?.type !== 'super_admin') return notFound()
238
+ const db = Db.get(contextDb(c))
239
+ const record = await Organizations.get(db, orgId)
240
+ if (!record) return notFound()
241
+ if (principal.type !== 'super_admin') {
242
+ const member = await Memberships.get(db, orgId, principal.id)
243
+ if (!member) return notFound()
244
+ if (Memberships.rank[member.role] < Memberships.rank[minimum])
245
+ return Response.error(c, {
246
+ code: 'forbidden',
247
+ message: `Requires the ${minimum} role`,
248
+ status: 403,
249
+ })
250
+ c.set('membership', member)
251
+ }
252
+ c.set('org', record)
253
+ await next()
254
+ return undefined
255
+ })
256
+ }
257
+
258
+ export declare namespace ensureOrg {
259
+ /** Options for the org guard. */
260
+ type Options = {
261
+ /** Minimum role required; defaults to `member` (any membership). */
262
+ role?: Memberships.Role | undefined
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Guards project-scoped routes (`:projectId`) nested under {@link ensureOrg},
268
+ * which must run first. Projects resolve only within the scoped organization;
269
+ * missing or cross-org projects return `404`. The resolved project lands on
270
+ * context — read it with {@link project}.
271
+ */
272
+ export function ensureProject(): MiddlewareHandler {
273
+ // Typed loosely (like {@link policy}) so route chains keep their inferred
274
+ // schema; a concrete environment parameter collapses Hono's inference.
275
+ return createMiddleware<Environment>(async (c, next) => {
276
+ const notFound = () =>
277
+ Response.error(c, {
278
+ code: 'project_not_found',
279
+ message: 'Project not found',
280
+ status: 404,
281
+ })
282
+ const projectId = c.req.param('projectId' as never) as string | undefined
283
+ if (!projectId) return notFound()
284
+ const scoped = c.get('org')
285
+ if (!scoped) return notFound()
286
+ const record = await Projects.get(Db.get(contextDb(c)), projectId)
287
+ if (!record || record.orgId !== scoped.id) return notFound()
288
+ c.set('project', record)
289
+ await next()
290
+ return undefined
291
+ })
292
+ }
293
+
294
+ /**
295
+ * Always false at runtime, but typed as `boolean` so handlers can include a
296
+ * never-reached scope-error branch in Hono's inferred response union (the
297
+ * ensure middlewares produce these `404`s before the handler runs).
298
+ */
299
+ export const narrowScope = false as boolean
300
+
301
+ /** Typed org-scope error response for Hono client inference. Never reached at runtime. */
302
+ export function ensureOrgError(c: hono_Context) {
303
+ return Response.error(c, {
304
+ code: 'organization_not_found',
305
+ message: 'Organization not found',
306
+ status: 404,
307
+ })
308
+ }
309
+
310
+ /** Typed project-scope error response for Hono client inference. Never reached at runtime. */
311
+ export function ensureProjectError(c: hono_Context) {
312
+ // Project routes run the org scope first, so either not-found can surface.
313
+ const code =
314
+ c.req.header('tempo-narrow-scope-code') === 'organization_not_found'
315
+ ? ('organization_not_found' as const)
316
+ : ('project_not_found' as const)
317
+ return Response.error(c, {
318
+ code,
319
+ message: 'Project not found',
320
+ status: 404,
321
+ })
322
+ }
323
+
324
+ /** Reads the caller's membership resolved by {@link ensureOrg}; `undefined` for `super_admin`. */
325
+ export function membership<environment extends Environment>(
326
+ c: hono_Context<environment>,
327
+ ): Memberships.Record | undefined {
328
+ return c.get('membership')
329
+ }
330
+
331
+ /** Reads the organization resolved by {@link ensureOrg}; throws when the middleware did not run. */
332
+ export function org<environment extends Environment>(
333
+ c: hono_Context<environment>,
334
+ ): Organizations.Record {
335
+ const record = c.get('org')
336
+ if (!record) throw new Error('`orgScope` middleware must run before reading the organization')
337
+ return record
338
+ }
339
+
340
+ /** Reads the project resolved by {@link ensureProject}; throws when the middleware did not run. */
341
+ export function project<environment extends Environment>(
342
+ c: hono_Context<environment>,
343
+ ): Projects.Record {
344
+ const record = c.get('project')
345
+ if (!record) throw new Error('`projectScope` middleware must run before reading the project')
346
+ return record
347
+ }
348
+
349
+ // Reads the app's database source from request context (set by `App.create`
350
+ // from its `db` option).
351
+ function contextDb(c: hono_Context): Db.Source {
352
+ return (c.get as (key: string) => unknown)('db') as Db.Source
353
+ }
354
+
161
355
  /**
162
356
  * Always false at runtime, but typed as `boolean` so handlers can include a
163
357
  * never-reached access-error branch in Hono's inferred response union.
@@ -201,6 +395,33 @@ export function accessError(c: hono_Context) {
201
395
  })
202
396
  }
203
397
 
398
+ /** Typed lane-less policy error response for Hono client inference. Never reached at runtime. */
399
+ export function superAdminAccessError(c: hono_Context) {
400
+ const status = Number(c.req.header('tempo-narrow-access-status'))
401
+ if (status === 400)
402
+ return Response.error(c, {
403
+ code: 'api_key_malformed',
404
+ message: 'Malformed API key',
405
+ status: 400,
406
+ })
407
+ if (status === 401) {
408
+ const code =
409
+ c.req.header('tempo-narrow-access-code') === 'api_key_missing'
410
+ ? 'api_key_missing'
411
+ : 'api_key_invalid'
412
+ return Response.error(c, {
413
+ code,
414
+ message: 'Missing or invalid API key',
415
+ status: 401,
416
+ })
417
+ }
418
+ return Response.error(c, {
419
+ code: 'forbidden',
420
+ message: 'API key not permitted for this route',
421
+ status: 403,
422
+ })
423
+ }
424
+
204
425
  /** Typed auth/payment error response for Hono client inference. Never reached at runtime. */
205
426
  export function paidAccessError(c: hono_Context) {
206
427
  const status = Number(c.req.header('tempo-narrow-access-status'))
@@ -217,7 +438,9 @@ export function middleware<
217
438
  >(options: middleware.Options<endpoint>): MiddlewareHandler<environment> {
218
439
  const { charge = {}, ...mpp } = options.mpp === false ? {} : (options.mpp ?? {})
219
440
  const defaults = laneDefaults(options)
220
- const base = basePolicy(options, defaults)
441
+ // Default-deny: no lanes open until a route grants them via `policy`.
442
+ // Otherwise the `super_admin` principal bypasses lane checks entirely.
443
+ const base: require.Policy = {}
221
444
  // Keys resolve against the app's KV state store on request context
222
445
  // (`App.create({ kv })`), so the middleware needs no storage wiring; when no
223
446
  // store is configured, every token resolves to `null` (closed).
@@ -252,6 +475,10 @@ export function middleware<
252
475
  }),
253
476
  }),
254
477
  rateLimit: RateLimit.memory(options.rateLimit),
478
+ ...(options.session ? { session: options.session } : {}),
479
+ ...(options.superAdmin?.secret
480
+ ? { superAdmin: { tokenHash: ApiKey.hash(options.superAdmin.secret) } }
481
+ : {}),
255
482
  }
256
483
 
257
484
  return createMiddleware<environment>((c, next) => {
@@ -314,6 +541,14 @@ export declare namespace middleware {
314
541
  | undefined
315
542
  /** Rate-limit store options. */
316
543
  rateLimit?: RateLimit.memory.Options | undefined
544
+ /** Session lane resolution — the session surface's capability; omit to leave the session lane closed. */
545
+ session?: Session | undefined
546
+ /**
547
+ * Super-admin machine credential. A presented API-key token matching this
548
+ * secret resolves to a `super_admin` principal — bypassing scope and quota
549
+ * checks — before the key lookup runs. Omit to disable the entry entirely.
550
+ */
551
+ superAdmin?: { secret: string } | undefined
317
552
  }
318
553
 
319
554
  /** Resolve-cache tuning for API-key resolution. */
@@ -333,6 +568,305 @@ export declare namespace middleware {
333
568
  }
334
569
  }
335
570
 
571
+ /** Zod schemas documenting the session sign-in surface (`/v1/auth/siwe`). */
572
+ namespace schema {
573
+ const siweMessage =
574
+ 'api.tempo.xyz wants you to sign in with your Ethereum account:\n0x0000000000000000000000000000000000000000\n\nURI: https://api.tempo.xyz\nVersion: 1\nChain ID: 0\nNonce: 3D0sZfnHqTBmc9tKR\nIssued At: 2026-01-01T00:00:00.000Z\nExpiration Time: 2026-01-01T00:10:00.000Z'
575
+
576
+ /** Error body returned by the sign-in handler (SDK shape, not the API error envelope). */
577
+ export const HandlerError = z.object({
578
+ error: z
579
+ .string()
580
+ .check(
581
+ z.describe('Human-readable failure reason.'),
582
+ z.meta({ examples: ['domain mismatch'] }),
583
+ ),
584
+ issues: z
585
+ .optional(z.array(z.unknown()))
586
+ .check(z.describe('Field-level validation issues, present on request-schema failures.')),
587
+ })
588
+
589
+ /** Schemas for `POST /v1/auth/siwe/challenge`. */
590
+ export namespace challenge {
591
+ /** Request body. Accepted fields beyond this document pass through to the SDK handler. */
592
+ export const Body = z.object({})
593
+
594
+ /** Response body. */
595
+ export const Response = z.object({
596
+ message: z
597
+ .string()
598
+ .check(
599
+ z.describe('Single-use EIP-4361 (SIWE) message to sign and submit for verification.'),
600
+ z.meta({ examples: [siweMessage] }),
601
+ ),
602
+ })
603
+ }
604
+
605
+ /** Schemas for `POST /v1/auth/siwe` (verify). */
606
+ export namespace verify {
607
+ /** Request body. */
608
+ export const Body = z.object({
609
+ address: z
610
+ .string()
611
+ .check(
612
+ z.describe('Wallet address that signed the message; becomes the session subject.'),
613
+ z.meta({ examples: ['0x0000000000000000000000000000000000000001'] }),
614
+ ),
615
+ idToken: z.optional(z.string()).check(
616
+ z.describe('Wallet-minted OIDC identity token (JWT) asserting a verified email, folded onto the session.'), // prettier-ignore
617
+ z.meta({ examples: ['eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIweDAwMDAifQ.c2lnbmF0dXJl'] }),
618
+ ),
619
+ keyAuthorization: z.optional(z.string()).check(
620
+ z.describe('RLP-serialized signed key authorization (TIP-1053) whose witness binds this message; verification recovers over its digest.'), // prettier-ignore
621
+ z.meta({ examples: [`0x${'aa'.repeat(60)}`] }),
622
+ ),
623
+ message: z
624
+ .string()
625
+ .check(
626
+ z.describe('The exact challenge message issued by `POST /v1/auth/siwe/challenge`.'),
627
+ z.meta({ examples: [siweMessage] }),
628
+ ),
629
+ returnToken: z.optional(z.boolean()).check(
630
+ z.describe('Return the session token in the body instead of setting the session cookie.'), // prettier-ignore
631
+ z.meta({ examples: [true] }),
632
+ ),
633
+ signature: z
634
+ .string()
635
+ .check(
636
+ z.describe('Signature over the challenge message (or the key-authorization digest).'),
637
+ z.meta({ examples: [`0x${'aa'.repeat(65)}`] }),
638
+ ),
639
+ })
640
+
641
+ /** Response body. */
642
+ export const Response = z.object({
643
+ token: z
644
+ .optional(z.string())
645
+ .check(
646
+ z.describe('Bearer session token; present only when `returnToken` is true.'),
647
+ z.meta({ examples: ['aa'.repeat(32)] }),
648
+ ),
649
+ })
650
+ }
651
+ }
652
+
653
+ /** `{ error }` response for a sign-in handler failure status. */
654
+ function handlerError(description: string) {
655
+ return {
656
+ content: { 'application/json': { schema: OpenApi.resolver(schema.HandlerError) } },
657
+ description,
658
+ }
659
+ }
660
+
661
+ /**
662
+ * Builds the built-in auth pieces from `App.create`'s `auth` option: the
663
+ * session-surface app to mount (when `session` is configured) and the
664
+ * enforcement middleware, wired with the surface's resolver. `auth: false`
665
+ * disables both.
666
+ */
667
+ export function install(options: install.Options) {
668
+ const { db, kv } = options
669
+ const { session: options_session, ...options_middleware } =
670
+ options.auth === false ? {} : (options.auth ?? {})
671
+ if (options_session && !kv)
672
+ throw new Error('`auth.session` requires the `kv` state store: sessions and sign-in challenges persist there.') // prettier-ignore
673
+
674
+ // Session sign-in surface (challenge, verify, logout — SIWE wallet sign-in
675
+ // today) plus the capability the session lane resolves through. Public but
676
+ // policy-gated: the hidden describeRoute engages the middleware for the
677
+ // mounted sub-app, and the public lane rate-limits challenge-store writes.
678
+ const app = (() => {
679
+ if (!options_session || !kv) return undefined
680
+ const { issuer, origin, ttl } =
681
+ (typeof options_session === 'object' ? options_session.wallet : undefined) ?? {}
682
+
683
+ const handler = Accounts.Handler.auth({
684
+ ...(issuer ? { identity: { issuer } } : {}),
685
+ // Sign-in upserts the `users` row keyed by address.
686
+ onAuthenticate: async ({ address }) => {
687
+ await Users.upsertByAddress(Db.get(db), { address })
688
+ },
689
+ // No pinned origin: on Workers the edge proxy headers are trustworthy, so
690
+ // opt in explicitly (the SDK's guard ignores its own Workers default).
691
+ // Off-Workers the SDK keeps throwing until `wallet.origin` is set.
692
+ ...(origin
693
+ ? { origin }
694
+ : globalThis.navigator?.userAgent === 'Cloudflare-Workers'
695
+ ? { trustProxy: true }
696
+ : {}),
697
+ // The app's KV store under the SDK's Kv contract: JSON values,
698
+ // second-based TTLs, `auth:`-prefixed keys disjoint from API-key
699
+ // records. `take` is omitted; the SDK falls back to get+delete.
700
+ store: {
701
+ async delete(key) {
702
+ await kv.store.delete(`auth:${key}`)
703
+ },
704
+ async get(key) {
705
+ const value = await kv.store.get(`auth:${key}`)
706
+ return value === null ? undefined : (JSON.parse(value) as never)
707
+ },
708
+ async set(key, value, options) {
709
+ await kv.store.put(
710
+ `auth:${key}`,
711
+ JSON.stringify(value),
712
+ options?.ttl ? { ttl: options.ttl * 1_000 } : {},
713
+ )
714
+ },
715
+ },
716
+ ...(ttl ? { ttl } : {}),
717
+ })
718
+
719
+ const session: Session = {
720
+ resolve: async (c) => {
721
+ const payload = await handler.getSession(c.req.raw)
722
+ if (!payload) return null
723
+ const database = Db.get(db)
724
+ const user = await Users.getByAddress(database, payload.address)
725
+ if (!user) return null
726
+ // Fold a newly verified email onto the user row: identity tokens are
727
+ // optional per sign-in, so the column fills lazily.
728
+ if (payload.email && user.email !== payload.email)
729
+ await Users.setEmail(database, user.id, payload.email)
730
+ const email = payload.email ?? user.email ?? undefined
731
+ return {
732
+ ...(email ? { email } : {}),
733
+ id: user.id,
734
+ identity: { provider: 'wallet', subject: payload.address },
735
+ type: 'session',
736
+ }
737
+ },
738
+ }
739
+
740
+ const policy_public = policy({ public: true })
741
+ const describe = OpenApi.describeRoute({ hide: true })
742
+ const app = new Hono<App.Environment>()
743
+ .use('/v1/auth/siwe', policy_public, describe)
744
+ .use('/v1/auth/siwe/*', policy_public, describe)
745
+ // Documented operations: pre-routes carrying the OpenAPI metadata and the
746
+ // envelope-shaped body validation; the mounted handler owns behavior.
747
+ // Statement form keeps them off the app type (`hc` surface).
748
+ app.post(
749
+ '/v1/auth/siwe/challenge',
750
+ policy_public,
751
+ OpenApi.validate('json', schema.challenge.Body, {
752
+ code: 'body_invalid',
753
+ message: 'Invalid request body',
754
+ }),
755
+ OpenApi.describeRoute({
756
+ description:
757
+ 'Issues a single-use SIWE challenge message to sign. The challenge expires after a short TTL.',
758
+ // Overrides the surface-wide hidden mount guard for this documented route.
759
+ hide: false,
760
+ operationId: 'createSiweChallenge',
761
+ responses: {
762
+ 200: {
763
+ content: { 'application/json': { schema: OpenApi.resolver(schema.challenge.Response) } }, // prettier-ignore
764
+ description: 'The challenge message to sign.',
765
+ headers: { ...OpenApi.successHeaders },
766
+ },
767
+ 400: OpenApi.standardError(400, 'Malformed request body.', ['body_invalid']),
768
+ 429: OpenApi.standardError(429, 'Rate limited.'),
769
+ 500: OpenApi.standardError(500, 'Internal server error.'),
770
+ },
771
+ summary: 'Create challenge (SIWE)',
772
+ tags: ['Authentication'],
773
+ }),
774
+ )
775
+ app.post(
776
+ '/v1/auth/siwe',
777
+ policy_public,
778
+ OpenApi.validate('json', schema.verify.Body, {
779
+ code: 'body_invalid',
780
+ message: 'Invalid request body',
781
+ }),
782
+ OpenApi.describeRoute({
783
+ description:
784
+ 'Verifies the signed challenge and establishes a session. Sets a session cookie by default; pass `returnToken` to receive a bearer token instead.',
785
+ hide: false,
786
+ operationId: 'verifySiwe',
787
+ responses: {
788
+ 200: {
789
+ content: { 'application/json': { schema: OpenApi.resolver(schema.verify.Response) } },
790
+ description: 'Session established. Cookie mode sets `Set-Cookie`; token mode returns `token`.', // prettier-ignore
791
+ headers: {
792
+ ...OpenApi.successHeaders,
793
+ 'Set-Cookie': {
794
+ description: 'Session cookie (`accounts_auth`); omitted when `returnToken` is true.', // prettier-ignore
795
+ schema: { type: 'string' },
796
+ },
797
+ },
798
+ },
799
+ 400: handlerError('Invalid, expired, or mismatched challenge message.'),
800
+ 401: handlerError('Signature or identity verification failed.'),
801
+ 409: handlerError('Challenge nonce already used or unknown.'),
802
+ 429: OpenApi.standardError(429, 'Rate limited.'),
803
+ 500: OpenApi.standardError(500, 'Internal server error.'),
804
+ },
805
+ summary: 'Authenticate (SIWE)',
806
+ tags: ['Authentication'],
807
+ }),
808
+ )
809
+ app.post(
810
+ '/v1/auth/siwe/logout',
811
+ policy_public,
812
+ OpenApi.describeRoute({
813
+ description: 'Revokes the current session and clears the session cookie.',
814
+ hide: false,
815
+ operationId: 'siweLogout',
816
+ responses: {
817
+ 204: { description: 'Session revoked and cookie cleared.' },
818
+ 429: OpenApi.standardError(429, 'Rate limited.'),
819
+ 500: OpenApi.standardError(500, 'Internal server error.'),
820
+ },
821
+ summary: 'Sign out',
822
+ tags: ['Authentication'],
823
+ }),
824
+ )
825
+ return Object.assign(
826
+ // The tag joins the `Platform API` group at `platform()`'s chosen slot;
827
+ // group listings filter to defined tags, so it drops out when unmounted.
828
+ Metadata.attach(app.route('/v1/auth/siwe', handler), {
829
+ tags: [{ name: 'Authentication', description: 'Authenticate into the Tempo Platform.' }],
830
+ }),
831
+ { session },
832
+ )
833
+ })()
834
+
835
+ const middleware_ =
836
+ options.auth === false
837
+ ? undefined
838
+ : middleware({ ...options_middleware, ...(app ? { session: app.session } : {}) })
839
+
840
+ return { app, middleware: middleware_ }
841
+ }
842
+
843
+ export declare namespace install {
844
+ /** Options for building the built-in auth pieces. */
845
+ type Options = {
846
+ auth:
847
+ | (Omit<middleware.Options, 'session'> & {
848
+ /** Session sign-in surface: mounts `/v1/auth/siwe` (challenge/verify/logout) and enables the session lane. Pass true for defaults or a wallet config. Requires `kv`. */
849
+ session?: boolean | { wallet?: Wallet | undefined } | undefined
850
+ })
851
+ | false
852
+ | undefined
853
+ /** Authoritative database holding user rows. */
854
+ db: Db.Source
855
+ /** KV state store, or undefined when key auth is closed. */
856
+ kv: { store: Store.State } | undefined
857
+ }
858
+
859
+ /** SIWE wallet sign-in configuration. */
860
+ type Wallet = {
861
+ /** Identity (verified email) issuer override; defaults to the Tempo wallet's production OIDC mount. */
862
+ issuer?: string | undefined
863
+ /** Pinned absolute origin for SIWE domain binding. Required off-Workers; on Workers the edge proxy headers are trusted by default. */
864
+ origin?: string | undefined
865
+ /** Session and challenge TTL overrides, in seconds. */
866
+ ttl?: { challenge?: number | undefined; session?: number | undefined } | undefined
867
+ }
868
+ }
869
+
336
870
  /** Creates route middleware that contributes an auth policy override. */
337
871
  export function policy(options: PolicyOverride): MiddlewareHandler {
338
872
  const middleware = createMiddleware((_, next) => next()) as MiddlewareHandler & PolicyHandler
@@ -376,6 +910,7 @@ type LaneDefaults = {
376
910
  apiKey: NonNullable<require.Policy['apiKey']>
377
911
  mpp: NonNullable<require.Policy['mpp']>
378
912
  public: NonNullable<require.Policy['public']>
913
+ session: NonNullable<require.Policy['session']>
379
914
  }
380
915
 
381
916
  type OpenApiHandler = {
@@ -409,19 +944,10 @@ function laneDefaults(options: middleware.Options): LaneDefaults {
409
944
  },
410
945
  mpp: { charge: {} },
411
946
  public: { rateLimit: public_?.rateLimit ?? { perMinute: 60 } },
947
+ session: true,
412
948
  }
413
949
  }
414
950
 
415
- /**
416
- * Base policy before route overrides. Default-closed except the API-key lane:
417
- * a route that declares nothing is API-key-only, and must opt into the public
418
- * and paid lanes explicitly. This makes a partial override fail safe — omitting
419
- * a lane leaves it closed rather than silently inheriting open access.
420
- */
421
- function basePolicy(options: middleware.Options, defaults: LaneDefaults): require.Policy {
422
- return options.apiKey === false ? {} : { apiKey: defaults.apiKey }
423
- }
424
-
425
951
  function resolvePolicy(
426
952
  base: require.Policy,
427
953
  defaults: LaneDefaults,
@@ -431,6 +957,7 @@ function resolvePolicy(
431
957
  let apiKey = base.apiKey
432
958
  let mpp = base.mpp
433
959
  let public_ = base.public
960
+ let session = base.session
434
961
 
435
962
  for (const override of overrides) {
436
963
  if (override === false) return undefined
@@ -438,6 +965,7 @@ function resolvePolicy(
438
965
  apiKey = resolveApiKeyLane(apiKey, override.apiKey, defaults.apiKey)
439
966
  mpp = resolveMppLane(mpp, override.mpp, defaults.mpp)
440
967
  public_ = resolvePublicLane(public_, override.public, defaults.public)
968
+ session = resolveSessionLane(session, override.session, defaults.session)
441
969
  }
442
970
 
443
971
  // The OpenAPI summary/description rides along as the payment-challenge
@@ -446,11 +974,13 @@ function resolvePolicy(
446
974
  if (mpp && description !== undefined && mpp.charge.description === undefined)
447
975
  mpp = { charge: { ...mpp.charge, description } }
448
976
 
449
- if (!apiKey && !mpp && !public_) return undefined
977
+ // A lane-less policy stays enforced for super admin; only an explicit
978
+ // `false` override opts a route out of auth entirely.
450
979
  return {
451
980
  ...(apiKey ? { apiKey } : {}),
452
981
  ...(mpp ? { mpp } : {}),
453
982
  ...(public_ ? { public: public_ } : {}),
983
+ ...(session ? { session } : {}),
454
984
  }
455
985
  }
456
986
 
@@ -493,6 +1023,16 @@ function resolvePublicLane(
493
1023
  return { rateLimit: override.rateLimit ?? base.rateLimit }
494
1024
  }
495
1025
 
1026
+ function resolveSessionLane(
1027
+ current: require.Policy['session'],
1028
+ override: PolicyOverride['session'],
1029
+ default_: LaneDefaults['session'],
1030
+ ): require.Policy['session'] {
1031
+ if (override === undefined) return current
1032
+ if (override === false) return undefined
1033
+ return current ?? default_
1034
+ }
1035
+
496
1036
  function openApiRoute(c: hono_Context): OpenApiRoute | undefined {
497
1037
  const route = matchedRoutes(c)
498
1038
  .slice(c.req.routeIndex + 1)
@@ -530,20 +1070,24 @@ function policyFromRoute(route: RouterRoute) {
530
1070
 
531
1071
  /** Resolved access lanes for one documented operation. */
532
1072
  export type OperationAccess = {
1073
+ /** Whether API-key access is allowed. */
1074
+ apiKey: boolean
533
1075
  /** Whether MPP payment is accepted (enables the `402` payment-challenge response). */
534
1076
  mpp: boolean
535
1077
  /** Whether anonymous public-quota access is allowed (keeps the anonymous security option). */
536
1078
  public: boolean
537
1079
  /** Required API-key scopes (`[]` accepts any valid key); empty when the API-key lane is disabled. */
538
1080
  scopes: readonly Scope.Id[]
1081
+ /** Whether signed-in sessions are allowed. */
1082
+ session: boolean
539
1083
  }
540
1084
 
541
1085
  /**
542
1086
  * Maps each documented operation id to its resolved access lanes by reading the
543
1087
  * `policy` overrides co-located on the app's routes. This is the single source
544
1088
  * of truth for per-operation `security` and the `402` response in the generated
545
- * OpenAPI document (see `App.create`): the API-key lane is default-on, and the
546
- * public/MPP lanes apply only where a route opts in via {@link policy}.
1089
+ * OpenAPI document (see `App.create`): all lanes are default-closed, and an
1090
+ * operation surfaces only the lanes its route opts into via {@link policy}.
547
1091
  */
548
1092
  export function describeAccess(app: Hono<any, any>): Record<string, OperationAccess> {
549
1093
  const groups = new Map<string, { operationId?: string; overrides: PolicyOverride[] }>()
@@ -560,18 +1104,20 @@ export function describeAccess(app: Hono<any, any>): Record<string, OperationAcc
560
1104
  const access: Record<string, OperationAccess> = {}
561
1105
  for (const { operationId, overrides } of groups.values()) {
562
1106
  if (!operationId) continue
563
- let apiKey = true
1107
+ let apiKey = false
564
1108
  let scopes: readonly Scope.Id[] = []
565
1109
  let public_ = false
566
1110
  let mpp = false
1111
+ let session = false
567
1112
  for (const override of overrides) {
568
1113
  if (override.apiKey !== undefined) apiKey = override.apiKey !== false
569
1114
  if (override.apiKey && override.apiKey !== true && override.apiKey.scopes)
570
1115
  scopes = override.apiKey.scopes
571
1116
  if (override.public !== undefined) public_ = override.public !== false
572
1117
  if (override.mpp !== undefined) mpp = override.mpp !== false
1118
+ if (override.session !== undefined) session = override.session !== false
573
1119
  }
574
- access[operationId] = { mpp, public: public_, scopes: apiKey ? scopes : [] }
1120
+ access[operationId] = { apiKey, mpp, public: public_, scopes: apiKey ? scopes : [], session }
575
1121
  }
576
1122
  return access
577
1123
  }
@@ -627,13 +1173,31 @@ export function require<environment extends Environment = Environment>(
627
1173
 
628
1174
  const hasPayment = hasPaymentCredential(c) && isPaymentEnabled(auth, policy)
629
1175
  if (credential) {
1176
+ // The super admin secret precedes key lookup: it is not a stored key.
1177
+ // It must never reach KV resolution or scope/quota checks.
1178
+ if (auth.superAdmin && ApiKey.hash(credential.token) === auth.superAdmin.tokenHash) {
1179
+ c.set('principal', { actor: 'super_admin', id: 'super_admin', type: 'super_admin' })
1180
+ return next()
1181
+ }
1182
+
630
1183
  const apiKey = await auth.apiKey?.resolve(c, credential.token)
631
- if (!apiKey)
1184
+ if (!apiKey) {
1185
+ // A bearer session token is indistinguishable from an API key at the
1186
+ // header, so a failed key lookup falls through to the session lane
1187
+ // before rejecting.
1188
+ if (policy.session) {
1189
+ const session = await resolveSession(c, auth)
1190
+ if (session) {
1191
+ c.set('principal', session)
1192
+ return next()
1193
+ }
1194
+ }
632
1195
  return jsonError(c, {
633
1196
  code: 'api_key_invalid',
634
1197
  message: 'Invalid API key',
635
1198
  status: 401,
636
1199
  })
1200
+ }
637
1201
 
638
1202
  // Sandbox keys are restricted to non-mainnet chains. An explicit
639
1203
  // `chainId` of mainnet makes the key unusable for this request, so it is
@@ -658,6 +1222,14 @@ export function require<environment extends Environment = Environment>(
658
1222
 
659
1223
  const principal = principalFromApiKey(apiKey)
660
1224
  c.set('principal', principal)
1225
+ // Default-deny: routes with no API-key lane allow only the super admin.
1226
+ // A valid key is authenticated but forbidden.
1227
+ if (!policy.apiKey)
1228
+ return jsonError(c, {
1229
+ code: 'forbidden',
1230
+ message: 'API key not permitted for this route',
1231
+ status: 403,
1232
+ })
661
1233
  const missing = apiKey.scopes.includes(Scope.wildcard)
662
1234
  ? []
663
1235
  : (policy.apiKey?.scopes.filter((scope) => !apiKey.scopes.includes(scope)) ?? [])
@@ -707,6 +1279,17 @@ export function require<environment extends Environment = Environment>(
707
1279
  }
708
1280
  }
709
1281
 
1282
+ // Cookie sessions carry no Authorization credential; resolve the session
1283
+ // lane before the public/paid lanes so signed-in browsers skip anonymous
1284
+ // quotas.
1285
+ if (policy.session) {
1286
+ const session = await resolveSession(c, auth)
1287
+ if (session) {
1288
+ c.set('principal', session)
1289
+ return next()
1290
+ }
1291
+ }
1292
+
710
1293
  const publicPrincipal = principalFromPublic(c)
711
1294
  if (hasPayment) {
712
1295
  c.set('principal', publicPrincipal)
@@ -788,6 +1371,8 @@ export declare namespace require {
788
1371
  rateLimit: RateLimit.Limit
789
1372
  }
790
1373
  | undefined
1374
+ /** Session access policy: `true` allows signed-in wallet sessions. */
1375
+ session?: boolean | undefined
791
1376
  }
792
1377
  }
793
1378
 
@@ -806,19 +1391,65 @@ type RateLimitAccess = PaymentAccess & {
806
1391
  rateLimit: RateLimit.Result
807
1392
  }
808
1393
 
1394
+ /**
1395
+ * How long to wait for the rate-limit counter before failing open. A hot
1396
+ * counter backend (e.g. a single Durable Object serving one busy quota bucket)
1397
+ * can stall for tens of seconds during a storage-latency blip; without this cap
1398
+ * every request for that bucket blocks on it, turning a localized backend
1399
+ * hiccup into an API-wide latency spike. The abandoned increment still applies
1400
+ * server-side — we just stop waiting for it.
1401
+ */
1402
+ const rateLimitConsumeTimeout = 300
1403
+
809
1404
  function consumeRateLimit(
810
1405
  auth: Context,
811
1406
  options: RateLimit.Store.ConsumeOptions,
812
1407
  ): Promise<RateLimit.Result | null> {
813
1408
  if (!auth.rateLimit) return Promise.resolve(null)
814
- // Fail open: a rate-limit store error (e.g. a lost Durable Object
815
- // connection) degrades to unenforced quota instead of failing the request.
816
- return auth.rateLimit.consume(options).catch((error) => {
817
- console.error('[auth] rate-limit consume failed; failing open', error)
1409
+ // Fail open: a rate-limit store error (e.g. a lost Durable Object connection)
1410
+ // or a stall past `rateLimitConsumeTimeout` degrades to unenforced quota
1411
+ // instead of failing — or blocking — the request.
1412
+ return withTimeout(auth.rateLimit.consume(options), rateLimitConsumeTimeout).catch((error) => {
1413
+ // A timeout or Cloudflare Durable Object overload is an expected
1414
+ // degradation (a slow/hot counter backend), logged at warn so it stays out
1415
+ // of error dashboards; an unexpected store throw is a genuine error.
1416
+ if (error instanceof RateLimitTimeoutError || isDurableObjectOverload(error))
1417
+ console.warn('[auth] rate-limit consume degraded; failing open', error)
1418
+ else console.error('[auth] rate-limit consume failed; failing open', error)
818
1419
  return null
819
1420
  })
820
1421
  }
821
1422
 
1423
+ function isDurableObjectOverload(error: unknown): boolean {
1424
+ return (
1425
+ typeof error === 'object' &&
1426
+ error !== null &&
1427
+ 'overloaded' in error &&
1428
+ (error as { overloaded?: unknown }).overloaded === true
1429
+ )
1430
+ }
1431
+
1432
+ /**
1433
+ * Rejects with a {@link RateLimitTimeoutError} if `promise` does not settle
1434
+ * within `ms`, clearing the timer on either outcome so it never keeps the
1435
+ * isolate alive.
1436
+ */
1437
+ function withTimeout<value>(promise: Promise<value>, ms: number): Promise<value> {
1438
+ return new Promise<value>((resolve, reject) => {
1439
+ const timer = setTimeout(() => reject(new RateLimitTimeoutError(ms)), ms)
1440
+ promise.then(
1441
+ (value) => {
1442
+ clearTimeout(timer)
1443
+ resolve(value)
1444
+ },
1445
+ (error) => {
1446
+ clearTimeout(timer)
1447
+ reject(error)
1448
+ },
1449
+ )
1450
+ })
1451
+ }
1452
+
822
1453
  /**
823
1454
  * Resolves the rate limit for an API-key request against a quota bucket.
824
1455
  * Most-specific wins: a per-key per-scope grant is the only thing that pierces a
@@ -959,6 +1590,7 @@ function principalFromApiKey(apiKey: ApiKey.ApiKey): Principal {
959
1590
  environment: apiKey.environment,
960
1591
  id: apiKey.id,
961
1592
  orgId: apiKey.orgId,
1593
+ ...(apiKey.projectId === undefined ? {} : { projectId: apiKey.projectId }),
962
1594
  type: 'api_key',
963
1595
  }
964
1596
  }
@@ -1032,6 +1664,32 @@ function contextKv(c: hono_Context): { store: Store.State } | undefined {
1032
1664
  return (c.get as (key: string) => unknown)('kv') as { store: Store.State } | undefined
1033
1665
  }
1034
1666
 
1667
+ /** Session principal resolved by the session lane. */
1668
+ export type SessionPrincipal = Extract<Principal, { type: 'session' }>
1669
+
1670
+ /**
1671
+ * Session resolution capability installed on app context (`session`) by
1672
+ * `App.create` when session auth is configured; `undefined` leaves the session
1673
+ * lane closed.
1674
+ */
1675
+ export type Session = {
1676
+ /** Resolves the request's session (cookie or bearer token) to a principal. */
1677
+ resolve: (c: hono_Context) => Promise<SessionPrincipal | null>
1678
+ }
1679
+
1680
+ // Resolve the session lane from the auth context's capability. Failures
1681
+ // resolve to `null` so the remaining lanes and the final `401` still apply.
1682
+ async function resolveSession(c: hono_Context, auth: Context): Promise<SessionPrincipal | null> {
1683
+ const session = auth.session
1684
+ if (!session) return null
1685
+ try {
1686
+ return await session.resolve(c)
1687
+ } catch (error) {
1688
+ console.error('[auth] session resolve failed; treating as no session', error)
1689
+ return null
1690
+ }
1691
+ }
1692
+
1035
1693
  /** Maximum resolve-cache entries held per isolate before eviction. */
1036
1694
  const cachedMaxEntries = 10_000
1037
1695
 
@@ -1083,3 +1741,11 @@ function cachedResolve(
1083
1741
  }
1084
1742
  }
1085
1743
  }
1744
+
1745
+ /** Thrown when a rate-limit consume exceeds {@link rateLimitConsumeTimeout}. */
1746
+ export class RateLimitTimeoutError extends Error {
1747
+ override name = 'Auth.RateLimitTimeoutError'
1748
+ constructor(ms: number) {
1749
+ super(`Rate-limit consume timed out after ${ms}ms; failing open.`)
1750
+ }
1751
+ }