x402-trust-layer 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/DEPLOY.md +53 -0
  3. package/Dockerfile +30 -0
  4. package/LICENSE +21 -0
  5. package/README.md +294 -0
  6. package/dist/agents/a2a-payment.d.ts +37 -0
  7. package/dist/agents/a2a-payment.js +105 -0
  8. package/dist/agents/agent-escrow.d.ts +30 -0
  9. package/dist/agents/agent-escrow.js +23 -0
  10. package/dist/agents/agent-verify.d.ts +15 -0
  11. package/dist/agents/agent-verify.js +112 -0
  12. package/dist/agents/api-router.d.ts +32 -0
  13. package/dist/agents/api-router.js +228 -0
  14. package/dist/agents/attestation-registry.d.ts +35 -0
  15. package/dist/agents/attestation-registry.js +76 -0
  16. package/dist/agents/audition-coach.d.ts +45 -0
  17. package/dist/agents/audition-coach.js +257 -0
  18. package/dist/agents/bedrock-bridge.d.ts +3 -0
  19. package/dist/agents/bedrock-bridge.js +60 -0
  20. package/dist/agents/budget-allocator.d.ts +24 -0
  21. package/dist/agents/budget-allocator.js +31 -0
  22. package/dist/agents/compliance-ledger.d.ts +66 -0
  23. package/dist/agents/compliance-ledger.js +80 -0
  24. package/dist/agents/dispute-resolver.d.ts +62 -0
  25. package/dist/agents/dispute-resolver.js +124 -0
  26. package/dist/agents/evidence-locker.d.ts +30 -0
  27. package/dist/agents/evidence-locker.js +47 -0
  28. package/dist/agents/facilitator-failover.d.ts +15 -0
  29. package/dist/agents/facilitator-failover.js +18 -0
  30. package/dist/agents/identity-gate.d.ts +20 -0
  31. package/dist/agents/identity-gate.js +79 -0
  32. package/dist/agents/mandate-compiler.d.ts +51 -0
  33. package/dist/agents/mandate-compiler.js +73 -0
  34. package/dist/agents/mandate-diff.d.ts +41 -0
  35. package/dist/agents/mandate-diff.js +170 -0
  36. package/dist/agents/market-buy-advisor.d.ts +65 -0
  37. package/dist/agents/market-buy-advisor.js +234 -0
  38. package/dist/agents/merchant-trust.d.ts +38 -0
  39. package/dist/agents/merchant-trust.js +171 -0
  40. package/dist/agents/mpp-session-broker.d.ts +27 -0
  41. package/dist/agents/mpp-session-broker.js +29 -0
  42. package/dist/agents/mpp-session-v2.d.ts +76 -0
  43. package/dist/agents/mpp-session-v2.js +269 -0
  44. package/dist/agents/payment-intent-compiler.d.ts +21 -0
  45. package/dist/agents/payment-intent-compiler.js +45 -0
  46. package/dist/agents/pipeline-execute.d.ts +40 -0
  47. package/dist/agents/pipeline-execute.js +100 -0
  48. package/dist/agents/pipeline-trust-v2.d.ts +31 -0
  49. package/dist/agents/pipeline-trust-v2.js +111 -0
  50. package/dist/agents/pre-x402-guard.d.ts +35 -0
  51. package/dist/agents/pre-x402-guard.js +84 -0
  52. package/dist/agents/quality-escrow-semantic.d.ts +88 -0
  53. package/dist/agents/quality-escrow-semantic.js +137 -0
  54. package/dist/agents/quality-escrow.d.ts +65 -0
  55. package/dist/agents/quality-escrow.js +104 -0
  56. package/dist/agents/quality-monitor.d.ts +32 -0
  57. package/dist/agents/quality-monitor.js +77 -0
  58. package/dist/agents/rail-optimizer.d.ts +33 -0
  59. package/dist/agents/rail-optimizer.js +133 -0
  60. package/dist/agents/receipt-auditor.d.ts +14 -0
  61. package/dist/agents/receipt-auditor.js +145 -0
  62. package/dist/agents/refund-arbiter.d.ts +24 -0
  63. package/dist/agents/refund-arbiter.js +70 -0
  64. package/dist/agents/research-brief.d.ts +14 -0
  65. package/dist/agents/research-brief.js +66 -0
  66. package/dist/agents/risk-gate.d.ts +11 -0
  67. package/dist/agents/risk-gate.js +78 -0
  68. package/dist/agents/settlement-graph.d.ts +16 -0
  69. package/dist/agents/settlement-graph.js +38 -0
  70. package/dist/agents/spend-governor.d.ts +2 -0
  71. package/dist/agents/spend-governor.js +70 -0
  72. package/dist/agents/trust-network.d.ts +138 -0
  73. package/dist/agents/trust-network.js +244 -0
  74. package/dist/agents/x402-proxy.d.ts +32 -0
  75. package/dist/agents/x402-proxy.js +90 -0
  76. package/dist/client/demo-alchemy-live.d.ts +1 -0
  77. package/dist/client/demo-alchemy-live.js +226 -0
  78. package/dist/client/demo-tail.d.ts +1 -0
  79. package/dist/client/demo-tail.js +100 -0
  80. package/dist/client/demo.d.ts +1 -0
  81. package/dist/client/demo.js +293 -0
  82. package/dist/config.d.ts +94 -0
  83. package/dist/config.js +223 -0
  84. package/dist/index.d.ts +1 -0
  85. package/dist/index.js +389 -0
  86. package/dist/lib/agent-response.d.ts +14 -0
  87. package/dist/lib/agent-response.js +13 -0
  88. package/dist/lib/agentic-gateways.d.ts +5 -0
  89. package/dist/lib/agentic-gateways.js +15 -0
  90. package/dist/lib/agentic-probes.d.ts +10 -0
  91. package/dist/lib/agentic-probes.js +49 -0
  92. package/dist/lib/alchemy-x402-fetch.d.ts +16 -0
  93. package/dist/lib/alchemy-x402-fetch.js +95 -0
  94. package/dist/lib/apply-verifier-body.d.ts +7 -0
  95. package/dist/lib/apply-verifier-body.js +179 -0
  96. package/dist/lib/attestation.d.ts +30 -0
  97. package/dist/lib/attestation.js +107 -0
  98. package/dist/lib/bazaar-extension.d.ts +15 -0
  99. package/dist/lib/bazaar-extension.js +265 -0
  100. package/dist/lib/bazaar.d.ts +100 -0
  101. package/dist/lib/bazaar.js +341 -0
  102. package/dist/lib/certified-sellers.d.ts +41 -0
  103. package/dist/lib/certified-sellers.js +129 -0
  104. package/dist/lib/chains.d.ts +20 -0
  105. package/dist/lib/chains.js +78 -0
  106. package/dist/lib/db-persistence.d.ts +7 -0
  107. package/dist/lib/db-persistence.js +65 -0
  108. package/dist/lib/db.d.ts +5 -0
  109. package/dist/lib/db.js +113 -0
  110. package/dist/lib/discovery-page.d.ts +2 -0
  111. package/dist/lib/discovery-page.js +71 -0
  112. package/dist/lib/ecosystem-telemetry.d.ts +20 -0
  113. package/dist/lib/ecosystem-telemetry.js +80 -0
  114. package/dist/lib/erc8004/agent-card.d.ts +34 -0
  115. package/dist/lib/erc8004/agent-card.js +151 -0
  116. package/dist/lib/erc8004/cache.d.ts +3 -0
  117. package/dist/lib/erc8004/cache.js +17 -0
  118. package/dist/lib/erc8004/constants.d.ts +22 -0
  119. package/dist/lib/erc8004/constants.js +35 -0
  120. package/dist/lib/erc8004/registry.d.ts +19 -0
  121. package/dist/lib/erc8004/registry.js +171 -0
  122. package/dist/lib/erc8004/resolve-agent.d.ts +7 -0
  123. package/dist/lib/erc8004/resolve-agent.js +70 -0
  124. package/dist/lib/erc8004/trust-score.d.ts +33 -0
  125. package/dist/lib/erc8004/trust-score.js +136 -0
  126. package/dist/lib/escrow-ledger.d.ts +14 -0
  127. package/dist/lib/escrow-ledger.js +54 -0
  128. package/dist/lib/escrow-unified.d.ts +15 -0
  129. package/dist/lib/escrow-unified.js +28 -0
  130. package/dist/lib/facilitator-extra.d.ts +13 -0
  131. package/dist/lib/facilitator-extra.js +52 -0
  132. package/dist/lib/facilitators.d.ts +20 -0
  133. package/dist/lib/facilitators.js +89 -0
  134. package/dist/lib/host-policy.d.ts +4 -0
  135. package/dist/lib/host-policy.js +20 -0
  136. package/dist/lib/idempotency.d.ts +4 -0
  137. package/dist/lib/idempotency.js +120 -0
  138. package/dist/lib/ledger.d.ts +2 -0
  139. package/dist/lib/ledger.js +17 -0
  140. package/dist/lib/logger.d.ts +6 -0
  141. package/dist/lib/logger.js +24 -0
  142. package/dist/lib/mandate-vc.d.ts +20 -0
  143. package/dist/lib/mandate-vc.js +25 -0
  144. package/dist/lib/mandate.d.ts +44 -0
  145. package/dist/lib/mandate.js +190 -0
  146. package/dist/lib/marketplace.d.ts +7 -0
  147. package/dist/lib/marketplace.js +127 -0
  148. package/dist/lib/migrations.d.ts +2 -0
  149. package/dist/lib/migrations.js +130 -0
  150. package/dist/lib/nonce-store.d.ts +6 -0
  151. package/dist/lib/nonce-store.js +109 -0
  152. package/dist/lib/openapi-agentcash.d.ts +5 -0
  153. package/dist/lib/openapi-agentcash.js +288 -0
  154. package/dist/lib/openapi-meta.d.ts +5 -0
  155. package/dist/lib/openapi-meta.js +235 -0
  156. package/dist/lib/otel.d.ts +2 -0
  157. package/dist/lib/otel.js +25 -0
  158. package/dist/lib/paid-resource-url.d.ts +6 -0
  159. package/dist/lib/paid-resource-url.js +47 -0
  160. package/dist/lib/parse-with-verifier-fallback.d.ts +3 -0
  161. package/dist/lib/parse-with-verifier-fallback.js +13 -0
  162. package/dist/lib/payment-request-context.d.ts +10 -0
  163. package/dist/lib/payment-request-context.js +5 -0
  164. package/dist/lib/payment-response.d.ts +13 -0
  165. package/dist/lib/payment-response.js +39 -0
  166. package/dist/lib/payto-guard.d.ts +10 -0
  167. package/dist/lib/payto-guard.js +20 -0
  168. package/dist/lib/probe.d.ts +29 -0
  169. package/dist/lib/probe.js +157 -0
  170. package/dist/lib/problem-detail.d.ts +10 -0
  171. package/dist/lib/problem-detail.js +14 -0
  172. package/dist/lib/rate-limit.d.ts +12 -0
  173. package/dist/lib/rate-limit.js +126 -0
  174. package/dist/lib/replay-middleware.d.ts +3 -0
  175. package/dist/lib/replay-middleware.js +27 -0
  176. package/dist/lib/response-guard.d.ts +5 -0
  177. package/dist/lib/response-guard.js +40 -0
  178. package/dist/lib/safe-fetch.d.ts +5 -0
  179. package/dist/lib/safe-fetch.js +19 -0
  180. package/dist/lib/security.d.ts +13 -0
  181. package/dist/lib/security.js +61 -0
  182. package/dist/lib/semantic-judge.d.ts +14 -0
  183. package/dist/lib/semantic-judge.js +107 -0
  184. package/dist/lib/semantic-judge.test.d.ts +1 -0
  185. package/dist/lib/semantic-judge.test.js +11 -0
  186. package/dist/lib/ssrf.d.ts +10 -0
  187. package/dist/lib/ssrf.js +130 -0
  188. package/dist/lib/ssrf.test.d.ts +1 -0
  189. package/dist/lib/ssrf.test.js +16 -0
  190. package/dist/lib/suite-catalog.d.ts +83 -0
  191. package/dist/lib/suite-catalog.js +131 -0
  192. package/dist/lib/telemetry.d.ts +5 -0
  193. package/dist/lib/telemetry.js +37 -0
  194. package/dist/lib/verifier-fast-path.d.ts +10 -0
  195. package/dist/lib/verifier-fast-path.js +44 -0
  196. package/dist/lib/verifier-probe-protocol.d.ts +7 -0
  197. package/dist/lib/verifier-probe-protocol.js +115 -0
  198. package/dist/lib/verify-examples.d.ts +2 -0
  199. package/dist/lib/verify-examples.js +438 -0
  200. package/dist/lib/version.d.ts +2 -0
  201. package/dist/lib/version.js +2 -0
  202. package/dist/lib/webhook-auth.d.ts +3 -0
  203. package/dist/lib/webhook-auth.js +34 -0
  204. package/dist/lib/webhook-routes.d.ts +2 -0
  205. package/dist/lib/webhook-routes.js +112 -0
  206. package/dist/lib/webhooks.d.ts +23 -0
  207. package/dist/lib/webhooks.js +123 -0
  208. package/dist/lib/webhooks.test.d.ts +1 -0
  209. package/dist/lib/webhooks.test.js +16 -0
  210. package/dist/lib/x402-client-options.d.ts +28 -0
  211. package/dist/lib/x402-client-options.js +138 -0
  212. package/dist/lib/x402-headers.d.ts +10 -0
  213. package/dist/lib/x402-headers.js +27 -0
  214. package/dist/lib/x402-paid.d.ts +5 -0
  215. package/dist/lib/x402-paid.js +252 -0
  216. package/dist/lib/x402-payment-replay.d.ts +22 -0
  217. package/dist/lib/x402-payment-replay.js +57 -0
  218. package/dist/lib/x402gle-host-verify.d.ts +3 -0
  219. package/dist/lib/x402gle-host-verify.js +27 -0
  220. package/dist/protocol/agent-passport.d.ts +34 -0
  221. package/dist/protocol/agent-passport.js +44 -0
  222. package/dist/protocol/compliance-v2.d.ts +21 -0
  223. package/dist/protocol/compliance-v2.js +19 -0
  224. package/dist/protocol/credit-bureau.d.ts +18 -0
  225. package/dist/protocol/credit-bureau.js +44 -0
  226. package/dist/protocol/crypto.d.ts +6 -0
  227. package/dist/protocol/crypto.js +41 -0
  228. package/dist/protocol/escrow-fsm.d.ts +33 -0
  229. package/dist/protocol/escrow-fsm.js +99 -0
  230. package/dist/protocol/fraud-engine.d.ts +28 -0
  231. package/dist/protocol/fraud-engine.js +77 -0
  232. package/dist/protocol/observability.d.ts +14 -0
  233. package/dist/protocol/observability.js +21 -0
  234. package/dist/protocol/pipeline-full-trust.d.ts +40 -0
  235. package/dist/protocol/pipeline-full-trust.js +96 -0
  236. package/dist/protocol/proof-of-execution.d.ts +36 -0
  237. package/dist/protocol/proof-of-execution.js +48 -0
  238. package/dist/protocol/reasoning-audit.d.ts +27 -0
  239. package/dist/protocol/reasoning-audit.js +51 -0
  240. package/dist/protocol/replay-guard.d.ts +28 -0
  241. package/dist/protocol/replay-guard.js +76 -0
  242. package/dist/protocol/replay-guard.test.d.ts +1 -0
  243. package/dist/protocol/replay-guard.test.js +10 -0
  244. package/dist/protocol/security-audit.d.ts +18 -0
  245. package/dist/protocol/security-audit.js +45 -0
  246. package/dist/protocol/store.d.ts +5 -0
  247. package/dist/protocol/store.js +59 -0
  248. package/dist/protocol/threat-catalog.d.ts +13 -0
  249. package/dist/protocol/threat-catalog.js +75 -0
  250. package/dist/protocol/trust-oracle.d.ts +23 -0
  251. package/dist/protocol/trust-oracle.js +30 -0
  252. package/dist/protocol/trust-score-v2.d.ts +33 -0
  253. package/dist/protocol/trust-score-v2.js +78 -0
  254. package/dist/protocol/zk-proofs.d.ts +24 -0
  255. package/dist/protocol/zk-proofs.js +32 -0
  256. package/dist/routes/a2a-agent-card.d.ts +3 -0
  257. package/dist/routes/a2a-agent-card.js +28 -0
  258. package/dist/routes/catalog.d.ts +5 -0
  259. package/dist/routes/catalog.js +47 -0
  260. package/dist/routes/register-all.d.ts +3 -0
  261. package/dist/routes/register-all.js +1240 -0
  262. package/dist/routes/schemas.d.ts +83 -0
  263. package/dist/routes/schemas.js +38 -0
  264. package/dist/routes/shared.d.ts +16 -0
  265. package/dist/routes/shared.js +27 -0
  266. package/dist/routes-protocol.d.ts +10 -0
  267. package/dist/routes-protocol.js +322 -0
  268. package/dist/routes.d.ts +2 -0
  269. package/dist/routes.js +2 -0
  270. package/dist/types.d.ts +66 -0
  271. package/dist/types.js +1 -0
  272. package/openapi.json +7940 -0
  273. package/package.json +124 -0
  274. package/public/.well-known/ai-plugin.json +12 -0
  275. package/public/assets/aegis-logo-blue.png +0 -0
  276. package/public/assets/aegis-logo-gold.png +0 -0
  277. package/public/assets/aegis-logo-green.png +0 -0
  278. package/public/assets/aegis-logo-purple.png +0 -0
  279. package/public/assets/aegis-logo-red.png +0 -0
  280. package/public/assets/aegis-logo-white.png +0 -0
  281. package/public/assets/aegis-logo.png +0 -0
  282. package/public/assets/x402-trustlayer-logo.png +0 -0
  283. package/public/assets/x402-trustlayer-logo.svg +5 -0
  284. package/public/data/agents.json +1528 -0
  285. package/public/index.html +198 -0
  286. package/public/landing.css +342 -0
  287. package/public/landing.js +405 -0
  288. package/public/llms-full.txt +582 -0
  289. package/public/llms.txt +132 -0
  290. package/public/skill.md +135 -0
  291. package/railway.toml +9 -0
  292. package/scripts/docker-entrypoint.sh +7 -0
  293. package/scripts/patch-facilitator-timeout.mjs +61 -0
