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.
- package/CHANGELOG.md +55 -0
- package/DEPLOY.md +53 -0
- package/Dockerfile +30 -0
- package/LICENSE +21 -0
- package/README.md +294 -0
- package/dist/agents/a2a-payment.d.ts +37 -0
- package/dist/agents/a2a-payment.js +105 -0
- package/dist/agents/agent-escrow.d.ts +30 -0
- package/dist/agents/agent-escrow.js +23 -0
- package/dist/agents/agent-verify.d.ts +15 -0
- package/dist/agents/agent-verify.js +112 -0
- package/dist/agents/api-router.d.ts +32 -0
- package/dist/agents/api-router.js +228 -0
- package/dist/agents/attestation-registry.d.ts +35 -0
- package/dist/agents/attestation-registry.js +76 -0
- package/dist/agents/audition-coach.d.ts +45 -0
- package/dist/agents/audition-coach.js +257 -0
- package/dist/agents/bedrock-bridge.d.ts +3 -0
- package/dist/agents/bedrock-bridge.js +60 -0
- package/dist/agents/budget-allocator.d.ts +24 -0
- package/dist/agents/budget-allocator.js +31 -0
- package/dist/agents/compliance-ledger.d.ts +66 -0
- package/dist/agents/compliance-ledger.js +80 -0
- package/dist/agents/dispute-resolver.d.ts +62 -0
- package/dist/agents/dispute-resolver.js +124 -0
- package/dist/agents/evidence-locker.d.ts +30 -0
- package/dist/agents/evidence-locker.js +47 -0
- package/dist/agents/facilitator-failover.d.ts +15 -0
- package/dist/agents/facilitator-failover.js +18 -0
- package/dist/agents/identity-gate.d.ts +20 -0
- package/dist/agents/identity-gate.js +79 -0
- package/dist/agents/mandate-compiler.d.ts +51 -0
- package/dist/agents/mandate-compiler.js +73 -0
- package/dist/agents/mandate-diff.d.ts +41 -0
- package/dist/agents/mandate-diff.js +170 -0
- package/dist/agents/market-buy-advisor.d.ts +65 -0
- package/dist/agents/market-buy-advisor.js +234 -0
- package/dist/agents/merchant-trust.d.ts +38 -0
- package/dist/agents/merchant-trust.js +171 -0
- package/dist/agents/mpp-session-broker.d.ts +27 -0
- package/dist/agents/mpp-session-broker.js +29 -0
- package/dist/agents/mpp-session-v2.d.ts +76 -0
- package/dist/agents/mpp-session-v2.js +269 -0
- package/dist/agents/payment-intent-compiler.d.ts +21 -0
- package/dist/agents/payment-intent-compiler.js +45 -0
- package/dist/agents/pipeline-execute.d.ts +40 -0
- package/dist/agents/pipeline-execute.js +100 -0
- package/dist/agents/pipeline-trust-v2.d.ts +31 -0
- package/dist/agents/pipeline-trust-v2.js +111 -0
- package/dist/agents/pre-x402-guard.d.ts +35 -0
- package/dist/agents/pre-x402-guard.js +84 -0
- package/dist/agents/quality-escrow-semantic.d.ts +88 -0
- package/dist/agents/quality-escrow-semantic.js +137 -0
- package/dist/agents/quality-escrow.d.ts +65 -0
- package/dist/agents/quality-escrow.js +104 -0
- package/dist/agents/quality-monitor.d.ts +32 -0
- package/dist/agents/quality-monitor.js +77 -0
- package/dist/agents/rail-optimizer.d.ts +33 -0
- package/dist/agents/rail-optimizer.js +133 -0
- package/dist/agents/receipt-auditor.d.ts +14 -0
- package/dist/agents/receipt-auditor.js +145 -0
- package/dist/agents/refund-arbiter.d.ts +24 -0
- package/dist/agents/refund-arbiter.js +70 -0
- package/dist/agents/research-brief.d.ts +14 -0
- package/dist/agents/research-brief.js +66 -0
- package/dist/agents/risk-gate.d.ts +11 -0
- package/dist/agents/risk-gate.js +78 -0
- package/dist/agents/settlement-graph.d.ts +16 -0
- package/dist/agents/settlement-graph.js +38 -0
- package/dist/agents/spend-governor.d.ts +2 -0
- package/dist/agents/spend-governor.js +70 -0
- package/dist/agents/trust-network.d.ts +138 -0
- package/dist/agents/trust-network.js +244 -0
- package/dist/agents/x402-proxy.d.ts +32 -0
- package/dist/agents/x402-proxy.js +90 -0
- package/dist/client/demo-alchemy-live.d.ts +1 -0
- package/dist/client/demo-alchemy-live.js +226 -0
- package/dist/client/demo-tail.d.ts +1 -0
- package/dist/client/demo-tail.js +100 -0
- package/dist/client/demo.d.ts +1 -0
- package/dist/client/demo.js +293 -0
- package/dist/config.d.ts +94 -0
- package/dist/config.js +223 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +389 -0
- package/dist/lib/agent-response.d.ts +14 -0
- package/dist/lib/agent-response.js +13 -0
- package/dist/lib/agentic-gateways.d.ts +5 -0
- package/dist/lib/agentic-gateways.js +15 -0
- package/dist/lib/agentic-probes.d.ts +10 -0
- package/dist/lib/agentic-probes.js +49 -0
- package/dist/lib/alchemy-x402-fetch.d.ts +16 -0
- package/dist/lib/alchemy-x402-fetch.js +95 -0
- package/dist/lib/apply-verifier-body.d.ts +7 -0
- package/dist/lib/apply-verifier-body.js +179 -0
- package/dist/lib/attestation.d.ts +30 -0
- package/dist/lib/attestation.js +107 -0
- package/dist/lib/bazaar-extension.d.ts +15 -0
- package/dist/lib/bazaar-extension.js +265 -0
- package/dist/lib/bazaar.d.ts +100 -0
- package/dist/lib/bazaar.js +341 -0
- package/dist/lib/certified-sellers.d.ts +41 -0
- package/dist/lib/certified-sellers.js +129 -0
- package/dist/lib/chains.d.ts +20 -0
- package/dist/lib/chains.js +78 -0
- package/dist/lib/db-persistence.d.ts +7 -0
- package/dist/lib/db-persistence.js +65 -0
- package/dist/lib/db.d.ts +5 -0
- package/dist/lib/db.js +113 -0
- package/dist/lib/discovery-page.d.ts +2 -0
- package/dist/lib/discovery-page.js +71 -0
- package/dist/lib/ecosystem-telemetry.d.ts +20 -0
- package/dist/lib/ecosystem-telemetry.js +80 -0
- package/dist/lib/erc8004/agent-card.d.ts +34 -0
- package/dist/lib/erc8004/agent-card.js +151 -0
- package/dist/lib/erc8004/cache.d.ts +3 -0
- package/dist/lib/erc8004/cache.js +17 -0
- package/dist/lib/erc8004/constants.d.ts +22 -0
- package/dist/lib/erc8004/constants.js +35 -0
- package/dist/lib/erc8004/registry.d.ts +19 -0
- package/dist/lib/erc8004/registry.js +171 -0
- package/dist/lib/erc8004/resolve-agent.d.ts +7 -0
- package/dist/lib/erc8004/resolve-agent.js +70 -0
- package/dist/lib/erc8004/trust-score.d.ts +33 -0
- package/dist/lib/erc8004/trust-score.js +136 -0
- package/dist/lib/escrow-ledger.d.ts +14 -0
- package/dist/lib/escrow-ledger.js +54 -0
- package/dist/lib/escrow-unified.d.ts +15 -0
- package/dist/lib/escrow-unified.js +28 -0
- package/dist/lib/facilitator-extra.d.ts +13 -0
- package/dist/lib/facilitator-extra.js +52 -0
- package/dist/lib/facilitators.d.ts +20 -0
- package/dist/lib/facilitators.js +89 -0
- package/dist/lib/host-policy.d.ts +4 -0
- package/dist/lib/host-policy.js +20 -0
- package/dist/lib/idempotency.d.ts +4 -0
- package/dist/lib/idempotency.js +120 -0
- package/dist/lib/ledger.d.ts +2 -0
- package/dist/lib/ledger.js +17 -0
- package/dist/lib/logger.d.ts +6 -0
- package/dist/lib/logger.js +24 -0
- package/dist/lib/mandate-vc.d.ts +20 -0
- package/dist/lib/mandate-vc.js +25 -0
- package/dist/lib/mandate.d.ts +44 -0
- package/dist/lib/mandate.js +190 -0
- package/dist/lib/marketplace.d.ts +7 -0
- package/dist/lib/marketplace.js +127 -0
- package/dist/lib/migrations.d.ts +2 -0
- package/dist/lib/migrations.js +130 -0
- package/dist/lib/nonce-store.d.ts +6 -0
- package/dist/lib/nonce-store.js +109 -0
- package/dist/lib/openapi-agentcash.d.ts +5 -0
- package/dist/lib/openapi-agentcash.js +288 -0
- package/dist/lib/openapi-meta.d.ts +5 -0
- package/dist/lib/openapi-meta.js +235 -0
- package/dist/lib/otel.d.ts +2 -0
- package/dist/lib/otel.js +25 -0
- package/dist/lib/paid-resource-url.d.ts +6 -0
- package/dist/lib/paid-resource-url.js +47 -0
- package/dist/lib/parse-with-verifier-fallback.d.ts +3 -0
- package/dist/lib/parse-with-verifier-fallback.js +13 -0
- package/dist/lib/payment-request-context.d.ts +10 -0
- package/dist/lib/payment-request-context.js +5 -0
- package/dist/lib/payment-response.d.ts +13 -0
- package/dist/lib/payment-response.js +39 -0
- package/dist/lib/payto-guard.d.ts +10 -0
- package/dist/lib/payto-guard.js +20 -0
- package/dist/lib/probe.d.ts +29 -0
- package/dist/lib/probe.js +157 -0
- package/dist/lib/problem-detail.d.ts +10 -0
- package/dist/lib/problem-detail.js +14 -0
- package/dist/lib/rate-limit.d.ts +12 -0
- package/dist/lib/rate-limit.js +126 -0
- package/dist/lib/replay-middleware.d.ts +3 -0
- package/dist/lib/replay-middleware.js +27 -0
- package/dist/lib/response-guard.d.ts +5 -0
- package/dist/lib/response-guard.js +40 -0
- package/dist/lib/safe-fetch.d.ts +5 -0
- package/dist/lib/safe-fetch.js +19 -0
- package/dist/lib/security.d.ts +13 -0
- package/dist/lib/security.js +61 -0
- package/dist/lib/semantic-judge.d.ts +14 -0
- package/dist/lib/semantic-judge.js +107 -0
- package/dist/lib/semantic-judge.test.d.ts +1 -0
- package/dist/lib/semantic-judge.test.js +11 -0
- package/dist/lib/ssrf.d.ts +10 -0
- package/dist/lib/ssrf.js +130 -0
- package/dist/lib/ssrf.test.d.ts +1 -0
- package/dist/lib/ssrf.test.js +16 -0
- package/dist/lib/suite-catalog.d.ts +83 -0
- package/dist/lib/suite-catalog.js +131 -0
- package/dist/lib/telemetry.d.ts +5 -0
- package/dist/lib/telemetry.js +37 -0
- package/dist/lib/verifier-fast-path.d.ts +10 -0
- package/dist/lib/verifier-fast-path.js +44 -0
- package/dist/lib/verifier-probe-protocol.d.ts +7 -0
- package/dist/lib/verifier-probe-protocol.js +115 -0
- package/dist/lib/verify-examples.d.ts +2 -0
- package/dist/lib/verify-examples.js +438 -0
- package/dist/lib/version.d.ts +2 -0
- package/dist/lib/version.js +2 -0
- package/dist/lib/webhook-auth.d.ts +3 -0
- package/dist/lib/webhook-auth.js +34 -0
- package/dist/lib/webhook-routes.d.ts +2 -0
- package/dist/lib/webhook-routes.js +112 -0
- package/dist/lib/webhooks.d.ts +23 -0
- package/dist/lib/webhooks.js +123 -0
- package/dist/lib/webhooks.test.d.ts +1 -0
- package/dist/lib/webhooks.test.js +16 -0
- package/dist/lib/x402-client-options.d.ts +28 -0
- package/dist/lib/x402-client-options.js +138 -0
- package/dist/lib/x402-headers.d.ts +10 -0
- package/dist/lib/x402-headers.js +27 -0
- package/dist/lib/x402-paid.d.ts +5 -0
- package/dist/lib/x402-paid.js +252 -0
- package/dist/lib/x402-payment-replay.d.ts +22 -0
- package/dist/lib/x402-payment-replay.js +57 -0
- package/dist/lib/x402gle-host-verify.d.ts +3 -0
- package/dist/lib/x402gle-host-verify.js +27 -0
- package/dist/protocol/agent-passport.d.ts +34 -0
- package/dist/protocol/agent-passport.js +44 -0
- package/dist/protocol/compliance-v2.d.ts +21 -0
- package/dist/protocol/compliance-v2.js +19 -0
- package/dist/protocol/credit-bureau.d.ts +18 -0
- package/dist/protocol/credit-bureau.js +44 -0
- package/dist/protocol/crypto.d.ts +6 -0
- package/dist/protocol/crypto.js +41 -0
- package/dist/protocol/escrow-fsm.d.ts +33 -0
- package/dist/protocol/escrow-fsm.js +99 -0
- package/dist/protocol/fraud-engine.d.ts +28 -0
- package/dist/protocol/fraud-engine.js +77 -0
- package/dist/protocol/observability.d.ts +14 -0
- package/dist/protocol/observability.js +21 -0
- package/dist/protocol/pipeline-full-trust.d.ts +40 -0
- package/dist/protocol/pipeline-full-trust.js +96 -0
- package/dist/protocol/proof-of-execution.d.ts +36 -0
- package/dist/protocol/proof-of-execution.js +48 -0
- package/dist/protocol/reasoning-audit.d.ts +27 -0
- package/dist/protocol/reasoning-audit.js +51 -0
- package/dist/protocol/replay-guard.d.ts +28 -0
- package/dist/protocol/replay-guard.js +76 -0
- package/dist/protocol/replay-guard.test.d.ts +1 -0
- package/dist/protocol/replay-guard.test.js +10 -0
- package/dist/protocol/security-audit.d.ts +18 -0
- package/dist/protocol/security-audit.js +45 -0
- package/dist/protocol/store.d.ts +5 -0
- package/dist/protocol/store.js +59 -0
- package/dist/protocol/threat-catalog.d.ts +13 -0
- package/dist/protocol/threat-catalog.js +75 -0
- package/dist/protocol/trust-oracle.d.ts +23 -0
- package/dist/protocol/trust-oracle.js +30 -0
- package/dist/protocol/trust-score-v2.d.ts +33 -0
- package/dist/protocol/trust-score-v2.js +78 -0
- package/dist/protocol/zk-proofs.d.ts +24 -0
- package/dist/protocol/zk-proofs.js +32 -0
- package/dist/routes/a2a-agent-card.d.ts +3 -0
- package/dist/routes/a2a-agent-card.js +28 -0
- package/dist/routes/catalog.d.ts +5 -0
- package/dist/routes/catalog.js +47 -0
- package/dist/routes/register-all.d.ts +3 -0
- package/dist/routes/register-all.js +1240 -0
- package/dist/routes/schemas.d.ts +83 -0
- package/dist/routes/schemas.js +38 -0
- package/dist/routes/shared.d.ts +16 -0
- package/dist/routes/shared.js +27 -0
- package/dist/routes-protocol.d.ts +10 -0
- package/dist/routes-protocol.js +322 -0
- package/dist/routes.d.ts +2 -0
- package/dist/routes.js +2 -0
- package/dist/types.d.ts +66 -0
- package/dist/types.js +1 -0
- package/openapi.json +7940 -0
- package/package.json +124 -0
- package/public/.well-known/ai-plugin.json +12 -0
- package/public/assets/aegis-logo-blue.png +0 -0
- package/public/assets/aegis-logo-gold.png +0 -0
- package/public/assets/aegis-logo-green.png +0 -0
- package/public/assets/aegis-logo-purple.png +0 -0
- package/public/assets/aegis-logo-red.png +0 -0
- package/public/assets/aegis-logo-white.png +0 -0
- package/public/assets/aegis-logo.png +0 -0
- package/public/assets/x402-trustlayer-logo.png +0 -0
- package/public/assets/x402-trustlayer-logo.svg +5 -0
- package/public/data/agents.json +1528 -0
- package/public/index.html +198 -0
- package/public/landing.css +342 -0
- package/public/landing.js +405 -0
- package/public/llms-full.txt +582 -0
- package/public/llms.txt +132 -0
- package/public/skill.md +135 -0
- package/railway.toml +9 -0
- package/scripts/docker-entrypoint.sh +7 -0
- package/scripts/patch-facilitator-timeout.mjs +61 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import helmet from "helmet";
|
|
3
|
+
import cors from "cors";
|
|
4
|
+
import { readFileSync, statSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { assertConfig, config } from "./config.js";
|
|
7
|
+
import { db, dbPath } from "./lib/db.js";
|
|
8
|
+
import "./lib/db.js";
|
|
9
|
+
import { logger } from "./lib/logger.js";
|
|
10
|
+
import { startOtelIfEnabled } from "./lib/otel.js";
|
|
11
|
+
import { sendProblem } from "./lib/problem-detail.js";
|
|
12
|
+
import { telemetryMiddleware, metricsPayload } from "./lib/telemetry.js";
|
|
13
|
+
import { createPaidMiddleware } from "./lib/x402-paid.js";
|
|
14
|
+
import { buildDiscoverCatalog, buildServicesManifest, buildWellKnownX402, } from "./lib/bazaar.js";
|
|
15
|
+
import { buildAgentCashOpenApi, buildWellKnownX402Resources, buildWellKnownX402V2, } from "./lib/openapi-agentcash.js";
|
|
16
|
+
import { registerA2AAgentCard } from "./routes/a2a-agent-card.js";
|
|
17
|
+
import { renderDiscoveryPage } from "./lib/discovery-page.js";
|
|
18
|
+
import { applyVerifierExampleBody } from "./lib/apply-verifier-body.js";
|
|
19
|
+
import { replayBindingMiddleware } from "./lib/replay-middleware.js";
|
|
20
|
+
import { registerAgenticProbes, stripTrailingSlash } from "./lib/agentic-probes.js";
|
|
21
|
+
import { registerWebhookRoutes } from "./lib/webhook-routes.js";
|
|
22
|
+
import { KILLER_SELLER_ENDPOINTS, PRIMARY_ENTRYPOINTS } from "./lib/suite-catalog.js";
|
|
23
|
+
import { listEndpoints, registerRoutes } from "./routes.js";
|
|
24
|
+
import { registerX402gleHostVerification } from "./lib/x402gle-host-verify.js";
|
|
25
|
+
import { ensureVerifierProbeMandate } from "./lib/mandate.js";
|
|
26
|
+
import { ensureVerifierProbeProtocol } from "./lib/verifier-probe-protocol.js";
|
|
27
|
+
import { SUITE_VERSION } from "./lib/version.js";
|
|
28
|
+
import { refreshFacilitatorExtras, startFacilitatorExtrasRefresh } from "./lib/facilitator-extra.js";
|
|
29
|
+
import { rateLimitPerMinute, rateLimitUnpaidProbes, rateLimitAgentLookup } from "./lib/rate-limit.js";
|
|
30
|
+
import { handleAgentLookup } from "./agents/agent-verify.js";
|
|
31
|
+
import { runCertifiedLookup, runCertifiedCatalog } from "./agents/trust-network.js";
|
|
32
|
+
assertConfig();
|
|
33
|
+
void startOtelIfEnabled();
|
|
34
|
+
void refreshFacilitatorExtras().catch((err) => {
|
|
35
|
+
logger.warn({ err: err instanceof Error ? err.message : String(err) }, "Facilitator /supported preload failed");
|
|
36
|
+
});
|
|
37
|
+
startFacilitatorExtrasRefresh();
|
|
38
|
+
void ensureVerifierProbeMandate().catch((err) => {
|
|
39
|
+
logger.warn({ err: err instanceof Error ? err.message : err }, "Verifier probe mandate seed skipped");
|
|
40
|
+
});
|
|
41
|
+
void ensureVerifierProbeProtocol().catch((err) => {
|
|
42
|
+
logger.warn({ err: err instanceof Error ? err.message : err }, "Verifier probe protocol seed skipped");
|
|
43
|
+
});
|
|
44
|
+
const app = express();
|
|
45
|
+
app.set("trust proxy", Number(process.env.TRUST_PROXY_HOPS ?? 1));
|
|
46
|
+
app.disable("x-powered-by");
|
|
47
|
+
app.use(helmet({
|
|
48
|
+
contentSecurityPolicy: {
|
|
49
|
+
directives: { defaultSrc: ["'none'"], scriptSrc: ["'none'"] },
|
|
50
|
+
},
|
|
51
|
+
crossOriginEmbedderPolicy: false,
|
|
52
|
+
}));
|
|
53
|
+
const corsOrigins = process.env.CORS_ORIGINS?.split(",").map((o) => o.trim()).filter(Boolean);
|
|
54
|
+
app.use(cors({ origin: corsOrigins?.length ? corsOrigins : false }));
|
|
55
|
+
app.use((req, res, next) => {
|
|
56
|
+
res.setHeader("API-Version", "1");
|
|
57
|
+
const orig = req.originalUrl;
|
|
58
|
+
if (orig.startsWith("/api/v1/")) {
|
|
59
|
+
req.url = `/api/${orig.slice("/api/v1/".length)}`;
|
|
60
|
+
}
|
|
61
|
+
else if (orig.startsWith("/api/")) {
|
|
62
|
+
res.setHeader("X-Deprecated", "true");
|
|
63
|
+
}
|
|
64
|
+
next();
|
|
65
|
+
});
|
|
66
|
+
app.use(telemetryMiddleware);
|
|
67
|
+
registerA2AAgentCard(app);
|
|
68
|
+
registerX402gleHostVerification(app);
|
|
69
|
+
app.use(stripTrailingSlash);
|
|
70
|
+
app.use(express.json({ limit: "512kb" }));
|
|
71
|
+
/** Unpaid probes → 402 (x402scan). Paid retries capped separately. */
|
|
72
|
+
app.use("/api", rateLimitUnpaidProbes(Number(process.env.RATE_LIMIT_UNPAID_PER_MIN ?? 600)));
|
|
73
|
+
app.use("/api", rateLimitPerMinute(Number(process.env.RATE_LIMIT_PER_MIN ?? 120)));
|
|
74
|
+
/** Canonical example bodies for x402gle / Dexter AI verifier (empty or partial POST) */
|
|
75
|
+
app.use("/api", (req, _res, next) => {
|
|
76
|
+
applyVerifierExampleBody(req);
|
|
77
|
+
next();
|
|
78
|
+
});
|
|
79
|
+
app.use("/api", replayBindingMiddleware);
|
|
80
|
+
/** Trust Layer brand landing page — served to browsers at `/`; machines still get JSON. */
|
|
81
|
+
let LANDING_HTML = "";
|
|
82
|
+
try {
|
|
83
|
+
LANDING_HTML = readFileSync(join(process.cwd(), "public", "index.html"), "utf8");
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
LANDING_HTML = "";
|
|
87
|
+
}
|
|
88
|
+
/** Static public files (landing.js, data, assets). index.html served via GET / negotiation. */
|
|
89
|
+
app.use(express.static(join(process.cwd(), "public"), {
|
|
90
|
+
index: false,
|
|
91
|
+
maxAge: "1h",
|
|
92
|
+
}));
|
|
93
|
+
const paid = createPaidMiddleware();
|
|
94
|
+
function asyncRoute(handler) {
|
|
95
|
+
return (req, res, next) => {
|
|
96
|
+
handler(req, res).catch(next);
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const GITHUB_REPO = "https://github.com/mimranchohan/x402-trust-layer";
|
|
100
|
+
function healthPayload() {
|
|
101
|
+
const dataDir = process.env.DATA_DIR?.trim() || "/app/data";
|
|
102
|
+
return {
|
|
103
|
+
ok: true,
|
|
104
|
+
service: "x402-trust-layer",
|
|
105
|
+
version: SUITE_VERSION,
|
|
106
|
+
protocol: "agent-trust-protocol-v4",
|
|
107
|
+
protocolArchitecture: `${config.publicBaseUrl}/api/protocol/architecture`,
|
|
108
|
+
chains: config.chains,
|
|
109
|
+
networks: config.networks,
|
|
110
|
+
facilitator: config.facilitatorUrl,
|
|
111
|
+
endpointCount: listEndpoints().length,
|
|
112
|
+
nonceBackend: metricsPayload().nonceBackend,
|
|
113
|
+
gitCommit: process.env.RAILWAY_GIT_COMMIT_SHA?.slice(0, 7) ?? null,
|
|
114
|
+
deploy: {
|
|
115
|
+
platform: process.env.RAILWAY_ENVIRONMENT ? "railway" : null,
|
|
116
|
+
docker: true,
|
|
117
|
+
volumeMount: "/app/data",
|
|
118
|
+
dataDir,
|
|
119
|
+
sqlitePath: dbPath(),
|
|
120
|
+
entrypoint: "scripts/docker-entrypoint.sh (chown volume for non-root app user)",
|
|
121
|
+
},
|
|
122
|
+
documentation: {
|
|
123
|
+
github: GITHUB_REPO,
|
|
124
|
+
railwayDeploy: `${GITHUB_REPO}/blob/main/docs/RAILWAY-DEPLOY.md`,
|
|
125
|
+
productionHardening: `${GITHUB_REPO}/blob/main/docs/PRODUCTION-HARDENING.md`,
|
|
126
|
+
npm: "https://www.npmjs.com/package/x402-trust-layer",
|
|
127
|
+
},
|
|
128
|
+
agenticGetProbes: true,
|
|
129
|
+
agenticReady: config.publicBaseUrl.startsWith("https://") &&
|
|
130
|
+
config.chains.includes("base") &&
|
|
131
|
+
config.payToEvm.length > 0,
|
|
132
|
+
agenticHint: !config.payToEvm
|
|
133
|
+
? "Set PAY_TO_EVM + NETWORKS=base,solana on Railway for agentic.market"
|
|
134
|
+
: config.publicBaseUrl.includes("railway.app") &&
|
|
135
|
+
!process.env.PUBLIC_BASE_URL &&
|
|
136
|
+
!process.env.CANONICAL_PUBLIC_URL
|
|
137
|
+
? `Set PUBLIC_BASE_URL=${config.canonicalOrigin} so discovery URLs match x402trustlayer.xyz`
|
|
138
|
+
: null,
|
|
139
|
+
agentCashDiscovery: {
|
|
140
|
+
openapi: `${config.publicBaseUrl}/openapi.json`,
|
|
141
|
+
wellKnown: `${config.publicBaseUrl}/.well-known/x402`,
|
|
142
|
+
ready: config.publicBaseUrl.startsWith("https://") && config.payToEvm.length > 0,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
app.get("/llms.txt", (_req, res) => {
|
|
147
|
+
try {
|
|
148
|
+
const txt = readFileSync(join(process.cwd(), "public", "llms.txt"), "utf8");
|
|
149
|
+
res.type("text/plain").send(txt);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
res.status(404).type("text/plain").send("llms.txt not found");
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
app.get("/llms-full.txt", (_req, res) => {
|
|
156
|
+
try {
|
|
157
|
+
const txt = readFileSync(join(process.cwd(), "public", "llms-full.txt"), "utf8");
|
|
158
|
+
res.type("text/plain").send(txt);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
res.status(404).type("text/plain").send("llms-full.txt not found");
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
app.get("/skill.md", (_req, res) => {
|
|
165
|
+
try {
|
|
166
|
+
const md = readFileSync(join(process.cwd(), "public", "skill.md"), "utf8");
|
|
167
|
+
res.type("text/markdown").send(md);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
res.status(404).type("text/plain").send("skill.md not found");
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
app.get("/metrics", (_req, res) => {
|
|
174
|
+
res.json(metricsPayload());
|
|
175
|
+
});
|
|
176
|
+
app.get("/robots.txt", (_req, res) => {
|
|
177
|
+
res
|
|
178
|
+
.type("text/plain")
|
|
179
|
+
.send("User-agent: *\nAllow: /\nSitemap: /sitemap.xml\n# Agent-friendly: see /llms.txt\n");
|
|
180
|
+
});
|
|
181
|
+
app.get("/.well-known/x402/v2", (_req, res) => {
|
|
182
|
+
res.json(buildWellKnownX402V2());
|
|
183
|
+
});
|
|
184
|
+
app.get("/health", (_req, res) => {
|
|
185
|
+
let dbOk = false;
|
|
186
|
+
let diskOk = false;
|
|
187
|
+
try {
|
|
188
|
+
db.prepare("SELECT 1").get();
|
|
189
|
+
dbOk = true;
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
/* db down */
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
statSync(process.cwd());
|
|
196
|
+
diskOk = true;
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
/* disk */
|
|
200
|
+
}
|
|
201
|
+
const status = dbOk && diskOk ? 200 : 503;
|
|
202
|
+
res.status(status).json({
|
|
203
|
+
...healthPayload(),
|
|
204
|
+
ok: dbOk && diskOk,
|
|
205
|
+
db: dbOk ? "ok" : "error",
|
|
206
|
+
disk: diskOk ? "ok" : "error",
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
// Compatibility aliases used by some external quality probes.
|
|
210
|
+
app.get("/api/health", (_req, res) => {
|
|
211
|
+
let dbOk = false;
|
|
212
|
+
let diskOk = false;
|
|
213
|
+
try {
|
|
214
|
+
db.prepare("SELECT 1").get();
|
|
215
|
+
dbOk = true;
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
/* */
|
|
219
|
+
}
|
|
220
|
+
try {
|
|
221
|
+
statSync(process.cwd());
|
|
222
|
+
diskOk = true;
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
/* */
|
|
226
|
+
}
|
|
227
|
+
const status = dbOk && diskOk ? 200 : 503;
|
|
228
|
+
res.status(status).json({
|
|
229
|
+
...healthPayload(),
|
|
230
|
+
ok: dbOk && diskOk,
|
|
231
|
+
db: dbOk ? "ok" : "error",
|
|
232
|
+
disk: diskOk ? "ok" : "error",
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
app.get("/api/version", (_req, res) => {
|
|
236
|
+
res.json({ service: "x402-agent-suite-pro", version: SUITE_VERSION });
|
|
237
|
+
});
|
|
238
|
+
app.get("/api/agents", (_req, res) => {
|
|
239
|
+
res.json({
|
|
240
|
+
count: listEndpoints().length,
|
|
241
|
+
endpoints: listEndpoints(),
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
app.get("/openapi.json", (_req, res) => {
|
|
245
|
+
res.json(buildAgentCashOpenApi());
|
|
246
|
+
});
|
|
247
|
+
/** AgentCash / x402scan discovery fan-out (canonical path) */
|
|
248
|
+
app.get("/.well-known/x402", (_req, res) => {
|
|
249
|
+
res.json(buildWellKnownX402Resources());
|
|
250
|
+
});
|
|
251
|
+
app.get("/.well-known/x402.json", (_req, res) => {
|
|
252
|
+
res.json(buildWellKnownX402());
|
|
253
|
+
});
|
|
254
|
+
/** Human-friendly discovery view (landing page links here instead of raw JSON path). */
|
|
255
|
+
app.get("/discovery", (_req, res) => {
|
|
256
|
+
res.type("html").send(renderDiscoveryPage(buildWellKnownX402Resources(), config.publicBaseUrl));
|
|
257
|
+
});
|
|
258
|
+
/** Same manifest as /.well-known/x402 — free catalog (HTTP 200). Not for agentic.market Validate. */
|
|
259
|
+
app.get("/discovery.json", (_req, res) => {
|
|
260
|
+
res.json({
|
|
261
|
+
...buildWellKnownX402Resources(),
|
|
262
|
+
agenticValidateNote: "Do not submit this URL to agentic.market Validate. Use paid /api/* URLs from GET /api/agentic/validate-urls instead.",
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
app.get("/x402/api/services.json", (_req, res) => {
|
|
266
|
+
res.json(buildServicesManifest());
|
|
267
|
+
});
|
|
268
|
+
function sendDiscoverCatalog(_req, res) {
|
|
269
|
+
res.json(buildDiscoverCatalog());
|
|
270
|
+
}
|
|
271
|
+
app.get("/x402/api/discover", sendDiscoverCatalog);
|
|
272
|
+
/** Redirects — canonical path is /x402/api/discover */
|
|
273
|
+
app.get("/x402/discover", (_req, res) => res.redirect(301, "/x402/api/discover"));
|
|
274
|
+
app.get("/discover", (_req, res) => res.redirect(301, "/x402/api/discover"));
|
|
275
|
+
app.get("/", (req, res) => {
|
|
276
|
+
const acceptsHtml = (req.headers.accept ?? "").includes("text/html");
|
|
277
|
+
if (acceptsHtml && LANDING_HTML) {
|
|
278
|
+
res.type("html").send(LANDING_HTML);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const all = listEndpoints();
|
|
282
|
+
res.json({
|
|
283
|
+
name: "x402 Trust Layer — Agent Suite",
|
|
284
|
+
version: SUITE_VERSION,
|
|
285
|
+
description: `${all.length} paid x402 infrastructure APIs — guard, semantic escrow, mandate diff, certified seller network, Agent Trust Protocol v4`,
|
|
286
|
+
github: GITHUB_REPO,
|
|
287
|
+
npm: "https://www.npmjs.com/package/x402-trust-layer",
|
|
288
|
+
docs: `${config.publicBaseUrl}/openapi.json`,
|
|
289
|
+
llmsTxt: `${config.publicBaseUrl}/llms.txt`,
|
|
290
|
+
skillMd: `${config.publicBaseUrl}/skill.md`,
|
|
291
|
+
discovery: `${config.publicBaseUrl}/x402/api/discover`,
|
|
292
|
+
bazaar: `${config.publicBaseUrl}/x402/api/services.json`,
|
|
293
|
+
deployDocs: `${GITHUB_REPO}/blob/main/docs/RAILWAY-DEPLOY.md`,
|
|
294
|
+
agenticMarket: "https://agentic.market/",
|
|
295
|
+
agentCash: "https://agentcash.dev/",
|
|
296
|
+
x402scanRegister: "https://www.x402scan.com/resources/register",
|
|
297
|
+
dexterSeller: `https://dexter.cash/sellers/${config.payTo}`,
|
|
298
|
+
pipeline: `${config.publicBaseUrl}/api/pipeline/full`,
|
|
299
|
+
onboarding: {
|
|
300
|
+
primary: PRIMARY_ENTRYPOINTS,
|
|
301
|
+
killerSeller: KILLER_SELLER_ENDPOINTS,
|
|
302
|
+
advancedCount: all.length - PRIMARY_ENTRYPOINTS.length - KILLER_SELLER_ENDPOINTS.length,
|
|
303
|
+
},
|
|
304
|
+
endpoints: all,
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
const postHandlers = registerRoutes(app, paid, asyncRoute);
|
|
308
|
+
registerWebhookRoutes(app);
|
|
309
|
+
/** Free ERC-8004 lookup — rate limited ~30/hr per IP */
|
|
310
|
+
app.get("/api/agent/lookup/:wallet", rateLimitAgentLookup(Number(process.env.RATE_LIMIT_AGENT_LOOKUP_PER_HOUR ?? 30)), asyncRoute(handleAgentLookup));
|
|
311
|
+
/** Free certified seller lookup — rate limited */
|
|
312
|
+
app.get("/api/merchant-trust/certified/:host", rateLimitAgentLookup(Number(process.env.RATE_LIMIT_AGENT_LOOKUP_PER_HOUR ?? 60)), asyncRoute(async (req, res) => {
|
|
313
|
+
const host = String(req.params.host ?? "").trim();
|
|
314
|
+
if (!host) {
|
|
315
|
+
res.status(400).json({ error: "host required" });
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
res.json(await runCertifiedLookup(host));
|
|
319
|
+
}));
|
|
320
|
+
app.get("/api/trust-network/catalog", rateLimitAgentLookup(Number(process.env.RATE_LIMIT_AGENT_LOOKUP_PER_HOUR ?? 60)), asyncRoute(async (req, res) => {
|
|
321
|
+
const limit = typeof req.query.limit === "string" ? Math.min(100, Math.max(1, Number(req.query.limit) || 50)) : 50;
|
|
322
|
+
res.json(await runCertifiedCatalog(limit));
|
|
323
|
+
}));
|
|
324
|
+
registerAgenticProbes(app, paid, postHandlers);
|
|
325
|
+
/** Copy-paste URLs for Agentic Validate Endpoint (free) */
|
|
326
|
+
app.get("/api/agentic/validate-urls", (_req, res) => {
|
|
327
|
+
const base = config.publicBaseUrl;
|
|
328
|
+
res.json({
|
|
329
|
+
note: "Paste these exact URLs into agentic.market Validate Endpoint. No trailing slash.",
|
|
330
|
+
doNotValidate: [
|
|
331
|
+
`${base}/discovery.json`,
|
|
332
|
+
`${base}/.well-known/x402`,
|
|
333
|
+
`${base}/health`,
|
|
334
|
+
`${base}/x402/api/discover`,
|
|
335
|
+
],
|
|
336
|
+
agenticGetProbes: true,
|
|
337
|
+
cdpBazaarHint: "For CDP Bazaar auto-index, set USE_CDP_FACILITATOR=1 and FACILITATOR_URL=https://api.cdp.coinbase.com/platform/v2/x402/facilitator with CDP API keys, then complete one settlement per route.",
|
|
338
|
+
facilitator: config.facilitatorUrl,
|
|
339
|
+
urls: listEndpoints().map((e) => {
|
|
340
|
+
const [, path] = e.path.split(" ");
|
|
341
|
+
return `${base}${path}`;
|
|
342
|
+
}),
|
|
343
|
+
example: `${base}/api/x402/proxy`,
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
app.use((err, _req, res, _next) => {
|
|
347
|
+
if (res.headersSent) {
|
|
348
|
+
logger.error({ err }, "API error after headers sent");
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
logger.error({ err }, "API error");
|
|
352
|
+
const expose = process.env.NODE_ENV !== "production" && !process.env.RAILWAY_ENVIRONMENT;
|
|
353
|
+
if (expose && err instanceof Error) {
|
|
354
|
+
sendProblem(res, 500, "Internal server error", err.message);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
sendProblem(res, 500, "Internal server error");
|
|
358
|
+
});
|
|
359
|
+
const host = "0.0.0.0";
|
|
360
|
+
const server = app.listen(config.port, host, () => {
|
|
361
|
+
logger.info({
|
|
362
|
+
version: SUITE_VERSION,
|
|
363
|
+
endpoints: listEndpoints().length,
|
|
364
|
+
chains: config.chains.join(","),
|
|
365
|
+
git: process.env.RAILWAY_GIT_COMMIT_SHA?.slice(0, 7) ?? "local",
|
|
366
|
+
public: config.publicBaseUrl,
|
|
367
|
+
}, "x402 Trust Layer listening");
|
|
368
|
+
});
|
|
369
|
+
async function shutdown(signal) {
|
|
370
|
+
logger.info({ signal }, "Shutdown initiated");
|
|
371
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
372
|
+
try {
|
|
373
|
+
db.close();
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
/* ignore */
|
|
377
|
+
}
|
|
378
|
+
logger.info({}, "Clean shutdown complete");
|
|
379
|
+
process.exit(0);
|
|
380
|
+
}
|
|
381
|
+
process.once("SIGTERM", () => void shutdown("SIGTERM"));
|
|
382
|
+
process.once("SIGINT", () => void shutdown("SIGINT"));
|
|
383
|
+
process.on("uncaughtException", (err) => {
|
|
384
|
+
logger.error({ err }, "Uncaught exception");
|
|
385
|
+
void shutdown("uncaughtException");
|
|
386
|
+
});
|
|
387
|
+
process.on("unhandledRejection", (err) => {
|
|
388
|
+
logger.error({ err }, "Unhandled rejection");
|
|
389
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Standard trust fields on paid agent responses (Dexter / fleet integrators). */
|
|
2
|
+
export type AgentTrustMeta = {
|
|
3
|
+
confidence: number;
|
|
4
|
+
checks_passed: string[];
|
|
5
|
+
sources: string[];
|
|
6
|
+
accuracy_note: string;
|
|
7
|
+
};
|
|
8
|
+
export type WithAgentTrust<T> = T & AgentTrustMeta;
|
|
9
|
+
export declare function agentTrustMeta(checks_passed: string[], options?: {
|
|
10
|
+
confidence?: number;
|
|
11
|
+
sources?: string[];
|
|
12
|
+
accuracy_note?: string;
|
|
13
|
+
}): AgentTrustMeta;
|
|
14
|
+
export declare function withAgentTrust<T extends Record<string, unknown>>(payload: T, meta: AgentTrustMeta): T & AgentTrustMeta;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const DEFAULT_NOTE = "Heuristic preflight only — not a guarantee of downstream API quality or settlement success.";
|
|
2
|
+
export function agentTrustMeta(checks_passed, options) {
|
|
3
|
+
const confidence = Math.max(0, Math.min(1, options?.confidence ?? 0.82));
|
|
4
|
+
return {
|
|
5
|
+
confidence,
|
|
6
|
+
checks_passed,
|
|
7
|
+
sources: options?.sources ?? ["x402-agent-suite-pro", "dexter-facilitator"],
|
|
8
|
+
accuracy_note: options?.accuracy_note ?? DEFAULT_NOTE,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function withAgentTrust(payload, meta) {
|
|
12
|
+
return { ...payload, ...meta };
|
|
13
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Hosts that require SIWE/SIWS before x402 payment (401/403 is expected on bare probes). */
|
|
2
|
+
export declare const KNOWN_AGENTIC_GATEWAY_HOSTS: readonly ["x402.alchemy.com", "api.cdp.coinbase.com", "x402.dexter.cash", "x402.org"];
|
|
3
|
+
export declare function isKnownAgenticGateway(host: string): boolean;
|
|
4
|
+
/** HTTP statuses that mean "auth/payment layer present" for SIWE-first gateways. */
|
|
5
|
+
export declare function isExpectedAgenticGatewayProbeStatus(status: number): boolean;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Hosts that require SIWE/SIWS before x402 payment (401/403 is expected on bare probes). */
|
|
2
|
+
export const KNOWN_AGENTIC_GATEWAY_HOSTS = [
|
|
3
|
+
"x402.alchemy.com",
|
|
4
|
+
"api.cdp.coinbase.com",
|
|
5
|
+
"x402.dexter.cash",
|
|
6
|
+
"x402.org",
|
|
7
|
+
];
|
|
8
|
+
export function isKnownAgenticGateway(host) {
|
|
9
|
+
const h = host.toLowerCase();
|
|
10
|
+
return KNOWN_AGENTIC_GATEWAY_HOSTS.some((pattern) => h === pattern || h.endsWith(`.${pattern}`));
|
|
11
|
+
}
|
|
12
|
+
/** HTTP statuses that mean "auth/payment layer present" for SIWE-first gateways. */
|
|
13
|
+
export function isExpectedAgenticGatewayProbeStatus(status) {
|
|
14
|
+
return status === 401 || status === 403 || status === 402;
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Express, Request, Response, RequestHandler } from "express";
|
|
2
|
+
type PaidFn = (amount: string, description: string) => import("express").RequestHandler;
|
|
3
|
+
/** Agentic / Bazaar crawlers often use GET or HEAD; some add a trailing slash. */
|
|
4
|
+
export declare function stripTrailingSlash(req: Request, _res: Response, next: () => void): void;
|
|
5
|
+
/**
|
|
6
|
+
* x402gle/Dexter often pay for GET after 402. Mount the same core handler as POST
|
|
7
|
+
* (payment verified on GET only — no second x402 middleware).
|
|
8
|
+
*/
|
|
9
|
+
export declare function registerAgenticProbes(app: Express, paid: PaidFn, postHandlers: Map<string, RequestHandler>): void;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { listEndpoints } from "../routes.js";
|
|
2
|
+
import { applyVerifierExampleBody } from "./apply-verifier-body.js";
|
|
3
|
+
/** Agentic / Bazaar crawlers often use GET or HEAD; some add a trailing slash. */
|
|
4
|
+
export function stripTrailingSlash(req, _res, next) {
|
|
5
|
+
if (req.path.length > 1 && req.path.endsWith("/")) {
|
|
6
|
+
const query = req.url.includes("?") ? req.url.slice(req.url.indexOf("?")) : "";
|
|
7
|
+
req.url = req.path.slice(0, -1) + query;
|
|
8
|
+
}
|
|
9
|
+
next();
|
|
10
|
+
}
|
|
11
|
+
function headWrap(core) {
|
|
12
|
+
return (req, res, next) => {
|
|
13
|
+
const origJson = res.json.bind(res);
|
|
14
|
+
res.json = ((body) => {
|
|
15
|
+
res.status(200);
|
|
16
|
+
res.end();
|
|
17
|
+
return res;
|
|
18
|
+
});
|
|
19
|
+
core(req, res, (err) => {
|
|
20
|
+
res.json = origJson;
|
|
21
|
+
next(err);
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function paidGetProbe(core) {
|
|
26
|
+
return (req, res, next) => {
|
|
27
|
+
applyVerifierExampleBody(req);
|
|
28
|
+
core(req, res, next);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* x402gle/Dexter often pay for GET after 402. Mount the same core handler as POST
|
|
33
|
+
* (payment verified on GET only — no second x402 middleware).
|
|
34
|
+
*/
|
|
35
|
+
export function registerAgenticProbes(app, paid, postHandlers) {
|
|
36
|
+
for (const ep of listEndpoints()) {
|
|
37
|
+
const [listedMethod, path] = ep.path.split(" ");
|
|
38
|
+
if (listedMethod === "GET")
|
|
39
|
+
continue;
|
|
40
|
+
const core = postHandlers.get(path);
|
|
41
|
+
if (!core)
|
|
42
|
+
continue;
|
|
43
|
+
const amount = ep.price.replace(/^\$/, "");
|
|
44
|
+
const description = `x402 paid probe for ${path} — GET/HEAD returns same JSON as POST`;
|
|
45
|
+
const paidMw = paid(amount, description);
|
|
46
|
+
app.get(path, paidMw, paidGetProbe(core));
|
|
47
|
+
app.head(path, paidMw, headWrap(paidGetProbe(core)));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { paymentResponseFromHeaders } from "./payment-response.js";
|
|
2
|
+
export type AlchemyX402Result = {
|
|
3
|
+
status: number;
|
|
4
|
+
body: string;
|
|
5
|
+
headers: Record<string, string | string[] | undefined>;
|
|
6
|
+
payment: ReturnType<typeof paymentResponseFromHeaders>;
|
|
7
|
+
ok: boolean;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Pay Alchemy x402 gateway via raw HTTPS.
|
|
11
|
+
* Node fetch rejects some x402 header names (e.g. Payment-Signature); use PAYMENT-SIGNATURE.
|
|
12
|
+
*/
|
|
13
|
+
export declare function alchemyX402Pay(url: string, body: string, privateKey: string, siweToken?: string): Promise<AlchemyX402Result>;
|
|
14
|
+
export declare function alchemyX402Fetch(url: string, init: RequestInit, privateKey: string): Promise<Response>;
|
|
15
|
+
export declare function createAlchemyX402Fetch(privateKey: string): Promise<typeof fetch>;
|
|
16
|
+
export declare const alchemyX402FetchOnce: typeof alchemyX402Fetch;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createPayment } from "@alchemy/x402";
|
|
2
|
+
import { request as httpsRequest } from "node:https";
|
|
3
|
+
import { paymentResponseFromHeaders } from "./payment-response.js";
|
|
4
|
+
function rawHttpsPost(url, headers, body) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
const u = new URL(url);
|
|
7
|
+
const req = httpsRequest({
|
|
8
|
+
hostname: u.hostname,
|
|
9
|
+
path: `${u.pathname}${u.search}`,
|
|
10
|
+
method: "POST",
|
|
11
|
+
headers: {
|
|
12
|
+
...headers,
|
|
13
|
+
"content-length": String(Buffer.byteLength(body)),
|
|
14
|
+
},
|
|
15
|
+
}, (res) => {
|
|
16
|
+
let data = "";
|
|
17
|
+
res.on("data", (chunk) => {
|
|
18
|
+
data += chunk;
|
|
19
|
+
});
|
|
20
|
+
res.on("end", () => {
|
|
21
|
+
resolve({ status: res.statusCode ?? 0, headers: res.headers, body: data });
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
req.on("error", reject);
|
|
25
|
+
req.write(body);
|
|
26
|
+
req.end();
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function headersToFetchLike(h) {
|
|
30
|
+
const out = new Headers();
|
|
31
|
+
for (const [k, v] of Object.entries(h)) {
|
|
32
|
+
if (typeof v === "string")
|
|
33
|
+
out.set(k, v);
|
|
34
|
+
else if (Array.isArray(v))
|
|
35
|
+
out.set(k, v.join(", "));
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
function paymentRequiredFrom402(headers, bodyText) {
|
|
40
|
+
const header = (typeof headers["payment-required"] === "string" && headers["payment-required"]) ||
|
|
41
|
+
(typeof headers["PAYMENT-REQUIRED"] === "string" && headers["PAYMENT-REQUIRED"]);
|
|
42
|
+
if (header)
|
|
43
|
+
return header;
|
|
44
|
+
return Buffer.from(bodyText, "utf8").toString("base64");
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Pay Alchemy x402 gateway via raw HTTPS.
|
|
48
|
+
* Node fetch rejects some x402 header names (e.g. Payment-Signature); use PAYMENT-SIGNATURE.
|
|
49
|
+
*/
|
|
50
|
+
export async function alchemyX402Pay(url, body, privateKey, siweToken) {
|
|
51
|
+
const baseHeaders = {
|
|
52
|
+
"content-type": "application/json",
|
|
53
|
+
accept: "application/json",
|
|
54
|
+
};
|
|
55
|
+
if (siweToken)
|
|
56
|
+
baseHeaders.authorization = `SIWE ${siweToken}`;
|
|
57
|
+
const first = await rawHttpsPost(url, baseHeaders, body);
|
|
58
|
+
if (first.status !== 402) {
|
|
59
|
+
return {
|
|
60
|
+
status: first.status,
|
|
61
|
+
body: first.body,
|
|
62
|
+
headers: first.headers,
|
|
63
|
+
payment: paymentResponseFromHeaders(headersToFetchLike(first.headers)),
|
|
64
|
+
ok: first.status >= 200 && first.status < 300,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const paymentRequiredHeader = paymentRequiredFrom402(first.headers, first.body);
|
|
68
|
+
const paymentSig = await createPayment({ privateKey, paymentRequiredHeader });
|
|
69
|
+
const paid = await rawHttpsPost(url, {
|
|
70
|
+
...baseHeaders,
|
|
71
|
+
"PAYMENT-SIGNATURE": paymentSig,
|
|
72
|
+
}, body);
|
|
73
|
+
const payment = paymentResponseFromHeaders(headersToFetchLike(paid.headers));
|
|
74
|
+
const paidOk = paid.status >= 200 && paid.status < 300;
|
|
75
|
+
const paymentSettled = payment?.raw != null && payment.raw.success === true;
|
|
76
|
+
return {
|
|
77
|
+
status: paid.status,
|
|
78
|
+
body: paid.body,
|
|
79
|
+
headers: paid.headers,
|
|
80
|
+
payment,
|
|
81
|
+
ok: paidOk || paymentSettled,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export async function alchemyX402Fetch(url, init, privateKey) {
|
|
85
|
+
const body = typeof init.body === "string" ? init.body : "";
|
|
86
|
+
const result = await alchemyX402Pay(url, body, privateKey);
|
|
87
|
+
return new Response(result.body, {
|
|
88
|
+
status: result.status,
|
|
89
|
+
headers: headersToFetchLike(result.headers),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
export async function createAlchemyX402Fetch(privateKey) {
|
|
93
|
+
return async (input, init) => alchemyX402Fetch(String(input), init ?? {}, privateKey);
|
|
94
|
+
}
|
|
95
|
+
export const alchemyX402FetchOnce = alchemyX402Fetch;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Request } from "express";
|
|
2
|
+
/** Normalize grader policy shapes (`[{host:"x.com"}]` → `["x.com"]`). */
|
|
3
|
+
export declare function normalizePolicyHosts(policy: unknown): unknown;
|
|
4
|
+
/** Safe merge for route-level zod fallback (ignore unknown/incompatible grader keys). */
|
|
5
|
+
export declare function mergeCompatibleProbeInput(example: Record<string, unknown>, input: Record<string, unknown>): Record<string, unknown>;
|
|
6
|
+
/** Merge canonical bodies so x402gle / Dexter paid probes get 200 instead of 400 */
|
|
7
|
+
export declare function applyVerifierExampleBody(req: Request): void;
|