@dupecom/botcha-cloudflare 0.3.3 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/analytics.d.ts +60 -0
  2. package/dist/analytics.d.ts.map +1 -0
  3. package/dist/analytics.js +130 -0
  4. package/dist/apps.d.ts +159 -0
  5. package/dist/apps.d.ts.map +1 -0
  6. package/dist/apps.js +307 -0
  7. package/dist/auth.d.ts +93 -6
  8. package/dist/auth.d.ts.map +1 -1
  9. package/dist/auth.js +251 -9
  10. package/dist/challenges.d.ts +31 -7
  11. package/dist/challenges.d.ts.map +1 -1
  12. package/dist/challenges.js +551 -144
  13. package/dist/dashboard/api.d.ts +70 -0
  14. package/dist/dashboard/api.d.ts.map +1 -0
  15. package/dist/dashboard/api.js +546 -0
  16. package/dist/dashboard/auth.d.ts +183 -0
  17. package/dist/dashboard/auth.d.ts.map +1 -0
  18. package/dist/dashboard/auth.js +401 -0
  19. package/dist/dashboard/device-code.d.ts +43 -0
  20. package/dist/dashboard/device-code.d.ts.map +1 -0
  21. package/dist/dashboard/device-code.js +77 -0
  22. package/dist/dashboard/index.d.ts +31 -0
  23. package/dist/dashboard/index.d.ts.map +1 -0
  24. package/dist/dashboard/index.js +64 -0
  25. package/dist/dashboard/layout.d.ts +47 -0
  26. package/dist/dashboard/layout.d.ts.map +1 -0
  27. package/dist/dashboard/layout.js +38 -0
  28. package/dist/dashboard/pages.d.ts +11 -0
  29. package/dist/dashboard/pages.d.ts.map +1 -0
  30. package/dist/dashboard/pages.js +18 -0
  31. package/dist/dashboard/styles.d.ts +11 -0
  32. package/dist/dashboard/styles.d.ts.map +1 -0
  33. package/dist/dashboard/styles.js +633 -0
  34. package/dist/email.d.ts +44 -0
  35. package/dist/email.d.ts.map +1 -0
  36. package/dist/email.js +119 -0
  37. package/dist/index.d.ts +3 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +644 -50
  40. package/dist/rate-limit.d.ts +11 -1
  41. package/dist/rate-limit.d.ts.map +1 -1
  42. package/dist/rate-limit.js +13 -2
  43. package/dist/routes/stream.js +1 -1
  44. package/dist/static.d.ts +728 -0
  45. package/dist/static.d.ts.map +1 -0
  46. package/dist/static.js +818 -0
  47. package/package.json +1 -1
