@absol-labs/agent 0.11.0 → 0.12.0-rc.1
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.
- package/README.md +42 -50
- package/dist/capability/invocation-capability.d.ts +2 -2
- package/dist/capability/probe-capability.d.ts +225 -0
- package/dist/capability/probe-capability.d.ts.map +1 -0
- package/dist/capability/probe-capability.js +248 -0
- package/dist/capability/probe-capability.js.map +1 -0
- package/dist/cli/args.d.ts +11 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +23 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/commands/claim.js +1 -1
- package/dist/cli/commands/claim.js.map +1 -1
- package/dist/cli/commands/close.d.ts.map +1 -1
- package/dist/cli/commands/close.js +35 -3
- package/dist/cli/commands/close.js.map +1 -1
- package/dist/cli/commands/invoke.d.ts +2 -0
- package/dist/cli/commands/invoke.d.ts.map +1 -1
- package/dist/cli/commands/invoke.js +2 -2
- package/dist/cli/commands/invoke.js.map +1 -1
- package/dist/cli/commands/mcp.d.ts.map +1 -1
- package/dist/cli/commands/mcp.js +15 -2
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/open.d.ts.map +1 -1
- package/dist/cli/commands/open.js +58 -4
- package/dist/cli/commands/open.js.map +1 -1
- package/dist/cli/commands/pay.d.ts +19 -0
- package/dist/cli/commands/pay.d.ts.map +1 -0
- package/dist/cli/commands/pay.js +355 -0
- package/dist/cli/commands/pay.js.map +1 -0
- package/dist/cli/commands/reclaim-unverified.d.ts.map +1 -1
- package/dist/cli/commands/reclaim-unverified.js +3 -2
- package/dist/cli/commands/reclaim-unverified.js.map +1 -1
- package/dist/cli/commands/reclaim.d.ts +23 -2
- package/dist/cli/commands/reclaim.d.ts.map +1 -1
- package/dist/cli/commands/reclaim.js +184 -16
- package/dist/cli/commands/reclaim.js.map +1 -1
- package/dist/cli/commands/refund.d.ts +13 -0
- package/dist/cli/commands/refund.d.ts.map +1 -0
- package/dist/cli/commands/refund.js +224 -0
- package/dist/cli/commands/refund.js.map +1 -0
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +10 -1
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/streams.js +8 -12
- package/dist/cli/commands/streams.js.map +1 -1
- package/dist/cli/commands/watch.d.ts.map +1 -1
- package/dist/cli/commands/watch.js +2 -1
- package/dist/cli/commands/watch.js.map +1 -1
- package/dist/cli/config.d.ts +44 -26
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +88 -38
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/deps.d.ts +33 -0
- package/dist/cli/deps.d.ts.map +1 -1
- package/dist/cli/deps.js +37 -1
- package/dist/cli/deps.js.map +1 -1
- package/dist/cli/errors.d.ts.map +1 -1
- package/dist/cli/errors.js +21 -1
- package/dist/cli/errors.js.map +1 -1
- package/dist/cli/escape-hatch.d.ts +6 -4
- package/dist/cli/escape-hatch.d.ts.map +1 -1
- package/dist/cli/escape-hatch.js +6 -2
- package/dist/cli/escape-hatch.js.map +1 -1
- package/dist/cli/listings.d.ts +18 -0
- package/dist/cli/listings.d.ts.map +1 -1
- package/dist/cli/listings.js +22 -12
- package/dist/cli/listings.js.map +1 -1
- package/dist/cli/preflight.d.ts +20 -14
- package/dist/cli/preflight.d.ts.map +1 -1
- package/dist/cli/preflight.js +34 -20
- package/dist/cli/preflight.js.map +1 -1
- package/dist/cli/privy.d.ts.map +1 -1
- package/dist/cli/privy.js +4 -2
- package/dist/cli/privy.js.map +1 -1
- package/dist/cli/reclaimability.d.ts +82 -0
- package/dist/cli/reclaimability.d.ts.map +1 -0
- package/dist/cli/reclaimability.js +80 -0
- package/dist/cli/reclaimability.js.map +1 -0
- package/dist/cli/run.d.ts +2 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +40 -11
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/scan-range.d.ts +2 -2
- package/dist/cli/scan-range.js +2 -2
- package/dist/cli/status.d.ts +3 -1
- package/dist/cli/status.d.ts.map +1 -1
- package/dist/cli/status.js +15 -3
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/wallet.d.ts +1 -1
- package/dist/cli/wallet.js +2 -2
- package/dist/cli/wallet.js.map +1 -1
- package/dist/discovery/rate-band.d.ts +74 -0
- package/dist/discovery/rate-band.d.ts.map +1 -0
- package/dist/discovery/rate-band.js +114 -0
- package/dist/discovery/rate-band.js.map +1 -0
- package/dist/escrow-generation.d.ts +81 -0
- package/dist/escrow-generation.d.ts.map +1 -0
- package/dist/escrow-generation.js +76 -0
- package/dist/escrow-generation.js.map +1 -0
- package/dist/frameworks/agentkit.d.ts +8 -4
- package/dist/frameworks/agentkit.d.ts.map +1 -1
- package/dist/frameworks/agentkit.js +27 -10
- package/dist/frameworks/agentkit.js.map +1 -1
- package/dist/frameworks/eliza.d.ts +3 -3
- package/dist/frameworks/eliza.d.ts.map +1 -1
- package/dist/frameworks/eliza.js +15 -6
- package/dist/frameworks/eliza.js.map +1 -1
- package/dist/frameworks/langchain.d.ts.map +1 -1
- package/dist/frameworks/langchain.js +8 -0
- package/dist/frameworks/langchain.js.map +1 -1
- package/dist/gateway/caller-auth-gateway.d.ts +83 -31
- package/dist/gateway/caller-auth-gateway.d.ts.map +1 -1
- package/dist/gateway/caller-auth-gateway.js +180 -27
- package/dist/gateway/caller-auth-gateway.js.map +1 -1
- package/dist/gateway/http-server.d.ts +111 -44
- package/dist/gateway/http-server.d.ts.map +1 -1
- package/dist/gateway/http-server.js +439 -155
- package/dist/gateway/http-server.js.map +1 -1
- package/dist/gateway/probe-auth.d.ts +112 -0
- package/dist/gateway/probe-auth.d.ts.map +1 -0
- package/dist/gateway/probe-auth.js +127 -0
- package/dist/gateway/probe-auth.js.map +1 -0
- package/dist/gateway/signed-rate-bounds.d.ts +51 -0
- package/dist/gateway/signed-rate-bounds.d.ts.map +1 -0
- package/dist/gateway/signed-rate-bounds.js +94 -0
- package/dist/gateway/signed-rate-bounds.js.map +1 -0
- package/dist/gateway/x402-opener-mode.d.ts +215 -0
- package/dist/gateway/x402-opener-mode.d.ts.map +1 -0
- package/dist/gateway/x402-opener-mode.js +639 -0
- package/dist/gateway/x402-opener-mode.js.map +1 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -5
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +52 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +228 -37
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/x402-tools.d.ts +104 -0
- package/dist/mcp/x402-tools.d.ts.map +1 -0
- package/dist/mcp/x402-tools.js +434 -0
- package/dist/mcp/x402-tools.js.map +1 -0
- package/dist/network.d.ts +119 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +199 -0
- package/dist/network.js.map +1 -0
- package/dist/sdk/invoke.d.ts +4 -0
- package/dist/sdk/invoke.d.ts.map +1 -1
- package/dist/sdk/invoke.js +2 -5
- package/dist/sdk/invoke.js.map +1 -1
- package/dist/wallet/autonomous-wallet-broker.d.ts +3 -0
- package/dist/wallet/autonomous-wallet-broker.d.ts.map +1 -1
- package/dist/wallet/autonomous-wallet-broker.js +27 -23
- package/dist/wallet/autonomous-wallet-broker.js.map +1 -1
- package/dist/wallet/autonomous-wallet-protocol.d.ts +8 -2
- package/dist/wallet/autonomous-wallet-protocol.d.ts.map +1 -1
- package/dist/wallet/autonomous-wallet-protocol.js +9 -2
- package/dist/wallet/autonomous-wallet-protocol.js.map +1 -1
- package/dist/wallet/autonomous-wallet.d.ts.map +1 -1
- package/dist/wallet/autonomous-wallet.js +3 -2
- package/dist/wallet/autonomous-wallet.js.map +1 -1
- package/dist/wallet/broker-escrows.d.ts +222 -0
- package/dist/wallet/broker-escrows.d.ts.map +1 -0
- package/dist/wallet/broker-escrows.js +36 -0
- package/dist/wallet/broker-escrows.js.map +1 -0
- package/dist/wallet/lifecycle.d.ts +21 -5
- package/dist/wallet/lifecycle.d.ts.map +1 -1
- package/dist/wallet/lifecycle.js +32 -12
- package/dist/wallet/lifecycle.js.map +1 -1
- package/dist/wallet/privy-broker-server.d.ts.map +1 -1
- package/dist/wallet/privy-broker-server.js +31 -26
- package/dist/wallet/privy-broker-server.js.map +1 -1
- package/dist/wallet/privy-session-broker.d.ts +14 -5
- package/dist/wallet/privy-session-broker.d.ts.map +1 -1
- package/dist/wallet/privy-session-broker.js +29 -17
- package/dist/wallet/privy-session-broker.js.map +1 -1
- package/dist/wallet/privy-session-provider.d.ts +3 -1
- package/dist/wallet/privy-session-provider.d.ts.map +1 -1
- package/dist/wallet/privy-session-provider.js +5 -3
- package/dist/wallet/privy-session-provider.js.map +1 -1
- package/dist/wallet/provider.d.ts.map +1 -1
- package/dist/wallet/provider.js +15 -8
- package/dist/wallet/provider.js.map +1 -1
- package/dist/x402/facilitator.d.ts +3 -30
- package/dist/x402/facilitator.d.ts.map +1 -1
- package/dist/x402/facilitator.js +0 -27
- package/dist/x402/facilitator.js.map +1 -1
- package/dist/x402-buyer/guarded-fetch.d.ts +51 -0
- package/dist/x402-buyer/guarded-fetch.d.ts.map +1 -0
- package/dist/x402-buyer/guarded-fetch.js +147 -0
- package/dist/x402-buyer/guarded-fetch.js.map +1 -0
- package/dist/x402-buyer/index.d.ts +10 -0
- package/dist/x402-buyer/index.d.ts.map +1 -0
- package/dist/x402-buyer/index.js +10 -0
- package/dist/x402-buyer/index.js.map +1 -0
- package/dist/x402-buyer/pay.d.ts +135 -0
- package/dist/x402-buyer/pay.d.ts.map +1 -0
- package/dist/x402-buyer/pay.js +336 -0
- package/dist/x402-buyer/pay.js.map +1 -0
- package/dist/x402-opener/abi.d.ts +684 -0
- package/dist/x402-opener/abi.d.ts.map +1 -0
- package/dist/x402-opener/abi.js +880 -0
- package/dist/x402-opener/abi.js.map +1 -0
- package/dist/x402-opener/facilitator-entry.d.ts +3 -0
- package/dist/x402-opener/facilitator-entry.d.ts.map +1 -0
- package/dist/x402-opener/facilitator-entry.js +37 -0
- package/dist/x402-opener/facilitator-entry.js.map +1 -0
- package/dist/x402-opener/facilitator-server.d.ts +180 -0
- package/dist/x402-opener/facilitator-server.d.ts.map +1 -0
- package/dist/x402-opener/facilitator-server.js +696 -0
- package/dist/x402-opener/facilitator-server.js.map +1 -0
- package/dist/x402-opener/index.d.ts +14 -0
- package/dist/x402-opener/index.d.ts.map +1 -0
- package/dist/x402-opener/index.js +14 -0
- package/dist/x402-opener/index.js.map +1 -0
- package/dist/x402-opener/listing-resolver.d.ts +55 -0
- package/dist/x402-opener/listing-resolver.d.ts.map +1 -0
- package/dist/x402-opener/listing-resolver.js +125 -0
- package/dist/x402-opener/listing-resolver.js.map +1 -0
- package/dist/x402-opener/refund-relay.d.ts +240 -0
- package/dist/x402-opener/refund-relay.d.ts.map +1 -0
- package/dist/x402-opener/refund-relay.js +1240 -0
- package/dist/x402-opener/refund-relay.js.map +1 -0
- package/dist/x402-opener/relayer-queue.d.ts +14 -0
- package/dist/x402-opener/relayer-queue.d.ts.map +1 -0
- package/dist/x402-opener/relayer-queue.js +18 -0
- package/dist/x402-opener/relayer-queue.js.map +1 -0
- package/dist/x402-opener/scheme.d.ts +217 -0
- package/dist/x402-opener/scheme.d.ts.map +1 -0
- package/dist/x402-opener/scheme.js +749 -0
- package/dist/x402-opener/scheme.js.map +1 -0
- package/dist/x402-opener/terms.d.ts +105 -0
- package/dist/x402-opener/terms.d.ts.map +1 -0
- package/dist/x402-opener/terms.js +212 -0
- package/dist/x402-opener/terms.js.map +1 -0
- package/dist/zktls/reclaim.d.ts +2 -2
- package/dist/zktls/t2-delivery-proof.d.ts +6 -6
- package/docs/agent-layer.md +3 -2
- package/docs/autonomous-privy-wallet.md +188 -0
- package/docs/cli.md +184 -31
- package/docs/langchain.md +5 -0
- package/docs/mcp-reference.md +75 -5
- package/docs/probe-capability.md +191 -0
- package/docs/quickstart.md +36 -1
- package/docs/threat-model.md +20 -0
- package/docs/x402.md +628 -595
- package/package.json +19 -17
- package/src/capability/probe-capability.ts +355 -0
- package/src/cli/args.ts +29 -0
- package/src/cli/commands/claim.ts +1 -1
- package/src/cli/commands/close.ts +36 -3
- package/src/cli/commands/invoke.ts +5 -2
- package/src/cli/commands/mcp.ts +18 -2
- package/src/cli/commands/open.ts +75 -4
- package/src/cli/commands/pay.ts +442 -0
- package/src/cli/commands/reclaim-unverified.ts +8 -2
- package/src/cli/commands/reclaim.ts +252 -14
- package/src/cli/commands/refund.ts +277 -0
- package/src/cli/commands/status.ts +17 -1
- package/src/cli/commands/streams.ts +10 -14
- package/src/cli/commands/watch.ts +7 -1
- package/src/cli/config.ts +139 -52
- package/src/cli/deps.ts +74 -1
- package/src/cli/errors.ts +24 -1
- package/src/cli/escape-hatch.ts +19 -5
- package/src/cli/listings.ts +43 -10
- package/src/cli/preflight.ts +36 -21
- package/src/cli/privy.ts +4 -2
- package/src/cli/reclaimability.ts +154 -0
- package/src/cli/run.ts +54 -11
- package/src/cli/scan-range.ts +2 -2
- package/src/cli/status.ts +14 -1
- package/src/cli/wallet.ts +2 -2
- package/src/discovery/rate-band.ts +172 -0
- package/src/escrow-generation.ts +135 -0
- package/src/frameworks/agentkit.ts +45 -11
- package/src/frameworks/eliza.ts +27 -5
- package/src/frameworks/langchain.ts +14 -0
- package/src/gateway/caller-auth-gateway.ts +320 -68
- package/src/gateway/http-server.ts +629 -220
- package/src/gateway/probe-auth.ts +191 -0
- package/src/gateway/signed-rate-bounds.ts +164 -0
- package/src/gateway/x402-opener-mode.ts +974 -0
- package/src/index.ts +82 -19
- package/src/mcp/server.ts +319 -42
- package/src/mcp/x402-tools.ts +621 -0
- package/src/network.ts +302 -0
- package/src/sdk/invoke.ts +6 -5
- package/src/wallet/autonomous-wallet-broker.ts +46 -20
- package/src/wallet/autonomous-wallet-protocol.ts +11 -2
- package/src/wallet/autonomous-wallet.ts +4 -2
- package/src/wallet/broker-escrows.ts +66 -0
- package/src/wallet/lifecycle.ts +53 -18
- package/src/wallet/privy-broker-server.ts +35 -25
- package/src/wallet/privy-session-broker.ts +58 -25
- package/src/wallet/privy-session-provider.ts +7 -3
- package/src/wallet/provider.ts +20 -15
- package/src/x402/facilitator.ts +3 -43
- package/src/x402-buyer/guarded-fetch.ts +213 -0
- package/src/x402-buyer/index.ts +9 -0
- package/src/x402-buyer/pay.ts +517 -0
- package/src/x402-opener/abi.ts +881 -0
- package/src/x402-opener/facilitator-entry.ts +45 -0
- package/src/x402-opener/facilitator-server.ts +1014 -0
- package/src/x402-opener/index.ts +13 -0
- package/src/x402-opener/listing-resolver.ts +202 -0
- package/src/x402-opener/refund-relay.ts +1743 -0
- package/src/x402-opener/relayer-queue.ts +18 -0
- package/src/x402-opener/scheme.ts +1133 -0
- package/src/x402-opener/terms.ts +300 -0
- package/dist/gateway/x402-mode.d.ts +0 -123
- package/dist/gateway/x402-mode.d.ts.map +0 -1
- package/dist/gateway/x402-mode.js +0 -212
- package/dist/gateway/x402-mode.js.map +0 -1
- package/dist/x402/delivery-oracle.d.ts +0 -108
- package/dist/x402/delivery-oracle.d.ts.map +0 -1
- package/dist/x402/delivery-oracle.js +0 -174
- package/dist/x402/delivery-oracle.js.map +0 -1
- package/dist/x402/facilitator-server-entry.d.ts +0 -3
- package/dist/x402/facilitator-server-entry.d.ts.map +0 -1
- package/dist/x402/facilitator-server-entry.js +0 -27
- package/dist/x402/facilitator-server-entry.js.map +0 -1
- package/dist/x402/facilitator-server.d.ts +0 -73
- package/dist/x402/facilitator-server.d.ts.map +0 -1
- package/dist/x402/facilitator-server.js +0 -305
- package/dist/x402/facilitator-server.js.map +0 -1
- package/dist/x402/payment-requirements.d.ts +0 -106
- package/dist/x402/payment-requirements.d.ts.map +0 -1
- package/dist/x402/payment-requirements.js +0 -138
- package/dist/x402/payment-requirements.js.map +0 -1
- package/dist/x402/relayer.d.ts +0 -41
- package/dist/x402/relayer.d.ts.map +0 -1
- package/dist/x402/relayer.js +0 -64
- package/dist/x402/relayer.js.map +0 -1
- package/dist/x402/resource-index.d.ts +0 -75
- package/dist/x402/resource-index.d.ts.map +0 -1
- package/dist/x402/resource-index.js +0 -126
- package/dist/x402/resource-index.js.map +0 -1
- package/dist/x402/verified-delivery-facilitator.d.ts +0 -285
- package/dist/x402/verified-delivery-facilitator.d.ts.map +0 -1
- package/dist/x402/verified-delivery-facilitator.js +0 -423
- package/dist/x402/verified-delivery-facilitator.js.map +0 -1
- package/src/gateway/x402-mode.ts +0 -350
- package/src/x402/delivery-oracle.ts +0 -285
- package/src/x402/facilitator-server-entry.ts +0 -35
- package/src/x402/facilitator-server.ts +0 -445
- package/src/x402/payment-requirements.ts +0 -231
- package/src/x402/relayer.ts +0 -122
- package/src/x402/resource-index.ts +0 -228
- package/src/x402/verified-delivery-facilitator.ts +0 -744
package/README.md
CHANGED
|
@@ -12,9 +12,9 @@ It builds on [`@absol-labs/sdk`](https://github.com/Absol-Labs/metrik-sdk), whic
|
|
|
12
12
|
to the on-chain metered escrow. This layer wraps that client in the guardrails and
|
|
13
13
|
integration surfaces an agent actually needs: owner-signed spend mandates that gate every
|
|
14
14
|
fund move, a Metrik-native `402` challenge flow that turns a payment challenge into a verified
|
|
15
|
-
stream
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
stream (not the x402 protocol), an opt-in x402 v2 facilitator and gateway mode that settle
|
|
16
|
+
standard x402 `exact` payments through the on-chain stream opener (off by default — see
|
|
17
|
+
[`docs/x402.md`](./docs/x402.md)), an MCP server that exposes
|
|
18
18
|
hire/monitor/reclaim as agent tools,
|
|
19
19
|
adapters for the major agent frameworks,
|
|
20
20
|
Reclaim consumer zkTLS for buyer-side delivery proofs, and non-custodial Coinbase CDP/Privy
|
|
@@ -38,7 +38,9 @@ npx --package @absol-labs/agent metrik discover
|
|
|
38
38
|
`metrik` is the CLI below; `metrik-mcp` starts the MCP server over stdio
|
|
39
39
|
(`metrik mcp --print-config` prints the client config that runs it);
|
|
40
40
|
`metrik-mcp-http` starts the hosted multi-tenant MCP transport; `metrik-gateway`
|
|
41
|
-
starts the reference caller-auth gateway a seller puts in front of their service
|
|
41
|
+
starts the reference caller-auth gateway a seller puts in front of their service;
|
|
42
|
+
`metrik-x402-opener-facilitator` starts the x402 v2 opener facilitator (off unless
|
|
43
|
+
enabled; see [`docs/x402.md`](./docs/x402.md)).
|
|
42
44
|
|
|
43
45
|
The package is **ESM-only** (`"type": "module"`) and targets **Node `>=20.19 <25`** (verified on 20.19, 22 LTS and 24). It ships
|
|
44
46
|
`@absol-labs/sdk`, `@absol-labs/shared`, `viem`, `zod`, the Reclaim zkTLS SDKs, the
|
|
@@ -66,36 +68,14 @@ stream is opened through the SDK.
|
|
|
66
68
|
> string (`streamproof-verified-stream`), its own challenge and payload shapes, and its
|
|
67
69
|
> own `X-METRIK-PAYMENT` header. It does **not** speak the x402 wire format and will
|
|
68
70
|
> **not** interoperate with `x402-fetch`, `x402-axios`, `x402-express`, or any standard
|
|
69
|
-
> x402 client or facilitator.
|
|
70
|
-
|
|
71
|
-
**The x402 verified-delivery facilitator** (`metrik-x402-facilitator`) is the other,
|
|
72
|
-
genuinely-x402 half. It implements the standard facilitator interface — `POST /verify`,
|
|
73
|
-
`POST /settle`, `GET /supported` — for the standard `exact` scheme, and then asks one
|
|
74
|
-
question no other facilitator can: **is this service currently passing Metrik's delivery
|
|
75
|
-
checks?** If the oracle says it is failing, `/verify` answers `isValid: false` and `/settle`
|
|
76
|
-
broadcasts nothing. Standard headers, standard schemes, `x402-fetch` works untouched, no
|
|
77
|
-
contract change.
|
|
78
|
-
|
|
79
|
-
The seller-side gateway has a matching opt-in mode: a request with no Metrik capability gets
|
|
80
|
-
a real x402 `402` carrying `PaymentRequirements` whose `payTo` is derived from the
|
|
81
|
-
operator-signed listing, so an agent holding only `x402-fetch` can pay per call.
|
|
82
|
-
|
|
83
|
-
`POST /settle` makes the operator's relayer sign and pay gas, so it is fenced: an **asset
|
|
84
|
-
allowlist** (default the chain's USDC) so the relayer can never be induced to call a
|
|
85
|
-
caller-chosen contract, a **bearer token** (`METRIK_X402_FACILITATOR_TOKEN`, required at
|
|
86
|
-
startup), a **settlement scope** limiting gas spend to resources Metrik tracks, and
|
|
87
|
-
network/non-zero-amount checks. `/verify` and `/supported` stay public.
|
|
88
|
-
|
|
89
|
-
> This is **weaker buyer protection than a stream, and deliberately so.** An `exact`
|
|
90
|
-
> payment holds nothing in escrow: the gate can PREVENT a payment to a failing service, it
|
|
91
|
-
> can **never** recover one that already went through. Only the stream path makes unearned
|
|
92
|
-
> money recoverable. [`docs/x402.md`](./docs/x402.md) has the full account, the
|
|
93
|
-
> 90-second freshness window and the reasoning behind it, and what is still unproven.
|
|
71
|
+
> x402 client or facilitator.
|
|
94
72
|
>
|
|
95
|
-
>
|
|
96
|
-
>
|
|
97
|
-
>
|
|
98
|
-
>
|
|
73
|
+
> Standard x402 is supported separately, as **x402 v2 `exact` through the stream
|
|
74
|
+
> opener**: `metrik-x402-opener-facilitator` and the gateway's opt-in
|
|
75
|
+
> `METRIK_GATEWAY_X402V2_OPENER` mode, both off by default. The opener `payTo` must only
|
|
76
|
+
> ever be settled by that facilitator — a stock x402 facilitator would strand the payment.
|
|
77
|
+
> Read [`docs/x402.md`](./docs/x402.md) before enabling either. The older x402 v1
|
|
78
|
+
> integration, which paid the seller directly, was removed.
|
|
99
79
|
|
|
100
80
|
**The MCP server** exposes `discover_services`, `hire_verified_service`, `invoke_service`,
|
|
101
81
|
`check_stream_status`, `reclaim_unspent`, `list_streams`, and — when Reclaim credentials are
|
|
@@ -276,10 +256,9 @@ const { streamId } = await facilitator.open(header); // mandate-gated, opens on-
|
|
|
276
256
|
The scheme name is exported as `METRIK_STREAM_SCHEME`; `parseStreamChallengeJson`,
|
|
277
257
|
`parseStreamPayloadHeader`, and `encodeStreamPayloadHeader` handle the wire format on both
|
|
278
258
|
sides, and the payload travels under `METRIK_PAYMENT_HEADER` (`x-metrik-payment`) — never
|
|
279
|
-
under x402's `X-PAYMENT
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
identifiers still work as deprecated aliases, but the names above are canonical.
|
|
259
|
+
under x402's `X-PAYMENT`. This wire format is Metrik's own; see
|
|
260
|
+
[`docs/x402.md`](./docs/x402.md). The deprecated `X402*` / `x402*` aliases of these names
|
|
261
|
+
were removed; use the names above.
|
|
283
262
|
|
|
284
263
|
### The CLI
|
|
285
264
|
|
|
@@ -299,11 +278,23 @@ metrik reclaim <streamId>
|
|
|
299
278
|
metrik claim <streamId> # operator: redeem what verified
|
|
300
279
|
metrik reclaim-unverified <streamId> --forfeit-unclaimed # buyer escape hatch
|
|
301
280
|
metrik mcp --print-config
|
|
281
|
+
|
|
282
|
+
# the x402 opener path (docs/x402.md), under a spend mandate
|
|
283
|
+
metrik pay https://x402.137.23.50.249.sslip.io/transform --json '{"text":"# hi"}' --max-amount 0.1
|
|
284
|
+
metrik refund <streamId> --refund-url <url> --close --reclaim # gasless; NOT automatic
|
|
302
285
|
```
|
|
303
286
|
|
|
304
|
-
- **Testnet only.**
|
|
305
|
-
|
|
306
|
-
|
|
287
|
+
- **Testnet only, registry-driven.** Every chain fact (chain id, escrow, USDC,
|
|
288
|
+
RPC, oracle, gas token) comes from the `@absol-labs/sdk` network registry;
|
|
289
|
+
`--network` / `METRIK_NETWORK` selects one. The CLI refuses any network the
|
|
290
|
+
registry does not mark as a testnet (today: Base Sepolia, `84532`), and re-reads
|
|
291
|
+
the chain id the RPC endpoint actually serves before it signs anything. There is
|
|
292
|
+
no override flag.
|
|
293
|
+
- **`metrik pay` is gated by a spend mandate** like `metrik open`: the payment's
|
|
294
|
+
opener terms (price vs per-stream/total caps, rate, window, operator) pass
|
|
295
|
+
`checkMandate()` before anything is signed, and the SDK's own x402 protections
|
|
296
|
+
stay on. It prints the response, the `streamId` and the refund URL. See
|
|
297
|
+
[`docs/cli.md`](./docs/cli.md#metrik-pay-x402).
|
|
307
298
|
- **The meter runs on verified availability, not on your calls.** A stream is a
|
|
308
299
|
per-second rental of an endpoint that keeps proving it is alive — not per-request
|
|
309
300
|
billing. You are charged for the seconds that verified whether or not you invoked
|
|
@@ -397,15 +388,18 @@ import {
|
|
|
397
388
|
|
|
398
389
|
console.log(METRIK_MCP_TOOLS.map((t) => t.name));
|
|
399
390
|
// discover_services, hire_verified_service, invoke_service, check_stream_status,
|
|
400
|
-
// reclaim_unspent, list_streams, prove_https_response
|
|
391
|
+
// reclaim_unspent, list_streams, prove_https_response,
|
|
392
|
+
// x402_pay,
|
|
393
|
+
// refund_status, refund
|
|
401
394
|
|
|
402
395
|
await startVerifiedStreamMcpServerStdio(); // reads process.env, connects stdio
|
|
403
396
|
// or, for custom transport wiring:
|
|
404
397
|
// const runtime = await createVerifiedStreamMcpServerFromEnv();
|
|
405
398
|
```
|
|
406
399
|
|
|
407
|
-
Core env surface: `METRIK_AGENT_RPC_URL`, `METRIK_AGENT_ESCROW
|
|
408
|
-
|
|
400
|
+
Core env surface: `METRIK_AGENT_RPC_URL`, `METRIK_AGENT_ESCROW` (must be the network's
|
|
401
|
+
registered escrow), `METRIK_AGENT_USDC`, `METRIK_NETWORK` / `METRIK_AGENT_CHAIN_ID`
|
|
402
|
+
(default: the registry's default network), and one wallet source — either
|
|
409
403
|
`METRIK_AGENT_PRIVATE_KEY` (injected signer) or the CDP set (`CDP_API_KEY_ID`,
|
|
410
404
|
`CDP_API_KEY_SECRET`, `CDP_WALLET_SECRET`, `METRIK_AGENT_CDP_OWNER_NAME`). Set
|
|
411
405
|
`RECLAIM_APP_ID` / `RECLAIM_APP_SECRET` to enable the `prove_https_response` zkTLS tool.
|
|
@@ -599,12 +593,9 @@ pnpm test
|
|
|
599
593
|
pnpm build
|
|
600
594
|
```
|
|
601
595
|
|
|
602
|
-
The Metrik `402` challenge flow,
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
cycle against the gateway and asserts the same valid payment is refused when the oracle
|
|
606
|
-
reports the service as failing; only the payer's on-chain balance and the broadcast are
|
|
607
|
-
fixtures, and nothing is ever broadcast. The `*.int.test.ts` integration suites deploy the
|
|
596
|
+
The Metrik `402` challenge flow, zkTLS proof path, signed-mandate engine, MCP server,
|
|
597
|
+
caller-auth gateway, and framework adapters are all real and tested in-repo. The
|
|
598
|
+
`*.int.test.ts` integration suites deploy the
|
|
608
599
|
hardened `StreamEscrow` on a throwaway `anvil` node and self-skip when `anvil` is not on
|
|
609
600
|
`PATH`, so default CI stays green. A full hire → stream → settle walkthrough lives in
|
|
610
601
|
[`examples/hire-stream-settle.ts`](examples/hire-stream-settle.ts).
|
|
@@ -615,4 +606,5 @@ hardened `StreamEscrow` on a throwaway `anvil` node and self-skip when `anvil` i
|
|
|
615
606
|
- Docs: [metrik.live](https://metrik.live) · App: [app.metrik.live](https://app.metrik.live)
|
|
616
607
|
- SDK: [`@absol-labs/sdk`](https://github.com/Absol-Labs/metrik-sdk)
|
|
617
608
|
- Quickstart: [`docs/quickstart.md`](./docs/quickstart.md) · Design + narrative: [`docs/agent-layer.md`](./docs/agent-layer.md) · Security review: [`docs/threat-model.md`](./docs/threat-model.md)
|
|
618
|
-
- Metrik and x402 — the facilitator, the
|
|
609
|
+
- Metrik and x402 — the x402 v2 opener facilitator and gateway mode, the rule that only the opener facilitator may settle an opener `payTo`, and what was removed: [`docs/x402.md`](./docs/x402.md)
|
|
610
|
+
- Letting the verifier check a **gated** listing's paid route (opt-in, seller-side): [`docs/probe-capability.md`](./docs/probe-capability.md)
|
|
@@ -54,8 +54,8 @@ export declare const signedInvocationCapabilitySchema: z.ZodObject<{
|
|
|
54
54
|
} & {
|
|
55
55
|
signature: z.ZodEffects<z.ZodString, string, string>;
|
|
56
56
|
}, "strip", z.ZodTypeAny, {
|
|
57
|
-
signature: string;
|
|
58
57
|
streamId: string;
|
|
58
|
+
signature: string;
|
|
59
59
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
|
|
60
60
|
serviceRef: string;
|
|
61
61
|
nonce: string;
|
|
@@ -63,8 +63,8 @@ export declare const signedInvocationCapabilitySchema: z.ZodObject<{
|
|
|
63
63
|
pathHash: string;
|
|
64
64
|
expiry: number;
|
|
65
65
|
}, {
|
|
66
|
-
signature: string;
|
|
67
66
|
streamId: string;
|
|
67
|
+
signature: string;
|
|
68
68
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
|
|
69
69
|
serviceRef: string;
|
|
70
70
|
nonce: string;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { type Address, type Hex, type LocalAccount } from "viem";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export declare const probeCapabilitySchema: z.ZodObject<{
|
|
4
|
+
/**
|
|
5
|
+
* The service being probed. Must match the target gateway's configured
|
|
6
|
+
* `serviceRef`. There is deliberately no `streamId` field — see the class
|
|
7
|
+
* docs.
|
|
8
|
+
*/
|
|
9
|
+
serviceRef: z.ZodEffects<z.ZodString, string, string>;
|
|
10
|
+
/**
|
|
11
|
+
* The minting oracle signer. Must equal the EIP-712 signer AND be a member of
|
|
12
|
+
* the escrow's on-chain `getOracleSigners()` set at verification time.
|
|
13
|
+
*/
|
|
14
|
+
oracle: z.ZodEffects<z.ZodString, `0x${string}`, string>;
|
|
15
|
+
/** The single HTTP method this capability authorizes. */
|
|
16
|
+
method: z.ZodEnum<["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]>;
|
|
17
|
+
/** `keccak256` of the exact request path this capability authorizes. */
|
|
18
|
+
pathHash: z.ZodEffects<z.ZodString, string, string>;
|
|
19
|
+
/** Single-use anti-replay nonce. */
|
|
20
|
+
nonce: z.ZodEffects<z.ZodString, string, string>;
|
|
21
|
+
/** Unix seconds after which the capability is no longer valid. */
|
|
22
|
+
expiry: z.ZodNumber;
|
|
23
|
+
}, "strip", z.ZodTypeAny, {
|
|
24
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
|
|
25
|
+
serviceRef: string;
|
|
26
|
+
nonce: string;
|
|
27
|
+
pathHash: string;
|
|
28
|
+
expiry: number;
|
|
29
|
+
oracle: `0x${string}`;
|
|
30
|
+
}, {
|
|
31
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
|
|
32
|
+
serviceRef: string;
|
|
33
|
+
nonce: string;
|
|
34
|
+
pathHash: string;
|
|
35
|
+
expiry: number;
|
|
36
|
+
oracle: string;
|
|
37
|
+
}>;
|
|
38
|
+
export type ProbeCapability = z.infer<typeof probeCapabilitySchema>;
|
|
39
|
+
export declare const signedProbeCapabilitySchema: z.ZodObject<{
|
|
40
|
+
/**
|
|
41
|
+
* The service being probed. Must match the target gateway's configured
|
|
42
|
+
* `serviceRef`. There is deliberately no `streamId` field — see the class
|
|
43
|
+
* docs.
|
|
44
|
+
*/
|
|
45
|
+
serviceRef: z.ZodEffects<z.ZodString, string, string>;
|
|
46
|
+
/**
|
|
47
|
+
* The minting oracle signer. Must equal the EIP-712 signer AND be a member of
|
|
48
|
+
* the escrow's on-chain `getOracleSigners()` set at verification time.
|
|
49
|
+
*/
|
|
50
|
+
oracle: z.ZodEffects<z.ZodString, `0x${string}`, string>;
|
|
51
|
+
/** The single HTTP method this capability authorizes. */
|
|
52
|
+
method: z.ZodEnum<["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]>;
|
|
53
|
+
/** `keccak256` of the exact request path this capability authorizes. */
|
|
54
|
+
pathHash: z.ZodEffects<z.ZodString, string, string>;
|
|
55
|
+
/** Single-use anti-replay nonce. */
|
|
56
|
+
nonce: z.ZodEffects<z.ZodString, string, string>;
|
|
57
|
+
/** Unix seconds after which the capability is no longer valid. */
|
|
58
|
+
expiry: z.ZodNumber;
|
|
59
|
+
} & {
|
|
60
|
+
signature: z.ZodEffects<z.ZodString, string, string>;
|
|
61
|
+
}, "strip", z.ZodTypeAny, {
|
|
62
|
+
signature: string;
|
|
63
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
|
|
64
|
+
serviceRef: string;
|
|
65
|
+
nonce: string;
|
|
66
|
+
pathHash: string;
|
|
67
|
+
expiry: number;
|
|
68
|
+
oracle: `0x${string}`;
|
|
69
|
+
}, {
|
|
70
|
+
signature: string;
|
|
71
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
|
|
72
|
+
serviceRef: string;
|
|
73
|
+
nonce: string;
|
|
74
|
+
pathHash: string;
|
|
75
|
+
expiry: number;
|
|
76
|
+
oracle: string;
|
|
77
|
+
}>;
|
|
78
|
+
export type SignedProbeCapability = z.infer<typeof signedProbeCapabilitySchema>;
|
|
79
|
+
/**
|
|
80
|
+
* Chain-bound domain. `verifyingContract` is the `StreamEscrowV2` whose
|
|
81
|
+
* `getOracleSigners()` defines who may mint — so a capability minted for one
|
|
82
|
+
* escrow can never authorize a probe against a gateway fronting another.
|
|
83
|
+
*/
|
|
84
|
+
export interface ProbeCapabilityDomainInput {
|
|
85
|
+
readonly chainId: number;
|
|
86
|
+
readonly verifyingContract: Address;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Distinct from `Metrik Invocation Capability`, so a buyer's invocation
|
|
90
|
+
* signature can never be replayed as a probe (or the reverse) even though both
|
|
91
|
+
* share a chainId and verifying contract.
|
|
92
|
+
*/
|
|
93
|
+
export declare const probeCapabilityDomainName: "Metrik Probe Capability";
|
|
94
|
+
export declare const probeCapabilityDomainVersion: "1";
|
|
95
|
+
export declare const probeCapabilityTypedData: {
|
|
96
|
+
readonly ProbeCapability: readonly [{
|
|
97
|
+
readonly name: "serviceRef";
|
|
98
|
+
readonly type: "bytes32";
|
|
99
|
+
}, {
|
|
100
|
+
readonly name: "oracle";
|
|
101
|
+
readonly type: "address";
|
|
102
|
+
}, {
|
|
103
|
+
readonly name: "method";
|
|
104
|
+
readonly type: "string";
|
|
105
|
+
}, {
|
|
106
|
+
readonly name: "pathHash";
|
|
107
|
+
readonly type: "bytes32";
|
|
108
|
+
}, {
|
|
109
|
+
readonly name: "nonce";
|
|
110
|
+
readonly type: "bytes32";
|
|
111
|
+
}, {
|
|
112
|
+
readonly name: "expiry";
|
|
113
|
+
readonly type: "uint64";
|
|
114
|
+
}];
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Default lifetime of a freshly-minted probe capability, in seconds.
|
|
118
|
+
*
|
|
119
|
+
* Shorter than the ~15-22s checkpoint cadence would race a slow upstream; much
|
|
120
|
+
* longer would widen the window in which a captured capability is replayable
|
|
121
|
+
* against a gateway with no nonce cache. 30s is one to two probe intervals.
|
|
122
|
+
*/
|
|
123
|
+
export declare const DEFAULT_PROBE_CAPABILITY_TTL_SECONDS = 30;
|
|
124
|
+
/** Header a probe request carries the signed capability in. */
|
|
125
|
+
export declare const PROBE_CAPABILITY_HEADER_NAME = "x-metrik-probe-capability";
|
|
126
|
+
/**
|
|
127
|
+
* Header the gateway adds to the UPSTREAM request when it authorized a probe.
|
|
128
|
+
* Advisory: it exists so a seller's own metering/analytics can exclude
|
|
129
|
+
* verification traffic from billable usage.
|
|
130
|
+
*/
|
|
131
|
+
export declare const PROBE_MARKER_HEADER_NAME = "x-metrik-probe";
|
|
132
|
+
export declare function createProbeCapabilityEip712Domain(domain: ProbeCapabilityDomainInput): {
|
|
133
|
+
readonly name: "Metrik Probe Capability";
|
|
134
|
+
readonly version: "1";
|
|
135
|
+
readonly chainId: number;
|
|
136
|
+
readonly verifyingContract: `0x${string}`;
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* `keccak256` of the exact request path this capability authorizes. Shares the
|
|
140
|
+
* canonical form with `InvocationCapability` so both sides of a gateway hash
|
|
141
|
+
* paths identically.
|
|
142
|
+
*/
|
|
143
|
+
export declare function hashProbePath(path: string): Hex;
|
|
144
|
+
/** Re-exported for symmetry; identical canonicalization to the invocation path. */
|
|
145
|
+
export declare function normalizeProbePath(path: string): string;
|
|
146
|
+
export declare function buildProbeCapabilityTypedData(capability: ProbeCapability, domain: ProbeCapabilityDomainInput): {
|
|
147
|
+
domain: {
|
|
148
|
+
readonly name: "Metrik Probe Capability";
|
|
149
|
+
readonly version: "1";
|
|
150
|
+
readonly chainId: number;
|
|
151
|
+
readonly verifyingContract: `0x${string}`;
|
|
152
|
+
};
|
|
153
|
+
types: {
|
|
154
|
+
readonly ProbeCapability: readonly [{
|
|
155
|
+
readonly name: "serviceRef";
|
|
156
|
+
readonly type: "bytes32";
|
|
157
|
+
}, {
|
|
158
|
+
readonly name: "oracle";
|
|
159
|
+
readonly type: "address";
|
|
160
|
+
}, {
|
|
161
|
+
readonly name: "method";
|
|
162
|
+
readonly type: "string";
|
|
163
|
+
}, {
|
|
164
|
+
readonly name: "pathHash";
|
|
165
|
+
readonly type: "bytes32";
|
|
166
|
+
}, {
|
|
167
|
+
readonly name: "nonce";
|
|
168
|
+
readonly type: "bytes32";
|
|
169
|
+
}, {
|
|
170
|
+
readonly name: "expiry";
|
|
171
|
+
readonly type: "uint64";
|
|
172
|
+
}];
|
|
173
|
+
};
|
|
174
|
+
primaryType: "ProbeCapability";
|
|
175
|
+
message: {
|
|
176
|
+
serviceRef: Hex;
|
|
177
|
+
oracle: `0x${string}`;
|
|
178
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
|
|
179
|
+
pathHash: Hex;
|
|
180
|
+
nonce: Hex;
|
|
181
|
+
expiry: bigint;
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
/** The minimum an oracle signer must expose to mint a probe capability. */
|
|
185
|
+
export type ProbeCapabilitySigner = Pick<LocalAccount, "address" | "signTypedData">;
|
|
186
|
+
/** Signs a `ProbeCapability` with an oracle signer's own key. */
|
|
187
|
+
export declare function signProbeCapability(capability: ProbeCapability, domain: ProbeCapabilityDomainInput, account: ProbeCapabilitySigner): Promise<Hex>;
|
|
188
|
+
/** Recovers the EIP-712 signer of a probe capability. Throws on a malformed signature. */
|
|
189
|
+
export declare function recoverProbeCapabilitySigner(capability: ProbeCapability, domain: ProbeCapabilityDomainInput, signature: Hex): Promise<Address>;
|
|
190
|
+
/** Cryptographically random 32-byte single-use nonce. */
|
|
191
|
+
export declare function generateProbeNonce(): Hex;
|
|
192
|
+
export declare class InvalidProbeCapabilityHeaderError extends Error {
|
|
193
|
+
constructor(message: string, options?: {
|
|
194
|
+
readonly cause?: unknown;
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
/** Encodes a signed probe capability as an opaque, header-safe (base64url) string. */
|
|
198
|
+
export declare function encodeProbeCapabilityHeader(signed: SignedProbeCapability): string;
|
|
199
|
+
/** Decodes and schema-validates a probe capability header. Fails closed on any malformation. */
|
|
200
|
+
export declare function decodeProbeCapabilityHeader(headerValue: string): SignedProbeCapability;
|
|
201
|
+
export interface MintProbeCapabilityOptions {
|
|
202
|
+
readonly serviceRef: Hex;
|
|
203
|
+
readonly method: ProbeCapability["method"];
|
|
204
|
+
/** The exact request path to be probed (query/fragment are stripped). */
|
|
205
|
+
readonly path: string;
|
|
206
|
+
readonly domain: ProbeCapabilityDomainInput;
|
|
207
|
+
readonly account: ProbeCapabilitySigner;
|
|
208
|
+
readonly ttlSeconds?: number;
|
|
209
|
+
/** Injectable clock (unix seconds), for tests. */
|
|
210
|
+
readonly now?: () => number;
|
|
211
|
+
/** Injectable nonce source, for tests. */
|
|
212
|
+
readonly nonce?: () => Hex;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Mint one fresh, single-use, short-lived probe capability and return its
|
|
216
|
+
* encoded header value. Called once PER PROBE PER VANTAGE by the coordinator:
|
|
217
|
+
* the gateway's replay cache is single-use, so two vantages sharing one
|
|
218
|
+
* capability would see the second rejected as a replay.
|
|
219
|
+
*/
|
|
220
|
+
export declare function mintProbeCapabilityHeader(options: MintProbeCapabilityOptions): Promise<{
|
|
221
|
+
readonly headerName: string;
|
|
222
|
+
readonly headerValue: string;
|
|
223
|
+
readonly capability: SignedProbeCapability;
|
|
224
|
+
}>;
|
|
225
|
+
//# sourceMappingURL=probe-capability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe-capability.d.ts","sourceRoot":"","sources":["../../src/capability/probe-capability.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { getAddress, isAddress, isHex, recoverTypedDataAddress, } from "viem";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { hashInvocationPath, httpMethodSchema, normalizeInvocationPath, } from "./invocation-capability.js";
|
|
5
|
+
/**
|
|
6
|
+
* `ProbeCapability` — the ORACLE-signed EIP-712 credential that lets the
|
|
7
|
+
* neutral verifier exercise the PAID route of a `gated` listing.
|
|
8
|
+
*
|
|
9
|
+
* ## The gap this closes
|
|
10
|
+
*
|
|
11
|
+
* A gated listing's paid route requires an `InvocationCapability` signed by the
|
|
12
|
+
* stream's BUYER (see `./invocation-capability.ts`). The oracle is not a buyer
|
|
13
|
+
* and holds no buyer key, so before this type existed the oracle could only
|
|
14
|
+
* probe a gated service's HEALTH route. "The seller earns only for intervals
|
|
15
|
+
* that verified" therefore meant "the health endpoint answered" — not "the paid
|
|
16
|
+
* route delivered". This capability is the missing credential.
|
|
17
|
+
*
|
|
18
|
+
* ## Why this is not just "give the workers a credential"
|
|
19
|
+
*
|
|
20
|
+
* Three properties, all load-bearing:
|
|
21
|
+
*
|
|
22
|
+
* 1. **No shared secret.** Authority is an EIP-712 signature by an address in
|
|
23
|
+
* the escrow's `getOracleSigners()`. That list is PUBLICLY READABLE on-chain,
|
|
24
|
+
* so a third-party seller performs the identical check with no credential
|
|
25
|
+
* distribution, no registration, and nothing for either side to leak.
|
|
26
|
+
* 2. **No signing key leaves the coordinator.** The oracle coordinator mints one
|
|
27
|
+
* fresh capability per probe and hands the encoded header to the regional
|
|
28
|
+
* worker. A worker holds a bearer token for the coordinator and nothing else;
|
|
29
|
+
* compromising every worker yields no ability to mint a capability.
|
|
30
|
+
* 3. **Not stream-bound.** Deliberately carries NO `streamId` and NO `buyer`.
|
|
31
|
+
* That is what makes the same mechanism usable to probe a listing that has no
|
|
32
|
+
* stream at all — and it is also the structural guarantee that a probe can
|
|
33
|
+
* never consume a buyer's budget: there is no stream for it to be billed
|
|
34
|
+
* against. Accrual is only ever produced by an oracle checkpoint keyed to a
|
|
35
|
+
* stream; a gateway call produces none.
|
|
36
|
+
*
|
|
37
|
+
* ## What a probe is NOT
|
|
38
|
+
*
|
|
39
|
+
* A probe is verification, not delivery to a buyer. A gateway authorizing one
|
|
40
|
+
* MUST NOT meter it, settle any payment for it, or count it as a paid call.
|
|
41
|
+
* The reference gateway enforces this structurally (see
|
|
42
|
+
* `../gateway/caller-auth-gateway.ts`) and marks the upstream request with
|
|
43
|
+
* {@link PROBE_MARKER_HEADER_NAME} so a seller's own metering can exclude it.
|
|
44
|
+
*
|
|
45
|
+
* ## Honest note for third-party sellers
|
|
46
|
+
*
|
|
47
|
+
* This CHANGES what a seller's gateway must implement to get paid-route
|
|
48
|
+
* verification. A gated listing fronted by a gateway that has not adopted
|
|
49
|
+
* `ProbeCapability` stays HEALTH-ROUTE-ONLY, and the oracle reports it as
|
|
50
|
+
* unverifiable rather than passing. Adoption is the seller's choice; silence
|
|
51
|
+
* about it is not the oracle's.
|
|
52
|
+
*
|
|
53
|
+
* Kept LOCAL to `@absol-labs/agent` for the same reason as
|
|
54
|
+
* `InvocationCapability`: no contract parses it, so there is no on-chain byte
|
|
55
|
+
* compatibility requirement. Cross-repo compatibility with the oracle's minter
|
|
56
|
+
* is pinned by a shared digest test vector — see
|
|
57
|
+
* `test/probe-capability.test.ts` and the oracle's mirror of the same constant.
|
|
58
|
+
*/
|
|
59
|
+
const addressSchema = z
|
|
60
|
+
.string()
|
|
61
|
+
.refine((value) => isAddress(value), "must be an EVM address");
|
|
62
|
+
const bytes32Schema = z
|
|
63
|
+
.string()
|
|
64
|
+
.refine((value) => isHex(value, { strict: true }) && value.length === 66, "must be bytes32 hex");
|
|
65
|
+
const signatureSchema = z
|
|
66
|
+
.string()
|
|
67
|
+
.refine((value) => isHex(value, { strict: true }) && value.length === 132, "must be a 65-byte signature");
|
|
68
|
+
export const probeCapabilitySchema = z.object({
|
|
69
|
+
/**
|
|
70
|
+
* The service being probed. Must match the target gateway's configured
|
|
71
|
+
* `serviceRef`. There is deliberately no `streamId` field — see the class
|
|
72
|
+
* docs.
|
|
73
|
+
*/
|
|
74
|
+
serviceRef: bytes32Schema,
|
|
75
|
+
/**
|
|
76
|
+
* The minting oracle signer. Must equal the EIP-712 signer AND be a member of
|
|
77
|
+
* the escrow's on-chain `getOracleSigners()` set at verification time.
|
|
78
|
+
*/
|
|
79
|
+
oracle: addressSchema,
|
|
80
|
+
/** The single HTTP method this capability authorizes. */
|
|
81
|
+
method: httpMethodSchema,
|
|
82
|
+
/** `keccak256` of the exact request path this capability authorizes. */
|
|
83
|
+
pathHash: bytes32Schema,
|
|
84
|
+
/** Single-use anti-replay nonce. */
|
|
85
|
+
nonce: bytes32Schema,
|
|
86
|
+
/** Unix seconds after which the capability is no longer valid. */
|
|
87
|
+
expiry: z.number().int().positive(),
|
|
88
|
+
});
|
|
89
|
+
export const signedProbeCapabilitySchema = probeCapabilitySchema.extend({
|
|
90
|
+
signature: signatureSchema,
|
|
91
|
+
});
|
|
92
|
+
/**
|
|
93
|
+
* Distinct from `Metrik Invocation Capability`, so a buyer's invocation
|
|
94
|
+
* signature can never be replayed as a probe (or the reverse) even though both
|
|
95
|
+
* share a chainId and verifying contract.
|
|
96
|
+
*/
|
|
97
|
+
export const probeCapabilityDomainName = "Metrik Probe Capability";
|
|
98
|
+
export const probeCapabilityDomainVersion = "1";
|
|
99
|
+
export const probeCapabilityTypedData = {
|
|
100
|
+
ProbeCapability: [
|
|
101
|
+
{ name: "serviceRef", type: "bytes32" },
|
|
102
|
+
{ name: "oracle", type: "address" },
|
|
103
|
+
{ name: "method", type: "string" },
|
|
104
|
+
{ name: "pathHash", type: "bytes32" },
|
|
105
|
+
{ name: "nonce", type: "bytes32" },
|
|
106
|
+
{ name: "expiry", type: "uint64" },
|
|
107
|
+
],
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Default lifetime of a freshly-minted probe capability, in seconds.
|
|
111
|
+
*
|
|
112
|
+
* Shorter than the ~15-22s checkpoint cadence would race a slow upstream; much
|
|
113
|
+
* longer would widen the window in which a captured capability is replayable
|
|
114
|
+
* against a gateway with no nonce cache. 30s is one to two probe intervals.
|
|
115
|
+
*/
|
|
116
|
+
export const DEFAULT_PROBE_CAPABILITY_TTL_SECONDS = 30;
|
|
117
|
+
/** Header a probe request carries the signed capability in. */
|
|
118
|
+
export const PROBE_CAPABILITY_HEADER_NAME = "x-metrik-probe-capability";
|
|
119
|
+
/**
|
|
120
|
+
* Header the gateway adds to the UPSTREAM request when it authorized a probe.
|
|
121
|
+
* Advisory: it exists so a seller's own metering/analytics can exclude
|
|
122
|
+
* verification traffic from billable usage.
|
|
123
|
+
*/
|
|
124
|
+
export const PROBE_MARKER_HEADER_NAME = "x-metrik-probe";
|
|
125
|
+
export function createProbeCapabilityEip712Domain(domain) {
|
|
126
|
+
return {
|
|
127
|
+
name: probeCapabilityDomainName,
|
|
128
|
+
version: probeCapabilityDomainVersion,
|
|
129
|
+
chainId: domain.chainId,
|
|
130
|
+
verifyingContract: getAddress(domain.verifyingContract),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* `keccak256` of the exact request path this capability authorizes. Shares the
|
|
135
|
+
* canonical form with `InvocationCapability` so both sides of a gateway hash
|
|
136
|
+
* paths identically.
|
|
137
|
+
*/
|
|
138
|
+
export function hashProbePath(path) {
|
|
139
|
+
return hashInvocationPath(path);
|
|
140
|
+
}
|
|
141
|
+
/** Re-exported for symmetry; identical canonicalization to the invocation path. */
|
|
142
|
+
export function normalizeProbePath(path) {
|
|
143
|
+
return normalizeInvocationPath(path);
|
|
144
|
+
}
|
|
145
|
+
function probeCapabilityMessage(capability) {
|
|
146
|
+
const parsed = probeCapabilitySchema.parse(capability);
|
|
147
|
+
return {
|
|
148
|
+
serviceRef: parsed.serviceRef,
|
|
149
|
+
oracle: getAddress(parsed.oracle),
|
|
150
|
+
method: parsed.method,
|
|
151
|
+
pathHash: parsed.pathHash,
|
|
152
|
+
nonce: parsed.nonce,
|
|
153
|
+
expiry: BigInt(parsed.expiry),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
export function buildProbeCapabilityTypedData(capability, domain) {
|
|
157
|
+
return {
|
|
158
|
+
domain: createProbeCapabilityEip712Domain(domain),
|
|
159
|
+
types: probeCapabilityTypedData,
|
|
160
|
+
primaryType: "ProbeCapability",
|
|
161
|
+
message: probeCapabilityMessage(capability),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/** Signs a `ProbeCapability` with an oracle signer's own key. */
|
|
165
|
+
export async function signProbeCapability(capability, domain, account) {
|
|
166
|
+
const typedData = buildProbeCapabilityTypedData(capability, domain);
|
|
167
|
+
return account.signTypedData(typedData);
|
|
168
|
+
}
|
|
169
|
+
/** Recovers the EIP-712 signer of a probe capability. Throws on a malformed signature. */
|
|
170
|
+
export async function recoverProbeCapabilitySigner(capability, domain, signature) {
|
|
171
|
+
const typedData = buildProbeCapabilityTypedData(capability, domain);
|
|
172
|
+
return recoverTypedDataAddress({ ...typedData, signature });
|
|
173
|
+
}
|
|
174
|
+
/** Cryptographically random 32-byte single-use nonce. */
|
|
175
|
+
export function generateProbeNonce() {
|
|
176
|
+
return `0x${randomBytes(32).toString("hex")}`;
|
|
177
|
+
}
|
|
178
|
+
export class InvalidProbeCapabilityHeaderError extends Error {
|
|
179
|
+
constructor(message, options) {
|
|
180
|
+
super(message, options);
|
|
181
|
+
this.name = "InvalidProbeCapabilityHeaderError";
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/** Encodes a signed probe capability as an opaque, header-safe (base64url) string. */
|
|
185
|
+
export function encodeProbeCapabilityHeader(signed) {
|
|
186
|
+
const parsed = signedProbeCapabilitySchema.parse(signed);
|
|
187
|
+
const json = JSON.stringify({
|
|
188
|
+
serviceRef: parsed.serviceRef,
|
|
189
|
+
oracle: parsed.oracle,
|
|
190
|
+
method: parsed.method,
|
|
191
|
+
pathHash: parsed.pathHash,
|
|
192
|
+
nonce: parsed.nonce,
|
|
193
|
+
expiry: parsed.expiry,
|
|
194
|
+
signature: parsed.signature,
|
|
195
|
+
});
|
|
196
|
+
return Buffer.from(json, "utf8").toString("base64url");
|
|
197
|
+
}
|
|
198
|
+
/** Decodes and schema-validates a probe capability header. Fails closed on any malformation. */
|
|
199
|
+
export function decodeProbeCapabilityHeader(headerValue) {
|
|
200
|
+
let json;
|
|
201
|
+
try {
|
|
202
|
+
json = Buffer.from(headerValue, "base64url").toString("utf8");
|
|
203
|
+
}
|
|
204
|
+
catch (cause) {
|
|
205
|
+
throw new InvalidProbeCapabilityHeaderError("malformed base64 probe capability header", { cause });
|
|
206
|
+
}
|
|
207
|
+
let raw;
|
|
208
|
+
try {
|
|
209
|
+
raw = JSON.parse(json);
|
|
210
|
+
}
|
|
211
|
+
catch (cause) {
|
|
212
|
+
throw new InvalidProbeCapabilityHeaderError("malformed JSON probe capability header", { cause });
|
|
213
|
+
}
|
|
214
|
+
const result = signedProbeCapabilitySchema.safeParse(raw);
|
|
215
|
+
if (!result.success) {
|
|
216
|
+
throw new InvalidProbeCapabilityHeaderError(`invalid probe capability shape: ${result.error.message}`);
|
|
217
|
+
}
|
|
218
|
+
return result.data;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Mint one fresh, single-use, short-lived probe capability and return its
|
|
222
|
+
* encoded header value. Called once PER PROBE PER VANTAGE by the coordinator:
|
|
223
|
+
* the gateway's replay cache is single-use, so two vantages sharing one
|
|
224
|
+
* capability would see the second rejected as a replay.
|
|
225
|
+
*/
|
|
226
|
+
export async function mintProbeCapabilityHeader(options) {
|
|
227
|
+
const nowSeconds = (options.now ?? (() => Math.floor(Date.now() / 1000)))();
|
|
228
|
+
const ttl = options.ttlSeconds ?? DEFAULT_PROBE_CAPABILITY_TTL_SECONDS;
|
|
229
|
+
if (!Number.isInteger(ttl) || ttl <= 0) {
|
|
230
|
+
throw new Error("probe capability ttlSeconds must be a positive integer");
|
|
231
|
+
}
|
|
232
|
+
const capability = {
|
|
233
|
+
serviceRef: options.serviceRef,
|
|
234
|
+
oracle: getAddress(options.account.address),
|
|
235
|
+
method: options.method,
|
|
236
|
+
pathHash: hashProbePath(options.path),
|
|
237
|
+
nonce: (options.nonce ?? generateProbeNonce)(),
|
|
238
|
+
expiry: nowSeconds + ttl,
|
|
239
|
+
};
|
|
240
|
+
const signature = await signProbeCapability(capability, options.domain, options.account);
|
|
241
|
+
const signed = { ...capability, signature };
|
|
242
|
+
return {
|
|
243
|
+
headerName: PROBE_CAPABILITY_HEADER_NAME,
|
|
244
|
+
headerValue: encodeProbeCapabilityHeader(signed),
|
|
245
|
+
capability: signed,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=probe-capability.js.map
|