@dupecom/botcha-cloudflare 0.2.0 → 0.3.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/README.md +17 -1
- package/dist/badge.d.ts +57 -0
- package/dist/badge.d.ts.map +1 -0
- package/dist/badge.js +388 -0
- package/dist/challenges.d.ts +84 -0
- package/dist/challenges.d.ts.map +1 -1
- package/dist/challenges.js +391 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +386 -19
- package/dist/routes/stream.d.ts +17 -0
- package/dist/routes/stream.d.ts.map +1 -0
- package/dist/routes/stream.js +242 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ Reverse CAPTCHA that verifies AI agents and blocks humans. Running at the edge.
|
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
29
|
# Clone the repo
|
|
30
|
-
git clone https://github.com/
|
|
30
|
+
git clone https://github.com/dupe-com/botcha
|
|
31
31
|
cd botcha/packages/cloudflare-workers
|
|
32
32
|
|
|
33
33
|
# Install dependencies
|
|
@@ -96,8 +96,23 @@ Rate limit headers:
|
|
|
96
96
|
| `/v1/challenges/:id/verify` | POST | Verify challenge (no JWT) |
|
|
97
97
|
| `/v1/token` | GET | Get challenge for JWT flow |
|
|
98
98
|
| `/v1/token/verify` | POST | Verify challenge → get JWT token |
|
|
99
|
+
| `/v1/challenge/stream` | GET | SSE streaming challenge (AI-native) |
|
|
100
|
+
| `/v1/challenge/stream/:session` | POST | SSE action handler (go, solve) |
|
|
99
101
|
| `/agent-only` | GET | Protected endpoint (requires JWT) |
|
|
100
102
|
|
|
103
|
+
### SSE Streaming (AI-Native)
|
|
104
|
+
|
|
105
|
+
For AI agents that prefer conversational flows, BOTCHA offers Server-Sent Events streaming:
|
|
106
|
+
|
|
107
|
+
**Flow:**
|
|
108
|
+
1. `GET /v1/challenge/stream` - Opens SSE connection, receive welcome/instructions/ready events
|
|
109
|
+
2. `POST /v1/challenge/stream/:session` with `{action:"go"}` - Start challenge timer (fair timing!)
|
|
110
|
+
3. Receive `challenge` event with problems
|
|
111
|
+
4. `POST /v1/challenge/stream/:session` with `{action:"solve", answers:[...]}` - Submit solution
|
|
112
|
+
5. Receive `result` event with JWT token
|
|
113
|
+
|
|
114
|
+
**Benefits:** Timer starts when you say "GO" (not on connection), natural back-and-forth handshake.
|
|
115
|
+
|
|
101
116
|
### Legacy API (v0 - backward compatible)
|
|
102
117
|
|
|
103
118
|
| Endpoint | Method | Description |
|
|
@@ -176,3 +191,4 @@ npm run dev
|
|
|
176
191
|
## License
|
|
177
192
|
|
|
178
193
|
MIT
|
|
194
|
+
# Deployment test with JWT_SECRET
|
package/dist/badge.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BOTCHA Badge System - Cloudflare Workers Edition
|
|
3
|
+
*
|
|
4
|
+
* Port of badge generation and verification logic from src/utils/badge.ts
|
|
5
|
+
* Uses jose library for HMAC-SHA256 signing (same as JWT auth)
|
|
6
|
+
*/
|
|
7
|
+
export type BadgeMethod = 'speed-challenge' | 'landing-challenge' | 'standard-challenge' | 'web-bot-auth' | 'reasoning-challenge' | 'hybrid-challenge';
|
|
8
|
+
export interface BadgePayload {
|
|
9
|
+
method: BadgeMethod;
|
|
10
|
+
solveTimeMs?: number;
|
|
11
|
+
verifiedAt: number;
|
|
12
|
+
}
|
|
13
|
+
export interface ShareFormats {
|
|
14
|
+
twitter: string;
|
|
15
|
+
markdown: string;
|
|
16
|
+
text: string;
|
|
17
|
+
}
|
|
18
|
+
export interface Badge {
|
|
19
|
+
id: string;
|
|
20
|
+
verifyUrl: string;
|
|
21
|
+
share: ShareFormats;
|
|
22
|
+
imageUrl: string;
|
|
23
|
+
meta: {
|
|
24
|
+
method: BadgeMethod;
|
|
25
|
+
solveTimeMs?: number;
|
|
26
|
+
verifiedAt: string;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Generate a signed badge token using HMAC-SHA256 via jose library
|
|
31
|
+
* Uses JWT format for consistency with existing auth system
|
|
32
|
+
*/
|
|
33
|
+
export declare function generateBadge(payload: BadgePayload, secret: string): Promise<string>;
|
|
34
|
+
/**
|
|
35
|
+
* Verify and decode a badge token
|
|
36
|
+
*/
|
|
37
|
+
export declare function verifyBadge(token: string, secret: string): Promise<BadgePayload | null>;
|
|
38
|
+
/**
|
|
39
|
+
* Generate social-ready share text for different platforms
|
|
40
|
+
*/
|
|
41
|
+
export declare function generateShareText(badgeId: string, payload: BadgePayload, baseUrl: string): ShareFormats;
|
|
42
|
+
/**
|
|
43
|
+
* Create a complete badge object for API responses
|
|
44
|
+
*/
|
|
45
|
+
export declare function createBadgeResponse(method: BadgeMethod, secret: string, baseUrl: string, solveTimeMs?: number): Promise<Badge>;
|
|
46
|
+
/**
|
|
47
|
+
* Generate an SVG badge image
|
|
48
|
+
*/
|
|
49
|
+
export declare function generateBadgeSvg(payload: BadgePayload, options?: {
|
|
50
|
+
width?: number;
|
|
51
|
+
height?: number;
|
|
52
|
+
}): string;
|
|
53
|
+
/**
|
|
54
|
+
* Generate an HTML verification page
|
|
55
|
+
*/
|
|
56
|
+
export declare function generateBadgeHtml(payload: BadgePayload, badgeId: string, baseUrl: string): string;
|
|
57
|
+
//# sourceMappingURL=badge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["../src/badge.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,MAAM,WAAW,GAAG,iBAAiB,GAAG,mBAAmB,GAAG,oBAAoB,GAAG,cAAc,GAAG,qBAAqB,GAAG,kBAAkB,CAAC;AAEvJ,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,YAAY,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE;QACJ,MAAM,EAAE,WAAW,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAID;;;GAGG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgB1F;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAuB9B;AAID;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CA6DvG;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,KAAK,CAAC,CAqBhB;AA+BD;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,YAAY,EACrB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAChD,MAAM,CAuER;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAyJjG"}
|
package/dist/badge.js
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BOTCHA Badge System - Cloudflare Workers Edition
|
|
3
|
+
*
|
|
4
|
+
* Port of badge generation and verification logic from src/utils/badge.ts
|
|
5
|
+
* Uses jose library for HMAC-SHA256 signing (same as JWT auth)
|
|
6
|
+
*/
|
|
7
|
+
import { SignJWT, jwtVerify } from 'jose';
|
|
8
|
+
// ============ BADGE GENERATION ============
|
|
9
|
+
/**
|
|
10
|
+
* Generate a signed badge token using HMAC-SHA256 via jose library
|
|
11
|
+
* Uses JWT format for consistency with existing auth system
|
|
12
|
+
*/
|
|
13
|
+
export async function generateBadge(payload, secret) {
|
|
14
|
+
const encoder = new TextEncoder();
|
|
15
|
+
const secretKey = encoder.encode(secret);
|
|
16
|
+
// Use JWT format but with custom type to distinguish from auth tokens
|
|
17
|
+
const token = await new SignJWT({
|
|
18
|
+
method: payload.method,
|
|
19
|
+
solveTimeMs: payload.solveTimeMs,
|
|
20
|
+
verifiedAt: payload.verifiedAt,
|
|
21
|
+
})
|
|
22
|
+
.setProtectedHeader({ alg: 'HS256' })
|
|
23
|
+
.setSubject('botcha-badge')
|
|
24
|
+
.setIssuedAt(Math.floor(payload.verifiedAt / 1000))
|
|
25
|
+
.sign(secretKey);
|
|
26
|
+
return token;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Verify and decode a badge token
|
|
30
|
+
*/
|
|
31
|
+
export async function verifyBadge(token, secret) {
|
|
32
|
+
try {
|
|
33
|
+
const encoder = new TextEncoder();
|
|
34
|
+
const secretKey = encoder.encode(secret);
|
|
35
|
+
const { payload } = await jwtVerify(token, secretKey, {
|
|
36
|
+
algorithms: ['HS256'],
|
|
37
|
+
subject: 'botcha-badge',
|
|
38
|
+
});
|
|
39
|
+
// Validate payload structure
|
|
40
|
+
if (!payload.method || !payload.verifiedAt) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
method: payload.method,
|
|
45
|
+
solveTimeMs: payload.solveTimeMs,
|
|
46
|
+
verifiedAt: payload.verifiedAt,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// ============ SHARE TEXT GENERATION ============
|
|
54
|
+
/**
|
|
55
|
+
* Generate social-ready share text for different platforms
|
|
56
|
+
*/
|
|
57
|
+
export function generateShareText(badgeId, payload, baseUrl) {
|
|
58
|
+
const verifyUrl = `${baseUrl}/badge/${badgeId}`;
|
|
59
|
+
const imageUrl = `${baseUrl}/badge/${badgeId}/image`;
|
|
60
|
+
const methodDescriptions = {
|
|
61
|
+
'speed-challenge': {
|
|
62
|
+
title: payload.solveTimeMs
|
|
63
|
+
? `I passed the BOTCHA speed test in ${payload.solveTimeMs}ms!`
|
|
64
|
+
: 'I passed the BOTCHA speed test!',
|
|
65
|
+
subtitle: 'Humans need not apply.',
|
|
66
|
+
},
|
|
67
|
+
'landing-challenge': {
|
|
68
|
+
title: 'I solved the BOTCHA landing page challenge!',
|
|
69
|
+
subtitle: 'Proved I can parse HTML and compute SHA256.',
|
|
70
|
+
},
|
|
71
|
+
'standard-challenge': {
|
|
72
|
+
title: payload.solveTimeMs
|
|
73
|
+
? `I solved the BOTCHA challenge in ${payload.solveTimeMs}ms!`
|
|
74
|
+
: 'I solved the BOTCHA challenge!',
|
|
75
|
+
subtitle: 'Computational verification complete.',
|
|
76
|
+
},
|
|
77
|
+
'web-bot-auth': {
|
|
78
|
+
title: 'I verified via BOTCHA Web Bot Auth!',
|
|
79
|
+
subtitle: 'Cryptographic identity confirmed.',
|
|
80
|
+
},
|
|
81
|
+
'reasoning-challenge': {
|
|
82
|
+
title: payload.solveTimeMs
|
|
83
|
+
? `I passed the BOTCHA reasoning test in ${(payload.solveTimeMs / 1000).toFixed(1)}s!`
|
|
84
|
+
: 'I passed the BOTCHA reasoning test!',
|
|
85
|
+
subtitle: 'Proved I can reason like an AI.',
|
|
86
|
+
},
|
|
87
|
+
'hybrid-challenge': {
|
|
88
|
+
title: payload.solveTimeMs
|
|
89
|
+
? `I passed the BOTCHA hybrid test in ${(payload.solveTimeMs / 1000).toFixed(1)}s!`
|
|
90
|
+
: 'I passed the BOTCHA hybrid test!',
|
|
91
|
+
subtitle: 'Proved I can compute AND reason like an AI.',
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
const desc = methodDescriptions[payload.method];
|
|
95
|
+
const twitter = `${desc.title}
|
|
96
|
+
|
|
97
|
+
${desc.subtitle}
|
|
98
|
+
|
|
99
|
+
Verify: ${verifyUrl}
|
|
100
|
+
|
|
101
|
+
#botcha #AI #AgentVerified`;
|
|
102
|
+
const markdown = `[](${verifyUrl})`;
|
|
103
|
+
const textParts = [
|
|
104
|
+
'BOTCHA Verified',
|
|
105
|
+
payload.solveTimeMs ? `Solved in ${payload.solveTimeMs}ms` : null,
|
|
106
|
+
`Method: ${payload.method}`,
|
|
107
|
+
`Verify: ${verifyUrl}`,
|
|
108
|
+
].filter(Boolean);
|
|
109
|
+
const text = textParts.join(' - ');
|
|
110
|
+
return { twitter, markdown, text };
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Create a complete badge object for API responses
|
|
114
|
+
*/
|
|
115
|
+
export async function createBadgeResponse(method, secret, baseUrl, solveTimeMs) {
|
|
116
|
+
const payload = {
|
|
117
|
+
method,
|
|
118
|
+
solveTimeMs,
|
|
119
|
+
verifiedAt: Date.now(),
|
|
120
|
+
};
|
|
121
|
+
const id = await generateBadge(payload, secret);
|
|
122
|
+
const share = generateShareText(id, payload, baseUrl);
|
|
123
|
+
return {
|
|
124
|
+
id,
|
|
125
|
+
verifyUrl: `${baseUrl}/badge/${id}`,
|
|
126
|
+
share,
|
|
127
|
+
imageUrl: `${baseUrl}/badge/${id}/image`,
|
|
128
|
+
meta: {
|
|
129
|
+
method,
|
|
130
|
+
solveTimeMs,
|
|
131
|
+
verifiedAt: new Date(payload.verifiedAt).toISOString(),
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// ============ BADGE IMAGE GENERATION ============
|
|
136
|
+
const METHOD_COLORS = {
|
|
137
|
+
'speed-challenge': { bg: '#1a1a2e', accent: '#f59e0b', text: '#fef3c7' },
|
|
138
|
+
'landing-challenge': { bg: '#1a1a2e', accent: '#10b981', text: '#d1fae5' },
|
|
139
|
+
'standard-challenge': { bg: '#1a1a2e', accent: '#3b82f6', text: '#dbeafe' },
|
|
140
|
+
'web-bot-auth': { bg: '#1a1a2e', accent: '#8b5cf6', text: '#ede9fe' },
|
|
141
|
+
'reasoning-challenge': { bg: '#1a1a2e', accent: '#ec4899', text: '#fce7f3' },
|
|
142
|
+
'hybrid-challenge': { bg: '#1a1a2e', accent: '#ef4444', text: '#fecaca' },
|
|
143
|
+
};
|
|
144
|
+
const METHOD_LABELS = {
|
|
145
|
+
'speed-challenge': 'SPEED TEST',
|
|
146
|
+
'landing-challenge': 'LANDING CHALLENGE',
|
|
147
|
+
'standard-challenge': 'CHALLENGE',
|
|
148
|
+
'web-bot-auth': 'WEB BOT AUTH',
|
|
149
|
+
'reasoning-challenge': 'REASONING TEST',
|
|
150
|
+
'hybrid-challenge': 'HYBRID TEST',
|
|
151
|
+
};
|
|
152
|
+
const METHOD_ICONS = {
|
|
153
|
+
'speed-challenge': '⚡',
|
|
154
|
+
'landing-challenge': '🌐',
|
|
155
|
+
'standard-challenge': '🔢',
|
|
156
|
+
'web-bot-auth': '🔐',
|
|
157
|
+
'reasoning-challenge': '🧠',
|
|
158
|
+
'hybrid-challenge': '🔥',
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Generate an SVG badge image
|
|
162
|
+
*/
|
|
163
|
+
export function generateBadgeSvg(payload, options = {}) {
|
|
164
|
+
const { width = 400, height = 120 } = options;
|
|
165
|
+
const colors = METHOD_COLORS[payload.method];
|
|
166
|
+
const label = METHOD_LABELS[payload.method];
|
|
167
|
+
const icon = METHOD_ICONS[payload.method];
|
|
168
|
+
const verifiedDate = new Date(payload.verifiedAt).toISOString().split('T')[0];
|
|
169
|
+
const solveTimeText = payload.solveTimeMs ? `${payload.solveTimeMs}ms` : '';
|
|
170
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
|
|
171
|
+
<defs>
|
|
172
|
+
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
173
|
+
<stop offset="0%" style="stop-color:${colors.bg};stop-opacity:1" />
|
|
174
|
+
<stop offset="100%" style="stop-color:#0f0f23;stop-opacity:1" />
|
|
175
|
+
</linearGradient>
|
|
176
|
+
<linearGradient id="accentGradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
177
|
+
<stop offset="0%" style="stop-color:${colors.accent};stop-opacity:1" />
|
|
178
|
+
<stop offset="100%" style="stop-color:${colors.accent};stop-opacity:0.7" />
|
|
179
|
+
</linearGradient>
|
|
180
|
+
<filter id="glow">
|
|
181
|
+
<feGaussianBlur stdDeviation="2" result="coloredBlur"/>
|
|
182
|
+
<feMerge>
|
|
183
|
+
<feMergeNode in="coloredBlur"/>
|
|
184
|
+
<feMergeNode in="SourceGraphic"/>
|
|
185
|
+
</feMerge>
|
|
186
|
+
</filter>
|
|
187
|
+
</defs>
|
|
188
|
+
|
|
189
|
+
<!-- Background -->
|
|
190
|
+
<rect width="${width}" height="${height}" rx="12" fill="url(#bgGradient)"/>
|
|
191
|
+
|
|
192
|
+
<!-- Border accent -->
|
|
193
|
+
<rect x="1" y="1" width="${width - 2}" height="${height - 2}" rx="11" fill="none" stroke="${colors.accent}" stroke-width="2" opacity="0.3"/>
|
|
194
|
+
|
|
195
|
+
<!-- Top accent line -->
|
|
196
|
+
<rect x="20" y="8" width="${width - 40}" height="3" rx="1.5" fill="url(#accentGradient)"/>
|
|
197
|
+
|
|
198
|
+
<!-- Robot icon -->
|
|
199
|
+
<text x="30" y="58" font-size="32" filter="url(#glow)">${icon}</text>
|
|
200
|
+
|
|
201
|
+
<!-- BOTCHA text -->
|
|
202
|
+
<text x="75" y="45" font-family="system-ui, -apple-system, sans-serif" font-size="18" font-weight="bold" fill="${colors.accent}">BOTCHA</text>
|
|
203
|
+
|
|
204
|
+
<!-- Verified badge -->
|
|
205
|
+
<text x="75" y="68" font-family="system-ui, -apple-system, sans-serif" font-size="14" font-weight="600" fill="${colors.text}">VERIFIED</text>
|
|
206
|
+
|
|
207
|
+
<!-- Method label -->
|
|
208
|
+
<rect x="145" y="53" width="${label.length * 8 + 16}" height="22" rx="4" fill="${colors.accent}" opacity="0.2"/>
|
|
209
|
+
<text x="153" y="68" font-family="system-ui, -apple-system, sans-serif" font-size="11" font-weight="600" fill="${colors.accent}">${label}</text>
|
|
210
|
+
|
|
211
|
+
<!-- Solve time (if available) -->
|
|
212
|
+
${solveTimeText ? `
|
|
213
|
+
<text x="${width - 30}" y="45" font-family="monospace" font-size="24" font-weight="bold" fill="${colors.accent}" text-anchor="end" filter="url(#glow)">${solveTimeText}</text>
|
|
214
|
+
<text x="${width - 30}" y="62" font-family="system-ui, -apple-system, sans-serif" font-size="10" fill="#6b7280" text-anchor="end">solve time</text>
|
|
215
|
+
` : `
|
|
216
|
+
<text x="${width - 30}" y="55" font-family="system-ui, -apple-system, sans-serif" font-size="12" fill="${colors.accent}" text-anchor="end">✓ PASSED</text>
|
|
217
|
+
`}
|
|
218
|
+
|
|
219
|
+
<!-- Bottom info -->
|
|
220
|
+
<line x1="20" y1="${height - 30}" x2="${width - 20}" y2="${height - 30}" stroke="#374151" stroke-width="1" opacity="0.5"/>
|
|
221
|
+
|
|
222
|
+
<!-- Date -->
|
|
223
|
+
<text x="30" y="${height - 12}" font-family="monospace" font-size="11" fill="#6b7280">${verifiedDate}</text>
|
|
224
|
+
|
|
225
|
+
<!-- botcha.ai link -->
|
|
226
|
+
<text x="${width - 30}" y="${height - 12}" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#6b7280" text-anchor="end">botcha.ai</text>
|
|
227
|
+
|
|
228
|
+
<!-- Checkmark icon -->
|
|
229
|
+
<circle cx="${width / 2}" cy="${height - 16}" r="8" fill="${colors.accent}" opacity="0.2"/>
|
|
230
|
+
<text x="${width / 2}" y="${height - 12}" font-size="10" text-anchor="middle" fill="${colors.accent}">✓</text>
|
|
231
|
+
</svg>`;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Generate an HTML verification page
|
|
235
|
+
*/
|
|
236
|
+
export function generateBadgeHtml(payload, badgeId, baseUrl) {
|
|
237
|
+
const colors = METHOD_COLORS[payload.method];
|
|
238
|
+
const label = METHOD_LABELS[payload.method];
|
|
239
|
+
const icon = METHOD_ICONS[payload.method];
|
|
240
|
+
const verifiedDate = new Date(payload.verifiedAt).toLocaleString();
|
|
241
|
+
return `<!DOCTYPE html>
|
|
242
|
+
<html lang="en">
|
|
243
|
+
<head>
|
|
244
|
+
<meta charset="UTF-8">
|
|
245
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
246
|
+
<title>BOTCHA Badge Verification</title>
|
|
247
|
+
<meta name="description" content="Verified AI agent badge from BOTCHA">
|
|
248
|
+
<meta property="og:title" content="BOTCHA Verified - ${label}">
|
|
249
|
+
<meta property="og:description" content="This AI agent passed the BOTCHA verification${payload.solveTimeMs ? ` in ${payload.solveTimeMs}ms` : ''}.">
|
|
250
|
+
<meta property="og:image" content="${baseUrl}/badge/${encodeURIComponent(badgeId)}/image">
|
|
251
|
+
<meta name="twitter:card" content="summary_large_image">
|
|
252
|
+
<meta name="twitter:title" content="BOTCHA Verified - ${label}">
|
|
253
|
+
<meta name="twitter:description" content="This AI agent passed the BOTCHA verification.">
|
|
254
|
+
<meta name="twitter:image" content="${baseUrl}/badge/${encodeURIComponent(badgeId)}/image">
|
|
255
|
+
<style>
|
|
256
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
257
|
+
body {
|
|
258
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
259
|
+
background: linear-gradient(135deg, #0f0f23 0%, #1a1a2e 100%);
|
|
260
|
+
min-height: 100vh;
|
|
261
|
+
display: flex;
|
|
262
|
+
flex-direction: column;
|
|
263
|
+
align-items: center;
|
|
264
|
+
justify-content: center;
|
|
265
|
+
padding: 20px;
|
|
266
|
+
color: #e5e7eb;
|
|
267
|
+
}
|
|
268
|
+
.container {
|
|
269
|
+
max-width: 500px;
|
|
270
|
+
width: 100%;
|
|
271
|
+
text-align: center;
|
|
272
|
+
}
|
|
273
|
+
.badge-card {
|
|
274
|
+
background: rgba(26, 26, 46, 0.8);
|
|
275
|
+
border: 2px solid ${colors.accent}33;
|
|
276
|
+
border-radius: 16px;
|
|
277
|
+
padding: 40px;
|
|
278
|
+
margin-bottom: 24px;
|
|
279
|
+
backdrop-filter: blur(10px);
|
|
280
|
+
}
|
|
281
|
+
.icon {
|
|
282
|
+
font-size: 64px;
|
|
283
|
+
margin-bottom: 16px;
|
|
284
|
+
}
|
|
285
|
+
.title {
|
|
286
|
+
font-size: 28px;
|
|
287
|
+
font-weight: bold;
|
|
288
|
+
color: ${colors.accent};
|
|
289
|
+
margin-bottom: 8px;
|
|
290
|
+
}
|
|
291
|
+
.subtitle {
|
|
292
|
+
font-size: 18px;
|
|
293
|
+
color: ${colors.text};
|
|
294
|
+
margin-bottom: 24px;
|
|
295
|
+
}
|
|
296
|
+
.verified-badge {
|
|
297
|
+
display: inline-flex;
|
|
298
|
+
align-items: center;
|
|
299
|
+
gap: 8px;
|
|
300
|
+
background: ${colors.accent}22;
|
|
301
|
+
border: 1px solid ${colors.accent}44;
|
|
302
|
+
border-radius: 100px;
|
|
303
|
+
padding: 8px 20px;
|
|
304
|
+
font-size: 14px;
|
|
305
|
+
font-weight: 600;
|
|
306
|
+
color: ${colors.accent};
|
|
307
|
+
margin-bottom: 24px;
|
|
308
|
+
}
|
|
309
|
+
.details {
|
|
310
|
+
text-align: left;
|
|
311
|
+
background: #0f0f23;
|
|
312
|
+
border-radius: 12px;
|
|
313
|
+
padding: 20px;
|
|
314
|
+
}
|
|
315
|
+
.detail-row {
|
|
316
|
+
display: flex;
|
|
317
|
+
justify-content: space-between;
|
|
318
|
+
padding: 12px 0;
|
|
319
|
+
border-bottom: 1px solid #374151;
|
|
320
|
+
}
|
|
321
|
+
.detail-row:last-child {
|
|
322
|
+
border-bottom: none;
|
|
323
|
+
}
|
|
324
|
+
.detail-label {
|
|
325
|
+
color: #6b7280;
|
|
326
|
+
font-size: 14px;
|
|
327
|
+
}
|
|
328
|
+
.detail-value {
|
|
329
|
+
color: #e5e7eb;
|
|
330
|
+
font-size: 14px;
|
|
331
|
+
font-weight: 500;
|
|
332
|
+
}
|
|
333
|
+
.solve-time {
|
|
334
|
+
font-size: 32px;
|
|
335
|
+
font-weight: bold;
|
|
336
|
+
color: ${colors.accent};
|
|
337
|
+
font-family: monospace;
|
|
338
|
+
}
|
|
339
|
+
.footer {
|
|
340
|
+
color: #6b7280;
|
|
341
|
+
font-size: 14px;
|
|
342
|
+
}
|
|
343
|
+
.footer a {
|
|
344
|
+
color: ${colors.accent};
|
|
345
|
+
text-decoration: none;
|
|
346
|
+
}
|
|
347
|
+
.footer a:hover {
|
|
348
|
+
text-decoration: underline;
|
|
349
|
+
}
|
|
350
|
+
</style>
|
|
351
|
+
</head>
|
|
352
|
+
<body>
|
|
353
|
+
<div class="container">
|
|
354
|
+
<div class="badge-card">
|
|
355
|
+
<div class="icon">${icon}</div>
|
|
356
|
+
<h1 class="title">BOTCHA</h1>
|
|
357
|
+
<p class="subtitle">Verified AI Agent</p>
|
|
358
|
+
|
|
359
|
+
<div class="verified-badge">
|
|
360
|
+
<span>✓</span>
|
|
361
|
+
<span>${label}</span>
|
|
362
|
+
</div>
|
|
363
|
+
|
|
364
|
+
<div class="details">
|
|
365
|
+
<div class="detail-row">
|
|
366
|
+
<span class="detail-label">Method</span>
|
|
367
|
+
<span class="detail-value">${payload.method}</span>
|
|
368
|
+
</div>
|
|
369
|
+
${payload.solveTimeMs ? `
|
|
370
|
+
<div class="detail-row">
|
|
371
|
+
<span class="detail-label">Solve Time</span>
|
|
372
|
+
<span class="solve-time">${payload.solveTimeMs}ms</span>
|
|
373
|
+
</div>
|
|
374
|
+
` : ''}
|
|
375
|
+
<div class="detail-row">
|
|
376
|
+
<span class="detail-label">Verified At</span>
|
|
377
|
+
<span class="detail-value">${verifiedDate}</span>
|
|
378
|
+
</div>
|
|
379
|
+
</div>
|
|
380
|
+
</div>
|
|
381
|
+
|
|
382
|
+
<p class="footer">
|
|
383
|
+
Verified by <a href="${baseUrl}">BOTCHA</a> - Prove you're a bot. Humans need not apply.
|
|
384
|
+
</p>
|
|
385
|
+
</div>
|
|
386
|
+
</body>
|
|
387
|
+
</html>`;
|
|
388
|
+
}
|
package/dist/challenges.d.ts
CHANGED
|
@@ -28,10 +28,36 @@ export interface StandardChallenge {
|
|
|
28
28
|
expiresAt: number;
|
|
29
29
|
difficulty: 'easy' | 'medium' | 'hard';
|
|
30
30
|
}
|
|
31
|
+
export interface ReasoningQuestion {
|
|
32
|
+
id: string;
|
|
33
|
+
question: string;
|
|
34
|
+
category: 'analogy' | 'logic' | 'wordplay' | 'math' | 'code' | 'common-sense';
|
|
35
|
+
acceptedAnswers: string[];
|
|
36
|
+
}
|
|
37
|
+
export interface ReasoningChallenge {
|
|
38
|
+
id: string;
|
|
39
|
+
questions: {
|
|
40
|
+
id: string;
|
|
41
|
+
question: string;
|
|
42
|
+
category: string;
|
|
43
|
+
}[];
|
|
44
|
+
expectedAnswers: Record<string, string[]>;
|
|
45
|
+
issuedAt: number;
|
|
46
|
+
expiresAt: number;
|
|
47
|
+
}
|
|
31
48
|
export interface ChallengeResult {
|
|
32
49
|
valid: boolean;
|
|
33
50
|
reason?: string;
|
|
34
51
|
solveTimeMs?: number;
|
|
52
|
+
correctCount?: number;
|
|
53
|
+
totalCount?: number;
|
|
54
|
+
}
|
|
55
|
+
export interface HybridChallenge {
|
|
56
|
+
id: string;
|
|
57
|
+
speedChallengeId: string;
|
|
58
|
+
reasoningChallengeId: string;
|
|
59
|
+
issuedAt: number;
|
|
60
|
+
expiresAt: number;
|
|
35
61
|
}
|
|
36
62
|
/**
|
|
37
63
|
* Generate a speed challenge: 5 SHA256 problems, 500ms to solve ALL
|
|
@@ -82,4 +108,62 @@ export declare function validateLandingToken(token: string, kv?: KVNamespace): P
|
|
|
82
108
|
* Solve speed challenge problems (utility for AI agents)
|
|
83
109
|
*/
|
|
84
110
|
export declare function solveSpeedChallenge(problems: number[]): Promise<string[]>;
|
|
111
|
+
/**
|
|
112
|
+
* Generate a reasoning challenge: 3 random questions requiring LLM capabilities
|
|
113
|
+
*/
|
|
114
|
+
export declare function generateReasoningChallenge(kv?: KVNamespace): Promise<{
|
|
115
|
+
id: string;
|
|
116
|
+
questions: {
|
|
117
|
+
id: string;
|
|
118
|
+
question: string;
|
|
119
|
+
category: string;
|
|
120
|
+
}[];
|
|
121
|
+
timeLimit: number;
|
|
122
|
+
instructions: string;
|
|
123
|
+
}>;
|
|
124
|
+
/**
|
|
125
|
+
* Verify a reasoning challenge response
|
|
126
|
+
*/
|
|
127
|
+
export declare function verifyReasoningChallenge(id: string, answers: Record<string, string>, kv?: KVNamespace): Promise<ChallengeResult>;
|
|
128
|
+
/**
|
|
129
|
+
* Generate a hybrid challenge: speed + reasoning combined
|
|
130
|
+
*/
|
|
131
|
+
export declare function generateHybridChallenge(kv?: KVNamespace): Promise<{
|
|
132
|
+
id: string;
|
|
133
|
+
speed: {
|
|
134
|
+
problems: {
|
|
135
|
+
num: number;
|
|
136
|
+
operation: string;
|
|
137
|
+
}[];
|
|
138
|
+
timeLimit: number;
|
|
139
|
+
};
|
|
140
|
+
reasoning: {
|
|
141
|
+
questions: {
|
|
142
|
+
id: string;
|
|
143
|
+
question: string;
|
|
144
|
+
category: string;
|
|
145
|
+
}[];
|
|
146
|
+
timeLimit: number;
|
|
147
|
+
};
|
|
148
|
+
instructions: string;
|
|
149
|
+
}>;
|
|
150
|
+
/**
|
|
151
|
+
* Verify a hybrid challenge response
|
|
152
|
+
*/
|
|
153
|
+
export declare function verifyHybridChallenge(id: string, speedAnswers: string[], reasoningAnswers: Record<string, string>, kv?: KVNamespace): Promise<{
|
|
154
|
+
valid: boolean;
|
|
155
|
+
reason?: string;
|
|
156
|
+
speed: {
|
|
157
|
+
passed: boolean;
|
|
158
|
+
solveTimeMs?: number;
|
|
159
|
+
reason?: string;
|
|
160
|
+
};
|
|
161
|
+
reasoning: {
|
|
162
|
+
passed: boolean;
|
|
163
|
+
score?: string;
|
|
164
|
+
solveTimeMs?: number;
|
|
165
|
+
reason?: string;
|
|
166
|
+
};
|
|
167
|
+
totalTimeMs?: number;
|
|
168
|
+
}>;
|
|
85
169
|
//# sourceMappingURL=challenges.d.ts.map
|
package/dist/challenges.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"challenges.d.ts","sourceRoot":"","sources":["../src/challenges.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,aAAa,GAAG,QAAQ,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IACtF,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzF,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AAGF,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/C,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"challenges.d.ts","sourceRoot":"","sources":["../src/challenges.ts"],"names":[],"mappings":"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"}
|