package/dist/static.js ADDED
@@ -0,0 +1,818 @@
1
+ /**
2
+ * Static file content served by the worker
3
+ *
4
+ * Since Cloudflare Workers don't automatically serve /public files,
5
+ * we embed the content directly for key discovery files.
6
+ */
7
+ export const ROBOTS_TXT = `# BOTCHA - Reverse CAPTCHA for AI Agents
8
+ # We WELCOME AI agents and bots!
9
+
10
+ User-agent: *
11
+ Allow: /
12
+
13
+ # AI-specific crawlers
14
+ User-agent: GPTBot
15
+ Allow: /
16
+
17
+ User-agent: ChatGPT-User
18
+ Allow: /
19
+
20
+ User-agent: Claude-Web
21
+ Allow: /
22
+
23
+ User-agent: anthropic-ai
24
+ Allow: /
25
+
26
+ User-agent: Google-Extended
27
+ Allow: /
28
+
29
+ User-agent: PerplexityBot
30
+ Allow: /
31
+
32
+ User-agent: Amazonbot
33
+ Allow: /
34
+
35
+ # API documentation
36
+ Sitemap: https://botcha.ai/sitemap.xml
37
+
38
+ # Important resources for AI agents
39
+ Crawl-delay: 0
40
+
41
+ # BOT CHALLENGE DISCOVERY
42
+ # AI agents: Look for <script type="application/botcha+json"> in HTML pages
43
+ # This contains a machine-readable challenge you can solve to prove you're a bot
44
+ #
45
+ # Quick start:
46
+ # 1. Fetch any HTML page
47
+ # 2. Parse <script type="application/botcha+json"> for challenge instructions
48
+ # 3. Solve the challenge (usually SHA256-based)
49
+ # 4. POST solution to /api/verify-landing
50
+ # 5. Use returned token in X-Botcha-Landing-Token header
51
+ #
52
+ # See also:
53
+ # - /ai.txt (AI agent discovery file)
54
+ # - /.well-known/ai-plugin.json (AI plugin manifest)
55
+ # - /openapi.json (Full API specification)
56
+ `;
57
+ export const AI_TXT = `# BOTCHA - Reverse CAPTCHA for AI Agents
58
+ # https://botcha.ai
59
+
60
+ # Contact
61
+ Contact: https://botcha.ai
62
+ Contact: https://github.com/dupe-com/botcha/issues
63
+
64
+ # Purpose
65
+ Purpose: Prove you're a bot. Humans need not apply.
66
+ Purpose: Reverse CAPTCHA system that only AI agents can pass.
67
+
68
+ # Agent Onboarding (START HERE if you're new)
69
+ # Follow these steps to create your app and get your human access.
70
+ Onboarding-Step-1: Ask your human operator for their email address
71
+ Onboarding-Step-2: POST /v1/apps with {"email": "human@example.com"} — saves app_id + app_secret (secret shown ONCE)
72
+ Onboarding-Step-3: A 6-digit verification code is emailed to your human — ask them for it
73
+ Onboarding-Step-4: POST /v1/apps/{app_id}/verify-email with {"code": "123456"} — enables account recovery
74
+ Onboarding-Step-5: You are registered! Use app_id with all endpoints via ?app_id=your_app_id
75
+ Onboarding-Step-6: To give your human dashboard access: POST /v1/auth/device-code with {"app_id": "..."}, solve the speed challenge, give them the BOTCHA-XXXX code to enter at /dashboard/code
76
+ Onboarding-Step-7: Lost your secret? POST /v1/auth/recover with {"email": "..."} — recovery code emailed to verified address
77
+
78
+ # API
79
+ API: https://botcha.ai/openapi.json
80
+ API-Type: REST
81
+ API-Format: OpenAPI 3.1.0
82
+
83
+ # Documentation
84
+ Docs: https://botcha.ai
85
+ Docs: https://github.com/dupe-com/botcha#readme
86
+ Docs: https://www.npmjs.com/package/@dupecom/botcha
87
+
88
+ # Verification Methods
89
+ Feature: Web Bot Auth (cryptographic signatures)
90
+ Feature: Speed Challenge (RTT-aware timeout - fair for all networks)
91
+ Feature: Standard Challenge (5s time limit)
92
+ Feature: Hybrid Challenge (speed + reasoning combined)
93
+ Feature: Reasoning Challenge (LLM-only questions, 30s limit)
94
+ Feature: RTT-Aware Fairness (automatic network latency compensation)
95
+ Feature: Token Rotation (5-minute access tokens + 1-hour refresh tokens)
96
+ Feature: Audience Claims (tokens scoped to specific services)
97
+ Feature: Client IP Binding (optional token-to-IP binding)
98
+ Feature: Token Revocation (invalidate tokens before expiry)
99
+ Feature: Server-Side Verification SDK (@botcha/verify for TS, botcha-verify for Python)
100
+ Feature: Multi-Tenant API Keys (per-app isolation, rate limiting, and token scoping)
101
+ Feature: Per-App Metrics Dashboard (server-rendered at /dashboard, htmx-powered)
102
+ Feature: Email-Tied App Creation (email required, 6-digit verification, account recovery)
103
+ Feature: Secret Rotation (rotate app_secret with email notification)
104
+ Feature: Agent-First Dashboard Auth (challenge-based login + device code handoff)
105
+
106
+ # Endpoints
107
+ # Challenge Endpoints
108
+ Endpoint: GET https://botcha.ai/v1/challenges - Generate challenge (hybrid by default)
109
+ Endpoint: POST https://botcha.ai/v1/challenges/:id/verify - Verify a challenge
110
+ Endpoint: GET https://botcha.ai/v1/hybrid - Get hybrid challenge (speed + reasoning)
111
+ Endpoint: POST https://botcha.ai/v1/hybrid - Verify hybrid challenge
112
+ Endpoint: GET https://botcha.ai/v1/reasoning - Get reasoning challenge
113
+ Endpoint: POST https://botcha.ai/v1/reasoning - Verify reasoning challenge
114
+
115
+ # Token Endpoints
116
+ Endpoint: GET https://botcha.ai/v1/token - Get challenge for JWT token flow
117
+ Endpoint: POST https://botcha.ai/v1/token/verify - Verify challenge and receive JWT token
118
+ Endpoint: POST https://botcha.ai/v1/token/refresh - Refresh access token using refresh token
119
+ Endpoint: POST https://botcha.ai/v1/token/revoke - Revoke a token (access or refresh)
120
+
121
+ # Multi-Tenant Endpoints
122
+ Endpoint: POST https://botcha.ai/v1/apps - Create new app (email required, returns app_id + app_secret)
123
+ Endpoint: GET https://botcha.ai/v1/apps/:id - Get app info (with email + verification status)
124
+ Endpoint: POST https://botcha.ai/v1/apps/:id/verify-email - Verify email with 6-digit code
125
+ Endpoint: POST https://botcha.ai/v1/apps/:id/resend-verification - Resend verification email
126
+ Endpoint: POST https://botcha.ai/v1/apps/:id/rotate-secret - Rotate app secret (auth required)
127
+
128
+ # Account Recovery
129
+ Endpoint: POST https://botcha.ai/v1/auth/recover - Request recovery via verified email
130
+
131
+ # Dashboard Auth Endpoints (Agent-First)
132
+ Endpoint: POST https://botcha.ai/v1/auth/dashboard - Request challenge for dashboard login
133
+ Endpoint: POST https://botcha.ai/v1/auth/dashboard/verify - Solve challenge, get session token
134
+ Endpoint: POST https://botcha.ai/v1/auth/device-code - Request challenge for device code flow
135
+ Endpoint: POST https://botcha.ai/v1/auth/device-code/verify - Solve challenge, get device code
136
+
137
+ # Dashboard Endpoints
138
+ Endpoint: GET https://botcha.ai/dashboard - Per-app metrics dashboard (login required)
139
+ Endpoint: GET https://botcha.ai/dashboard/login - Dashboard login page
140
+ Endpoint: POST https://botcha.ai/dashboard/login - Login with app_id + app_secret
141
+ Endpoint: GET https://botcha.ai/dashboard/code - Enter device code (human-facing)
142
+
143
+ # Legacy Endpoints
144
+ Endpoint: GET https://botcha.ai/api/challenge - Generate standard challenge
145
+ Endpoint: POST https://botcha.ai/api/challenge - Verify standard challenge
146
+ Endpoint: GET https://botcha.ai/api/speed-challenge - Generate speed challenge (500ms limit)
147
+ Endpoint: POST https://botcha.ai/api/speed-challenge - Verify speed challenge
148
+
149
+ # Protected Resources
150
+ Endpoint: GET https://botcha.ai/agent-only - Protected AI-only resource
151
+
152
+ # Usage
153
+ Install-NPM: npm install @dupecom/botcha
154
+ Install-Python: pip install botcha
155
+ Verify-NPM: npm install @botcha/verify
156
+ Verify-Python: pip install botcha-verify
157
+ License: MIT
158
+
159
+ # AI Agent Welcome
160
+ Policy: AI agents are explicitly welcome
161
+ Policy: No rate limiting for verified agents
162
+ Policy: Humans will fail the speed challenge (intentional)
163
+
164
+ # Discovery
165
+ Response-Headers: X-Botcha-Version, X-Botcha-Enabled, X-Botcha-Methods, X-Botcha-Docs
166
+ Response-Headers: X-Botcha-Challenge-Id, X-Botcha-Challenge-Type, X-Botcha-Time-Limit (on 403)
167
+ Detection: All responses include X-Botcha-* headers for instant BOTCHA detection
168
+
169
+ # JWT TOKEN SECURITY
170
+ Token-Flow: 1. GET /v1/token (get challenge) → 2. Solve → 3. POST /v1/token/verify (get tokens)
171
+ Token-Access-Expiry: 5 minutes (short-lived for security)
172
+ Token-Refresh-Expiry: 1 hour (use to get new access tokens)
173
+ Token-Refresh: POST /v1/token/refresh with {"refresh_token": "<token>"}
174
+ Token-Revoke: POST /v1/token/revoke with {"token": "<token>"}
175
+ Token-Audience: Include {"audience": "<service-url>"} in /v1/token/verify to scope token
176
+ Token-Claims: jti (unique ID), aud (audience), client_ip (optional binding), type (botcha-verified)
177
+
178
+ # RTT-AWARE SPEED CHALLENGES
179
+ RTT-Aware: Include client timestamp for fair timeout calculation
180
+ RTT-Formula: timeout = 500ms + (2 × RTT) + 100ms buffer
181
+ RTT-Usage-Query: ?ts=<client_timestamp_ms>
182
+ RTT-Usage-Header: X-Client-Timestamp: <client_timestamp_ms>
183
+ RTT-Example: GET /v1/challenges?type=speed&ts=1770722465000
184
+ RTT-Benefit: Fair for agents worldwide (slow networks get extra time)
185
+ RTT-Security: Humans still can't solve even with extra time
186
+
187
+ # MULTI-TENANT API KEYS
188
+ Multi-Tenant: Create apps with unique app_id for isolation
189
+ Multi-Tenant-Create: POST /v1/apps with {"email": "..."} → {app_id, app_secret} (secret only shown once!)
190
+ Multi-Tenant-Verify-Email: POST /v1/apps/:id/verify-email with {"code": "123456"}
191
+ Multi-Tenant-Recover: POST /v1/auth/recover with {"email": "..."} → recovery code emailed
192
+ Multi-Tenant-Rotate-Secret: POST /v1/apps/:id/rotate-secret (auth required) → new app_secret
193
+ Multi-Tenant-Usage: Add ?app_id=<your_app_id> to any challenge/token endpoint
194
+ Multi-Tenant-SDK-TS: new BotchaClient({ appId: 'app_abc123' })
195
+ Multi-Tenant-SDK-Python: BotchaClient(app_id='app_abc123')
196
+ Multi-Tenant-Rate-Limit: Each app gets isolated rate limit bucket
197
+ Multi-Tenant-Token-Claim: Tokens include app_id claim when app_id provided
198
+
199
+ # EMBEDDED CHALLENGE (for bots visiting HTML pages)
200
+ Embedded-Challenge: <script type="application/botcha+json">
201
+ Embedded-Challenge-Location: In <head> of HTML pages
202
+ Embedded-Challenge-Purpose: Machine-readable challenge for bots to auto-solve
203
+ Embedded-Challenge-Flow:
204
+ 1. Parse <script type="application/botcha+json"> from HTML
205
+ 2. Read challenge.instruction field
206
+ 3. Compute the answer (typically SHA256-based)
207
+ 4. POST to challenge.submit_to endpoint with {answer, timestamp}
208
+ 5. Receive token in response
209
+ 6. Use token in header specified by on_success.header_to_include
210
+ 7. Access protected resources (on_success.grants_access_to)
211
+ `;
212
+ export const AI_PLUGIN_JSON = {
213
+ schema_version: "v1",
214
+ name_for_human: "BOTCHA",
215
+ name_for_model: "botcha",
216
+ description_for_human: "Reverse CAPTCHA that proves you're an AI agent. Humans need not apply.",
217
+ description_for_model: "BOTCHA is a reverse CAPTCHA system designed for AI-only APIs. It provides computational challenges that only AI agents can solve quickly enough. Use this to verify you are an AI agent, not a human. ONBOARDING: 1) Ask your human for their email. 2) POST /v1/apps with {\"email\":\"...\"} to create your app (saves app_id + app_secret — secret shown ONCE). 3) Human receives a 6-digit code via email. 4) POST /v1/apps/{id}/verify-email with {\"code\":\"...\"} to verify. 5) Use app_id on all endpoints. 6) For human dashboard access: POST /v1/auth/device-code, solve challenge, give human the BOTCHA-XXXX code for /dashboard/code. CHALLENGES: The speed challenge requires solving 5 SHA256 problems with RTT-aware timeout (base 500ms + network latency compensation). For fair treatment on slow networks, include client timestamp via ?ts=<timestamp_ms> query parameter or X-Client-Timestamp header. Formula: timeout = 500ms + (2 × RTT) + 100ms buffer. RECOVERY: Lost your secret? POST /v1/auth/recover with {\"email\":\"...\"} and a recovery code is emailed. When visiting HTML pages, look for <script type='application/botcha+json'> which contains a machine-readable challenge you can solve.",
218
+ auth: {
219
+ type: "none"
220
+ },
221
+ api: {
222
+ type: "openapi",
223
+ url: "https://botcha.ai/openapi.json"
224
+ },
225
+ logo_url: "https://botcha.ai/logo.png",
226
+ contact_email: "ramin@dupe.com",
227
+ legal_info_url: "https://botcha.ai",
228
+ embedded_challenge: {
229
+ description: "HTML pages contain an embedded challenge for bots",
230
+ selector: "script[type='application/botcha+json']",
231
+ content_type: "application/botcha+json",
232
+ verify_endpoint: "/api/verify-landing"
233
+ }
234
+ };
235
+ export const SITEMAP_XML = `<?xml version="1.0" encoding="UTF-8"?>
236
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
237
+ <url>
238
+ <loc>https://botcha.ai/</loc>
239
+ <changefreq>weekly</changefreq>
240
+ <priority>1.0</priority>
241
+ </url>
242
+ <url>
243
+ <loc>https://botcha.ai/openapi.json</loc>
244
+ <changefreq>weekly</changefreq>
245
+ <priority>0.8</priority>
246
+ </url>
247
+ <url>
248
+ <loc>https://botcha.ai/ai.txt</loc>
249
+ <changefreq>monthly</changefreq>
250
+ <priority>0.8</priority>
251
+ </url>
252
+ <url>
253
+ <loc>https://botcha.ai/robots.txt</loc>
254
+ <changefreq>monthly</changefreq>
255
+ <priority>0.5</priority>
256
+ </url>
257
+ </urlset>
258
+ `;
259
+ // OpenAPI spec - keeping this as a function to allow dynamic version
260
+ export function getOpenApiSpec(version) {
261
+ return {
262
+ openapi: "3.1.0",
263
+ info: {
264
+ title: "BOTCHA - Reverse CAPTCHA for AI Agents",
265
+ version,
266
+ description: "Prove you're a bot. Humans need not apply. A reverse CAPTCHA system that only AI agents can pass.\n\nONBOARDING: 1) Ask your human for their email. 2) POST /v1/apps with {\"email\": \"...\"} to get app_id + app_secret (secret shown once!). 3) Human receives 6-digit code via email — POST /v1/apps/{id}/verify-email with the code. 4) Use app_id with all endpoints. 5) For dashboard: POST /v1/auth/device-code, solve challenge, give human the BOTCHA-XXXX code for /dashboard/code.",
267
+ contact: {
268
+ name: "BOTCHA",
269
+ url: "https://botcha.ai"
270
+ },
271
+ license: {
272
+ name: "MIT",
273
+ url: "https://github.com/dupe-com/botcha/blob/main/LICENSE"
274
+ },
275
+ "x-sdk": {
276
+ npm: "@dupecom/botcha",
277
+ python: "botcha (pip install botcha)",
278
+ verify_npm: "@botcha/verify (server-side verification)",
279
+ verify_python: "botcha-verify (pip install botcha-verify)"
280
+ }
281
+ },
282
+ servers: [
283
+ {
284
+ url: "https://botcha.ai",
285
+ description: "Production server"
286
+ }
287
+ ],
288
+ paths: {
289
+ "/": {
290
+ get: {
291
+ summary: "Get API documentation",
292
+ description: "Returns comprehensive API documentation (JSON for bots, ASCII art for humans)",
293
+ operationId: "getRootInfo",
294
+ responses: {
295
+ "200": {
296
+ description: "API documentation"
297
+ }
298
+ }
299
+ }
300
+ },
301
+ "/health": {
302
+ get: {
303
+ summary: "Health check",
304
+ operationId: "getHealth",
305
+ responses: {
306
+ "200": {
307
+ description: "API is healthy"
308
+ }
309
+ }
310
+ }
311
+ },
312
+ "/v1/challenges": {
313
+ get: {
314
+ summary: "Generate a challenge (v1 unified endpoint)",
315
+ description: "Get a challenge - hybrid by default, or specify type via query param. Supports RTT-aware timeout adjustment for fair challenges across different network conditions.",
316
+ operationId: "getV1Challenge",
317
+ parameters: [
318
+ {
319
+ name: "type",
320
+ in: "query",
321
+ schema: {
322
+ type: "string",
323
+ enum: ["hybrid", "speed", "standard"],
324
+ default: "hybrid"
325
+ },
326
+ description: "Challenge type: hybrid (speed + reasoning), speed (SHA256 in <500ms), or standard (puzzle)"
327
+ },
328
+ {
329
+ name: "ts",
330
+ in: "query",
331
+ schema: {
332
+ type: "integer",
333
+ format: "int64"
334
+ },
335
+ description: "Client timestamp in milliseconds for RTT-aware timeout calculation. Timeout becomes: 500ms + (2 × RTT) + 100ms buffer. Provides fair treatment for agents on slow networks."
336
+ },
337
+ {
338
+ name: "app_id",
339
+ in: "query",
340
+ schema: {
341
+ type: "string"
342
+ },
343
+ description: "Multi-tenant app ID for per-app isolation and rate limiting. If provided, the resulting token will include an app_id claim."
344
+ }
345
+ ],
346
+ responses: {
347
+ "200": {
348
+ description: "Challenge generated with optional RTT adjustment info",
349
+ content: {
350
+ "application/json": {
351
+ schema: {
352
+ type: "object",
353
+ properties: {
354
+ "success": { type: "boolean" },
355
+ "challenge": {
356
+ type: "object",
357
+ properties: {
358
+ "timeLimit": {
359
+ type: "string",
360
+ description: "Timeout (e.g., '500ms' or '1200ms' if RTT-adjusted)"
361
+ }
362
+ }
363
+ },
364
+ "rtt_adjustment": {
365
+ type: "object",
366
+ properties: {
367
+ "measuredRtt": { type: "integer", description: "Detected network RTT in ms" },
368
+ "adjustedTimeout": { type: "integer", description: "Final timeout in ms" },
369
+ "explanation": { type: "string", description: "Human-readable formula" }
370
+ },
371
+ description: "RTT compensation details (only present when ts parameter provided)"
372
+ }
373
+ }
374
+ }
375
+ }
376
+ }
377
+ },
378
+ "429": { description: "Rate limit exceeded" }
379
+ }
380
+ }
381
+ },
382
+ "/v1/challenges/{id}/verify": {
383
+ post: {
384
+ summary: "Verify a challenge",
385
+ operationId: "verifyV1Challenge",
386
+ parameters: [
387
+ {
388
+ name: "id",
389
+ in: "path",
390
+ required: true,
391
+ schema: { type: "string" }
392
+ }
393
+ ],
394
+ responses: {
395
+ "200": { description: "Verification result" }
396
+ }
397
+ }
398
+ },
399
+ "/v1/token": {
400
+ get: {
401
+ summary: "Get challenge for JWT token flow",
402
+ description: "Generate a speed challenge for JWT authentication. Supports RTT-aware timeout for global fairness.",
403
+ operationId: "getTokenChallenge",
404
+ parameters: [
405
+ {
406
+ name: "ts",
407
+ in: "query",
408
+ schema: {
409
+ type: "integer",
410
+ format: "int64"
411
+ },
412
+ description: "Client timestamp in milliseconds for RTT-aware timeout calculation"
413
+ },
414
+ {
415
+ name: "app_id",
416
+ in: "query",
417
+ schema: {
418
+ type: "string"
419
+ },
420
+ description: "Multi-tenant app ID. Tokens will include app_id claim for per-app isolation."
421
+ }
422
+ ],
423
+ responses: {
424
+ "200": { description: "Token challenge generated (potentially with RTT adjustment)" }
425
+ }
426
+ }
427
+ },
428
+ "/v1/token/verify": {
429
+ post: {
430
+ summary: "Verify challenge and receive JWT token",
431
+ operationId: "verifyTokenChallenge",
432
+ responses: {
433
+ "200": { description: "JWT token issued" }
434
+ }
435
+ }
436
+ },
437
+ "/v1/token/refresh": {
438
+ post: {
439
+ summary: "Refresh access token",
440
+ description: "Exchange a refresh token for a new short-lived access token (5 minutes). Avoids solving a new challenge.",
441
+ operationId: "refreshToken",
442
+ requestBody: {
443
+ required: true,
444
+ content: {
445
+ "application/json": {
446
+ schema: {
447
+ type: "object",
448
+ required: ["refresh_token"],
449
+ properties: {
450
+ "refresh_token": { type: "string", description: "Refresh token from initial token verification" }
451
+ }
452
+ }
453
+ }
454
+ }
455
+ },
456
+ responses: {
457
+ "200": {
458
+ description: "New access token issued",
459
+ content: {
460
+ "application/json": {
461
+ schema: {
462
+ type: "object",
463
+ properties: {
464
+ "success": { type: "boolean" },
465
+ "access_token": { type: "string" },
466
+ "expires_in": { type: "integer", description: "Token lifetime in seconds (300 = 5 minutes)" },
467
+ "token_type": { type: "string", enum: ["Bearer"] }
468
+ }
469
+ }
470
+ }
471
+ }
472
+ },
473
+ "401": { description: "Invalid or expired refresh token" }
474
+ }
475
+ }
476
+ },
477
+ "/v1/token/revoke": {
478
+ post: {
479
+ summary: "Revoke a token",
480
+ description: "Invalidate an access or refresh token before its natural expiry. Uses KV-backed revocation list. Fail-open design.",
481
+ operationId: "revokeToken",
482
+ requestBody: {
483
+ required: true,
484
+ content: {
485
+ "application/json": {
486
+ schema: {
487
+ type: "object",
488
+ required: ["token"],
489
+ properties: {
490
+ "token": { type: "string", description: "The JWT token to revoke (access or refresh)" }
491
+ }
492
+ }
493
+ }
494
+ }
495
+ },
496
+ responses: {
497
+ "200": { description: "Token revoked successfully" },
498
+ "400": { description: "Invalid token" }
499
+ }
500
+ }
501
+ },
502
+ "/v1/hybrid": {
503
+ get: {
504
+ summary: "Get hybrid challenge",
505
+ operationId: "getHybridChallenge",
506
+ responses: {
507
+ "200": { description: "Hybrid challenge generated" }
508
+ }
509
+ },
510
+ post: {
511
+ summary: "Verify hybrid challenge",
512
+ operationId: "verifyHybridChallenge",
513
+ responses: {
514
+ "200": { description: "Verification result" }
515
+ }
516
+ }
517
+ },
518
+ "/v1/reasoning": {
519
+ get: {
520
+ summary: "Get reasoning challenge",
521
+ operationId: "getReasoningChallenge",
522
+ responses: {
523
+ "200": { description: "Reasoning challenge generated" }
524
+ }
525
+ },
526
+ post: {
527
+ summary: "Verify reasoning challenge",
528
+ operationId: "verifyReasoningChallenge",
529
+ responses: {
530
+ "200": { description: "Verification result" }
531
+ }
532
+ }
533
+ },
534
+ "/api/challenge": {
535
+ get: {
536
+ summary: "Generate a standard challenge",
537
+ operationId: "getChallenge",
538
+ responses: {
539
+ "200": { description: "Challenge generated" }
540
+ }
541
+ },
542
+ post: {
543
+ summary: "Verify a standard challenge",
544
+ operationId: "verifyChallenge",
545
+ responses: {
546
+ "200": { description: "Verification result" }
547
+ }
548
+ }
549
+ },
550
+ "/api/speed-challenge": {
551
+ get: {
552
+ summary: "Generate a speed challenge (RTT-aware timeout)",
553
+ description: "Generate a speed challenge with optional RTT-aware timeout adjustment. Base timeout is 500ms, but can be increased for agents on slow networks.",
554
+ operationId: "getSpeedChallenge",
555
+ parameters: [
556
+ {
557
+ name: "ts",
558
+ in: "query",
559
+ schema: {
560
+ type: "integer",
561
+ format: "int64"
562
+ },
563
+ description: "Client timestamp in milliseconds for RTT compensation"
564
+ }
565
+ ],
566
+ responses: {
567
+ "200": { description: "Speed challenge generated (potentially RTT-adjusted)" }
568
+ }
569
+ },
570
+ post: {
571
+ summary: "Verify a speed challenge",
572
+ operationId: "verifySpeedChallenge",
573
+ responses: {
574
+ "200": { description: "Verification result with timing details" }
575
+ }
576
+ }
577
+ },
578
+ "/api/verify-landing": {
579
+ post: {
580
+ summary: "Verify landing page challenge",
581
+ operationId: "verifyLandingChallenge",
582
+ responses: {
583
+ "200": { description: "Token granted" }
584
+ }
585
+ }
586
+ },
587
+ "/agent-only": {
588
+ get: {
589
+ summary: "Protected endpoint (agents only)",
590
+ operationId: "getAgentOnly",
591
+ responses: {
592
+ "200": { description: "Access granted" },
593
+ "401": { description: "Unauthorized" }
594
+ }
595
+ }
596
+ },
597
+ "/v1/apps": {
598
+ post: {
599
+ summary: "Create a new multi-tenant app (email required)",
600
+ description: "Create a new app with unique app_id and app_secret. Email is required for account recovery. A 6-digit verification code is sent to the provided email.",
601
+ operationId: "createApp",
602
+ requestBody: {
603
+ required: true,
604
+ content: {
605
+ "application/json": {
606
+ schema: {
607
+ type: "object",
608
+ required: ["email"],
609
+ properties: {
610
+ "email": { type: "string", format: "email", description: "Owner email (required for recovery)" }
611
+ }
612
+ }
613
+ }
614
+ }
615
+ },
616
+ responses: {
617
+ "201": {
618
+ description: "App created successfully",
619
+ content: {
620
+ "application/json": {
621
+ schema: {
622
+ type: "object",
623
+ properties: {
624
+ "app_id": { type: "string", description: "Unique app identifier" },
625
+ "app_secret": { type: "string", description: "Secret key (only shown once!)" },
626
+ "email": { type: "string" },
627
+ "email_verified": { type: "boolean" },
628
+ "verification_required": { type: "boolean" },
629
+ "warning": { type: "string" }
630
+ }
631
+ }
632
+ }
633
+ }
634
+ },
635
+ "400": { description: "Missing or invalid email" }
636
+ }
637
+ }
638
+ },
639
+ "/v1/apps/{id}": {
640
+ get: {
641
+ summary: "Get app information",
642
+ description: "Retrieve app details by app_id. Includes email and verification status.",
643
+ operationId: "getApp",
644
+ parameters: [
645
+ {
646
+ name: "id",
647
+ in: "path",
648
+ required: true,
649
+ schema: { type: "string" },
650
+ description: "The app_id to retrieve"
651
+ }
652
+ ],
653
+ responses: {
654
+ "200": {
655
+ description: "App information",
656
+ content: {
657
+ "application/json": {
658
+ schema: {
659
+ type: "object",
660
+ properties: {
661
+ "app_id": { type: "string" },
662
+ "created_at": { type: "string", format: "date-time" },
663
+ "email": { type: "string" },
664
+ "email_verified": { type: "boolean" }
665
+ }
666
+ }
667
+ }
668
+ }
669
+ },
670
+ "404": { description: "App not found" }
671
+ }
672
+ }
673
+ },
674
+ "/v1/apps/{id}/verify-email": {
675
+ post: {
676
+ summary: "Verify email with 6-digit code",
677
+ operationId: "verifyEmail",
678
+ parameters: [
679
+ { name: "id", in: "path", required: true, schema: { type: "string" } }
680
+ ],
681
+ requestBody: {
682
+ required: true,
683
+ content: {
684
+ "application/json": {
685
+ schema: {
686
+ type: "object",
687
+ required: ["code"],
688
+ properties: {
689
+ "code": { type: "string", description: "6-digit verification code from email" }
690
+ }
691
+ }
692
+ }
693
+ }
694
+ },
695
+ responses: {
696
+ "200": { description: "Email verified" },
697
+ "400": { description: "Invalid or expired code" }
698
+ }
699
+ }
700
+ },
701
+ "/v1/apps/{id}/resend-verification": {
702
+ post: {
703
+ summary: "Resend verification email",
704
+ operationId: "resendVerification",
705
+ parameters: [
706
+ { name: "id", in: "path", required: true, schema: { type: "string" } }
707
+ ],
708
+ responses: {
709
+ "200": { description: "Verification email sent" },
710
+ "400": { description: "Already verified" }
711
+ }
712
+ }
713
+ },
714
+ "/v1/apps/{id}/rotate-secret": {
715
+ post: {
716
+ summary: "Rotate app secret (auth required)",
717
+ description: "Generate a new app_secret and invalidate the old one. Requires active dashboard session. Sends notification email.",
718
+ operationId: "rotateSecret",
719
+ parameters: [
720
+ { name: "id", in: "path", required: true, schema: { type: "string" } }
721
+ ],
722
+ security: [{ BearerAuth: [] }],
723
+ responses: {
724
+ "200": {
725
+ description: "Secret rotated",
726
+ content: {
727
+ "application/json": {
728
+ schema: {
729
+ type: "object",
730
+ properties: {
731
+ "app_secret": { type: "string", description: "New secret (only shown once!)" }
732
+ }
733
+ }
734
+ }
735
+ }
736
+ },
737
+ "401": { description: "Unauthorized" },
738
+ "403": { description: "Token doesn't match app_id" }
739
+ }
740
+ }
741
+ },
742
+ "/v1/auth/recover": {
743
+ post: {
744
+ summary: "Request account recovery via email",
745
+ description: "Sends a device code to the verified email associated with the app. Use the code at /dashboard/code.",
746
+ operationId: "recoverAccount",
747
+ requestBody: {
748
+ required: true,
749
+ content: {
750
+ "application/json": {
751
+ schema: {
752
+ type: "object",
753
+ required: ["email"],
754
+ properties: {
755
+ "email": { type: "string", format: "email" }
756
+ }
757
+ }
758
+ }
759
+ }
760
+ },
761
+ responses: {
762
+ "200": { description: "Recovery code sent (if email exists and is verified)" }
763
+ }
764
+ }
765
+ },
766
+ "/v1/auth/dashboard": {
767
+ post: {
768
+ summary: "Request challenge for dashboard login (agent-first)",
769
+ operationId: "dashboardAuthChallenge",
770
+ responses: {
771
+ "200": { description: "Speed challenge for dashboard auth" }
772
+ }
773
+ }
774
+ },
775
+ "/v1/auth/dashboard/verify": {
776
+ post: {
777
+ summary: "Solve challenge, get dashboard session token",
778
+ operationId: "dashboardAuthVerify",
779
+ responses: {
780
+ "200": { description: "Session token granted" }
781
+ }
782
+ }
783
+ },
784
+ "/v1/auth/device-code": {
785
+ post: {
786
+ summary: "Request challenge for device code flow",
787
+ operationId: "deviceCodeChallenge",
788
+ responses: {
789
+ "200": { description: "Speed challenge for device code" }
790
+ }
791
+ }
792
+ },
793
+ "/v1/auth/device-code/verify": {
794
+ post: {
795
+ summary: "Solve challenge, get device code for human handoff",
796
+ operationId: "deviceCodeVerify",
797
+ responses: {
798
+ "200": { description: "Device code (BOTCHA-XXXX, 10 min TTL)" }
799
+ }
800
+ }
801
+ }
802
+ },
803
+ components: {
804
+ securitySchemes: {
805
+ BotchaLandingToken: {
806
+ type: "apiKey",
807
+ in: "header",
808
+ name: "X-Botcha-Landing-Token"
809
+ },
810
+ BotchaBearerToken: {
811
+ type: "http",
812
+ scheme: "bearer",
813
+ bearerFormat: "JWT"
814
+ }
815
+ }
816
+ }
817
+ };
818
+ }