@@ -0,0 +1,123 @@
1
+ import { createHmac, randomBytes } from "node:crypto";
2
+ import { readFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { db } from "./db.js";
5
+ import { assertSafeOutboundUrl, UnsafeUrlError } from "./ssrf.js";
6
+ const LEGACY_STORE_PATH = join(process.cwd(), "data", "webhooks.json");
7
+ const selectActive = db.prepare("SELECT * FROM webhook_subscriptions WHERE active = 1 ORDER BY created_at DESC");
8
+ const selectByIdFleet = db.prepare("SELECT * FROM webhook_subscriptions WHERE id = ? AND fleet_id = ?");
9
+ const insertSub = db.prepare(`
10
+ INSERT INTO webhook_subscriptions (id, fleet_id, url, events, secret, created_at, active)
11
+ VALUES (?, ?, ?, ?, ?, ?, 1)
12
+ `);
13
+ const deactivateSub = db.prepare("UPDATE webhook_subscriptions SET active = 0 WHERE id = ? AND fleet_id = ?");
14
+ function rowToSub(row) {
15
+ return {
16
+ id: row.id,
17
+ fleetId: row.fleet_id,
18
+ url: row.url,
19
+ events: JSON.parse(row.events),
20
+ secret: row.secret,
21
+ createdAt: row.created_at,
22
+ active: row.active === 1,
23
+ };
24
+ }
25
+ async function migrateLegacyOnce() {
26
+ const count = db.prepare("SELECT COUNT(*) AS c FROM webhook_subscriptions").get()
27
+ .c;
28
+ if (count > 0)
29
+ return;
30
+ try {
31
+ const raw = await readFile(LEGACY_STORE_PATH, "utf8");
32
+ const parsed = JSON.parse(raw);
33
+ for (const s of parsed.subscriptions ?? []) {
34
+ insertSub.run(s.id, s.fleetId, s.url, JSON.stringify(s.events), s.secret, s.createdAt);
35
+ if (!s.active)
36
+ deactivateSub.run(s.id, s.fleetId);
37
+ }
38
+ }
39
+ catch {
40
+ /* no legacy file */
41
+ }
42
+ }
43
+ function signPayload(secret, body) {
44
+ return `sha256=${createHmac("sha256", secret).update(body).digest("hex")}`;
45
+ }
46
+ /** Reject SSRF targets (localhost, metadata, private IPs) for outbound webhook delivery. */
47
+ export function assertValidWebhookUrl(url) {
48
+ assertSafeOutboundUrl(url);
49
+ if (!url.startsWith("https://")) {
50
+ throw new UnsafeUrlError("Webhook URL must use HTTPS");
51
+ }
52
+ }
53
+ export async function registerWebhook(input) {
54
+ await migrateLegacyOnce();
55
+ assertValidWebhookUrl(input.url);
56
+ const sub = {
57
+ id: `wh_${randomBytes(8).toString("hex")}`,
58
+ fleetId: input.fleetId,
59
+ url: input.url,
60
+ events: input.events,
61
+ secret: randomBytes(24).toString("hex"),
62
+ createdAt: new Date().toISOString(),
63
+ active: true,
64
+ };
65
+ insertSub.run(sub.id, sub.fleetId, sub.url, JSON.stringify(sub.events), sub.secret, sub.createdAt);
66
+ return sub;
67
+ }
68
+ export async function listWebhooks(fleetId) {
69
+ await migrateLegacyOnce();
70
+ const subs = selectActive.all().map(rowToSub);
71
+ if (!fleetId)
72
+ return subs;
73
+ return subs.filter((s) => s.fleetId === fleetId);
74
+ }
75
+ export async function deactivateWebhook(id, fleetId) {
76
+ await migrateLegacyOnce();
77
+ const row = selectByIdFleet.get(id, fleetId);
78
+ if (!row)
79
+ return false;
80
+ deactivateSub.run(id, fleetId);
81
+ return true;
82
+ }
83
+ export async function dispatchWebhooks(event, payload, fleetId) {
84
+ const subs = (await listWebhooks(fleetId)).filter((s) => s.events.includes(event));
85
+ let delivered = 0;
86
+ let failed = 0;
87
+ const body = JSON.stringify({
88
+ event,
89
+ timestamp: new Date().toISOString(),
90
+ ...payload,
91
+ });
92
+ await Promise.all(subs.map(async (sub) => {
93
+ try {
94
+ assertValidWebhookUrl(sub.url);
95
+ const controller = new AbortController();
96
+ const timer = setTimeout(() => controller.abort(), 8_000);
97
+ const res = await fetch(sub.url, {
98
+ method: "POST",
99
+ redirect: "manual",
100
+ headers: {
101
+ "content-type": "application/json",
102
+ "x-trust-layer-event": event,
103
+ "x-hub-signature-256": signPayload(sub.secret, body),
104
+ },
105
+ body,
106
+ signal: controller.signal,
107
+ });
108
+ if (res.status >= 300 && res.status < 400) {
109
+ failed++;
110
+ return;
111
+ }
112
+ clearTimeout(timer);
113
+ if (res.ok)
114
+ delivered++;
115
+ else
116
+ failed++;
117
+ }
118
+ catch {
119
+ failed++;
120
+ }
121
+ }));
122
+ return { delivered, failed };
123
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createHmac } from "node:crypto";
3
+ function signPayload(secret, body) {
4
+ return `sha256=${createHmac("sha256", secret).update(body).digest("hex")}`;
5
+ }
6
+ describe("webhook HMAC", () => {
7
+ it("uses HMAC not plain hash", () => {
8
+ const secret = "test-secret";
9
+ const body = '{"event":"guard.allowed"}';
10
+ const sig = signPayload(secret, body);
11
+ expect(sig.startsWith("sha256=")).toBe(true);
12
+ const expected = signPayload(secret, body);
13
+ expect(sig).toBe(expected);
14
+ expect(signPayload("other", body)).not.toBe(sig);
15
+ });
16
+ });
@@ -0,0 +1,28 @@
1
+ import { type WrapFetchOptions } from "@dexterai/x402/client";
2
+ /** Public mainnet RPC — Dexter proxy can return shapes web3.js 1.98 cannot parse */
3
+ export declare const DEFAULT_SOLANA_RPC_URL = "https://api.mainnet-beta.solana.com";
4
+ /**
5
+ * Dexter createEvmKeypairWallet() only exposes signTypedData — Base Permit2 also needs
6
+ * sendTransaction for the one-time USDC → Permit2 approval tx.
7
+ */
8
+ export declare function createEvmPermit2CapableWallet(privateKey: string, preferredNetwork?: string): Promise<{
9
+ address: `0x${string}`;
10
+ signTypedData: (params: Parameters<(<const typedData extends import("viem").TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: import("viem").TypedDataDefinition<typedData, primaryType>) => Promise<import("viem").Hex>)>[0]) => Promise<`0x${string}`>;
11
+ sendTransaction: (tx: {
12
+ to: string;
13
+ data?: string;
14
+ value?: bigint;
15
+ }) => Promise<`0x${string}`>;
16
+ }>;
17
+ /** Paid fetch with Permit2-capable EVM wallet (use instead of wrapFetch + evmPrivateKey). */
18
+ export declare function buildX402Fetch(fetchImpl?: typeof fetch, overrides?: Partial<WrapFetchOptions>): Promise<typeof fetch>;
19
+ /** Shared wrapFetch options for demo, scripts, and integrators */
20
+ export declare function buildWrapFetchOptions(overrides?: Partial<WrapFetchOptions>): WrapFetchOptions;
21
+ export declare function assertPayerKeys(): {
22
+ evm: boolean;
23
+ solana: boolean;
24
+ };
25
+ /**
26
+ * Receive wallets (PAY_TO_*) must not be used as demo payers — facilitator rejects self-payment.
27
+ */
28
+ export declare function assertDemoPayerNotReceiveWallet(): Promise<void>;
@@ -0,0 +1,138 @@
1
+ import { createEvmKeypairWallet, createKeypairWallet, createX402Client, } from "@dexterai/x402/client";
2
+ import { createWalletClient, http } from "viem";
3
+ import { privateKeyToAccount } from "viem/accounts";
4
+ import { base, polygon } from "viem/chains";
5
+ import { CHAIN_IDS } from "./chains.js";
6
+ const EVM_CHAIN_BY_CAIP2 = {
7
+ [CHAIN_IDS.base]: base,
8
+ [CHAIN_IDS.polygon]: polygon,
9
+ };
10
+ /** Public mainnet RPC — Dexter proxy can return shapes web3.js 1.98 cannot parse */
11
+ export const DEFAULT_SOLANA_RPC_URL = "https://api.mainnet-beta.solana.com";
12
+ function preferredPaymentNetwork() {
13
+ const explicit = process.env.X402_PREFERRED_NETWORK?.trim();
14
+ if (explicit)
15
+ return explicit;
16
+ const nets = (process.env.NETWORKS ?? process.env.NETWORK ?? "base,solana").toLowerCase();
17
+ if (nets.split(",").map((s) => s.trim()).includes("base")) {
18
+ return CHAIN_IDS.base;
19
+ }
20
+ return CHAIN_IDS.solana;
21
+ }
22
+ function evmRpcUrl(network) {
23
+ if (network === CHAIN_IDS.polygon) {
24
+ return process.env.POLYGON_RPC_URL?.trim() || "https://polygon-rpc.com";
25
+ }
26
+ return process.env.BASE_RPC_URL?.trim() || "https://mainnet.base.org";
27
+ }
28
+ /**
29
+ * Dexter createEvmKeypairWallet() only exposes signTypedData — Base Permit2 also needs
30
+ * sendTransaction for the one-time USDC → Permit2 approval tx.
31
+ */
32
+ export async function createEvmPermit2CapableWallet(privateKey, preferredNetwork = CHAIN_IDS.base) {
33
+ const normalizedKey = privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`;
34
+ const account = privateKeyToAccount(normalizedKey);
35
+ const chain = EVM_CHAIN_BY_CAIP2[preferredNetwork] ?? base;
36
+ const client = createWalletClient({
37
+ account,
38
+ chain,
39
+ transport: http(evmRpcUrl(preferredNetwork)),
40
+ });
41
+ return {
42
+ address: account.address,
43
+ signTypedData: (params) => account.signTypedData(params),
44
+ sendTransaction: async (tx) => {
45
+ return client.sendTransaction({
46
+ account,
47
+ chain,
48
+ to: tx.to,
49
+ data: tx.data,
50
+ value: tx.value ?? 0n,
51
+ });
52
+ },
53
+ };
54
+ }
55
+ /** Paid fetch with Permit2-capable EVM wallet (use instead of wrapFetch + evmPrivateKey). */
56
+ export async function buildX402Fetch(fetchImpl = fetch, overrides) {
57
+ const opts = buildWrapFetchOptions(overrides);
58
+ const evmKey = process.env.EVM_PRIVATE_KEY?.trim();
59
+ const solKey = process.env.SOLANA_PRIVATE_KEY?.trim();
60
+ const wallets = {};
61
+ if (solKey)
62
+ wallets.solana = await createKeypairWallet(solKey);
63
+ if (evmKey) {
64
+ const evmNet = opts.preferredNetwork ?? CHAIN_IDS.base;
65
+ wallets.evm = await createEvmPermit2CapableWallet(evmKey, evmNet);
66
+ }
67
+ const client = createX402Client({
68
+ wallets,
69
+ preferredNetwork: opts.preferredNetwork,
70
+ rpcUrls: opts.rpcUrls,
71
+ maxAmountAtomic: opts.maxAmountAtomic,
72
+ verbose: opts.verbose,
73
+ fetch: fetchImpl,
74
+ onPaymentRequired: opts.onPaymentRequired,
75
+ accessPass: opts.accessPass,
76
+ });
77
+ return client.fetch.bind(client);
78
+ }
79
+ /** Shared wrapFetch options for demo, scripts, and integrators */
80
+ export function buildWrapFetchOptions(overrides) {
81
+ const evmKey = process.env.EVM_PRIVATE_KEY?.trim();
82
+ const solKey = process.env.SOLANA_PRIVATE_KEY?.trim();
83
+ const solRpc = process.env.SOLANA_RPC_URL?.trim() || DEFAULT_SOLANA_RPC_URL;
84
+ const opts = {
85
+ rpcUrls: {
86
+ [CHAIN_IDS.solana]: solRpc,
87
+ },
88
+ };
89
+ if (solKey)
90
+ opts.walletPrivateKey = solKey;
91
+ if (evmKey)
92
+ opts.evmPrivateKey = evmKey;
93
+ if (evmKey && solKey) {
94
+ opts.preferredNetwork = preferredPaymentNetwork();
95
+ }
96
+ else if (evmKey) {
97
+ opts.preferredNetwork = CHAIN_IDS.base;
98
+ }
99
+ if (process.env.X402_VERBOSE === "1" || overrides?.verbose)
100
+ opts.verbose = true;
101
+ return { ...opts, ...overrides };
102
+ }
103
+ export function assertPayerKeys() {
104
+ const evm = !!process.env.EVM_PRIVATE_KEY?.trim();
105
+ const solana = !!process.env.SOLANA_PRIVATE_KEY?.trim();
106
+ if (!evm && !solana) {
107
+ throw new Error("Set EVM_PRIVATE_KEY and/or SOLANA_PRIVATE_KEY in .env (demo payer only — never commit)");
108
+ }
109
+ return { evm, solana };
110
+ }
111
+ /**
112
+ * Receive wallets (PAY_TO_*) must not be used as demo payers — facilitator rejects self-payment.
113
+ */
114
+ export async function assertDemoPayerNotReceiveWallet() {
115
+ const payToSol = process.env.PAY_TO_ADDRESS?.trim();
116
+ const payToEvm = (process.env.PAY_TO_EVM?.trim() ?? "").toLowerCase();
117
+ const solKey = process.env.SOLANA_PRIVATE_KEY?.trim();
118
+ const evmKey = process.env.EVM_PRIVATE_KEY?.trim();
119
+ if (solKey && payToSol) {
120
+ const wallet = await createKeypairWallet(solKey);
121
+ const pubkey = wallet.publicKey;
122
+ if (!pubkey) {
123
+ throw new Error("SOLANA_PRIVATE_KEY could not be loaded — check key format");
124
+ }
125
+ const payer = pubkey.toBase58();
126
+ if (payer === payToSol) {
127
+ throw new Error(`SOLANA_PRIVATE_KEY is your seller receive wallet (${payToSol}). ` +
128
+ "Use a different Solana key with USDC for demo payments, or set EVM_PRIVATE_KEY to a separate Base wallet.");
129
+ }
130
+ }
131
+ if (evmKey && payToEvm) {
132
+ const wallet = await createEvmKeypairWallet(evmKey);
133
+ if (wallet.address.toLowerCase() === payToEvm) {
134
+ throw new Error(`EVM_PRIVATE_KEY is your seller receive wallet (${payToEvm}). ` +
135
+ "Use a different Base wallet with USDC for demo payments.");
136
+ }
137
+ }
138
+ }
@@ -0,0 +1,10 @@
1
+ import type { Request, Response } from "express";
2
+ /** x402 HTTP transport v2 header names (December 2025+). */
3
+ export declare const PAYMENT_SIGNATURE = "payment-signature";
4
+ export declare const PAYMENT_RESPONSE = "PAYMENT-RESPONSE";
5
+ export declare const PAYMENT_REQUIRED = "PAYMENT-REQUIRED";
6
+ /** Read client payment signature (v2 primary, v1 legacy for probes). */
7
+ export declare function getPaymentSignatureHeader(req: Request): string | undefined;
8
+ export declare function hasPaymentSignatureHeader(req: Request): boolean;
9
+ /** Normalize settlement response header reads (v2 + legacy). */
10
+ export declare function getPaymentResponseHeader(res: Response): string | undefined;
@@ -0,0 +1,27 @@
1
+ /** x402 HTTP transport v2 header names (December 2025+). */
2
+ export const PAYMENT_SIGNATURE = "payment-signature";
3
+ export const PAYMENT_RESPONSE = "PAYMENT-RESPONSE";
4
+ export const PAYMENT_REQUIRED = "PAYMENT-REQUIRED";
5
+ /** Read client payment signature (v2 primary, v1 legacy for probes). */
6
+ export function getPaymentSignatureHeader(req) {
7
+ const raw = req.headers[PAYMENT_SIGNATURE] ??
8
+ req.headers["x-payment"] ??
9
+ req.headers["x402-payment"];
10
+ if (typeof raw === "string")
11
+ return raw;
12
+ if (Array.isArray(raw))
13
+ return raw[0];
14
+ return undefined;
15
+ }
16
+ export function hasPaymentSignatureHeader(req) {
17
+ return Boolean(getPaymentSignatureHeader(req));
18
+ }
19
+ /** Normalize settlement response header reads (v2 + legacy). */
20
+ export function getPaymentResponseHeader(res) {
21
+ const h = res.getHeader(PAYMENT_RESPONSE) ?? res.getHeader("payment-response");
22
+ if (typeof h === "string")
23
+ return h;
24
+ if (Array.isArray(h))
25
+ return h[0];
26
+ return undefined;
27
+ }
@@ -0,0 +1,5 @@
1
+ import { x402Middleware } from "@dexterai/x402/server";
2
+ type PaidMw = ReturnType<typeof x402Middleware>;
3
+ /** x402 middleware with PAYMENT-REQUIRED header + Bazaar extension for Agentic Market */
4
+ export declare function createPaidMiddleware(): (amount: string, description: string) => PaidMw;
5
+ export {};
@@ -0,0 +1,252 @@
1
+ import { USDC_BASE, x402Middleware } from "@dexterai/x402/server";
2
+ import { config, isAllowedNetwork } from "../config.js";
3
+ import { CHAIN_IDS, usdcAssetForCaip2 } from "./chains.js";
4
+ import { bazaarExtensionForRequest } from "./bazaar-extension.js";
5
+ import { resolvePaidResourceUrl } from "./paid-resource-url.js";
6
+ import { getPaymentSignatureHeader, PAYMENT_REQUIRED } from "./x402-headers.js";
7
+ import { extractNonceFromPaymentHeader, isNonceAlreadyUsed, markNonceUsed, isIdempotencyKeyConsumed, markIdempotencyKeyUsed, } from "./x402-payment-replay.js";
8
+ import { incCounter } from "./telemetry.js";
9
+ import { isTrustedPayTo } from "./payto-guard.js";
10
+ import { enrichAcceptFromFacilitator, ensureFacilitatorExtras } from "./facilitator-extra.js";
11
+ import { paymentRequestAls } from "./payment-request-context.js";
12
+ import { guardResponseWrites, isResponseLocked, lockResponse } from "./response-guard.js";
13
+ function resolvePayTo() {
14
+ if (!config.payToEvm)
15
+ return config.payTo;
16
+ const map = {
17
+ [CHAIN_IDS.solana]: config.payTo,
18
+ [CHAIN_IDS.base]: config.payToEvm,
19
+ };
20
+ for (const chain of config.chains) {
21
+ if (chain === "solana" || chain === "solana_devnet") {
22
+ map[CHAIN_IDS[chain]] = config.payTo;
23
+ }
24
+ else if (chain === "base" || chain === "polygon" || chain === "base_sepolia") {
25
+ map[CHAIN_IDS[chain]] = config.payToEvm;
26
+ }
27
+ }
28
+ return map;
29
+ }
30
+ function syncResourceUrl(parsed, req) {
31
+ const resource = parsed.resource;
32
+ if (resource) {
33
+ resource.url = resolvePaidResourceUrl(req);
34
+ }
35
+ }
36
+ const PAID_REQUEST_BUDGET_MS = Number(process.env.PAID_REQUEST_TIMEOUT_MS ?? 70_000);
37
+ const baseMiddleware = {
38
+ payTo: resolvePayTo(),
39
+ facilitatorUrl: config.facilitatorUrl,
40
+ network: [...config.networks],
41
+ onSettlement: (info) => {
42
+ console.log(`[x402] settled tx=${info.transaction} payer=${info.payer} network=${info.network}`);
43
+ },
44
+ };
45
+ function withPaidRequestBudget(handler) {
46
+ return async (req, res, next) => {
47
+ guardResponseWrites(res);
48
+ let done = false;
49
+ const timer = setTimeout(() => {
50
+ if (done || isResponseLocked(res))
51
+ return;
52
+ console.error("[x402-paid] request budget exceeded", PAID_REQUEST_BUDGET_MS, "ms", req.path);
53
+ lockResponse(res);
54
+ res.status(504).json({
55
+ error: "Payment settlement or handler timed out",
56
+ code: "gateway_timeout",
57
+ budgetMs: PAID_REQUEST_BUDGET_MS,
58
+ hint: "Set X402_FACILITATOR_TIMEOUT_MS=25000 and X402_FACILITATOR_MAX_RETRIES=1 on Railway; or pay on Solana.",
59
+ });
60
+ }, PAID_REQUEST_BUDGET_MS);
61
+ try {
62
+ await handler(req, res, next);
63
+ }
64
+ catch (err) {
65
+ if (!isResponseLocked(res))
66
+ next(err);
67
+ else
68
+ console.error("[x402-paid] error after response locked:", err);
69
+ }
70
+ finally {
71
+ done = true;
72
+ clearTimeout(timer);
73
+ }
74
+ };
75
+ }
76
+ function attachBazaarToPayload(parsed, req) {
77
+ const bazaar = bazaarExtensionForRequest(req);
78
+ const extensions = parsed.extensions ?? {};
79
+ parsed.extensions = { ...extensions, bazaar };
80
+ if (parsed.x402Version == null)
81
+ parsed.x402Version = 2;
82
+ }
83
+ function normalizeAccepts(parsed) {
84
+ const accepts = parsed.accepts;
85
+ if (!Array.isArray(accepts))
86
+ return;
87
+ const chainOrder = config.chains.map((c) => CHAIN_IDS[c]);
88
+ for (const accept of accepts) {
89
+ if (!accept.network)
90
+ continue;
91
+ if (!isAllowedNetwork(accept.network))
92
+ continue;
93
+ if (accept.payTo && !isTrustedPayTo(accept.payTo))
94
+ continue;
95
+ enrichAcceptFromFacilitator(accept);
96
+ if (accept.network === CHAIN_IDS.base) {
97
+ accept.asset = USDC_BASE;
98
+ }
99
+ else if (accept.network.startsWith("solana:")) {
100
+ accept.asset = usdcAssetForCaip2(accept.network) ?? accept.asset;
101
+ }
102
+ else {
103
+ const correctAsset = usdcAssetForCaip2(accept.network);
104
+ if (correctAsset)
105
+ accept.asset = correctAsset;
106
+ }
107
+ }
108
+ accepts.sort((a, b) => {
109
+ const ia = chainOrder.indexOf(a.network ?? "");
110
+ const ib = chainOrder.indexOf(b.network ?? "");
111
+ return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
112
+ });
113
+ parsed.accepts = accepts;
114
+ }
115
+ function injectBazaarExtension(encoded, req) {
116
+ try {
117
+ const parsed = JSON.parse(Buffer.from(encoded, "base64").toString("utf8"));
118
+ normalizeAccepts(parsed);
119
+ attachBazaarToPayload(parsed, req);
120
+ syncResourceUrl(parsed, req);
121
+ return Buffer.from(JSON.stringify(parsed)).toString("base64");
122
+ }
123
+ catch (err) {
124
+ console.error("[x402-paid] injectBazaarExtension failed:", err);
125
+ return encoded;
126
+ }
127
+ }
128
+ function bodyHasAccepts(body) {
129
+ return (!!body &&
130
+ typeof body === "object" &&
131
+ Array.isArray(body.accepts));
132
+ }
133
+ /** x402 middleware with PAYMENT-REQUIRED header + Bazaar extension for Agentic Market */
134
+ export function createPaidMiddleware() {
135
+ return (amount, description) => {
136
+ const inner = x402Middleware({
137
+ ...baseMiddleware,
138
+ amount,
139
+ description,
140
+ verbose: process.env.X402_VERBOSE === "1",
141
+ timeoutSeconds: 120,
142
+ getResourceUrl: (req) => resolvePaidResourceUrl(req),
143
+ onSettlement: (info) => {
144
+ incCounter("x402_settlements");
145
+ const store = paymentRequestAls.getStore();
146
+ const pending = store?.x402PendingNonce;
147
+ if (pending) {
148
+ void markNonceUsed(pending.nonce, pending.network).catch((err) => {
149
+ console.error("[x402-paid] markNonceUsed failed:", err);
150
+ });
151
+ }
152
+ if (store) {
153
+ void markIdempotencyKeyUsed(store, store.path).catch((err) => {
154
+ console.error("[x402-paid] idempotency mark failed:", err);
155
+ });
156
+ }
157
+ baseMiddleware.onSettlement(info);
158
+ },
159
+ });
160
+ const paidHandler = async (req, res, next) => {
161
+ const paymentSig = getPaymentSignatureHeader(req);
162
+ if (!paymentSig) {
163
+ try {
164
+ await ensureFacilitatorExtras();
165
+ }
166
+ catch (err) {
167
+ console.warn("[x402-paid] facilitator extras cache:", err instanceof Error ? err.message : err);
168
+ }
169
+ }
170
+ const paymentReq = req;
171
+ if (paymentSig && isIdempotencyKeyConsumed(req, req.path)) {
172
+ incCounter("idempotency_replay");
173
+ res.status(409).json({
174
+ error: "Idempotency-Key already fulfilled for this resource — use a new key",
175
+ });
176
+ return;
177
+ }
178
+ if (paymentSig) {
179
+ const { nonce, network, payTo } = extractNonceFromPaymentHeader(paymentSig);
180
+ if (payTo && !isTrustedPayTo(payTo)) {
181
+ res.status(403).json({
182
+ error: "Payment address mismatch — possible payTo redirect attack",
183
+ });
184
+ return;
185
+ }
186
+ if (network && !isAllowedNetwork(network)) {
187
+ res.status(403).json({ error: `Network not allowed: ${network}` });
188
+ return;
189
+ }
190
+ if (nonce) {
191
+ if (isNonceAlreadyUsed(nonce)) {
192
+ incCounter("replay_blocked");
193
+ res.status(409).json({ error: "Replay attack detected: nonce already used" });
194
+ return;
195
+ }
196
+ paymentReq.x402PendingNonce = { nonce, network: network ?? "unknown" };
197
+ }
198
+ }
199
+ const origJson = res.json.bind(res);
200
+ res.json = ((body) => {
201
+ if (isResponseLocked(res))
202
+ return res;
203
+ if (body && typeof body === "object") {
204
+ const payload = body;
205
+ if (res.statusCode === 402 &&
206
+ payload.error === "Payment settlement failed" &&
207
+ payload.reason) {
208
+ incCounter("x402_settlement_failures");
209
+ if (!res.headersSent) {
210
+ res.setHeader("Retry-After", String(process.env.SETTLEMENT_RETRY_AFTER_SEC ?? "15"));
211
+ }
212
+ console.error("[x402] settlement failed:", payload.reason);
213
+ }
214
+ }
215
+ if (bodyHasAccepts(body)) {
216
+ const payload = body;
217
+ normalizeAccepts(payload);
218
+ attachBazaarToPayload(payload, req);
219
+ syncResourceUrl(payload, req);
220
+ const message = typeof payload.error === "string" ? payload.error : "Payment required";
221
+ const out = { error: message };
222
+ if (typeof payload.reason === "string")
223
+ out.reason = payload.reason;
224
+ return origJson(out);
225
+ }
226
+ return origJson(body);
227
+ });
228
+ const origSetHeader = res.setHeader.bind(res);
229
+ res.setHeader = ((name, value) => {
230
+ if (isResponseLocked(res))
231
+ return res;
232
+ let headerValue = value;
233
+ if (name.toUpperCase() === PAYMENT_REQUIRED && typeof headerValue === "string") {
234
+ headerValue = injectBazaarExtension(headerValue, req);
235
+ }
236
+ return origSetHeader(name, headerValue);
237
+ });
238
+ try {
239
+ await paymentRequestAls.run(paymentReq, async () => {
240
+ await inner(req, res, next);
241
+ });
242
+ }
243
+ catch (err) {
244
+ if (!isResponseLocked(res))
245
+ next(err);
246
+ else
247
+ console.error("[x402-paid] inner error after response locked:", err);
248
+ }
249
+ };
250
+ return withPaidRequestBudget(paidHandler);
251
+ };
252
+ }
@@ -0,0 +1,22 @@
1
+ import { extractIdempotencyKey } from "./nonce-store.js";
2
+ export { extractIdempotencyKey };
3
+ /** True if this nonce was already used for a successful settlement. */
4
+ export declare function isNonceAlreadyUsed(nonce: string): boolean;
5
+ /** Record nonce only after facilitator settlement succeeds. */
6
+ export declare function markNonceUsed(nonce: string, network: string): Promise<void>;
7
+ export declare function checkAndConsumeNonce(nonce: string, network: string): Promise<boolean>;
8
+ export declare function idempotencyCompositeKey(req: {
9
+ headers: Record<string, unknown>;
10
+ }, resourcePath: string): string | undefined;
11
+ /** Block only after a prior request with this key completed settlement. */
12
+ export declare function isIdempotencyKeyConsumed(req: {
13
+ headers: Record<string, unknown>;
14
+ }, resourcePath: string): boolean;
15
+ export declare function markIdempotencyKeyUsed(req: {
16
+ headers: Record<string, unknown>;
17
+ }, resourcePath: string): Promise<void>;
18
+ export declare function extractNonceFromPaymentHeader(header: string): {
19
+ nonce?: string;
20
+ network?: string;
21
+ payTo?: string;
22
+ };