@absol-labs/agent 0.11.0 → 0.12.0-rc.2

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 (366) hide show
  1. package/README.md +43 -51
  2. package/dist/capability/invocation-capability.d.ts +2 -2
  3. package/dist/capability/probe-capability.d.ts +225 -0
  4. package/dist/capability/probe-capability.d.ts.map +1 -0
  5. package/dist/capability/probe-capability.js +248 -0
  6. package/dist/capability/probe-capability.js.map +1 -0
  7. package/dist/cli/args.d.ts +11 -0
  8. package/dist/cli/args.d.ts.map +1 -1
  9. package/dist/cli/args.js +23 -0
  10. package/dist/cli/args.js.map +1 -1
  11. package/dist/cli/commands/claim.d.ts +32 -0
  12. package/dist/cli/commands/claim.d.ts.map +1 -1
  13. package/dist/cli/commands/claim.js +116 -28
  14. package/dist/cli/commands/claim.js.map +1 -1
  15. package/dist/cli/commands/close.d.ts.map +1 -1
  16. package/dist/cli/commands/close.js +35 -3
  17. package/dist/cli/commands/close.js.map +1 -1
  18. package/dist/cli/commands/invoke.d.ts +2 -0
  19. package/dist/cli/commands/invoke.d.ts.map +1 -1
  20. package/dist/cli/commands/invoke.js +2 -2
  21. package/dist/cli/commands/invoke.js.map +1 -1
  22. package/dist/cli/commands/mcp.d.ts.map +1 -1
  23. package/dist/cli/commands/mcp.js +15 -2
  24. package/dist/cli/commands/mcp.js.map +1 -1
  25. package/dist/cli/commands/open.d.ts.map +1 -1
  26. package/dist/cli/commands/open.js +58 -4
  27. package/dist/cli/commands/open.js.map +1 -1
  28. package/dist/cli/commands/pay.d.ts +34 -0
  29. package/dist/cli/commands/pay.d.ts.map +1 -0
  30. package/dist/cli/commands/pay.js +479 -0
  31. package/dist/cli/commands/pay.js.map +1 -0
  32. package/dist/cli/commands/reclaim-unverified.d.ts.map +1 -1
  33. package/dist/cli/commands/reclaim-unverified.js +3 -2
  34. package/dist/cli/commands/reclaim-unverified.js.map +1 -1
  35. package/dist/cli/commands/reclaim.d.ts +23 -2
  36. package/dist/cli/commands/reclaim.d.ts.map +1 -1
  37. package/dist/cli/commands/reclaim.js +204 -20
  38. package/dist/cli/commands/reclaim.js.map +1 -1
  39. package/dist/cli/commands/refund.d.ts +31 -0
  40. package/dist/cli/commands/refund.d.ts.map +1 -0
  41. package/dist/cli/commands/refund.js +397 -0
  42. package/dist/cli/commands/refund.js.map +1 -0
  43. package/dist/cli/commands/status.d.ts.map +1 -1
  44. package/dist/cli/commands/status.js +21 -2
  45. package/dist/cli/commands/status.js.map +1 -1
  46. package/dist/cli/commands/streams.js +8 -12
  47. package/dist/cli/commands/streams.js.map +1 -1
  48. package/dist/cli/commands/watch.d.ts.map +1 -1
  49. package/dist/cli/commands/watch.js +4 -1
  50. package/dist/cli/commands/watch.js.map +1 -1
  51. package/dist/cli/config.d.ts +44 -26
  52. package/dist/cli/config.d.ts.map +1 -1
  53. package/dist/cli/config.js +88 -38
  54. package/dist/cli/config.js.map +1 -1
  55. package/dist/cli/context.d.ts +19 -0
  56. package/dist/cli/context.d.ts.map +1 -1
  57. package/dist/cli/deps.d.ts +53 -0
  58. package/dist/cli/deps.d.ts.map +1 -1
  59. package/dist/cli/deps.js +51 -1
  60. package/dist/cli/deps.js.map +1 -1
  61. package/dist/cli/errors.d.ts.map +1 -1
  62. package/dist/cli/errors.js +21 -1
  63. package/dist/cli/errors.js.map +1 -1
  64. package/dist/cli/escape-hatch.d.ts +6 -4
  65. package/dist/cli/escape-hatch.d.ts.map +1 -1
  66. package/dist/cli/escape-hatch.js +6 -2
  67. package/dist/cli/escape-hatch.js.map +1 -1
  68. package/dist/cli/listings.d.ts +18 -0
  69. package/dist/cli/listings.d.ts.map +1 -1
  70. package/dist/cli/listings.js +22 -12
  71. package/dist/cli/listings.js.map +1 -1
  72. package/dist/cli/preflight.d.ts +20 -14
  73. package/dist/cli/preflight.d.ts.map +1 -1
  74. package/dist/cli/preflight.js +34 -20
  75. package/dist/cli/preflight.js.map +1 -1
  76. package/dist/cli/privy.d.ts.map +1 -1
  77. package/dist/cli/privy.js +4 -2
  78. package/dist/cli/privy.js.map +1 -1
  79. package/dist/cli/progress.d.ts +13 -1
  80. package/dist/cli/progress.d.ts.map +1 -1
  81. package/dist/cli/progress.js +7 -1
  82. package/dist/cli/progress.js.map +1 -1
  83. package/dist/cli/reclaimability.d.ts +82 -0
  84. package/dist/cli/reclaimability.d.ts.map +1 -0
  85. package/dist/cli/reclaimability.js +80 -0
  86. package/dist/cli/reclaimability.js.map +1 -0
  87. package/dist/cli/run.d.ts +2 -1
  88. package/dist/cli/run.d.ts.map +1 -1
  89. package/dist/cli/run.js +62 -21
  90. package/dist/cli/run.js.map +1 -1
  91. package/dist/cli/scan-range.d.ts +2 -2
  92. package/dist/cli/scan-range.js +2 -2
  93. package/dist/cli/status.d.ts +106 -1
  94. package/dist/cli/status.d.ts.map +1 -1
  95. package/dist/cli/status.js +325 -44
  96. package/dist/cli/status.js.map +1 -1
  97. package/dist/cli/wallet.d.ts +2 -2
  98. package/dist/cli/wallet.d.ts.map +1 -1
  99. package/dist/cli/wallet.js +29 -5
  100. package/dist/cli/wallet.js.map +1 -1
  101. package/dist/discovery/rate-band.d.ts +74 -0
  102. package/dist/discovery/rate-band.d.ts.map +1 -0
  103. package/dist/discovery/rate-band.js +114 -0
  104. package/dist/discovery/rate-band.js.map +1 -0
  105. package/dist/escrow-generation.d.ts +81 -0
  106. package/dist/escrow-generation.d.ts.map +1 -0
  107. package/dist/escrow-generation.js +76 -0
  108. package/dist/escrow-generation.js.map +1 -0
  109. package/dist/frameworks/agentkit.d.ts +8 -4
  110. package/dist/frameworks/agentkit.d.ts.map +1 -1
  111. package/dist/frameworks/agentkit.js +27 -10
  112. package/dist/frameworks/agentkit.js.map +1 -1
  113. package/dist/frameworks/eliza.d.ts +3 -3
  114. package/dist/frameworks/eliza.d.ts.map +1 -1
  115. package/dist/frameworks/eliza.js +15 -6
  116. package/dist/frameworks/eliza.js.map +1 -1
  117. package/dist/frameworks/langchain.d.ts.map +1 -1
  118. package/dist/frameworks/langchain.js +8 -0
  119. package/dist/frameworks/langchain.js.map +1 -1
  120. package/dist/gateway/caller-auth-gateway.d.ts +83 -31
  121. package/dist/gateway/caller-auth-gateway.d.ts.map +1 -1
  122. package/dist/gateway/caller-auth-gateway.js +180 -27
  123. package/dist/gateway/caller-auth-gateway.js.map +1 -1
  124. package/dist/gateway/http-server.d.ts +128 -44
  125. package/dist/gateway/http-server.d.ts.map +1 -1
  126. package/dist/gateway/http-server.js +506 -155
  127. package/dist/gateway/http-server.js.map +1 -1
  128. package/dist/gateway/probe-auth.d.ts +112 -0
  129. package/dist/gateway/probe-auth.d.ts.map +1 -0
  130. package/dist/gateway/probe-auth.js +127 -0
  131. package/dist/gateway/probe-auth.js.map +1 -0
  132. package/dist/gateway/signed-rate-bounds.d.ts +51 -0
  133. package/dist/gateway/signed-rate-bounds.d.ts.map +1 -0
  134. package/dist/gateway/signed-rate-bounds.js +94 -0
  135. package/dist/gateway/signed-rate-bounds.js.map +1 -0
  136. package/dist/gateway/x402-opener-mode.d.ts +252 -0
  137. package/dist/gateway/x402-opener-mode.d.ts.map +1 -0
  138. package/dist/gateway/x402-opener-mode.js +772 -0
  139. package/dist/gateway/x402-opener-mode.js.map +1 -0
  140. package/dist/index.d.ts +6 -2
  141. package/dist/index.d.ts.map +1 -1
  142. package/dist/index.js +10 -5
  143. package/dist/index.js.map +1 -1
  144. package/dist/mcp/server.d.ts +64 -1
  145. package/dist/mcp/server.d.ts.map +1 -1
  146. package/dist/mcp/server.js +247 -37
  147. package/dist/mcp/server.js.map +1 -1
  148. package/dist/mcp/x402-tools.d.ts +109 -0
  149. package/dist/mcp/x402-tools.d.ts.map +1 -0
  150. package/dist/mcp/x402-tools.js +554 -0
  151. package/dist/mcp/x402-tools.js.map +1 -0
  152. package/dist/network.d.ts +119 -0
  153. package/dist/network.d.ts.map +1 -0
  154. package/dist/network.js +199 -0
  155. package/dist/network.js.map +1 -0
  156. package/dist/sdk/client.d.ts +13 -0
  157. package/dist/sdk/client.d.ts.map +1 -1
  158. package/dist/sdk/client.js +15 -0
  159. package/dist/sdk/client.js.map +1 -1
  160. package/dist/sdk/invoke.d.ts +4 -0
  161. package/dist/sdk/invoke.d.ts.map +1 -1
  162. package/dist/sdk/invoke.js +2 -5
  163. package/dist/sdk/invoke.js.map +1 -1
  164. package/dist/wallet/autonomous-wallet-broker.d.ts +3 -0
  165. package/dist/wallet/autonomous-wallet-broker.d.ts.map +1 -1
  166. package/dist/wallet/autonomous-wallet-broker.js +27 -23
  167. package/dist/wallet/autonomous-wallet-broker.js.map +1 -1
  168. package/dist/wallet/autonomous-wallet-protocol.d.ts +8 -2
  169. package/dist/wallet/autonomous-wallet-protocol.d.ts.map +1 -1
  170. package/dist/wallet/autonomous-wallet-protocol.js +9 -2
  171. package/dist/wallet/autonomous-wallet-protocol.js.map +1 -1
  172. package/dist/wallet/autonomous-wallet.d.ts.map +1 -1
  173. package/dist/wallet/autonomous-wallet.js +3 -2
  174. package/dist/wallet/autonomous-wallet.js.map +1 -1
  175. package/dist/wallet/broker-escrows.d.ts +222 -0
  176. package/dist/wallet/broker-escrows.d.ts.map +1 -0
  177. package/dist/wallet/broker-escrows.js +36 -0
  178. package/dist/wallet/broker-escrows.js.map +1 -0
  179. package/dist/wallet/lifecycle.d.ts +21 -5
  180. package/dist/wallet/lifecycle.d.ts.map +1 -1
  181. package/dist/wallet/lifecycle.js +32 -12
  182. package/dist/wallet/lifecycle.js.map +1 -1
  183. package/dist/wallet/privy-broker-server.d.ts.map +1 -1
  184. package/dist/wallet/privy-broker-server.js +31 -26
  185. package/dist/wallet/privy-broker-server.js.map +1 -1
  186. package/dist/wallet/privy-session-broker.d.ts +14 -5
  187. package/dist/wallet/privy-session-broker.d.ts.map +1 -1
  188. package/dist/wallet/privy-session-broker.js +29 -17
  189. package/dist/wallet/privy-session-broker.js.map +1 -1
  190. package/dist/wallet/privy-session-provider.d.ts +3 -1
  191. package/dist/wallet/privy-session-provider.d.ts.map +1 -1
  192. package/dist/wallet/privy-session-provider.js +5 -3
  193. package/dist/wallet/privy-session-provider.js.map +1 -1
  194. package/dist/wallet/provider.d.ts.map +1 -1
  195. package/dist/wallet/provider.js +15 -8
  196. package/dist/wallet/provider.js.map +1 -1
  197. package/dist/x402/facilitator.d.ts +3 -30
  198. package/dist/x402/facilitator.d.ts.map +1 -1
  199. package/dist/x402/facilitator.js +0 -27
  200. package/dist/x402/facilitator.js.map +1 -1
  201. package/dist/x402-buyer/guarded-fetch.d.ts +51 -0
  202. package/dist/x402-buyer/guarded-fetch.d.ts.map +1 -0
  203. package/dist/x402-buyer/guarded-fetch.js +147 -0
  204. package/dist/x402-buyer/guarded-fetch.js.map +1 -0
  205. package/dist/x402-buyer/index.d.ts +10 -0
  206. package/dist/x402-buyer/index.d.ts.map +1 -0
  207. package/dist/x402-buyer/index.js +10 -0
  208. package/dist/x402-buyer/index.js.map +1 -0
  209. package/dist/x402-buyer/pay.d.ts +157 -0
  210. package/dist/x402-buyer/pay.d.ts.map +1 -0
  211. package/dist/x402-buyer/pay.js +366 -0
  212. package/dist/x402-buyer/pay.js.map +1 -0
  213. package/dist/x402-opener/abi.d.ts +684 -0
  214. package/dist/x402-opener/abi.d.ts.map +1 -0
  215. package/dist/x402-opener/abi.js +880 -0
  216. package/dist/x402-opener/abi.js.map +1 -0
  217. package/dist/x402-opener/facilitator-entry.d.ts +3 -0
  218. package/dist/x402-opener/facilitator-entry.d.ts.map +1 -0
  219. package/dist/x402-opener/facilitator-entry.js +40 -0
  220. package/dist/x402-opener/facilitator-entry.js.map +1 -0
  221. package/dist/x402-opener/facilitator-server.d.ts +209 -0
  222. package/dist/x402-opener/facilitator-server.d.ts.map +1 -0
  223. package/dist/x402-opener/facilitator-server.js +762 -0
  224. package/dist/x402-opener/facilitator-server.js.map +1 -0
  225. package/dist/x402-opener/index.d.ts +14 -0
  226. package/dist/x402-opener/index.d.ts.map +1 -0
  227. package/dist/x402-opener/index.js +14 -0
  228. package/dist/x402-opener/index.js.map +1 -0
  229. package/dist/x402-opener/listing-resolver.d.ts +55 -0
  230. package/dist/x402-opener/listing-resolver.d.ts.map +1 -0
  231. package/dist/x402-opener/listing-resolver.js +125 -0
  232. package/dist/x402-opener/listing-resolver.js.map +1 -0
  233. package/dist/x402-opener/refund-relay.d.ts +266 -0
  234. package/dist/x402-opener/refund-relay.d.ts.map +1 -0
  235. package/dist/x402-opener/refund-relay.js +1341 -0
  236. package/dist/x402-opener/refund-relay.js.map +1 -0
  237. package/dist/x402-opener/relayer-queue.d.ts +14 -0
  238. package/dist/x402-opener/relayer-queue.d.ts.map +1 -0
  239. package/dist/x402-opener/relayer-queue.js +18 -0
  240. package/dist/x402-opener/relayer-queue.js.map +1 -0
  241. package/dist/x402-opener/scheme.d.ts +296 -0
  242. package/dist/x402-opener/scheme.d.ts.map +1 -0
  243. package/dist/x402-opener/scheme.js +1035 -0
  244. package/dist/x402-opener/scheme.js.map +1 -0
  245. package/dist/x402-opener/terms.d.ts +165 -0
  246. package/dist/x402-opener/terms.d.ts.map +1 -0
  247. package/dist/x402-opener/terms.js +304 -0
  248. package/dist/x402-opener/terms.js.map +1 -0
  249. package/dist/zktls/reclaim.d.ts +2 -2
  250. package/dist/zktls/t2-delivery-proof.d.ts +6 -6
  251. package/docs/agent-layer.md +3 -2
  252. package/docs/autonomous-privy-wallet.md +188 -0
  253. package/docs/cli.md +265 -34
  254. package/docs/langchain.md +5 -0
  255. package/docs/mcp-reference.md +91 -5
  256. package/docs/probe-capability.md +191 -0
  257. package/docs/quickstart.md +39 -4
  258. package/docs/threat-model.md +20 -0
  259. package/docs/x402.md +766 -593
  260. package/package.json +19 -17
  261. package/src/capability/probe-capability.ts +355 -0
  262. package/src/cli/args.ts +29 -0
  263. package/src/cli/commands/claim.ts +168 -33
  264. package/src/cli/commands/close.ts +36 -3
  265. package/src/cli/commands/invoke.ts +5 -2
  266. package/src/cli/commands/mcp.ts +18 -2
  267. package/src/cli/commands/open.ts +75 -4
  268. package/src/cli/commands/pay.ts +584 -0
  269. package/src/cli/commands/reclaim-unverified.ts +8 -2
  270. package/src/cli/commands/reclaim.ts +277 -19
  271. package/src/cli/commands/refund.ts +475 -0
  272. package/src/cli/commands/status.ts +28 -2
  273. package/src/cli/commands/streams.ts +10 -14
  274. package/src/cli/commands/watch.ts +11 -1
  275. package/src/cli/config.ts +139 -52
  276. package/src/cli/context.ts +16 -0
  277. package/src/cli/deps.ts +111 -1
  278. package/src/cli/errors.ts +24 -1
  279. package/src/cli/escape-hatch.ts +19 -5
  280. package/src/cli/listings.ts +43 -10
  281. package/src/cli/preflight.ts +36 -21
  282. package/src/cli/privy.ts +4 -2
  283. package/src/cli/progress.ts +21 -2
  284. package/src/cli/reclaimability.ts +154 -0
  285. package/src/cli/run.ts +77 -21
  286. package/src/cli/scan-range.ts +2 -2
  287. package/src/cli/status.ts +454 -44
  288. package/src/cli/wallet.ts +45 -8
  289. package/src/discovery/rate-band.ts +172 -0
  290. package/src/escrow-generation.ts +135 -0
  291. package/src/frameworks/agentkit.ts +45 -11
  292. package/src/frameworks/eliza.ts +27 -5
  293. package/src/frameworks/langchain.ts +14 -0
  294. package/src/gateway/caller-auth-gateway.ts +320 -68
  295. package/src/gateway/http-server.ts +717 -220
  296. package/src/gateway/probe-auth.ts +191 -0
  297. package/src/gateway/signed-rate-bounds.ts +164 -0
  298. package/src/gateway/x402-opener-mode.ts +1171 -0
  299. package/src/index.ts +82 -19
  300. package/src/mcp/server.ts +351 -42
  301. package/src/mcp/x402-tools.ts +785 -0
  302. package/src/network.ts +302 -0
  303. package/src/sdk/client.ts +25 -0
  304. package/src/sdk/invoke.ts +6 -5
  305. package/src/wallet/autonomous-wallet-broker.ts +46 -20
  306. package/src/wallet/autonomous-wallet-protocol.ts +11 -2
  307. package/src/wallet/autonomous-wallet.ts +4 -2
  308. package/src/wallet/broker-escrows.ts +66 -0
  309. package/src/wallet/lifecycle.ts +53 -18
  310. package/src/wallet/privy-broker-server.ts +35 -25
  311. package/src/wallet/privy-session-broker.ts +58 -25
  312. package/src/wallet/privy-session-provider.ts +7 -3
  313. package/src/wallet/provider.ts +20 -15
  314. package/src/x402/facilitator.ts +3 -43
  315. package/src/x402-buyer/guarded-fetch.ts +213 -0
  316. package/src/x402-buyer/index.ts +9 -0
  317. package/src/x402-buyer/pay.ts +555 -0
  318. package/src/x402-opener/abi.ts +881 -0
  319. package/src/x402-opener/facilitator-entry.ts +48 -0
  320. package/src/x402-opener/facilitator-server.ts +1114 -0
  321. package/src/x402-opener/index.ts +13 -0
  322. package/src/x402-opener/listing-resolver.ts +202 -0
  323. package/src/x402-opener/refund-relay.ts +1873 -0
  324. package/src/x402-opener/relayer-queue.ts +18 -0
  325. package/src/x402-opener/scheme.ts +1551 -0
  326. package/src/x402-opener/terms.ts +453 -0
  327. package/dist/gateway/x402-mode.d.ts +0 -123
  328. package/dist/gateway/x402-mode.d.ts.map +0 -1
  329. package/dist/gateway/x402-mode.js +0 -212
  330. package/dist/gateway/x402-mode.js.map +0 -1
  331. package/dist/x402/delivery-oracle.d.ts +0 -108
  332. package/dist/x402/delivery-oracle.d.ts.map +0 -1
  333. package/dist/x402/delivery-oracle.js +0 -174
  334. package/dist/x402/delivery-oracle.js.map +0 -1
  335. package/dist/x402/facilitator-server-entry.d.ts +0 -3
  336. package/dist/x402/facilitator-server-entry.d.ts.map +0 -1
  337. package/dist/x402/facilitator-server-entry.js +0 -27
  338. package/dist/x402/facilitator-server-entry.js.map +0 -1
  339. package/dist/x402/facilitator-server.d.ts +0 -73
  340. package/dist/x402/facilitator-server.d.ts.map +0 -1
  341. package/dist/x402/facilitator-server.js +0 -305
  342. package/dist/x402/facilitator-server.js.map +0 -1
  343. package/dist/x402/payment-requirements.d.ts +0 -106
  344. package/dist/x402/payment-requirements.d.ts.map +0 -1
  345. package/dist/x402/payment-requirements.js +0 -138
  346. package/dist/x402/payment-requirements.js.map +0 -1
  347. package/dist/x402/relayer.d.ts +0 -41
  348. package/dist/x402/relayer.d.ts.map +0 -1
  349. package/dist/x402/relayer.js +0 -64
  350. package/dist/x402/relayer.js.map +0 -1
  351. package/dist/x402/resource-index.d.ts +0 -75
  352. package/dist/x402/resource-index.d.ts.map +0 -1
  353. package/dist/x402/resource-index.js +0 -126
  354. package/dist/x402/resource-index.js.map +0 -1
  355. package/dist/x402/verified-delivery-facilitator.d.ts +0 -285
  356. package/dist/x402/verified-delivery-facilitator.d.ts.map +0 -1
  357. package/dist/x402/verified-delivery-facilitator.js +0 -423
  358. package/dist/x402/verified-delivery-facilitator.js.map +0 -1
  359. package/src/gateway/x402-mode.ts +0 -350
  360. package/src/x402/delivery-oracle.ts +0 -285
  361. package/src/x402/facilitator-server-entry.ts +0 -35
  362. package/src/x402/facilitator-server.ts +0 -445
  363. package/src/x402/payment-requirements.ts +0 -231
  364. package/src/x402/relayer.ts +0 -122
  365. package/src/x402/resource-index.ts +0 -228
  366. package/src/x402/verified-delivery-facilitator.ts +0 -744
