@absol-labs/agent 0.10.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 +106 -30
- package/dist/capability/invocation-capability.d.ts +20 -8
- package/dist/capability/invocation-capability.d.ts.map +1 -1
- package/dist/capability/invocation-capability.js.map +1 -1
- 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.d.ts.map +1 -1
- package/dist/cli/commands/claim.js +21 -5
- 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 +51 -7
- 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 +3 -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 +121 -15
- 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 +15 -6
- 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 +200 -21
- 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 +9 -13
- 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 +86 -7
- package/dist/cli/deps.d.ts.map +1 -1
- package/dist/cli/deps.js +102 -16
- 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 +83 -0
- package/dist/cli/privy.d.ts.map +1 -0
- package/dist/cli/privy.js +232 -0
- package/dist/cli/privy.js.map +1 -0
- 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/spend-policy.d.ts +60 -0
- package/dist/cli/spend-policy.d.ts.map +1 -0
- package/dist/cli/spend-policy.js +65 -0
- package/dist/cli/spend-policy.js.map +1 -0
- 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 +109 -40
- package/dist/cli/wallet.d.ts.map +1 -1
- package/dist/cli/wallet.js +109 -44
- 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 +91 -4
- package/dist/gateway/caller-auth-gateway.d.ts.map +1 -1
- package/dist/gateway/caller-auth-gateway.js +184 -7
- package/dist/gateway/caller-auth-gateway.js.map +1 -1
- package/dist/gateway/http-server.d.ts +121 -1
- package/dist/gateway/http-server.d.ts.map +1 -1
- package/dist/gateway/http-server.js +474 -5
- 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 +11 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -5
- package/dist/index.js.map +1 -1
- package/dist/mandates/env.d.ts +3 -2
- package/dist/mandates/env.d.ts.map +1 -1
- package/dist/mandates/env.js +3 -2
- package/dist/mandates/env.js.map +1 -1
- package/dist/mcp/server.d.ts +100 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +719 -39
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/service-invoker.d.ts +97 -0
- package/dist/mcp/service-invoker.d.ts.map +1 -0
- package/dist/mcp/service-invoker.js +167 -0
- package/dist/mcp/service-invoker.js.map +1 -0
- 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 +23 -4
- package/dist/sdk/invoke.d.ts.map +1 -1
- package/dist/sdk/invoke.js +71 -10
- 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 +42 -17
- package/dist/x402/facilitator.d.ts.map +1 -1
- package/dist/x402/facilitator.js +49 -33
- 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/docs/agent-layer.md +13 -8
- package/docs/autonomous-privy-wallet.md +188 -0
- package/docs/cli.md +342 -53
- package/docs/langchain.md +5 -0
- package/docs/mcp-hosted.md +4 -3
- package/docs/mcp-reference.md +245 -0
- package/docs/probe-capability.md +191 -0
- package/docs/quickstart.md +54 -5
- package/docs/threat-model.md +24 -3
- package/docs/x402.md +681 -0
- package/package.json +19 -6
- package/src/capability/invocation-capability.ts +17 -1
- package/src/capability/probe-capability.ts +355 -0
- package/src/cli/args.ts +29 -0
- package/src/cli/commands/claim.ts +31 -5
- package/src/cli/commands/close.ts +56 -7
- package/src/cli/commands/invoke.ts +6 -2
- package/src/cli/commands/mcp.ts +18 -2
- package/src/cli/commands/open.ts +151 -14
- package/src/cli/commands/pay.ts +442 -0
- package/src/cli/commands/reclaim-unverified.ts +24 -6
- package/src/cli/commands/reclaim.ts +274 -19
- package/src/cli/commands/refund.ts +277 -0
- package/src/cli/commands/status.ts +17 -1
- package/src/cli/commands/streams.ts +11 -15
- package/src/cli/commands/watch.ts +7 -1
- package/src/cli/config.ts +139 -52
- package/src/cli/deps.ts +189 -25
- 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 +398 -0
- 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/spend-policy.ts +139 -0
- package/src/cli/status.ts +14 -1
- package/src/cli/wallet.ts +189 -58
- 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 +336 -13
- package/src/gateway/http-server.ts +690 -7
- 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 +112 -14
- package/src/mandates/env.ts +3 -2
- package/src/mcp/server.ts +973 -44
- package/src/mcp/service-invoker.ts +285 -0
- package/src/mcp/x402-tools.ts +621 -0
- package/src/network.ts +302 -0
- package/src/sdk/invoke.ts +96 -17
- 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 +85 -46
- 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
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { MetrikClient } from "@absol-labs/sdk";
|
|
2
1
|
import { isAddress, isHex, type Address, type Hex } from "viem";
|
|
3
2
|
|
|
4
3
|
import {
|
|
@@ -10,6 +9,29 @@ import {
|
|
|
10
9
|
type InvocationCapabilityDomainInput,
|
|
11
10
|
type SignedInvocationCapability,
|
|
12
11
|
} from "../capability/invocation-capability.js";
|
|
12
|
+
import {
|
|
13
|
+
decodeProbeCapabilityHeader,
|
|
14
|
+
hashProbePath,
|
|
15
|
+
InvalidProbeCapabilityHeaderError,
|
|
16
|
+
PROBE_CAPABILITY_HEADER_NAME,
|
|
17
|
+
recoverProbeCapabilitySigner,
|
|
18
|
+
type ProbeCapabilityDomainInput,
|
|
19
|
+
type SignedProbeCapability,
|
|
20
|
+
} from "../capability/probe-capability.js";
|
|
21
|
+
import {
|
|
22
|
+
CachedOracleSignerSet,
|
|
23
|
+
createSdkOracleSignerSetReader,
|
|
24
|
+
FixedWindowProbeRateLimiter,
|
|
25
|
+
type GatewayProbeMode,
|
|
26
|
+
type ProbeAuthorization,
|
|
27
|
+
type ProbeRateLimiter,
|
|
28
|
+
} from "./probe-auth.js";
|
|
29
|
+
import { defaultNetwork, readOnlyMetrikClient } from "../network.js";
|
|
30
|
+
import {
|
|
31
|
+
isVerifiedSignedRateBounds,
|
|
32
|
+
rateOutsideBounds,
|
|
33
|
+
type SignedRateBounds,
|
|
34
|
+
} from "./signed-rate-bounds.js";
|
|
13
35
|
|
|
14
36
|
/**
|
|
15
37
|
* Seller-side caller-auth gateway (metrik-agent#63, impl of #62 part 1/2).
|
|
@@ -69,7 +91,45 @@ export type CallerAuthDenialReason =
|
|
|
69
91
|
| "stream-not-active"
|
|
70
92
|
| "stream-expired"
|
|
71
93
|
| "stream-not-funded"
|
|
72
|
-
| "stream-state-stale"
|
|
94
|
+
| "stream-state-stale"
|
|
95
|
+
/**
|
|
96
|
+
* The stream's on-chain `ratePerSecond` is outside the operator-SIGNED
|
|
97
|
+
* `minRatePerSecond`/`maxRatePerSecond` of this service's descriptor
|
|
98
|
+
* (metrik-contracts#41: the escrow accepts any non-zero rate, so a buyer
|
|
99
|
+
* calling it directly could otherwise open an underpriced stream and be
|
|
100
|
+
* served). Irreversible: the rate is fixed at open.
|
|
101
|
+
*/
|
|
102
|
+
| "stream-rate-out-of-bounds"
|
|
103
|
+
/**
|
|
104
|
+
* Signed rate bounds are configured but the stream view carries no
|
|
105
|
+
* `ratePerSecond`, so the band cannot be checked. Fail closed.
|
|
106
|
+
*/
|
|
107
|
+
| "stream-rate-unknown"
|
|
108
|
+
/**
|
|
109
|
+
* The request carried an oracle probe capability AND a buyer capability or an
|
|
110
|
+
* `X-PAYMENT` header. Refused rather than guessed: a probe is free and unbilled,
|
|
111
|
+
* a buyer call is neither, and the two must never be conflated.
|
|
112
|
+
*/
|
|
113
|
+
| "ambiguous-probe-mode"
|
|
114
|
+
/**
|
|
115
|
+
* A probe capability was presented but this gateway has probe verification
|
|
116
|
+
* turned off. Distinct from the malformed/invalid reasons: it tells the
|
|
117
|
+
* oracle the paid route is UNVERIFIABLE here, not that it failed.
|
|
118
|
+
*/
|
|
119
|
+
| "probe-not-enabled"
|
|
120
|
+
| "malformed-probe-capability"
|
|
121
|
+
| "probe-capability-expired"
|
|
122
|
+
| "probe-invalid-signature"
|
|
123
|
+
| "probe-method-mismatch"
|
|
124
|
+
| "probe-path-mismatch"
|
|
125
|
+
| "probe-service-ref-mismatch"
|
|
126
|
+
| "probe-nonce-replayed"
|
|
127
|
+
/** The recovered signer is not in the escrow's `getOracleSigners()` set. */
|
|
128
|
+
| "probe-signer-not-authorized"
|
|
129
|
+
/** The signer set could not be read fresh enough to decide. Fail closed. */
|
|
130
|
+
| "probe-signer-set-unavailable"
|
|
131
|
+
/** The free-probe budget for this window is spent. */
|
|
132
|
+
| "probe-rate-limited";
|
|
73
133
|
|
|
74
134
|
/**
|
|
75
135
|
* Default freshness bound for on-chain stream state, in seconds. Base blocks
|
|
@@ -92,7 +152,9 @@ const DENIAL_PRECEDENCE: readonly CallerAuthDenialReason[] = [
|
|
|
92
152
|
"stream-expired",
|
|
93
153
|
"buyer-mismatch",
|
|
94
154
|
"service-ref-mismatch",
|
|
155
|
+
"stream-rate-out-of-bounds",
|
|
95
156
|
"stream-not-funded",
|
|
157
|
+
"stream-rate-unknown",
|
|
96
158
|
"stream-state-stale",
|
|
97
159
|
"stream-not-found",
|
|
98
160
|
];
|
|
@@ -110,15 +172,36 @@ const DENIAL_PRECEDENCE: readonly CallerAuthDenialReason[] = [
|
|
|
110
172
|
const TERMINAL_DENIALS: ReadonlySet<CallerAuthDenialReason> = new Set([
|
|
111
173
|
"stream-not-active",
|
|
112
174
|
"stream-not-funded",
|
|
175
|
+
// `ratePerSecond` is fixed at open and the signed band is this process's
|
|
176
|
+
// configuration, so an out-of-band stream stays out of band.
|
|
177
|
+
"stream-rate-out-of-bounds",
|
|
113
178
|
]);
|
|
114
179
|
|
|
180
|
+
/**
|
|
181
|
+
* A probe carrying an `X-PAYMENT` header is refused as `ambiguous-probe-mode`.
|
|
182
|
+
* This gateway implements no per-call payment mode (the x402 v1 integration was
|
|
183
|
+
* removed in metrik-agent#115), but the refusal is kept deliberately: a probe
|
|
184
|
+
* is free, unbilled verification traffic and must never be mixed with anything
|
|
185
|
+
* that looks like a payment, and keeping it leaves the deployed gateway's
|
|
186
|
+
* behaviour byte-for-byte unchanged.
|
|
187
|
+
*/
|
|
188
|
+
const PAYMENT_HEADER_REFUSED_ON_PROBES = "x-payment";
|
|
189
|
+
|
|
115
190
|
export interface CallerAuthDecision {
|
|
116
191
|
readonly authorized: boolean;
|
|
117
192
|
readonly reason?: CallerAuthDenialReason;
|
|
118
193
|
/** Set only when `authorized === false`. */
|
|
119
|
-
readonly httpStatus?: 402 | 403;
|
|
120
|
-
/** Set only when `authorized === true
|
|
194
|
+
readonly httpStatus?: 400 | 402 | 403 | 429;
|
|
195
|
+
/** Set only when `authorized === true` via the Metrik stream path. */
|
|
121
196
|
readonly stream?: GatewayStreamView;
|
|
197
|
+
/**
|
|
198
|
+
* Set only when `authorized === true` via the ORACLE PROBE path. Mutually
|
|
199
|
+
* exclusive with {@link stream} by construction: the probe branch returns
|
|
200
|
+
* before a stream is ever produced, so an authorized probe can never draw on
|
|
201
|
+
* a stream's budget. It is
|
|
202
|
+
* verification traffic and MUST NOT be metered or billed.
|
|
203
|
+
*/
|
|
204
|
+
readonly probe?: ProbeAuthorization;
|
|
122
205
|
/** The decoded (but not necessarily valid) capability, when one was present. */
|
|
123
206
|
readonly capability?: SignedInvocationCapability;
|
|
124
207
|
}
|
|
@@ -131,6 +214,13 @@ export interface GatewayStreamView {
|
|
|
131
214
|
readonly claimedCumulative: bigint;
|
|
132
215
|
readonly expiresAt: number;
|
|
133
216
|
readonly status: "active" | "closed";
|
|
217
|
+
/**
|
|
218
|
+
* The stream's contracted `ratePerSecond` (USDC base units per verified
|
|
219
|
+
* second), fixed at open. Required when the gateway enforces operator-signed
|
|
220
|
+
* rate bounds; a view without it is refused (`stream-rate-unknown`) there.
|
|
221
|
+
* The default SDK reader always reports it.
|
|
222
|
+
*/
|
|
223
|
+
readonly ratePerSecond?: bigint;
|
|
134
224
|
/**
|
|
135
225
|
* The chain head this view was observed against. Readers SHOULD report it:
|
|
136
226
|
* when present, the gateway rejects the view outright if the block is older
|
|
@@ -203,11 +293,10 @@ export class UnknownStreamGatewayError extends Error {
|
|
|
203
293
|
export function createSdkStreamReader(options: {
|
|
204
294
|
readonly escrowAddress: Address;
|
|
205
295
|
readonly rpcUrl: string;
|
|
296
|
+
/** Chain of the escrow; must be a registered network. Default: the registry default. */
|
|
297
|
+
readonly chainId?: number;
|
|
206
298
|
}): StreamReader {
|
|
207
|
-
const metrik =
|
|
208
|
-
escrow: options.escrowAddress,
|
|
209
|
-
rpcUrl: options.rpcUrl,
|
|
210
|
-
});
|
|
299
|
+
const metrik = readOnlyMetrikClient(options);
|
|
211
300
|
return {
|
|
212
301
|
async getStream(streamId) {
|
|
213
302
|
try {
|
|
@@ -226,6 +315,7 @@ export function createSdkStreamReader(options: {
|
|
|
226
315
|
claimedCumulative: stream.claimedCumulative,
|
|
227
316
|
expiresAt: stream.expiresAt,
|
|
228
317
|
status: stream.status,
|
|
318
|
+
ratePerSecond: stream.ratePerSecond,
|
|
229
319
|
observedAt: {
|
|
230
320
|
blockNumber: block.number,
|
|
231
321
|
blockTimestamp: Number(block.timestamp),
|
|
@@ -284,7 +374,7 @@ export interface CallerAuthGatewayConfig {
|
|
|
284
374
|
readonly serviceRef: Hex;
|
|
285
375
|
/** Additional service refs accepted during signed-descriptor migrations. */
|
|
286
376
|
readonly serviceRefs?: readonly Hex[];
|
|
287
|
-
/** Chain id for EIP-712 domain separation. Default
|
|
377
|
+
/** Chain id for EIP-712 domain separation and the default readers. Default: the registry's default network. */
|
|
288
378
|
readonly chainId?: number;
|
|
289
379
|
/** Capability header name. Default {@link CAPABILITY_HEADER_NAME}. */
|
|
290
380
|
readonly headerName?: string;
|
|
@@ -316,6 +406,28 @@ export interface CallerAuthGatewayConfig {
|
|
|
316
406
|
readonly nonceCache?: NonceReplayCache;
|
|
317
407
|
/** Injectable clock (unix seconds), for tests. */
|
|
318
408
|
readonly now?: () => number;
|
|
409
|
+
/**
|
|
410
|
+
* Optional ORACLE PROBE mode. When set, a request carrying a valid
|
|
411
|
+
* oracle-signed `ProbeCapability` is authorized and proxied WITHOUT any
|
|
412
|
+
* stream, payment or metering, so the neutral verifier can exercise this
|
|
413
|
+
* gateway's PAID route even though the listing is gated.
|
|
414
|
+
*
|
|
415
|
+
* OFF by default. A gateway that leaves it unset behaves byte for byte as
|
|
416
|
+
* before and answers a probe with `probe-not-enabled` / `403` — which the
|
|
417
|
+
* oracle reports as UNVERIFIABLE (health-route-only), never as passing.
|
|
418
|
+
*/
|
|
419
|
+
readonly probe?: GatewayProbeMode;
|
|
420
|
+
/**
|
|
421
|
+
* Operator-SIGNED rate bounds to enforce on the capability path, one per
|
|
422
|
+
* served serviceRef (metrik-contracts#41). Each entry must come from
|
|
423
|
+
* {@link verifySignedRateBounds} (or `loadSignedRateBoundsFile`) and name one
|
|
424
|
+
* of this gateway's service refs. A stream whose serviceRef has bounds is
|
|
425
|
+
* served only while its on-chain `ratePerSecond` lies inside them.
|
|
426
|
+
*
|
|
427
|
+
* Unset = no rate enforcement here (the previous behaviour); the reference
|
|
428
|
+
* server warns when it starts without them.
|
|
429
|
+
*/
|
|
430
|
+
readonly signedRateBounds?: readonly SignedRateBounds[];
|
|
319
431
|
}
|
|
320
432
|
|
|
321
433
|
export interface CallerAuthRequestInput {
|
|
@@ -341,6 +453,11 @@ export class CallerAuthGateway {
|
|
|
341
453
|
private readonly domain: InvocationCapabilityDomainInput;
|
|
342
454
|
private readonly serviceRefs: ReadonlySet<string>;
|
|
343
455
|
private readonly now: () => number;
|
|
456
|
+
private readonly probeHeaderName: string;
|
|
457
|
+
private readonly probeDomain: ProbeCapabilityDomainInput;
|
|
458
|
+
private readonly probeSigners: CachedOracleSignerSet | undefined;
|
|
459
|
+
private readonly probeRateLimiter: ProbeRateLimiter | undefined;
|
|
460
|
+
private readonly rateBounds: ReadonlyMap<string, SignedRateBounds>;
|
|
344
461
|
|
|
345
462
|
constructor(config: CallerAuthGatewayConfig) {
|
|
346
463
|
if (!isAddress(config.escrowAddress)) {
|
|
@@ -358,7 +475,29 @@ export class CallerAuthGateway {
|
|
|
358
475
|
);
|
|
359
476
|
}
|
|
360
477
|
|
|
478
|
+
const chainId = config.chainId ?? defaultNetwork().chain.id;
|
|
361
479
|
this.serviceRefs = new Set(serviceRefs.map((value) => value.toLowerCase()));
|
|
480
|
+
const rateBounds = new Map<string, SignedRateBounds>();
|
|
481
|
+
for (const bounds of config.signedRateBounds ?? []) {
|
|
482
|
+
if (!isVerifiedSignedRateBounds(bounds)) {
|
|
483
|
+
throw new Error(
|
|
484
|
+
"signedRateBounds entries must come from verifySignedRateBounds() / loadSignedRateBoundsFile(); an unverified object is not operator-signed",
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
const key = bounds.serviceRef.toLowerCase();
|
|
488
|
+
if (!this.serviceRefs.has(key)) {
|
|
489
|
+
throw new Error(
|
|
490
|
+
`signed rate bounds for ${bounds.serviceRef} are not a service ref this gateway serves`,
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
if (rateBounds.has(key)) {
|
|
494
|
+
throw new Error(
|
|
495
|
+
`more than one set of signed rate bounds for ${bounds.serviceRef}`,
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
rateBounds.set(key, bounds);
|
|
499
|
+
}
|
|
500
|
+
this.rateBounds = rateBounds;
|
|
362
501
|
this.headerName = (
|
|
363
502
|
config.headerName ?? CAPABILITY_HEADER_NAME
|
|
364
503
|
).toLowerCase();
|
|
@@ -367,6 +506,7 @@ export class CallerAuthGateway {
|
|
|
367
506
|
createSdkStreamReader({
|
|
368
507
|
escrowAddress: config.escrowAddress,
|
|
369
508
|
rpcUrl: config.rpcUrl as string,
|
|
509
|
+
chainId,
|
|
370
510
|
});
|
|
371
511
|
const corroborating =
|
|
372
512
|
config.corroboratingStreamReaders ??
|
|
@@ -374,6 +514,7 @@ export class CallerAuthGateway {
|
|
|
374
514
|
createSdkStreamReader({
|
|
375
515
|
escrowAddress: config.escrowAddress,
|
|
376
516
|
rpcUrl,
|
|
517
|
+
chainId,
|
|
377
518
|
}),
|
|
378
519
|
);
|
|
379
520
|
if (corroborating.length > MAX_CORROBORATING_STREAM_READERS) {
|
|
@@ -393,23 +534,105 @@ export class CallerAuthGateway {
|
|
|
393
534
|
config.revocationSet ?? new InMemoryStreamRevocationSet();
|
|
394
535
|
this.nonceCache = config.nonceCache ?? new InMemoryNonceReplayCache();
|
|
395
536
|
this.domain = {
|
|
396
|
-
chainId
|
|
537
|
+
chainId,
|
|
397
538
|
verifyingContract: config.escrowAddress,
|
|
398
539
|
};
|
|
399
540
|
this.now = config.now ?? (() => Math.floor(Date.now() / 1000));
|
|
541
|
+
|
|
542
|
+
// Probe mode shares the chain binding with the invocation domain (same
|
|
543
|
+
// escrow, same chain) but a DIFFERENT EIP-712 domain name, so neither
|
|
544
|
+
// signature is ever valid as the other.
|
|
545
|
+
this.probeDomain = {
|
|
546
|
+
chainId,
|
|
547
|
+
verifyingContract: config.escrowAddress,
|
|
548
|
+
};
|
|
549
|
+
this.probeHeaderName = (
|
|
550
|
+
config.probe?.headerName ?? PROBE_CAPABILITY_HEADER_NAME
|
|
551
|
+
).toLowerCase();
|
|
552
|
+
if (this.probeHeaderName === this.headerName) {
|
|
553
|
+
throw new Error(
|
|
554
|
+
"the probe capability header must differ from the invocation capability header",
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
if (config.probe === undefined) {
|
|
558
|
+
this.probeSigners = undefined;
|
|
559
|
+
this.probeRateLimiter = undefined;
|
|
560
|
+
} else {
|
|
561
|
+
const reader =
|
|
562
|
+
config.probe.signerSetReader ??
|
|
563
|
+
(config.rpcUrl === undefined
|
|
564
|
+
? undefined
|
|
565
|
+
: createSdkOracleSignerSetReader({
|
|
566
|
+
escrowAddress: config.escrowAddress,
|
|
567
|
+
rpcUrl: config.rpcUrl,
|
|
568
|
+
chainId,
|
|
569
|
+
}));
|
|
570
|
+
if (reader === undefined) {
|
|
571
|
+
throw new Error(
|
|
572
|
+
"probe mode requires rpcUrl unless an explicit signerSetReader is supplied",
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
this.probeSigners = new CachedOracleSignerSet(
|
|
576
|
+
reader,
|
|
577
|
+
config.probe.signerCacheSeconds,
|
|
578
|
+
this.now,
|
|
579
|
+
);
|
|
580
|
+
this.probeRateLimiter =
|
|
581
|
+
config.probe.rateLimiter ??
|
|
582
|
+
new FixedWindowProbeRateLimiter(
|
|
583
|
+
config.probe.maxProbesPerMinute,
|
|
584
|
+
this.now,
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/** Lower-cased header name that carries the buyer's invocation capability. */
|
|
590
|
+
get capabilityHeaderName(): string {
|
|
591
|
+
return this.headerName;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/** Lower-cased header name that carries an oracle probe capability. */
|
|
595
|
+
get probeCapabilityHeaderName(): string {
|
|
596
|
+
return this.probeHeaderName;
|
|
400
597
|
}
|
|
401
598
|
|
|
402
599
|
async authorize(
|
|
403
600
|
request: CallerAuthRequestInput,
|
|
404
601
|
): Promise<CallerAuthDecision> {
|
|
405
602
|
const headerValue = lookupHeader(request.headers, this.headerName);
|
|
406
|
-
|
|
603
|
+
const hasCapabilityHeader =
|
|
604
|
+
headerValue !== undefined && headerValue.length > 0;
|
|
605
|
+
|
|
606
|
+
// ── Oracle probe path ────────────────────────────────────────────────
|
|
607
|
+
// Evaluated FIRST and returning unconditionally, so an authorized probe can
|
|
608
|
+
// never fall through into the stream branch. That ordering is the
|
|
609
|
+
// structural guarantee that a probe consumes no budget: no `stream` is ever
|
|
610
|
+
// produced on this path.
|
|
611
|
+
const probeHeaderValue = lookupHeader(
|
|
612
|
+
request.headers,
|
|
613
|
+
this.probeHeaderName,
|
|
614
|
+
);
|
|
615
|
+
if (probeHeaderValue !== undefined && probeHeaderValue.length > 0) {
|
|
616
|
+
const paymentHeader = lookupHeader(
|
|
617
|
+
request.headers,
|
|
618
|
+
PAYMENT_HEADER_REFUSED_ON_PROBES,
|
|
619
|
+
);
|
|
620
|
+
if (
|
|
621
|
+
hasCapabilityHeader ||
|
|
622
|
+
(paymentHeader !== undefined && paymentHeader.trim().length > 0)
|
|
623
|
+
) {
|
|
624
|
+
return deny("ambiguous-probe-mode", 400);
|
|
625
|
+
}
|
|
626
|
+
return this.authorizeProbe(probeHeaderValue, request);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (!hasCapabilityHeader) {
|
|
407
630
|
return deny("missing-capability-header", 403);
|
|
408
631
|
}
|
|
409
632
|
|
|
410
633
|
let capability: SignedInvocationCapability;
|
|
411
634
|
try {
|
|
412
|
-
capability = decodeCapabilityHeader(headerValue);
|
|
635
|
+
capability = decodeCapabilityHeader(headerValue as string);
|
|
413
636
|
} catch (error) {
|
|
414
637
|
if (error instanceof InvalidCapabilityHeaderError) {
|
|
415
638
|
return deny("malformed-capability-header", 403);
|
|
@@ -501,6 +724,95 @@ export class CallerAuthGateway {
|
|
|
501
724
|
return { authorized: true, stream: freshestView(views), capability };
|
|
502
725
|
}
|
|
503
726
|
|
|
727
|
+
/**
|
|
728
|
+
* Authorize one oracle probe. Cheapest, most local gates first; the on-chain
|
|
729
|
+
* signer-set read is last so an unsigned or replayed flood cannot amplify
|
|
730
|
+
* into RPC load. Every failure mode is distinct and fail-closed.
|
|
731
|
+
*/
|
|
732
|
+
private async authorizeProbe(
|
|
733
|
+
headerValue: string,
|
|
734
|
+
request: CallerAuthRequestInput,
|
|
735
|
+
): Promise<CallerAuthDecision> {
|
|
736
|
+
const signerSet = this.probeSigners;
|
|
737
|
+
const rateLimiter = this.probeRateLimiter;
|
|
738
|
+
if (signerSet === undefined || rateLimiter === undefined) {
|
|
739
|
+
// Probe mode is off. Say so precisely: the oracle distinguishes
|
|
740
|
+
// "unverifiable here" from "the paid route failed".
|
|
741
|
+
return deny("probe-not-enabled", 403);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
let capability: SignedProbeCapability;
|
|
745
|
+
try {
|
|
746
|
+
capability = decodeProbeCapabilityHeader(headerValue);
|
|
747
|
+
} catch (error) {
|
|
748
|
+
if (error instanceof InvalidProbeCapabilityHeaderError) {
|
|
749
|
+
return deny("malformed-probe-capability", 403);
|
|
750
|
+
}
|
|
751
|
+
throw error;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
const nowSeconds = this.now();
|
|
755
|
+
if (nowSeconds >= capability.expiry) {
|
|
756
|
+
return deny("probe-capability-expired", 403);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
let signer: Address;
|
|
760
|
+
try {
|
|
761
|
+
signer = await recoverProbeCapabilitySigner(
|
|
762
|
+
capability,
|
|
763
|
+
this.probeDomain,
|
|
764
|
+
capability.signature as Hex,
|
|
765
|
+
);
|
|
766
|
+
} catch {
|
|
767
|
+
return deny("probe-invalid-signature", 403);
|
|
768
|
+
}
|
|
769
|
+
if (signer.toLowerCase() !== capability.oracle.toLowerCase()) {
|
|
770
|
+
return deny("probe-invalid-signature", 403);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
if (capability.method.toUpperCase() !== request.method.toUpperCase()) {
|
|
774
|
+
return deny("probe-method-mismatch", 403);
|
|
775
|
+
}
|
|
776
|
+
if (
|
|
777
|
+
hashProbePath(request.path).toLowerCase() !==
|
|
778
|
+
capability.pathHash.toLowerCase()
|
|
779
|
+
) {
|
|
780
|
+
return deny("probe-path-mismatch", 403);
|
|
781
|
+
}
|
|
782
|
+
if (!this.serviceRefs.has(capability.serviceRef.toLowerCase())) {
|
|
783
|
+
return deny("probe-service-ref-mismatch", 403);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// Replay BEFORE the budget check: a captured capability replayed at volume
|
|
787
|
+
// must be rejected as a replay, not silently eat the honest probe budget.
|
|
788
|
+
// The SAME nonce cache as the buyer path, so a nonce can never be spent
|
|
789
|
+
// once as a probe and once as an invocation.
|
|
790
|
+
if (!this.nonceCache.consume(capability.nonce as Hex, capability.expiry)) {
|
|
791
|
+
return deny("probe-nonce-replayed", 403);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (!rateLimiter.tryConsume()) {
|
|
795
|
+
return deny("probe-rate-limited", 429);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const signers = await signerSet.resolve();
|
|
799
|
+
if (signers === undefined) {
|
|
800
|
+
return deny("probe-signer-set-unavailable", 403);
|
|
801
|
+
}
|
|
802
|
+
if (!signers.has(signer.toLowerCase())) {
|
|
803
|
+
return deny("probe-signer-not-authorized", 403);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
return {
|
|
807
|
+
authorized: true,
|
|
808
|
+
probe: {
|
|
809
|
+
oracle: signer,
|
|
810
|
+
serviceRef: capability.serviceRef as Hex,
|
|
811
|
+
expiry: capability.expiry,
|
|
812
|
+
},
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
|
|
504
816
|
/** Returns the denial reason this single view produces, or `undefined` if it authorizes. */
|
|
505
817
|
private evaluateView(
|
|
506
818
|
stream: GatewayStreamView,
|
|
@@ -524,6 +836,17 @@ export class CallerAuthGateway {
|
|
|
524
836
|
) {
|
|
525
837
|
return "service-ref-mismatch";
|
|
526
838
|
}
|
|
839
|
+
// Operator-signed price band (metrik-contracts#41). Checked against the
|
|
840
|
+
// STREAM's serviceRef, which equals the capability's by the check above.
|
|
841
|
+
const bounds = this.rateBounds.get(stream.serviceRef.toLowerCase());
|
|
842
|
+
if (bounds !== undefined) {
|
|
843
|
+
if (stream.ratePerSecond === undefined) {
|
|
844
|
+
return "stream-rate-unknown";
|
|
845
|
+
}
|
|
846
|
+
if (rateOutsideBounds(stream.ratePerSecond, bounds) !== undefined) {
|
|
847
|
+
return "stream-rate-out-of-bounds";
|
|
848
|
+
}
|
|
849
|
+
}
|
|
527
850
|
if (stream.status !== "active") {
|
|
528
851
|
return "stream-not-active";
|
|
529
852
|
}
|
|
@@ -569,7 +892,7 @@ function freshestView(views: readonly GatewayStreamView[]): GatewayStreamView {
|
|
|
569
892
|
|
|
570
893
|
function deny(
|
|
571
894
|
reason: CallerAuthDenialReason,
|
|
572
|
-
httpStatus: 402 | 403,
|
|
895
|
+
httpStatus: 400 | 402 | 403 | 429,
|
|
573
896
|
capability?: SignedInvocationCapability,
|
|
574
897
|
): CallerAuthDecision {
|
|
575
898
|
return {
|