trustsource 0.2.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/.vscode/settings.json +9 -0
- package/LICENSE +21 -0
- package/Procfile +1 -0
- package/README.md +142 -0
- package/mcp-server/.env.example +7 -0
- package/mcp-server/README.md +116 -0
- package/mcp-server/package-lock.json +1815 -0
- package/mcp-server/package.json +58 -0
- package/mcp-server/smithery.yaml +45 -0
- package/mcp-server/src/index.ts +171 -0
- package/mcp-server/tsconfig.json +19 -0
- package/package.json +35 -0
- package/public/index.html +954 -0
- package/railway.json +11 -0
- package/skills/trustsource-domain/skill.md +182 -0
- package/src/openapi.ts +660 -0
- package/src/routes/headers.ts +448 -0
- package/src/routes/robots.ts +455 -0
- package/src/routes/sslcheck.ts +408 -0
- package/src/routes/trustscore.ts +268 -0
- package/src/server.ts +318 -0
- package/src/types/whois-json.d.ts +4 -0
- package/tsconfig.json +13 -0
package/src/openapi.ts
ADDED
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
import { Router, Request, Response } from "express";
|
|
2
|
+
|
|
3
|
+
const router = Router();
|
|
4
|
+
|
|
5
|
+
const spec = {
|
|
6
|
+
openapi: "3.1.0",
|
|
7
|
+
info: {
|
|
8
|
+
title: "TrustSource API",
|
|
9
|
+
version: "1.0.0",
|
|
10
|
+
description: "x402-powered domain, SSL, security, and crawler-policy intelligence for AI agents. Four endpoints return structured trust intelligence on any domain — no API keys, no accounts. Pay per use with USDC via the x402 protocol on Base Mainnet.",
|
|
11
|
+
contact: {
|
|
12
|
+
url: "https://trustsource.cc",
|
|
13
|
+
},
|
|
14
|
+
license: {
|
|
15
|
+
name: "Commercial",
|
|
16
|
+
url: "https://trustsource.cc/terms",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
servers: [
|
|
20
|
+
{
|
|
21
|
+
url: "https://api.trustsource.cc",
|
|
22
|
+
description: "Production (Base Mainnet) — DNS-only, x402-aware",
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
paths: {
|
|
26
|
+
"/": {
|
|
27
|
+
get: {
|
|
28
|
+
operationId: "getApiInfo",
|
|
29
|
+
summary: "API discovery info",
|
|
30
|
+
description: "Returns API metadata, available endpoints, pricing, and payment details. Free — no payment required.",
|
|
31
|
+
tags: ["Discovery"],
|
|
32
|
+
responses: {
|
|
33
|
+
"200": {
|
|
34
|
+
description: "API info",
|
|
35
|
+
content: {
|
|
36
|
+
"application/json": {
|
|
37
|
+
schema: { $ref: "#/components/schemas/ApiInfo" },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
"/health": {
|
|
45
|
+
get: {
|
|
46
|
+
operationId: "getHealth",
|
|
47
|
+
summary: "Health check",
|
|
48
|
+
description: "Returns server status. Free — no payment required.",
|
|
49
|
+
tags: ["Discovery"],
|
|
50
|
+
responses: {
|
|
51
|
+
"200": {
|
|
52
|
+
description: "Server is healthy",
|
|
53
|
+
content: {
|
|
54
|
+
"application/json": {
|
|
55
|
+
schema: {
|
|
56
|
+
type: "object",
|
|
57
|
+
properties: {
|
|
58
|
+
status: { type: "string", example: "ok" },
|
|
59
|
+
timestamp: { type: "string", format: "date-time" },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
"/openapi.json": {
|
|
69
|
+
get: {
|
|
70
|
+
operationId: "getOpenApiSpec",
|
|
71
|
+
summary: "OpenAPI specification",
|
|
72
|
+
description: "Returns this machine-readable OpenAPI 3.1 spec. Free — no payment required.",
|
|
73
|
+
tags: ["Discovery"],
|
|
74
|
+
responses: {
|
|
75
|
+
"200": {
|
|
76
|
+
description: "OpenAPI spec",
|
|
77
|
+
content: {
|
|
78
|
+
"application/json": {
|
|
79
|
+
schema: { type: "object" },
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
"/trustscore": {
|
|
87
|
+
get: {
|
|
88
|
+
operationId: "getTrustScore",
|
|
89
|
+
security: [{ x402: [] }],
|
|
90
|
+
summary: "Domain trust and safety score",
|
|
91
|
+
description: [
|
|
92
|
+
"Returns a 0–100 trust score for any domain or URL.",
|
|
93
|
+
"Analyzes domain age (WHOIS), TLD risk, DNS presence, and registrar reputation.",
|
|
94
|
+
"Returns structured JSON including a tier (TRUSTED/MODERATE/CAUTION/HIGH_RISK),",
|
|
95
|
+
"full scoring breakdown, and detailed signals.",
|
|
96
|
+
"",
|
|
97
|
+
"**Payment:** 0.003 USDC per call via x402 protocol (Base Mainnet).",
|
|
98
|
+
"Clients must handle HTTP 402 responses by paying the specified amount",
|
|
99
|
+
"and retrying with the payment proof header.",
|
|
100
|
+
"",
|
|
101
|
+
"**Caching:** Results are cached for 1 hour per domain.",
|
|
102
|
+
].join("\n"),
|
|
103
|
+
tags: ["Trust"],
|
|
104
|
+
parameters: [
|
|
105
|
+
{
|
|
106
|
+
name: "domain",
|
|
107
|
+
in: "query",
|
|
108
|
+
description: "Domain name to score (e.g. example.com)",
|
|
109
|
+
required: false,
|
|
110
|
+
schema: {
|
|
111
|
+
type: "string",
|
|
112
|
+
maxLength: 253,
|
|
113
|
+
example: "google.com",
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: "url",
|
|
118
|
+
in: "query",
|
|
119
|
+
description: "Full URL to score — domain is extracted automatically (e.g. https://example.com/path)",
|
|
120
|
+
required: false,
|
|
121
|
+
schema: {
|
|
122
|
+
type: "string",
|
|
123
|
+
example: "https://example.com/some/page",
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
responses: {
|
|
128
|
+
"200": {
|
|
129
|
+
description: "Trust score returned successfully",
|
|
130
|
+
content: {
|
|
131
|
+
"application/json": {
|
|
132
|
+
schema: { $ref: "#/components/schemas/TrustScoreResponse" },
|
|
133
|
+
example: {
|
|
134
|
+
domain: "google.com",
|
|
135
|
+
score: 90,
|
|
136
|
+
maxScore: 100,
|
|
137
|
+
tier: "TRUSTED",
|
|
138
|
+
breakdown: {
|
|
139
|
+
domainAge: 30,
|
|
140
|
+
tld: 20,
|
|
141
|
+
dnsPresence: 30,
|
|
142
|
+
registrar: 10,
|
|
143
|
+
},
|
|
144
|
+
details: {
|
|
145
|
+
age: {
|
|
146
|
+
days: 10477,
|
|
147
|
+
label: "established (5+ years)",
|
|
148
|
+
created: "1997-09-15T07:00:00+0000",
|
|
149
|
+
expires: null,
|
|
150
|
+
},
|
|
151
|
+
tld: ".com",
|
|
152
|
+
dns: {
|
|
153
|
+
hasARecord: true,
|
|
154
|
+
hasMxRecord: true,
|
|
155
|
+
mxRecords: ["smtp.google.com"],
|
|
156
|
+
},
|
|
157
|
+
registrar: "markmonitor, inc.",
|
|
158
|
+
},
|
|
159
|
+
meta: {
|
|
160
|
+
checkedAt: "2026-05-23T12:00:00.000Z",
|
|
161
|
+
apiVersion: "1.0",
|
|
162
|
+
paidWith: "x402/USDC",
|
|
163
|
+
cached: false,
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
"400": {
|
|
170
|
+
description: "Invalid or missing domain parameter",
|
|
171
|
+
content: {
|
|
172
|
+
"application/json": {
|
|
173
|
+
schema: { $ref: "#/components/schemas/ErrorResponse" },
|
|
174
|
+
example: { error: "Missing parameter", message: "Provide ?domain=example.com or ?url=https://example.com" },
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
"402": {
|
|
179
|
+
description: "Payment required — client must pay 0.003 USDC via x402 and retry",
|
|
180
|
+
headers: {
|
|
181
|
+
"PAYMENT-REQUIRED": {
|
|
182
|
+
description: "Base64-encoded JSON payment requirements including price, network, and payTo address",
|
|
183
|
+
schema: { type: "string" },
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
"429": {
|
|
188
|
+
description: "Rate limit exceeded — max 60 requests per minute",
|
|
189
|
+
content: {
|
|
190
|
+
"application/json": {
|
|
191
|
+
schema: { $ref: "#/components/schemas/ErrorResponse" },
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
"500": {
|
|
196
|
+
description: "Lookup failed — WHOIS or DNS error",
|
|
197
|
+
content: {
|
|
198
|
+
"application/json": {
|
|
199
|
+
schema: { $ref: "#/components/schemas/ErrorResponse" },
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
"/sslcheck": {
|
|
207
|
+
get: {
|
|
208
|
+
operationId: "getSslCheck",
|
|
209
|
+
security: [{ x402: [] }],
|
|
210
|
+
summary: "SSL/TLS certificate intelligence",
|
|
211
|
+
description: [
|
|
212
|
+
"Live TLS handshake to the target domain. Returns 0–100 SSL score, certificate",
|
|
213
|
+
"chain details, expiry, trusted CA detection, TLS protocol version, cipher quality,",
|
|
214
|
+
"and security warnings.",
|
|
215
|
+
"",
|
|
216
|
+
"**Payment:** 0.002 USDC per call via x402 protocol (Base Mainnet).",
|
|
217
|
+
"**Caching:** Results are cached for 1 hour per domain.",
|
|
218
|
+
].join("\n"),
|
|
219
|
+
tags: ["Trust"],
|
|
220
|
+
parameters: [
|
|
221
|
+
{
|
|
222
|
+
name: "domain",
|
|
223
|
+
in: "query",
|
|
224
|
+
description: "Domain name to check (e.g. example.com)",
|
|
225
|
+
required: false,
|
|
226
|
+
schema: { type: "string", maxLength: 253, example: "google.com" },
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
name: "url",
|
|
230
|
+
in: "query",
|
|
231
|
+
description: "Full URL — domain extracted automatically",
|
|
232
|
+
required: false,
|
|
233
|
+
schema: { type: "string", example: "https://example.com/page" },
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
responses: {
|
|
237
|
+
"200": {
|
|
238
|
+
description: "SSL check completed",
|
|
239
|
+
content: {
|
|
240
|
+
"application/json": {
|
|
241
|
+
schema: { $ref: "#/components/schemas/SslCheckResponse" },
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
"400": { description: "Invalid or missing domain parameter" },
|
|
246
|
+
"402": {
|
|
247
|
+
description: "Payment required — 0.002 USDC via x402",
|
|
248
|
+
headers: {
|
|
249
|
+
"PAYMENT-REQUIRED": {
|
|
250
|
+
description: "Base64-encoded JSON payment requirements",
|
|
251
|
+
schema: { type: "string" },
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
"429": { description: "Rate limit exceeded" },
|
|
256
|
+
"502": { description: "TLS handshake failed (timeout, no cert, connection refused)" },
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
"/headers": {
|
|
261
|
+
get: {
|
|
262
|
+
operationId: "getSecurityHeaders",
|
|
263
|
+
security: [{ x402: [] }],
|
|
264
|
+
summary: "HTTP security header audit",
|
|
265
|
+
description: [
|
|
266
|
+
"Fetches the target URL and audits HTTP security headers (HSTS, CSP, X-Frame-Options,",
|
|
267
|
+
"X-Content-Type-Options, Referrer-Policy, Permissions-Policy, Cross-Origin-*). Returns",
|
|
268
|
+
"a defense-in-depth letter grade A+ through F with per-header analysis and notes.",
|
|
269
|
+
"",
|
|
270
|
+
"**Note:** This is a hardening signal, not an active vulnerability scan. Many",
|
|
271
|
+
"legitimate marketing sites grade F.",
|
|
272
|
+
"",
|
|
273
|
+
"**Payment:** 0.003 USDC per call via x402 protocol (Base Mainnet).",
|
|
274
|
+
"**Caching:** Results cached up to 12 hours per URL.",
|
|
275
|
+
].join("\n"),
|
|
276
|
+
tags: ["Trust"],
|
|
277
|
+
parameters: [
|
|
278
|
+
{
|
|
279
|
+
name: "url",
|
|
280
|
+
in: "query",
|
|
281
|
+
description: "Full URL to audit (e.g. https://example.com)",
|
|
282
|
+
required: true,
|
|
283
|
+
schema: { type: "string", maxLength: 2048, example: "https://example.com" },
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
responses: {
|
|
287
|
+
"200": {
|
|
288
|
+
description: "Header audit completed",
|
|
289
|
+
content: {
|
|
290
|
+
"application/json": {
|
|
291
|
+
schema: { $ref: "#/components/schemas/HeadersResponse" },
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
"400": { description: "Invalid or missing url parameter" },
|
|
296
|
+
"402": {
|
|
297
|
+
description: "Payment required — 0.003 USDC via x402",
|
|
298
|
+
headers: {
|
|
299
|
+
"PAYMENT-REQUIRED": {
|
|
300
|
+
description: "Base64-encoded JSON payment requirements",
|
|
301
|
+
schema: { type: "string" },
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
"429": { description: "Rate limit exceeded" },
|
|
306
|
+
"502": { description: "Fetch failed — target unreachable, blocked, or returned no usable response" },
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
"/robots": {
|
|
311
|
+
get: {
|
|
312
|
+
operationId: "getRobots",
|
|
313
|
+
security: [{ x402: [] }],
|
|
314
|
+
summary: "robots.txt + AI bot policy detection",
|
|
315
|
+
description: [
|
|
316
|
+
"Fetches and parses the target domain's robots.txt and detects policies across",
|
|
317
|
+
"24 known AI crawlers (GPTBot, ClaudeBot, PerplexityBot, Google-Extended, CCBot,",
|
|
318
|
+
"Bytespider, and more). Returns parsed rules, sitemap URLs, and an AI-friendliness",
|
|
319
|
+
"tier (OPEN / SELECTIVE / BLOCKED_AI / BLOCKED_ALL / NO_ROBOTS_TXT).",
|
|
320
|
+
"",
|
|
321
|
+
"**Payment:** 0.002 USDC per call via x402 protocol (Base Mainnet).",
|
|
322
|
+
"**Caching:** Results cached up to 12 hours per domain.",
|
|
323
|
+
].join("\n"),
|
|
324
|
+
tags: ["Trust"],
|
|
325
|
+
parameters: [
|
|
326
|
+
{
|
|
327
|
+
name: "domain",
|
|
328
|
+
in: "query",
|
|
329
|
+
description: "Domain name (e.g. example.com)",
|
|
330
|
+
required: true,
|
|
331
|
+
schema: { type: "string", maxLength: 253, example: "example.com" },
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
responses: {
|
|
335
|
+
"200": {
|
|
336
|
+
description: "robots.txt analysis completed",
|
|
337
|
+
content: {
|
|
338
|
+
"application/json": {
|
|
339
|
+
schema: { $ref: "#/components/schemas/RobotsResponse" },
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
"400": { description: "Invalid or missing domain parameter" },
|
|
344
|
+
"402": {
|
|
345
|
+
description: "Payment required — 0.002 USDC via x402",
|
|
346
|
+
headers: {
|
|
347
|
+
"PAYMENT-REQUIRED": {
|
|
348
|
+
description: "Base64-encoded JSON payment requirements",
|
|
349
|
+
schema: { type: "string" },
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
"429": { description: "Rate limit exceeded" },
|
|
354
|
+
"502": { description: "robots.txt fetch failed" },
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
components: {
|
|
360
|
+
schemas: {
|
|
361
|
+
TrustScoreResponse: {
|
|
362
|
+
type: "object",
|
|
363
|
+
required: ["domain", "score", "maxScore", "tier", "breakdown", "details", "meta"],
|
|
364
|
+
properties: {
|
|
365
|
+
domain: { type: "string", description: "The domain that was scored", example: "google.com" },
|
|
366
|
+
score: { type: "integer", description: "Overall trust score (0–100)", example: 90 },
|
|
367
|
+
maxScore: { type: "integer", description: "Maximum possible score", example: 100 },
|
|
368
|
+
tier: {
|
|
369
|
+
type: "string",
|
|
370
|
+
enum: ["TRUSTED", "MODERATE", "CAUTION", "HIGH_RISK"],
|
|
371
|
+
description: "Risk tier derived from score. TRUSTED=75+, MODERATE=50–74, CAUTION=25–49, HIGH_RISK=0–24",
|
|
372
|
+
example: "TRUSTED",
|
|
373
|
+
},
|
|
374
|
+
breakdown: {
|
|
375
|
+
type: "object",
|
|
376
|
+
description: "Score contribution from each signal (all values sum to score)",
|
|
377
|
+
properties: {
|
|
378
|
+
domainAge: { type: "integer", description: "Domain age score (0–30)", example: 30 },
|
|
379
|
+
tld: { type: "integer", description: "TLD risk score (0–20)", example: 20 },
|
|
380
|
+
dnsPresence: { type: "integer", description: "DNS presence score (0–30)", example: 30 },
|
|
381
|
+
registrar: { type: "integer", description: "Registrar score (10–20)", example: 10 },
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
details: {
|
|
385
|
+
type: "object",
|
|
386
|
+
properties: {
|
|
387
|
+
age: {
|
|
388
|
+
type: "object",
|
|
389
|
+
properties: {
|
|
390
|
+
days: { type: "integer", description: "Domain age in days (-1 if unknown)", example: 10477 },
|
|
391
|
+
label: { type: "string", description: "Human-readable age label", example: "established (5+ years)" },
|
|
392
|
+
created: { type: ["string", "null"], description: "Creation date from WHOIS", example: "1997-09-15T07:00:00+0000" },
|
|
393
|
+
expires: { type: ["string", "null"], description: "Expiry date from WHOIS", example: null },
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
tld: { type: "string", description: "Top-level domain", example: ".com" },
|
|
397
|
+
dns: {
|
|
398
|
+
type: "object",
|
|
399
|
+
properties: {
|
|
400
|
+
hasARecord: { type: "boolean", description: "Domain resolves to an IP", example: true },
|
|
401
|
+
hasMxRecord: { type: "boolean", description: "Domain has email (MX record)", example: true },
|
|
402
|
+
mxRecords: { type: "array", items: { type: "string" }, example: ["smtp.google.com"] },
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
registrar: { type: "string", description: "Domain registrar name from WHOIS", example: "markmonitor, inc." },
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
meta: {
|
|
409
|
+
type: "object",
|
|
410
|
+
properties: {
|
|
411
|
+
checkedAt: { type: "string", format: "date-time", description: "ISO8601 timestamp of the check" },
|
|
412
|
+
apiVersion: { type: "string", description: "API version", example: "1.0" },
|
|
413
|
+
paidWith: { type: "string", description: "Payment method", example: "x402/USDC" },
|
|
414
|
+
cached: { type: "boolean", description: "True if result was served from 1-hour cache", example: false },
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
SslCheckResponse: {
|
|
420
|
+
type: "object",
|
|
421
|
+
required: ["domain", "score", "maxScore", "tier", "breakdown", "certificate", "chain", "connection", "meta"],
|
|
422
|
+
properties: {
|
|
423
|
+
domain: { type: "string", example: "google.com" },
|
|
424
|
+
score: { type: "integer", example: 100 },
|
|
425
|
+
maxScore: { type: "integer", example: 100 },
|
|
426
|
+
tier: {
|
|
427
|
+
type: "string",
|
|
428
|
+
enum: ["VALID", "WEAK", "EXPIRING", "EXPIRED", "UNTRUSTED", "INVALID"],
|
|
429
|
+
description: "Risk tier based on certificate state and score",
|
|
430
|
+
},
|
|
431
|
+
breakdown: {
|
|
432
|
+
type: "object",
|
|
433
|
+
properties: {
|
|
434
|
+
chainValid: { type: "integer", description: "Certificate chain validity (0–30)" },
|
|
435
|
+
trustedCa: { type: "integer", description: "Trusted root CA (0–25)" },
|
|
436
|
+
notExpired: { type: "integer", description: "Expiry margin (0–25)" },
|
|
437
|
+
strongCrypto: { type: "integer", description: "Signature strength (0–10)" },
|
|
438
|
+
modernTls: { type: "integer", description: "TLS protocol version (0–10)" },
|
|
439
|
+
},
|
|
440
|
+
},
|
|
441
|
+
warnings: { type: "array", items: { type: "string" }, example: [] },
|
|
442
|
+
certificate: {
|
|
443
|
+
type: "object",
|
|
444
|
+
properties: {
|
|
445
|
+
subject: { type: "string", example: "*.google.com" },
|
|
446
|
+
issuer: { type: "string", example: "Google Trust Services" },
|
|
447
|
+
validFrom: { type: "string", format: "date-time" },
|
|
448
|
+
validTo: { type: "string", format: "date-time" },
|
|
449
|
+
daysRemaining: { type: "integer", example: 67 },
|
|
450
|
+
signatureAlgorithm: { type: "string", example: "RSA-SHA256" },
|
|
451
|
+
san: { type: "array", items: { type: "string" } },
|
|
452
|
+
fingerprint256: { type: "string" },
|
|
453
|
+
serialNumber: { type: "string" },
|
|
454
|
+
isSelfSigned: { type: "boolean" },
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
chain: {
|
|
458
|
+
type: "object",
|
|
459
|
+
properties: {
|
|
460
|
+
depth: { type: "integer", example: 3 },
|
|
461
|
+
valid: { type: "boolean" },
|
|
462
|
+
trusted: { type: "boolean" },
|
|
463
|
+
rootCa: { type: ["string", "null"] },
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
connection: {
|
|
467
|
+
type: "object",
|
|
468
|
+
properties: {
|
|
469
|
+
protocol: { type: ["string", "null"], example: "TLSv1.3" },
|
|
470
|
+
cipher: { type: ["object", "null"] },
|
|
471
|
+
authorized: { type: "boolean" },
|
|
472
|
+
authError: { type: ["string", "null"] },
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
meta: {
|
|
476
|
+
type: "object",
|
|
477
|
+
properties: {
|
|
478
|
+
checkedAt: { type: "string", format: "date-time" },
|
|
479
|
+
apiVersion: { type: "string" },
|
|
480
|
+
paidWith: { type: "string", example: "x402/USDC" },
|
|
481
|
+
cached: { type: "boolean" },
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
},
|
|
485
|
+
},
|
|
486
|
+
HeadersResponse: {
|
|
487
|
+
type: "object",
|
|
488
|
+
required: ["url", "hostname", "grade", "score", "maxScore", "analysis", "warnings", "response", "meta"],
|
|
489
|
+
properties: {
|
|
490
|
+
url: { type: "string", description: "The final URL after redirects", example: "https://example.com/" },
|
|
491
|
+
hostname: { type: "string", description: "Hostname extracted from the URL", example: "example.com" },
|
|
492
|
+
grade: {
|
|
493
|
+
type: "string",
|
|
494
|
+
enum: ["A+", "A", "B", "C", "D", "F"],
|
|
495
|
+
description: "Defense-in-depth letter grade. A+/A = enterprise-hardened, B = decent, C/D = legacy, F = unhardened.",
|
|
496
|
+
example: "A",
|
|
497
|
+
},
|
|
498
|
+
score: { type: "integer", description: "Numeric score backing the grade", example: 82 },
|
|
499
|
+
maxScore: { type: "integer", description: "Maximum possible score", example: 100 },
|
|
500
|
+
analysis: {
|
|
501
|
+
type: "object",
|
|
502
|
+
description: "Per-header analysis. Each header is keyed by its lower-case name.",
|
|
503
|
+
additionalProperties: { $ref: "#/components/schemas/HeaderAnalysis" },
|
|
504
|
+
example: {
|
|
505
|
+
"strict-transport-security": {
|
|
506
|
+
present: true,
|
|
507
|
+
value: "max-age=31536000; includeSubDomains",
|
|
508
|
+
score: 20,
|
|
509
|
+
maxScore: 20,
|
|
510
|
+
notes: ["HSTS enabled with 1-year max-age and subdomain coverage"],
|
|
511
|
+
},
|
|
512
|
+
},
|
|
513
|
+
},
|
|
514
|
+
warnings: {
|
|
515
|
+
type: "array",
|
|
516
|
+
items: { type: "string" },
|
|
517
|
+
description: "Human-readable issues flagged during the audit",
|
|
518
|
+
example: ["Server header reveals nginx version"],
|
|
519
|
+
},
|
|
520
|
+
response: {
|
|
521
|
+
type: "object",
|
|
522
|
+
properties: {
|
|
523
|
+
status: { type: "integer", description: "Final HTTP status code", example: 200 },
|
|
524
|
+
redirects: { type: "integer", description: "Number of redirects followed", example: 1 },
|
|
525
|
+
},
|
|
526
|
+
},
|
|
527
|
+
meta: {
|
|
528
|
+
type: "object",
|
|
529
|
+
properties: {
|
|
530
|
+
checkedAt: { type: "string", format: "date-time" },
|
|
531
|
+
apiVersion: { type: "string" },
|
|
532
|
+
paidWith: { type: "string", example: "x402/USDC" },
|
|
533
|
+
cached: { type: "boolean" },
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
HeaderAnalysis: {
|
|
539
|
+
type: "object",
|
|
540
|
+
required: ["present", "value", "score", "maxScore", "notes"],
|
|
541
|
+
properties: {
|
|
542
|
+
present: { type: "boolean", description: "True if the header is present in the response" },
|
|
543
|
+
value: { type: ["string", "null"], description: "Raw header value or null if absent" },
|
|
544
|
+
score: { type: "integer", description: "Points awarded for this header's configuration" },
|
|
545
|
+
maxScore: { type: "integer", description: "Maximum points this header could contribute" },
|
|
546
|
+
notes: { type: "array", items: { type: "string" }, description: "Human-readable observations" },
|
|
547
|
+
},
|
|
548
|
+
},
|
|
549
|
+
RobotsResponse: {
|
|
550
|
+
type: "object",
|
|
551
|
+
required: ["domain", "exists", "tier", "aiFriendly", "meta"],
|
|
552
|
+
properties: {
|
|
553
|
+
domain: { type: "string", example: "example.com" },
|
|
554
|
+
exists: { type: "boolean", description: "True if a robots.txt file was found", example: true },
|
|
555
|
+
tier: {
|
|
556
|
+
type: "string",
|
|
557
|
+
enum: ["OPEN", "SELECTIVE", "BLOCKED_AI", "BLOCKED_ALL", "NO_ROBOTS_TXT"],
|
|
558
|
+
description: "Classification of overall crawler policy",
|
|
559
|
+
},
|
|
560
|
+
aiFriendly: {
|
|
561
|
+
type: "boolean",
|
|
562
|
+
description: "True if AI training crawlers are not broadly blocked",
|
|
563
|
+
example: true,
|
|
564
|
+
},
|
|
565
|
+
summary: {
|
|
566
|
+
type: ["object", "null"],
|
|
567
|
+
properties: {
|
|
568
|
+
userAgentGroups: { type: "integer", description: "Number of User-agent groups parsed", example: 3 },
|
|
569
|
+
sitemaps: { type: "integer", description: "Number of Sitemap declarations", example: 1 },
|
|
570
|
+
rawLines: { type: "integer", description: "Total non-empty lines parsed", example: 42 },
|
|
571
|
+
truncated: { type: "boolean", description: "True if response exceeded 100KB body cap" },
|
|
572
|
+
hasParseErrors: { type: "boolean", description: "True if any directive failed to parse" },
|
|
573
|
+
},
|
|
574
|
+
},
|
|
575
|
+
ai: {
|
|
576
|
+
type: ["object", "null"],
|
|
577
|
+
description: "Per-AI-bot policy analysis across 24 known crawlers",
|
|
578
|
+
properties: {
|
|
579
|
+
globalBlock: { type: "boolean", description: "True if User-agent: * Disallow: / is present" },
|
|
580
|
+
globalAllow: { type: "boolean", description: "True if User-agent: * Allow: / is present" },
|
|
581
|
+
knownBotsChecked: { type: "integer", description: "Count of AI bots evaluated (currently 24)", example: 24 },
|
|
582
|
+
knownBotsBlocked: { type: "integer", description: "Bots with full disallow rules", example: 5 },
|
|
583
|
+
knownBotsPartial: { type: "integer", description: "Bots with partial disallow rules", example: 2 },
|
|
584
|
+
policies: {
|
|
585
|
+
type: "array",
|
|
586
|
+
description: "Per-bot policy details",
|
|
587
|
+
items: {
|
|
588
|
+
type: "object",
|
|
589
|
+
properties: {
|
|
590
|
+
userAgent: { type: "string", example: "GPTBot" },
|
|
591
|
+
blocked: { type: "boolean" },
|
|
592
|
+
partial: { type: "boolean" },
|
|
593
|
+
disallow: { type: "array", items: { type: "string" } },
|
|
594
|
+
allow: { type: "array", items: { type: "string" } },
|
|
595
|
+
},
|
|
596
|
+
},
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
},
|
|
600
|
+
sitemaps: { type: "array", items: { type: "string" }, example: ["https://example.com/sitemap.xml"] },
|
|
601
|
+
userAgents: { type: "array", items: { type: "string" }, example: ["*", "GPTBot", "Googlebot"] },
|
|
602
|
+
response: {
|
|
603
|
+
type: "object",
|
|
604
|
+
properties: {
|
|
605
|
+
status: { type: "integer", description: "HTTP status of the robots.txt fetch", example: 200 },
|
|
606
|
+
},
|
|
607
|
+
},
|
|
608
|
+
meta: {
|
|
609
|
+
type: "object",
|
|
610
|
+
properties: {
|
|
611
|
+
checkedAt: { type: "string", format: "date-time" },
|
|
612
|
+
apiVersion: { type: "string" },
|
|
613
|
+
paidWith: { type: "string", example: "x402/USDC" },
|
|
614
|
+
cached: { type: "boolean" },
|
|
615
|
+
},
|
|
616
|
+
},
|
|
617
|
+
},
|
|
618
|
+
},
|
|
619
|
+
ErrorResponse: {
|
|
620
|
+
type: "object",
|
|
621
|
+
properties: {
|
|
622
|
+
error: { type: "string", example: "Invalid domain" },
|
|
623
|
+
message: { type: "string", example: "Must be a valid public domain (e.g. example.com)" },
|
|
624
|
+
},
|
|
625
|
+
},
|
|
626
|
+
ApiInfo: {
|
|
627
|
+
type: "object",
|
|
628
|
+
properties: {
|
|
629
|
+
name: { type: "string", example: "TrustSource API" },
|
|
630
|
+
description: { type: "string" },
|
|
631
|
+
version: { type: "string", example: "0.3.0" },
|
|
632
|
+
endpoints: { type: "object" },
|
|
633
|
+
payment: { type: "object" },
|
|
634
|
+
links: { type: "object" },
|
|
635
|
+
},
|
|
636
|
+
},
|
|
637
|
+
},
|
|
638
|
+
securitySchemes: {
|
|
639
|
+
x402: {
|
|
640
|
+
type: "http",
|
|
641
|
+
scheme: "x402",
|
|
642
|
+
description: "Pay-per-use via x402 protocol. On a 402 response, read the PAYMENT-REQUIRED header, sign a USDC transfer on Base Mainnet, and retry with the X-PAYMENT header.",
|
|
643
|
+
},
|
|
644
|
+
},
|
|
645
|
+
},
|
|
646
|
+
"x-x402": {
|
|
647
|
+
protocol: "x402",
|
|
648
|
+
version: "2",
|
|
649
|
+
currency: "USDC",
|
|
650
|
+
network: "eip155:8453",
|
|
651
|
+
facilitator: "https://api.cdp.coinbase.com/platform/v2/x402",
|
|
652
|
+
discovery: "https://agentic.market",
|
|
653
|
+
},
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
router.get("/openapi.json", (_req: Request, res: Response) => {
|
|
657
|
+
res.json(spec);
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
export default router;
|