@@ -0,0 +1,191 @@
1
+ import { streamEscrowV2Abi } from "@absol-labs/shared";
2
+ import type { Address } from "viem";
3
+ import { readOnlyMetrikClient } from "../network.js";
4
+
5
+ /**
6
+ * Seller-side plumbing for oracle-signed probe verification.
7
+ *
8
+ * Two pieces, both deliberately small and both fail-closed:
9
+ *
10
+ * 1. {@link OracleSignerSetReader} — WHO may mint a `ProbeCapability`. Read
11
+ * straight off the escrow's `getOracleSigners()`, which is a public `view`.
12
+ * There is no registration step, no shared secret, and no trust in the
13
+ * oracle's own say-so: a third-party seller performs the identical read.
14
+ * 2. {@link ProbeRateLimiter} — how often a probe may be served for free.
15
+ * Without a bound, an oracle-signed capability is a licence to consume the
16
+ * seller's service indefinitely at no cost.
17
+ */
18
+
19
+ /** Resolves the escrow's current oracle signer set (lowercased). */
20
+ export interface OracleSignerSetReader {
21
+ getOracleSigners(): Promise<readonly Address[]>;
22
+ }
23
+
24
+ /** Default reader: a read-only `getOracleSigners()` call. No wallet, no writes. */
25
+ export function createSdkOracleSignerSetReader(options: {
26
+ readonly escrowAddress: Address;
27
+ readonly rpcUrl: string;
28
+ /** Chain of the escrow; must be a registered network. Default: the registry default. */
29
+ readonly chainId?: number;
30
+ }): OracleSignerSetReader {
31
+ const metrik = readOnlyMetrikClient(options);
32
+ return {
33
+ async getOracleSigners() {
34
+ return (await metrik.publicClient.readContract({
35
+ address: options.escrowAddress,
36
+ abi: streamEscrowV2Abi,
37
+ functionName: "getOracleSigners",
38
+ })) as readonly Address[];
39
+ },
40
+ };
41
+ }
42
+
43
+ /**
44
+ * Default freshness bound for the cached signer set, in seconds. The set only
45
+ * changes by an explicit escrow transaction, so a short cache costs nothing in
46
+ * correctness while keeping one RPC read off every probe.
47
+ */
48
+ export const DEFAULT_ORACLE_SIGNER_CACHE_SECONDS = 60;
49
+
50
+ /**
51
+ * Bounded-staleness cache over {@link OracleSignerSetReader}.
52
+ *
53
+ * FAIL-CLOSED by construction: once the cached set is older than its TTL it is
54
+ * discarded, and a refresh that fails resolves to `undefined` rather than
55
+ * serving the stale set. A probe is a free call, so "the RPC is down" must deny
56
+ * — which the oracle then reports as unverifiable, never as passing.
57
+ *
58
+ * Concurrent callers share one in-flight refresh, so a burst of probes cannot
59
+ * fan out into a burst of RPC reads.
60
+ */
61
+ export class CachedOracleSignerSet {
62
+ private cached:
63
+ | { readonly signers: ReadonlySet<string>; readonly at: number }
64
+ | undefined;
65
+ private inFlight: Promise<ReadonlySet<string> | undefined> | undefined;
66
+
67
+ constructor(
68
+ private readonly reader: OracleSignerSetReader,
69
+ private readonly ttlSeconds: number = DEFAULT_ORACLE_SIGNER_CACHE_SECONDS,
70
+ private readonly now: () => number = () => Math.floor(Date.now() / 1000),
71
+ ) {
72
+ if (!Number.isFinite(ttlSeconds) || ttlSeconds <= 0) {
73
+ throw new Error(
74
+ "oracle signer cache ttlSeconds must be a positive number",
75
+ );
76
+ }
77
+ }
78
+
79
+ /** The current set, or `undefined` when it could not be read fresh enough. */
80
+ async resolve(): Promise<ReadonlySet<string> | undefined> {
81
+ const cached = this.cached;
82
+ if (cached !== undefined && this.now() - cached.at <= this.ttlSeconds) {
83
+ return cached.signers;
84
+ }
85
+ if (this.inFlight !== undefined) {
86
+ return this.inFlight;
87
+ }
88
+ const refresh = (async (): Promise<ReadonlySet<string> | undefined> => {
89
+ try {
90
+ const signers = await this.reader.getOracleSigners();
91
+ const set = new Set(signers.map((signer) => signer.toLowerCase()));
92
+ // An empty set is a real answer, and it means nobody may probe.
93
+ this.cached = { signers: set, at: this.now() };
94
+ return set;
95
+ } catch {
96
+ // Drop the stale entry rather than serving it: a signer REMOVED from
97
+ // the escrow must stop being able to probe within one TTL, not within
98
+ // "however long the RPC stays down".
99
+ this.cached = undefined;
100
+ return undefined;
101
+ } finally {
102
+ this.inFlight = undefined;
103
+ }
104
+ })();
105
+ this.inFlight = refresh;
106
+ return refresh;
107
+ }
108
+ }
109
+
110
+ /** Bounds how many probes this gateway will serve for free. */
111
+ export interface ProbeRateLimiter {
112
+ /** Returns `true` (and consumes budget) IFF a probe may be served now. */
113
+ tryConsume(): boolean;
114
+ }
115
+
116
+ /**
117
+ * Default probe budget, in probes per minute.
118
+ *
119
+ * Derived from the real cadence, not picked round: the oracle checkpoints every
120
+ * ~15-22s, so a single vantage probes a given service at most ~4 times a
121
+ * minute. T1 multi-vantage fans that out across at most 9 vantages (the
122
+ * coordinator plus the 8-worker cap in the oracle's vantage config), giving a
123
+ * worst-case honest load of ~36 probes/minute. 60 leaves headroom for a
124
+ * re-execution sample and clock jitter while capping the free-service surface
125
+ * at one request per second — orders of magnitude below what would make this an
126
+ * economically interesting way to get the service for nothing.
127
+ */
128
+ export const DEFAULT_MAX_PROBES_PER_MINUTE = 60;
129
+
130
+ /**
131
+ * Fixed-window probe limiter. One counter for the whole gateway (which fronts
132
+ * one service), so the bound holds no matter how many oracle signers exist or
133
+ * how many vantages a coordinator runs.
134
+ *
135
+ * A useful secondary effect: because every probe capability is single-use and
136
+ * short-lived, this limiter is also what bounds the shared nonce replay cache —
137
+ * at most `maxPerMinute x ttl/60` probe nonces can be live at once.
138
+ */
139
+ export class FixedWindowProbeRateLimiter implements ProbeRateLimiter {
140
+ private windowStart: number;
141
+ private count = 0;
142
+
143
+ constructor(
144
+ private readonly maxPerMinute: number = DEFAULT_MAX_PROBES_PER_MINUTE,
145
+ private readonly now: () => number = () => Math.floor(Date.now() / 1000),
146
+ ) {
147
+ if (!Number.isInteger(maxPerMinute) || maxPerMinute <= 0) {
148
+ throw new Error("maxProbesPerMinute must be a positive integer");
149
+ }
150
+ this.windowStart = this.now();
151
+ }
152
+
153
+ tryConsume(): boolean {
154
+ const nowSeconds = this.now();
155
+ if (nowSeconds - this.windowStart >= 60) {
156
+ this.windowStart = nowSeconds;
157
+ this.count = 0;
158
+ }
159
+ if (this.count >= this.maxPerMinute) {
160
+ return false;
161
+ }
162
+ this.count += 1;
163
+ return true;
164
+ }
165
+ }
166
+
167
+ /** Opt-in probe verification mode for {@link CallerAuthGateway}. */
168
+ export interface GatewayProbeMode {
169
+ /**
170
+ * Who may mint. Defaults to a read-only `getOracleSigners()` reader built
171
+ * from the gateway's own escrow + RPC.
172
+ */
173
+ readonly signerSetReader?: OracleSignerSetReader;
174
+ /** Freshness bound for the cached signer set. Default {@link DEFAULT_ORACLE_SIGNER_CACHE_SECONDS}. */
175
+ readonly signerCacheSeconds?: number;
176
+ /** Probe budget. Default {@link DEFAULT_MAX_PROBES_PER_MINUTE}. */
177
+ readonly maxProbesPerMinute?: number;
178
+ /** Override the limiter entirely (tests, or a shared multi-instance store). */
179
+ readonly rateLimiter?: ProbeRateLimiter;
180
+ /** Probe capability header name. Default `x-metrik-probe-capability`. */
181
+ readonly headerName?: string;
182
+ }
183
+
184
+ /** What an authorized probe tells the caller. Carries NO stream and NO payment. */
185
+ export interface ProbeAuthorization {
186
+ /** The oracle signer that minted the capability. */
187
+ readonly oracle: Address;
188
+ readonly serviceRef: `0x${string}`;
189
+ /** Unix seconds the capability expires at. */
190
+ readonly expiry: number;
191
+ }
@@ -0,0 +1,164 @@
1
+ import { readFile } from "node:fs/promises";
2
+
3
+ import {
4
+ deriveServiceRef,
5
+ recoverServiceDescriptorSigner,
6
+ signedServiceDescriptorSchema,
7
+ } from "@absol-labs/shared";
8
+ import { getAddress, type Address, type Hex } from "viem";
9
+
10
+ /**
11
+ * Operator-SIGNED rate bounds for the gateway's capability path
12
+ * (metrik-agent#121 item 5, metrik-contracts#41).
13
+ *
14
+ * `StreamEscrowV2.openStream` only requires `ratePerSecond != 0`; the seller's
15
+ * signed `minRatePerSecond` / `maxRatePerSecond` are checked by the buyer-side
16
+ * SDK and the marketplace, not by the contract. A buyer who calls the escrow
17
+ * directly can therefore open a stream at 1 base unit per second. The gateway
18
+ * is the seller's own trust boundary, so it refuses to SERVE such a stream:
19
+ * the on-chain `ratePerSecond` must fall inside the band the operator signed.
20
+ *
21
+ * Bounds are only ever taken from a signed descriptor that verifies here — the
22
+ * serviceRef re-derives from the signed content and the EIP-712 signature
23
+ * recovers to the descriptor's operator. `CallerAuthGateway` accepts only
24
+ * objects this module produced, so a hand-built `{ minRatePerSecond: 0n }`
25
+ * cannot slip through as "signed".
26
+ */
27
+ export interface SignedRateBounds {
28
+ /** The serviceRef the bounds were signed for (re-derived, lower-case). */
29
+ readonly serviceRef: Hex;
30
+ /** The operator whose EIP-712 signature was recovered. */
31
+ readonly operator: Address;
32
+ /** Signed minimum rate, USDC base units per second; `null` = none signed. */
33
+ readonly minRatePerSecond: bigint | null;
34
+ /** Signed maximum rate, USDC base units per second; `null` = none signed. */
35
+ readonly maxRatePerSecond: bigint | null;
36
+ }
37
+
38
+ export class SignedRateBoundsError extends Error {
39
+ constructor(message: string, options?: { readonly cause?: unknown }) {
40
+ super(message, options);
41
+ this.name = "SignedRateBoundsError";
42
+ }
43
+ }
44
+
45
+ const verified = new WeakSet<object>();
46
+
47
+ /** Whether `bounds` came out of {@link verifySignedRateBounds} in this process. */
48
+ export function isVerifiedSignedRateBounds(
49
+ bounds: unknown,
50
+ ): bounds is SignedRateBounds {
51
+ return typeof bounds === "object" && bounds !== null && verified.has(bounds);
52
+ }
53
+
54
+ /**
55
+ * Verify a signed service descriptor and extract its operator-signed rate band.
56
+ * Throws {@link SignedRateBoundsError} unless the schema parses, the serviceRef
57
+ * re-derives from the signed content, and the signature recovers to the
58
+ * descriptor's operator.
59
+ */
60
+ export async function verifySignedRateBounds(
61
+ signedDescriptor: unknown,
62
+ ): Promise<SignedRateBounds> {
63
+ const parsed = signedServiceDescriptorSchema.safeParse(signedDescriptor);
64
+ if (!parsed.success) {
65
+ throw new SignedRateBoundsError(
66
+ `not a signed service descriptor: ${parsed.error.issues
67
+ .map((issue) => `${issue.path.join(".")}: ${issue.message}`)
68
+ .join("; ")}`,
69
+ );
70
+ }
71
+ const signed = parsed.data;
72
+ const derived = deriveServiceRef(signed.descriptor).toLowerCase() as Hex;
73
+ if (signed.serviceRef.toLowerCase() !== derived) {
74
+ throw new SignedRateBoundsError(
75
+ `the signed descriptor's serviceRef ${signed.serviceRef} does not derive from its content (${derived}); it was tampered with or re-labelled`,
76
+ );
77
+ }
78
+ let signer: Address | null;
79
+ try {
80
+ signer = await recoverServiceDescriptorSigner(
81
+ signed.descriptor,
82
+ signed.signature as Hex,
83
+ );
84
+ } catch (cause) {
85
+ throw new SignedRateBoundsError(
86
+ "the signed descriptor's signature could not be recovered",
87
+ { cause },
88
+ );
89
+ }
90
+ if (
91
+ signer === null ||
92
+ signer.toLowerCase() !== signed.descriptor.operator.toLowerCase()
93
+ ) {
94
+ throw new SignedRateBoundsError(
95
+ `the descriptor for ${derived} is not signed by its operator ${signed.descriptor.operator}`,
96
+ );
97
+ }
98
+ const commercial = signed.descriptor.commercial;
99
+ const bounds: SignedRateBounds = Object.freeze({
100
+ serviceRef: derived,
101
+ operator: getAddress(signed.descriptor.operator),
102
+ minRatePerSecond:
103
+ commercial?.minRatePerSecond === undefined
104
+ ? null
105
+ : BigInt(commercial.minRatePerSecond),
106
+ maxRatePerSecond:
107
+ commercial?.maxRatePerSecond === undefined
108
+ ? null
109
+ : BigInt(commercial.maxRatePerSecond),
110
+ });
111
+ verified.add(bounds);
112
+ return bounds;
113
+ }
114
+
115
+ /**
116
+ * Load and verify the signed descriptor(s) in a JSON file: one signed
117
+ * descriptor object, or an array of them (one per served serviceRef, e.g. a
118
+ * migration alias). Fail closed on any read, parse or verification error.
119
+ */
120
+ export async function loadSignedRateBoundsFile(
121
+ path: string,
122
+ ): Promise<SignedRateBounds[]> {
123
+ let raw: string;
124
+ try {
125
+ raw = await readFile(path, "utf8");
126
+ } catch (cause) {
127
+ throw new SignedRateBoundsError(
128
+ `could not read the signed descriptor file ${path}`,
129
+ { cause },
130
+ );
131
+ }
132
+ let json: unknown;
133
+ try {
134
+ json = JSON.parse(raw);
135
+ } catch (cause) {
136
+ throw new SignedRateBoundsError(
137
+ `the signed descriptor file ${path} is not valid JSON`,
138
+ { cause },
139
+ );
140
+ }
141
+ const entries = Array.isArray(json) ? json : [json];
142
+ if (entries.length === 0) {
143
+ throw new SignedRateBoundsError(
144
+ `the signed descriptor file ${path} holds no descriptor`,
145
+ );
146
+ }
147
+ return await Promise.all(
148
+ entries.map((entry) => verifySignedRateBounds(entry)),
149
+ );
150
+ }
151
+
152
+ /** `undefined` when `rate` is inside the band, otherwise why it is not. */
153
+ export function rateOutsideBounds(
154
+ rate: bigint,
155
+ bounds: SignedRateBounds,
156
+ ): string | undefined {
157
+ if (bounds.minRatePerSecond !== null && rate < bounds.minRatePerSecond) {
158
+ return `rate ${rate} is below the operator-signed minimum ${bounds.minRatePerSecond}`;
159
+ }
160
+ if (bounds.maxRatePerSecond !== null && rate > bounds.maxRatePerSecond) {
161
+ return `rate ${rate} is above the operator-signed maximum ${bounds.maxRatePerSecond}`;
162
+ }
163
+ return undefined;
164
+ }