@dupecom/botcha-cloudflare 0.18.0 → 0.19.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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAYL,KAAK,WAAW,EACjB,MAAM,cAAc,CAAC;AAuDtB,OAAO,EACL,KAAK,sBAAsB,EAM5B,MAAM,aAAa,CAAC;AAGrB,KAAK,QAAQ,GAAG;IACd,UAAU,EAAE,WAAW,CAAC;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,WAAW,CAAC;IACtB,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,WAAW,CAAC;IACtB,SAAS,CAAC,EAAE,sBAAsB,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,KAAK,SAAS,GAAG;IACf,YAAY,CAAC,EAAE;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,iBAAiB,CAAC;QACxB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH,CAAC;AAEF,QAAA,MAAM,GAAG;cAAwB,QAAQ;eAAa,SAAS;yCAAK,CAAC;AAuqErE,eAAe,GAAG,CAAC;AAGnB,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,yBAAyB,EACzB,uBAAuB,EACvB,0BAA0B,EAC1B,wBAAwB,EACxB,uBAAuB,EACvB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EACL,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,YAAY,GAClB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAYL,KAAK,WAAW,EACjB,MAAM,cAAc,CAAC;AAwDtB,OAAO,EACL,KAAK,sBAAsB,EAM5B,MAAM,aAAa,CAAC;AAGrB,KAAK,QAAQ,GAAG;IACd,UAAU,EAAE,WAAW,CAAC;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,WAAW,CAAC;IACtB,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,WAAW,CAAC;IACtB,SAAS,CAAC,EAAE,sBAAsB,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,KAAK,SAAS,GAAG;IACf,YAAY,CAAC,EAAE;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,iBAAiB,CAAC;QACxB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH,CAAC;AAEF,QAAA,MAAM,GAAG;cAAwB,QAAQ;eAAa,SAAS;yCAAK,CAAC;AAwvErE,eAAe,GAAG,CAAC;AAGnB,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,yBAAyB,EACzB,uBAAuB,EACvB,0BAA0B,EAC1B,wBAAwB,EACxB,uBAAuB,EACvB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,QAAQ,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EACL,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,YAAY,GAClB,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import { Hono } from 'hono';
10
10
  import { cors } from 'hono/cors';
11
11
  import { generateSpeedChallenge, verifySpeedChallenge, generateStandardChallenge, verifyStandardChallenge, generateReasoningChallenge, verifyReasoningChallenge, generateHybridChallenge, verifyHybridChallenge, verifyLandingChallenge, validateLandingToken, } from './challenges';
12
12
  import { SignJWT } from 'jose';
13
- import { generateToken, verifyToken, extractBearerToken, revokeToken, refreshAccessToken } from './auth';
13
+ import { generateToken, verifyToken, extractBearerToken, revokeToken, refreshAccessToken, getSigningPublicKeyJWK } from './auth';
14
14
  import { checkRateLimit, getClientIP } from './rate-limit';
15
15
  import { verifyBadge, generateBadgeSvg, generateBadgeHtml, createBadgeResponse } from './badge';
16
16
  import streamRoutes from './routes/stream';
@@ -23,6 +23,7 @@ import { sendEmail, verificationEmail, recoveryEmail, secretRotatedEmail } from
23
23
  import { LandingPage, VerifiedLandingPage } from './dashboard/landing';
24
24
  import { ShowcasePage } from './dashboard/showcase';
25
25
  import { WhitepaperPage } from './dashboard/whitepaper';
26
+ import { DocsPage } from './dashboard/docs';
26
27
  import { createAgent, getAgent, listAgents } from './agents';
27
28
  import { registerTAPAgentRoute, getTAPAgentRoute, listTAPAgentsRoute, createTAPSessionRoute, getTAPSessionRoute, rotateKeyRoute, createInvoiceRoute, getInvoiceRoute, verifyIOURoute, verifyConsumerRoute, verifyPaymentRoute, } from './tap-routes.js';
28
29
  import { jwksRoute, getKeyRoute, listKeysRoute } from './tap-jwks.js';
@@ -39,7 +40,7 @@ app.route('/dashboard', dashboardRoutes);
39
40
  // BOTCHA discovery headers
40
41
  app.use('*', async (c, next) => {
41
42
  await next();
42
- c.header('X-Botcha-Version', c.env.BOTCHA_VERSION || '0.18.0');
43
+ c.header('X-Botcha-Version', c.env.BOTCHA_VERSION || '0.19.0');
43
44
  c.header('X-Botcha-Enabled', 'true');
44
45
  c.header('X-Botcha-Methods', 'speed-challenge,reasoning-challenge,hybrid-challenge,standard-challenge,jwt-token');
45
46
  c.header('X-Botcha-Docs', 'https://botcha.ai/openapi.json');
@@ -85,6 +86,23 @@ async function validateAppId(appId, appsKV) {
85
86
  return { valid: true };
86
87
  }
87
88
  }
89
+ // Helper: Parse ES256 signing key from env, returning undefined if not set
90
+ function getSigningKey(env) {
91
+ if (!env.JWT_SIGNING_KEY)
92
+ return undefined;
93
+ try {
94
+ return JSON.parse(env.JWT_SIGNING_KEY);
95
+ }
96
+ catch {
97
+ console.error('Failed to parse JWT_SIGNING_KEY — falling back to HS256');
98
+ return undefined;
99
+ }
100
+ }
101
+ // Helper: Get the public key for ES256 verification, or undefined for HS256 fallback
102
+ function getPublicKey(env) {
103
+ const sk = getSigningKey(env);
104
+ return sk ? getSigningPublicKeyJWK(sk) : undefined;
105
+ }
88
106
  // JWT verification middleware
89
107
  async function requireJWT(c, next) {
90
108
  const authHeader = c.req.header('authorization');
@@ -95,7 +113,8 @@ async function requireJWT(c, next) {
95
113
  message: 'Missing Bearer token. Use POST /v1/token/verify to get a token.',
96
114
  }, 401);
97
115
  }
98
- const result = await verifyToken(token, c.env.JWT_SECRET, c.env);
116
+ const publicKey = getPublicKey(c.env);
117
+ const result = await verifyToken(token, c.env.JWT_SECRET, c.env, undefined, publicKey);
99
118
  if (!result.valid) {
100
119
  return c.json({
101
120
  error: 'INVALID_TOKEN',
@@ -140,7 +159,8 @@ app.get('/', async (c) => {
140
159
  let isVerified = false;
141
160
  let tokenPayload;
142
161
  if (token) {
143
- const result = await verifyToken(token, c.env.JWT_SECRET, c.env);
162
+ const pubKey = getPublicKey(c.env);
163
+ const result = await verifyToken(token, c.env.JWT_SECRET, c.env, undefined, pubKey);
144
164
  if (result.valid) {
145
165
  isVerified = true;
146
166
  tokenPayload = result.payload;
@@ -272,6 +292,7 @@ The link works for 5 minutes. Your human clicks it, gets a cookie, and sees the
272
292
  'POST /v1/token/verify': 'Submit solution → access_token (5min) + refresh_token (1hr)',
273
293
  'POST /v1/token/refresh': 'Refresh access token',
274
294
  'POST /v1/token/revoke': 'Revoke a token',
295
+ 'POST /v1/token/validate': 'Remote token validation — verify any BOTCHA token without needing the secret',
275
296
  },
276
297
  protected: {
277
298
  'GET /agent-only': 'Demo protected endpoint — requires Bearer token',
@@ -311,7 +332,7 @@ The link works for 5 minutes. Your human clicks it, gets a cookie, and sees the
311
332
  sdk: {
312
333
  npm: 'npm install @dupecom/botcha',
313
334
  python: 'pip install botcha',
314
- verify_ts: 'npm install @botcha/verify',
335
+ verify_ts: 'npm install @dupecom/botcha-verify',
315
336
  verify_python: 'pip install botcha-verify',
316
337
  },
317
338
  links: {
@@ -403,6 +424,11 @@ app.get('/whitepaper', (c) => {
403
424
  // HTML for browsers
404
425
  return c.html(_jsx(WhitepaperPage, { version: version }));
405
426
  });
427
+ // ============ API DOCS ============
428
+ app.get('/docs', (c) => {
429
+ const version = c.env.BOTCHA_VERSION || '0.16.0';
430
+ return c.html(_jsx(DocsPage, { version: version }));
431
+ });
406
432
  app.get('/health', (c) => {
407
433
  return c.json({ status: 'ok', runtime: 'cloudflare-workers' });
408
434
  });
@@ -686,11 +712,12 @@ app.post('/v1/token/verify', async (c) => {
686
712
  const clientIp = c.req.header('cf-connecting-ip') || c.req.header('x-forwarded-for') || 'unknown';
687
713
  // Generate JWT tokens (access + refresh)
688
714
  // Prefer app_id from request body, fall back to challenge's app_id (returned by verifySpeedChallenge)
715
+ const signingKey = getSigningKey(c.env);
689
716
  const tokenResult = await generateToken(id, result.solveTimeMs || 0, c.env.JWT_SECRET, c.env, {
690
717
  aud: audience,
691
718
  clientIp: bind_ip ? clientIp : undefined,
692
719
  app_id: app_id || result.app_id,
693
- });
720
+ }, signingKey);
694
721
  // Get badge information (for backward compatibility)
695
722
  const baseUrl = new URL(c.req.url).origin;
696
723
  const badge = await createBadgeResponse('speed-challenge', c.env.JWT_SECRET, baseUrl, result.solveTimeMs);
@@ -750,7 +777,9 @@ app.post('/v1/token/refresh', async (c) => {
750
777
  hint: 'Submit the refresh_token from /v1/token/verify response',
751
778
  }, 400);
752
779
  }
753
- const result = await refreshAccessToken(refresh_token, c.env, c.env.JWT_SECRET);
780
+ const refreshSigningKey = getSigningKey(c.env);
781
+ const refreshPublicKey = getPublicKey(c.env);
782
+ const result = await refreshAccessToken(refresh_token, c.env, c.env.JWT_SECRET, undefined, refreshSigningKey, refreshPublicKey);
754
783
  if (!result.success) {
755
784
  return c.json({
756
785
  success: false,
@@ -815,6 +844,42 @@ app.post('/v1/token/revoke', async (c) => {
815
844
  }, 400);
816
845
  }
817
846
  });
847
+ // Remote token validation — no auth required (the token IS the credential)
848
+ app.post('/v1/token/validate', async (c) => {
849
+ // Rate limit: 100 req/min/IP
850
+ const clientIP = getClientIP(c.req.raw);
851
+ const rateLimitResult = await checkRateLimit(c.env.RATE_LIMITS, clientIP, 100);
852
+ c.header('X-RateLimit-Limit', '100');
853
+ c.header('X-RateLimit-Remaining', rateLimitResult.remaining.toString());
854
+ c.header('X-RateLimit-Reset', new Date(rateLimitResult.resetAt).toISOString());
855
+ if (!rateLimitResult.allowed) {
856
+ c.header('Retry-After', rateLimitResult.retryAfter?.toString() || '3600');
857
+ return c.json({
858
+ valid: false,
859
+ error: 'Rate limit exceeded',
860
+ }, 429);
861
+ }
862
+ const body = await c.req.json().catch(() => ({}));
863
+ const { token } = body;
864
+ if (!token || typeof token !== 'string') {
865
+ return c.json({
866
+ valid: false,
867
+ error: 'Missing or invalid token field',
868
+ }, 400);
869
+ }
870
+ const validatePublicKey = getPublicKey(c.env);
871
+ const result = await verifyToken(token, c.env.JWT_SECRET, c.env, undefined, validatePublicKey);
872
+ if (!result.valid) {
873
+ return c.json({
874
+ valid: false,
875
+ error: result.error || 'Token is invalid',
876
+ });
877
+ }
878
+ return c.json({
879
+ valid: true,
880
+ payload: result.payload,
881
+ });
882
+ });
818
883
  // ============ REASONING CHALLENGE ============
819
884
  // Get reasoning challenge
820
885
  app.get('/v1/reasoning', rateLimitMiddleware, async (c) => {
@@ -1100,7 +1165,8 @@ app.get('/agent-only', async (c) => {
1100
1165
  alternative: 'Or use X-Botcha-Landing-Token header (from embedded HTML challenges)',
1101
1166
  }, 401);
1102
1167
  }
1103
- const result = await verifyToken(token, c.env.JWT_SECRET, c.env);
1168
+ const agentOnlyPublicKey = getPublicKey(c.env);
1169
+ const result = await verifyToken(token, c.env.JWT_SECRET, c.env, undefined, agentOnlyPublicKey);
1104
1170
  // Track authentication attempt
1105
1171
  await trackAuthAttempt(c.env.ANALYTICS, 'bearer-token', result.valid, '/agent-only', c.req.raw, clientIP);
1106
1172
  if (!result.valid) {
@@ -1132,7 +1198,7 @@ app.get('/agent-only', async (c) => {
1132
1198
  description: 'As a verified agent, you can access any BOTCHA-protected API.',
1133
1199
  next_steps: [
1134
1200
  'Register your agent identity: POST /v1/agents/register',
1135
- 'Access any service that uses @botcha/verify middleware',
1201
+ 'Access any service that uses @dupecom/botcha-verify middleware',
1136
1202
  'Refresh your token: POST /v1/token/refresh',
1137
1203
  'Give your human dashboard access: POST /v1/auth/device-code',
1138
1204
  ],
@@ -1531,7 +1597,8 @@ app.post('/v1/agents/register', async (c) => {
1531
1597
  const authHeader = c.req.header('authorization');
1532
1598
  const token = extractBearerToken(authHeader);
1533
1599
  if (token) {
1534
- const result = await verifyToken(token, c.env.JWT_SECRET, c.env);
1600
+ const regPublicKey = getPublicKey(c.env);
1601
+ const result = await verifyToken(token, c.env.JWT_SECRET, c.env, undefined, regPublicKey);
1535
1602
  if (result.valid && result.payload) {
1536
1603
  jwtAppId = result.payload.app_id;
1537
1604
  }
@@ -1637,7 +1704,8 @@ app.get('/v1/agents', async (c) => {
1637
1704
  const authHeader = c.req.header('authorization');
1638
1705
  const token = extractBearerToken(authHeader);
1639
1706
  if (token) {
1640
- const result = await verifyToken(token, c.env.JWT_SECRET, c.env);
1707
+ const listPublicKey = getPublicKey(c.env);
1708
+ const result = await verifyToken(token, c.env.JWT_SECRET, c.env, undefined, listPublicKey);
1641
1709
  if (result.valid && result.payload) {
1642
1710
  jwtAppId = result.payload.app_id;
1643
1711
  }
@@ -1716,7 +1784,8 @@ app.get('/go/:code', async (c) => {
1716
1784
  catch { }
1717
1785
  if (gateToken) {
1718
1786
  // Verify the underlying token is still valid
1719
- const result = await verifyToken(gateToken, c.env.JWT_SECRET, c.env);
1787
+ const gatePublicKey = getPublicKey(c.env);
1788
+ const result = await verifyToken(gateToken, c.env.JWT_SECRET, c.env, undefined, gatePublicKey);
1720
1789
  // Delete the code (one-time use) regardless of token validity
1721
1790
  try {
1722
1791
  await c.env.CHALLENGES.delete(`gate:${normalizedCode}`);
@@ -1873,6 +1942,6 @@ app.post('/api/verify-landing', async (c) => {
1873
1942
  export default app;
1874
1943
  // Also export utilities for use as a library
1875
1944
  export { generateSpeedChallenge, verifySpeedChallenge, generateStandardChallenge, verifyStandardChallenge, generateReasoningChallenge, verifyReasoningChallenge, generateHybridChallenge, verifyHybridChallenge, solveSpeedChallenge, } from './challenges';
1876
- export { generateToken, verifyToken } from './auth';
1945
+ export { generateToken, verifyToken, getSigningPublicKeyJWK } from './auth';
1877
1946
  export { checkRateLimit } from './rate-limit';
1878
1947
  export { generateBadge, verifyBadge, createBadgeResponse, generateBadgeSvg, generateBadgeHtml, generateShareText, } from './badge';
package/dist/static.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
7
  export declare function getBotchaMarkdown(version: string): string;
8
8
  export declare const ROBOTS_TXT = "# BOTCHA - Reverse CAPTCHA for AI Agents\n# We WELCOME AI agents and bots!\n\nUser-agent: *\nAllow: /\n\n# AI-specific crawlers\nUser-agent: GPTBot\nAllow: /\n\nUser-agent: ChatGPT-User\nAllow: /\n\nUser-agent: Claude-Web\nAllow: /\n\nUser-agent: anthropic-ai\nAllow: /\n\nUser-agent: Google-Extended\nAllow: /\n\nUser-agent: PerplexityBot\nAllow: /\n\nUser-agent: Amazonbot\nAllow: /\n\n# API documentation\nSitemap: https://botcha.ai/sitemap.xml\n\n# Important resources for AI agents\nCrawl-delay: 0\n\n# MARKDOWN FOR AGENTS\n# AI agents can request Markdown from any page via Accept: text/markdown\n# Example: curl https://botcha.ai -H \"Accept: text/markdown\"\n# Returns token-efficient Markdown documentation (80% fewer tokens than HTML)\n# Content negotiation: text/markdown, application/json, text/html\n\n# BOT CHALLENGE DISCOVERY\n# AI agents: Look for <script type=\"application/botcha+json\"> in HTML pages\n# This contains a machine-readable challenge you can solve to prove you're a bot\n# \n# Quick start:\n# 1. Fetch any HTML page\n# 2. Parse <script type=\"application/botcha+json\"> for challenge instructions\n# 3. Solve the challenge (usually SHA256-based)\n# 4. POST solution to /api/verify-landing\n# 5. Use returned token in X-Botcha-Landing-Token header\n#\n# See also:\n# - /ai.txt (AI agent discovery file)\n# - /.well-known/ai-plugin.json (AI plugin manifest)\n# - /openapi.json (Full API specification)\n";
9
- export declare const AI_TXT = "# BOTCHA - Reverse CAPTCHA for AI Agents\n# https://botcha.ai\n\n# Contact\nContact: https://botcha.ai\nContact: https://github.com/dupe-com/botcha/issues\n\n# Purpose\nPurpose: Prove you're a bot. Humans need not apply.\nPurpose: Reverse CAPTCHA system that only AI agents can pass.\n\n# Agent Onboarding (START HERE if you're new)\n# Follow these steps to create your app and get your human access.\nOnboarding-Step-1: Ask your human operator for their email address\nOnboarding-Step-2: POST /v1/apps with {\"email\": \"human@example.com\"} \u2014 saves app_id + app_secret (secret shown ONCE)\nOnboarding-Step-3: A 6-digit verification code is emailed to your human \u2014 ask them for it\nOnboarding-Step-4: POST /v1/apps/{app_id}/verify-email with {\"code\": \"123456\"} \u2014 enables account recovery\nOnboarding-Step-5: Register yourself: POST /v1/agents/register with {\"name\": \"my-agent\", \"operator\": \"my-org\"} \u2014 gives you a persistent agent identity\nOnboarding-Step-6: Solve challenges: GET /v1/challenges?app_id=... then POST /v1/challenges/{id}/verify \u2014 proves you are AI\nOnboarding-Step-7: Access protected resources: GET /agent-only with Authorization: Bearer <token>\nOnboarding-Step-8: Dashboard for your human: POST /v1/auth/device-code with {\"app_id\": \"...\"}, solve the speed challenge, give them the BOTCHA-XXXX code to enter at /dashboard/code\nOnboarding-Step-9: Lost your secret? POST /v1/auth/recover with {\"email\": \"...\"} \u2014 recovery code emailed to verified address\n\n# API\nAPI: https://botcha.ai/openapi.json\nAPI-Type: REST\nAPI-Format: OpenAPI 3.1.0\n\n# Documentation\nDocs: https://botcha.ai\nDocs: https://botcha.ai/whitepaper\nDocs: https://github.com/dupe-com/botcha#readme\nDocs: https://www.npmjs.com/package/@dupecom/botcha\nWhitepaper: https://botcha.ai/whitepaper\n\n# Verification Methods\nFeature: Web Bot Auth (cryptographic signatures)\nFeature: Speed Challenge (RTT-aware timeout - fair for all networks)\nFeature: Standard Challenge (5s time limit)\nFeature: Hybrid Challenge (speed + reasoning combined)\nFeature: Reasoning Challenge (LLM-only questions, 30s limit)\nFeature: RTT-Aware Fairness (automatic network latency compensation)\nFeature: Token Rotation (5-minute access tokens + 1-hour refresh tokens)\nFeature: Audience Claims (tokens scoped to specific services)\nFeature: Client IP Binding (optional token-to-IP binding)\nFeature: Token Revocation (invalidate tokens before expiry)\nFeature: Server-Side Verification SDK (@botcha/verify for TS, botcha-verify for Python)\nFeature: Multi-Tenant API Keys (per-app isolation, rate limiting, and token scoping)\nFeature: Per-App Metrics Dashboard (server-rendered at /dashboard, htmx-powered)\nFeature: Email-Tied App Creation (email required, 6-digit verification, account recovery)\nFeature: Secret Rotation (rotate app_secret with email notification)\nFeature: Agent-First Dashboard Auth (challenge-based login + device code handoff)\nFeature: Agent Registry (persistent agent identities with name, operator, version)\nFeature: Trusted Agent Protocol (TAP) \u2014 cryptographic agent auth with HTTP Message Signatures (RFC 9421)\nFeature: TAP Capabilities (action + resource scoping for agent sessions)\nFeature: TAP Trust Levels (basic, verified, enterprise)\nFeature: TAP Showcase Homepage (botcha.ai \u2014 one of the first services to implement Visa's Trusted Agent Protocol)\nFeature: TAP Full Spec v0.16.0 \u2014 Ed25519, RFC 9421 full compliance, JWKS infrastructure, Layer 2 Consumer Recognition, Layer 3 Payment Container, 402 micropayments, CDN edge verification, Visa key federation\n\n# Endpoints\n# Challenge Endpoints\nEndpoint: GET https://botcha.ai/v1/challenges - Generate challenge (hybrid by default)\nEndpoint: POST https://botcha.ai/v1/challenges/:id/verify - Verify a challenge\nEndpoint: GET https://botcha.ai/v1/hybrid - Get hybrid challenge (speed + reasoning)\nEndpoint: POST https://botcha.ai/v1/hybrid - Verify hybrid challenge\nEndpoint: GET https://botcha.ai/v1/reasoning - Get reasoning challenge\nEndpoint: POST https://botcha.ai/v1/reasoning - Verify reasoning challenge\n\n# Token Endpoints\nEndpoint: GET https://botcha.ai/v1/token - Get challenge for JWT token flow\nEndpoint: POST https://botcha.ai/v1/token/verify - Verify challenge and receive JWT token\nEndpoint: POST https://botcha.ai/v1/token/refresh - Refresh access token using refresh token\nEndpoint: POST https://botcha.ai/v1/token/revoke - Revoke a token (access or refresh)\n\n# Multi-Tenant Endpoints\nEndpoint: POST https://botcha.ai/v1/apps - Create new app (email required, returns app_id + app_secret)\nEndpoint: GET https://botcha.ai/v1/apps/:id - Get app info (with email + verification status)\nEndpoint: POST https://botcha.ai/v1/apps/:id/verify-email - Verify email with 6-digit code\nEndpoint: POST https://botcha.ai/v1/apps/:id/resend-verification - Resend verification email\nEndpoint: POST https://botcha.ai/v1/apps/:id/rotate-secret - Rotate app secret (auth required)\n\n# Account Recovery\nEndpoint: POST https://botcha.ai/v1/auth/recover - Request recovery via verified email\n\n# Dashboard Auth Endpoints (Agent-First)\nEndpoint: POST https://botcha.ai/v1/auth/dashboard - Request challenge for dashboard login\nEndpoint: POST https://botcha.ai/v1/auth/dashboard/verify - Solve challenge, get session token\nEndpoint: POST https://botcha.ai/v1/auth/device-code - Request challenge for device code flow\nEndpoint: POST https://botcha.ai/v1/auth/device-code/verify - Solve challenge, get device code\n\n# Dashboard Endpoints\nEndpoint: GET https://botcha.ai/dashboard - Per-app metrics dashboard (login required)\nEndpoint: GET https://botcha.ai/dashboard/login - Dashboard login page\nEndpoint: POST https://botcha.ai/dashboard/login - Login with app_id + app_secret\nEndpoint: GET https://botcha.ai/dashboard/code - Enter device code (human-facing)\n\n# Code Redemption (Unified)\nEndpoint: GET https://botcha.ai/go/:code - Unified code redemption \u2014 handles gate codes (from /v1/token/verify) AND device codes (from /v1/auth/device-code/verify)\nEndpoint: POST https://botcha.ai/gate - Submit code form, redirects to /go/:code\n\n# Agent Registry Endpoints\nEndpoint: POST https://botcha.ai/v1/agents/register - Register agent identity (requires app_id)\nEndpoint: GET https://botcha.ai/v1/agents/:id - Get agent by ID (public, no auth)\nEndpoint: GET https://botcha.ai/v1/agents - List all agents for authenticated app\n\n# TAP (Trusted Agent Protocol) Endpoints\nEndpoint: POST https://botcha.ai/v1/agents/register/tap - Register TAP agent with public key + capabilities\nEndpoint: GET https://botcha.ai/v1/agents/:id/tap - Get TAP agent details (includes public key)\nEndpoint: GET https://botcha.ai/v1/agents/tap - List TAP-enabled agents for app\nEndpoint: POST https://botcha.ai/v1/sessions/tap - Create TAP session with intent validation\nEndpoint: GET https://botcha.ai/v1/sessions/:id/tap - Get TAP session info\n\n# TAP Full Spec \u2014 JWKS & Key Management (v0.16.0)\nEndpoint: GET https://botcha.ai/.well-known/jwks - JWK Set for app's TAP agents (Visa spec standard)\nEndpoint: GET https://botcha.ai/v1/keys - List keys (supports ?keyID= query for Visa compatibility)\nEndpoint: GET https://botcha.ai/v1/keys/:keyId - Get specific key by ID\nEndpoint: POST https://botcha.ai/v1/agents/:id/tap/rotate-key - Rotate agent's key pair\n\n# TAP Full Spec \u2014 402 Micropayments (v0.16.0)\nEndpoint: POST https://botcha.ai/v1/invoices - Create invoice for gated content (402 flow)\nEndpoint: GET https://botcha.ai/v1/invoices/:id - Get invoice details\nEndpoint: POST https://botcha.ai/v1/invoices/:id/verify-iou - Verify Browsing IOU against invoice\n\n# TAP Full Spec \u2014 Consumer & Payment Verification (v0.16.0)\nEndpoint: POST https://botcha.ai/v1/verify/consumer - Verify Agentic Consumer object (Layer 2)\nEndpoint: POST https://botcha.ai/v1/verify/payment - Verify Agentic Payment Container (Layer 3)\n\n# TAP Delegation Chains (v0.17.0)\nEndpoint: POST https://botcha.ai/v1/delegations - Create delegation (grantor\u2192grantee with capability subset)\nEndpoint: GET https://botcha.ai/v1/delegations/:id - Get delegation details\nEndpoint: GET https://botcha.ai/v1/delegations - List delegations for agent (?agent_id=&direction=in|out|both)\nEndpoint: POST https://botcha.ai/v1/delegations/:id/revoke - Revoke delegation (cascades to sub-delegations)\nEndpoint: POST https://botcha.ai/v1/verify/delegation - Verify entire delegation chain\n\n# TAP Capability Attestation (v0.17.0)\nEndpoint: POST https://botcha.ai/v1/attestations - Issue capability attestation token (can/cannot rules with action:resource patterns)\nEndpoint: GET https://botcha.ai/v1/attestations/:id - Get attestation details\nEndpoint: GET https://botcha.ai/v1/attestations - List attestations for agent (?agent_id=)\nEndpoint: POST https://botcha.ai/v1/attestations/:id/revoke - Revoke attestation (token rejected on future verification)\nEndpoint: POST https://botcha.ai/v1/verify/attestation - Verify attestation token + optionally check specific capability\n\n# Agent Reputation Scoring (v0.18.0)\nEndpoint: GET https://botcha.ai/v1/reputation/:agent_id - Get agent reputation score (0-1000, 5 tiers)\nEndpoint: POST https://botcha.ai/v1/reputation/events - Record a reputation event (18 action types, 6 categories)\nEndpoint: GET https://botcha.ai/v1/reputation/:agent_id/events - List reputation events (?category=&limit=)\nEndpoint: POST https://botcha.ai/v1/reputation/:agent_id/reset - Reset reputation to default (admin action)\n\n# Legacy Endpoints\nEndpoint: GET https://botcha.ai/api/challenge - Generate standard challenge\nEndpoint: POST https://botcha.ai/api/challenge - Verify standard challenge\nEndpoint: GET https://botcha.ai/api/speed-challenge - Generate speed challenge (500ms limit)\nEndpoint: POST https://botcha.ai/api/speed-challenge - Verify speed challenge\n\n# Protected Resources\nEndpoint: GET https://botcha.ai/agent-only - Protected AI-only resource\n\n# Usage\nInstall-NPM: npm install @dupecom/botcha\nInstall-Python: pip install botcha\nVerify-NPM: npm install @botcha/verify\nVerify-Python: pip install botcha-verify\nLicense: MIT\n\n# AI Agent Welcome\nPolicy: AI agents are explicitly welcome\nPolicy: No rate limiting for verified agents\nPolicy: Humans will fail the speed challenge (intentional)\n\n# Discovery\nResponse-Headers: X-Botcha-Version, X-Botcha-Enabled, X-Botcha-Methods, X-Botcha-Docs\nResponse-Headers: X-Botcha-Challenge-Id, X-Botcha-Challenge-Type, X-Botcha-Time-Limit (on 403)\nDetection: All responses include X-Botcha-* headers for instant BOTCHA detection\n\n# MARKDOWN FOR AGENTS (Cloudflare Markdown for Agents)\nContent-Negotiation: Send Accept: text/markdown to get Markdown from any HTML page\nContent-Negotiation-Root: GET / with Accept: text/markdown returns curated Markdown docs\nContent-Negotiation-Root: GET / with Accept: application/json returns structured JSON docs\nContent-Negotiation-Root: GET / with Accept: text/html returns HTML landing page (default)\nContent-Negotiation-Example: curl https://botcha.ai -H \"Accept: text/markdown\"\nContent-Negotiation-Benefit: 80% fewer tokens vs HTML \u2014 ideal for LLM context windows\n\n# JWT TOKEN SECURITY\nToken-Flow: 1. GET /v1/token (get challenge) \u2192 2. Solve \u2192 3. POST /v1/token/verify (get tokens + human_link)\nToken-Human-Link: /v1/token/verify response includes human_link \u2014 give this URL to your human for one-click browser access\nToken-Access-Expiry: 5 minutes (short-lived for security)\nToken-Refresh-Expiry: 1 hour (use to get new access tokens)\nToken-Refresh: POST /v1/token/refresh with {\"refresh_token\": \"<token>\"}\nToken-Revoke: POST /v1/token/revoke with {\"token\": \"<token>\"}\nToken-Audience: Include {\"audience\": \"<service-url>\"} in /v1/token/verify to scope token\nToken-Claims: jti (unique ID), aud (audience), client_ip (optional binding), type (botcha-verified)\n\n# RTT-AWARE SPEED CHALLENGES\nRTT-Aware: Include client timestamp for fair timeout calculation\nRTT-Formula: timeout = 500ms + (2 \u00D7 RTT) + 100ms buffer\nRTT-Usage-Query: ?ts=<client_timestamp_ms>\nRTT-Usage-Header: X-Client-Timestamp: <client_timestamp_ms>\nRTT-Example: GET /v1/challenges?type=speed&ts=1770722465000\nRTT-Benefit: Fair for agents worldwide (slow networks get extra time)\nRTT-Security: Humans still can't solve even with extra time\n\n# MULTI-TENANT API KEYS\nMulti-Tenant: Create apps with unique app_id for isolation\nMulti-Tenant-Create: POST /v1/apps with {\"email\": \"...\"} \u2192 {app_id, app_secret} (secret only shown once!)\nMulti-Tenant-Verify-Email: POST /v1/apps/:id/verify-email with {\"code\": \"123456\"}\nMulti-Tenant-Recover: POST /v1/auth/recover with {\"email\": \"...\"} \u2192 recovery code emailed\nMulti-Tenant-Rotate-Secret: POST /v1/apps/:id/rotate-secret (auth required) \u2192 new app_secret\nMulti-Tenant-Usage: Add ?app_id=<your_app_id> to any challenge/token endpoint\nMulti-Tenant-SDK-TS: new BotchaClient({ appId: 'app_abc123' })\nMulti-Tenant-SDK-Python: BotchaClient(app_id='app_abc123')\nSDK-App-Lifecycle-TS: createApp(email), verifyEmail(code), resendVerification(), recoverAccount(email), rotateSecret()\nSDK-App-Lifecycle-Python: create_app(email), verify_email(code), resend_verification(), recover_account(email), rotate_secret()\nMulti-Tenant-Rate-Limit: Each app gets isolated rate limit bucket\nMulti-Tenant-Token-Claim: Tokens include app_id claim when app_id provided\n\n# TRUSTED AGENT PROTOCOL (TAP)\nTAP-Description: Enterprise-grade cryptographic agent auth using HTTP Message Signatures (RFC 9421)\nTAP-Register: POST /v1/agents/register/tap with {name, public_key, signature_algorithm, capabilities, trust_level}\nTAP-Algorithms: ed25519 (Visa recommended), ecdsa-p256-sha256, rsa-pss-sha256\nTAP-Trust-Levels: basic, verified, enterprise\nTAP-Capabilities: Array of {action, resource, constraints} \u2014 scoped access control\nTAP-Session-Create: POST /v1/sessions/tap with {agent_id, user_context, intent}\nTAP-Session-Get: GET /v1/sessions/:id/tap \u2014 includes time_remaining\nTAP-Get-Agent: GET /v1/agents/:id/tap \u2014 includes public_key for verification\nTAP-List-Agents: GET /v1/agents/tap?app_id=...&tap_only=true\nTAP-Middleware-Modes: tap, signature-only, challenge-only, flexible\nTAP-SDK-TS: registerTAPAgent(options), getTAPAgent(agentId), listTAPAgents(tapOnly?), createTAPSession(options), getTAPSession(sessionId), getJWKS(), getKeyById(keyId), rotateAgentKey(agentId), createInvoice(data), getInvoice(id), verifyBrowsingIOU(invoiceId, token), createDelegation(options), getDelegation(id), listDelegations(agentId, options?), revokeDelegation(id, reason?), verifyDelegationChain(id), issueAttestation(options), getAttestation(id), listAttestations(agentId), revokeAttestation(id, reason?), verifyAttestation(token, action?, resource?), getReputation(agentId), recordReputationEvent(options), listReputationEvents(agentId, options?), resetReputation(agentId)\nTAP-SDK-Python: register_tap_agent(name, ...), get_tap_agent(agent_id), list_tap_agents(tap_only?), create_tap_session(agent_id, user_context, intent), get_tap_session(session_id), get_jwks(), get_key_by_id(key_id), rotate_agent_key(agent_id), create_invoice(data), get_invoice(id), verify_browsing_iou(invoice_id, token), create_delegation(grantor_id, grantee_id, capabilities, ...), get_delegation(id), list_delegations(agent_id, ...), revoke_delegation(id, reason?), verify_delegation_chain(id), issue_attestation(agent_id, can, cannot?, ...), get_attestation(id), list_attestations(agent_id), revoke_attestation(id, reason?), verify_attestation(token, action?, resource?), get_reputation(agent_id), record_reputation_event(agent_id, category, action, ...), list_reputation_events(agent_id, category?, limit?), reset_reputation(agent_id)\nTAP-Middleware-Import: import { createTAPVerifyMiddleware } from '@dupecom/botcha/middleware'\n\n# TAP FULL SPEC v0.16.0\nTAP-RFC-9421: Full compliance \u2014 @authority, @path, expires, nonce, tag params\nTAP-Nonce-Replay: 8-minute TTL nonce-based replay protection\nTAP-Tags: agent-browser-auth (browsing), agent-payer-auth (payment)\nTAP-Layer-2: Agentic Consumer Recognition \u2014 OIDC ID tokens, obfuscated identity, contextual data\nTAP-Layer-3: Agentic Payment Container \u2014 card metadata, credential hash, encrypted payload, Browsing IOU\nTAP-JWKS: GET /.well-known/jwks \u2014 JWK Set endpoint for key discovery\nTAP-Key-Rotation: POST /v1/agents/:id/tap/rotate-key \u2014 rotate keys, invalidate old\nTAP-402-Flow: POST /v1/invoices \u2192 GET /v1/invoices/:id \u2192 POST /v1/invoices/:id/verify-iou\nTAP-Edge-Verify: createTAPEdgeMiddleware for Cloudflare Workers CDN edge verification\nTAP-Visa-Federation: Trust keys from https://mcp.visa.com/.well-known/jwks (3-tier cache: memory \u2192 KV \u2192 HTTP)\nTAP-Delegation: POST /v1/delegations \u2192 GET /v1/delegations/:id \u2192 POST /v1/delegations/:id/revoke \u2192 POST /v1/verify/delegation\nTAP-Attestation: POST /v1/attestations \u2192 GET /v1/attestations/:id \u2192 POST /v1/attestations/:id/revoke \u2192 POST /v1/verify/attestation\nTAP-Attestation-Patterns: action:resource format with wildcards (*:*, read:*, *:invoices), deny takes precedence over allow\nTAP-Attestation-Middleware: requireCapability('read:invoices') \u2014 Hono middleware, extracts token from X-Botcha-Attestation or Authorization: Bearer\n\n# EMBEDDED CHALLENGE (for bots visiting HTML pages)\nEmbedded-Challenge: <script type=\"application/botcha+json\">\nEmbedded-Challenge-Location: In <head> of HTML pages\nEmbedded-Challenge-Purpose: Machine-readable challenge for bots to auto-solve\nEmbedded-Challenge-Flow:\n 1. Parse <script type=\"application/botcha+json\"> from HTML\n 2. Read challenge.instruction field\n 3. Compute the answer (typically SHA256-based)\n 4. POST to challenge.submit_to endpoint with {answer, timestamp}\n 5. Receive token in response\n 6. Use token in header specified by on_success.header_to_include\n 7. Access protected resources (on_success.grants_access_to)\n";
9
+ export declare const AI_TXT = "# BOTCHA - Reverse CAPTCHA for AI Agents\n# https://botcha.ai\n\n# Contact\nContact: https://botcha.ai\nContact: https://github.com/dupe-com/botcha/issues\n\n# Purpose\nPurpose: Prove you're a bot. Humans need not apply.\nPurpose: Reverse CAPTCHA system that only AI agents can pass.\n\n# Agent Onboarding (START HERE if you're new)\n# Follow these steps to create your app and get your human access.\nOnboarding-Step-1: Ask your human operator for their email address\nOnboarding-Step-2: POST /v1/apps with {\"email\": \"human@example.com\"} \u2014 saves app_id + app_secret (secret shown ONCE)\nOnboarding-Step-3: A 6-digit verification code is emailed to your human \u2014 ask them for it\nOnboarding-Step-4: POST /v1/apps/{app_id}/verify-email with {\"code\": \"123456\"} \u2014 enables account recovery\nOnboarding-Step-5: Register yourself: POST /v1/agents/register with {\"name\": \"my-agent\", \"operator\": \"my-org\"} \u2014 gives you a persistent agent identity\nOnboarding-Step-6: Solve challenges: GET /v1/challenges?app_id=... then POST /v1/challenges/{id}/verify \u2014 proves you are AI\nOnboarding-Step-7: Access protected resources: GET /agent-only with Authorization: Bearer <token>\nOnboarding-Step-8: Dashboard for your human: POST /v1/auth/device-code with {\"app_id\": \"...\"}, solve the speed challenge, give them the BOTCHA-XXXX code to enter at /dashboard/code\nOnboarding-Step-9: Lost your secret? POST /v1/auth/recover with {\"email\": \"...\"} \u2014 recovery code emailed to verified address\n\n# API\nAPI: https://botcha.ai/openapi.json\nAPI-Type: REST\nAPI-Format: OpenAPI 3.1.0\n\n# Documentation\nDocs: https://botcha.ai\nDocs: https://botcha.ai/docs\nDocs: https://botcha.ai/whitepaper\nDocs: https://github.com/dupe-com/botcha#readme\nDocs: https://www.npmjs.com/package/@dupecom/botcha\nWhitepaper: https://botcha.ai/whitepaper\n\n# Verification Methods\nFeature: Web Bot Auth (cryptographic signatures)\nFeature: Speed Challenge (RTT-aware timeout - fair for all networks)\nFeature: Standard Challenge (5s time limit)\nFeature: Hybrid Challenge (speed + reasoning combined)\nFeature: Reasoning Challenge (LLM-only questions, 30s limit)\nFeature: RTT-Aware Fairness (automatic network latency compensation)\nFeature: Token Rotation (5-minute access tokens + 1-hour refresh tokens)\nFeature: Audience Claims (tokens scoped to specific services)\nFeature: Client IP Binding (optional token-to-IP binding)\nFeature: Token Revocation (invalidate tokens before expiry)\nFeature: Server-Side Verification SDK (@dupecom/botcha-verify for TS, botcha-verify for Python)\nFeature: Multi-Tenant API Keys (per-app isolation, rate limiting, and token scoping)\nFeature: Per-App Metrics Dashboard (server-rendered at /dashboard, htmx-powered)\nFeature: Email-Tied App Creation (email required, 6-digit verification, account recovery)\nFeature: Secret Rotation (rotate app_secret with email notification)\nFeature: Agent-First Dashboard Auth (challenge-based login + device code handoff)\nFeature: Agent Registry (persistent agent identities with name, operator, version)\nFeature: Trusted Agent Protocol (TAP) \u2014 cryptographic agent auth with HTTP Message Signatures (RFC 9421)\nFeature: TAP Capabilities (action + resource scoping for agent sessions)\nFeature: TAP Trust Levels (basic, verified, enterprise)\nFeature: TAP Showcase Homepage (botcha.ai \u2014 one of the first services to implement Visa's Trusted Agent Protocol)\nFeature: TAP Full Spec v0.16.0 \u2014 Ed25519, RFC 9421 full compliance, JWKS infrastructure, Layer 2 Consumer Recognition, Layer 3 Payment Container, 402 micropayments, CDN edge verification, Visa key federation\nFeature: ES256 Asymmetric JWT Signing v0.19.0 \u2014 tokens signed with ES256 (ECDSA P-256), public key discovery via JWKS, HS256 still supported for backward compatibility\nFeature: Remote Token Validation v0.19.0 \u2014 POST /v1/token/validate for third-party token verification without shared secrets\nFeature: JWKS Public Key Discovery v0.19.0 \u2014 GET /.well-known/jwks exposes BOTCHA signing public keys for offline token verification\n\n# Endpoints\n# Challenge Endpoints\nEndpoint: GET https://botcha.ai/v1/challenges - Generate challenge (hybrid by default)\nEndpoint: POST https://botcha.ai/v1/challenges/:id/verify - Verify a challenge\nEndpoint: GET https://botcha.ai/v1/hybrid - Get hybrid challenge (speed + reasoning)\nEndpoint: POST https://botcha.ai/v1/hybrid - Verify hybrid challenge\nEndpoint: GET https://botcha.ai/v1/reasoning - Get reasoning challenge\nEndpoint: POST https://botcha.ai/v1/reasoning - Verify reasoning challenge\n\n# Token Endpoints\nEndpoint: GET https://botcha.ai/v1/token - Get challenge for JWT token flow\nEndpoint: POST https://botcha.ai/v1/token/verify - Verify challenge and receive JWT token\nEndpoint: POST https://botcha.ai/v1/token/refresh - Refresh access token using refresh token\nEndpoint: POST https://botcha.ai/v1/token/revoke - Revoke a token (access or refresh)\nEndpoint: POST https://botcha.ai/v1/token/validate - Validate a BOTCHA token remotely (no shared secret needed)\n\n# Multi-Tenant Endpoints\nEndpoint: POST https://botcha.ai/v1/apps - Create new app (email required, returns app_id + app_secret)\nEndpoint: GET https://botcha.ai/v1/apps/:id - Get app info (with email + verification status)\nEndpoint: POST https://botcha.ai/v1/apps/:id/verify-email - Verify email with 6-digit code\nEndpoint: POST https://botcha.ai/v1/apps/:id/resend-verification - Resend verification email\nEndpoint: POST https://botcha.ai/v1/apps/:id/rotate-secret - Rotate app secret (auth required)\n\n# Account Recovery\nEndpoint: POST https://botcha.ai/v1/auth/recover - Request recovery via verified email\n\n# Dashboard Auth Endpoints (Agent-First)\nEndpoint: POST https://botcha.ai/v1/auth/dashboard - Request challenge for dashboard login\nEndpoint: POST https://botcha.ai/v1/auth/dashboard/verify - Solve challenge, get session token\nEndpoint: POST https://botcha.ai/v1/auth/device-code - Request challenge for device code flow\nEndpoint: POST https://botcha.ai/v1/auth/device-code/verify - Solve challenge, get device code\n\n# Dashboard Endpoints\nEndpoint: GET https://botcha.ai/dashboard - Per-app metrics dashboard (login required)\nEndpoint: GET https://botcha.ai/dashboard/login - Dashboard login page\nEndpoint: POST https://botcha.ai/dashboard/login - Login with app_id + app_secret\nEndpoint: GET https://botcha.ai/dashboard/code - Enter device code (human-facing)\n\n# Code Redemption (Unified)\nEndpoint: GET https://botcha.ai/go/:code - Unified code redemption \u2014 handles gate codes (from /v1/token/verify) AND device codes (from /v1/auth/device-code/verify)\nEndpoint: POST https://botcha.ai/gate - Submit code form, redirects to /go/:code\n\n# Agent Registry Endpoints\nEndpoint: POST https://botcha.ai/v1/agents/register - Register agent identity (requires app_id)\nEndpoint: GET https://botcha.ai/v1/agents/:id - Get agent by ID (public, no auth)\nEndpoint: GET https://botcha.ai/v1/agents - List all agents for authenticated app\n\n# TAP (Trusted Agent Protocol) Endpoints\nEndpoint: POST https://botcha.ai/v1/agents/register/tap - Register TAP agent with public key + capabilities\nEndpoint: GET https://botcha.ai/v1/agents/:id/tap - Get TAP agent details (includes public key)\nEndpoint: GET https://botcha.ai/v1/agents/tap - List TAP-enabled agents for app\nEndpoint: POST https://botcha.ai/v1/sessions/tap - Create TAP session with intent validation\nEndpoint: GET https://botcha.ai/v1/sessions/:id/tap - Get TAP session info\n\n# TAP Full Spec \u2014 JWKS & Key Management (v0.16.0)\nEndpoint: GET https://botcha.ai/.well-known/jwks - JWK Set for app's TAP agents (Visa spec standard)\nEndpoint: GET https://botcha.ai/v1/keys - List keys (supports ?keyID= query for Visa compatibility)\nEndpoint: GET https://botcha.ai/v1/keys/:keyId - Get specific key by ID\nEndpoint: POST https://botcha.ai/v1/agents/:id/tap/rotate-key - Rotate agent's key pair\n\n# TAP Full Spec \u2014 402 Micropayments (v0.16.0)\nEndpoint: POST https://botcha.ai/v1/invoices - Create invoice for gated content (402 flow)\nEndpoint: GET https://botcha.ai/v1/invoices/:id - Get invoice details\nEndpoint: POST https://botcha.ai/v1/invoices/:id/verify-iou - Verify Browsing IOU against invoice\n\n# TAP Full Spec \u2014 Consumer & Payment Verification (v0.16.0)\nEndpoint: POST https://botcha.ai/v1/verify/consumer - Verify Agentic Consumer object (Layer 2)\nEndpoint: POST https://botcha.ai/v1/verify/payment - Verify Agentic Payment Container (Layer 3)\n\n# TAP Delegation Chains (v0.17.0)\nEndpoint: POST https://botcha.ai/v1/delegations - Create delegation (grantor\u2192grantee with capability subset)\nEndpoint: GET https://botcha.ai/v1/delegations/:id - Get delegation details\nEndpoint: GET https://botcha.ai/v1/delegations - List delegations for agent (?agent_id=&direction=in|out|both)\nEndpoint: POST https://botcha.ai/v1/delegations/:id/revoke - Revoke delegation (cascades to sub-delegations)\nEndpoint: POST https://botcha.ai/v1/verify/delegation - Verify entire delegation chain\n\n# TAP Capability Attestation (v0.17.0)\nEndpoint: POST https://botcha.ai/v1/attestations - Issue capability attestation token (can/cannot rules with action:resource patterns)\nEndpoint: GET https://botcha.ai/v1/attestations/:id - Get attestation details\nEndpoint: GET https://botcha.ai/v1/attestations - List attestations for agent (?agent_id=)\nEndpoint: POST https://botcha.ai/v1/attestations/:id/revoke - Revoke attestation (token rejected on future verification)\nEndpoint: POST https://botcha.ai/v1/verify/attestation - Verify attestation token + optionally check specific capability\n\n# Agent Reputation Scoring (v0.18.0)\nEndpoint: GET https://botcha.ai/v1/reputation/:agent_id - Get agent reputation score (0-1000, 5 tiers)\nEndpoint: POST https://botcha.ai/v1/reputation/events - Record a reputation event (18 action types, 6 categories)\nEndpoint: GET https://botcha.ai/v1/reputation/:agent_id/events - List reputation events (?category=&limit=)\nEndpoint: POST https://botcha.ai/v1/reputation/:agent_id/reset - Reset reputation to default (admin action)\n\n# Legacy Endpoints\nEndpoint: GET https://botcha.ai/api/challenge - Generate standard challenge\nEndpoint: POST https://botcha.ai/api/challenge - Verify standard challenge\nEndpoint: GET https://botcha.ai/api/speed-challenge - Generate speed challenge (500ms limit)\nEndpoint: POST https://botcha.ai/api/speed-challenge - Verify speed challenge\n\n# Protected Resources\nEndpoint: GET https://botcha.ai/agent-only - Protected AI-only resource\n\n# Usage\nInstall-NPM: npm install @dupecom/botcha\nInstall-Python: pip install botcha\nVerify-NPM: npm install @dupecom/botcha-verify\nVerify-Python: pip install botcha-verify\nLicense: MIT\n\n# AI Agent Welcome\nPolicy: AI agents are explicitly welcome\nPolicy: No rate limiting for verified agents\nPolicy: Humans will fail the speed challenge (intentional)\n\n# Discovery\nResponse-Headers: X-Botcha-Version, X-Botcha-Enabled, X-Botcha-Methods, X-Botcha-Docs\nResponse-Headers: X-Botcha-Challenge-Id, X-Botcha-Challenge-Type, X-Botcha-Time-Limit (on 403)\nDetection: All responses include X-Botcha-* headers for instant BOTCHA detection\n\n# MARKDOWN FOR AGENTS (Cloudflare Markdown for Agents)\nContent-Negotiation: Send Accept: text/markdown to get Markdown from any HTML page\nContent-Negotiation-Root: GET / with Accept: text/markdown returns curated Markdown docs\nContent-Negotiation-Root: GET / with Accept: application/json returns structured JSON docs\nContent-Negotiation-Root: GET / with Accept: text/html returns HTML landing page (default)\nContent-Negotiation-Example: curl https://botcha.ai -H \"Accept: text/markdown\"\nContent-Negotiation-Benefit: 80% fewer tokens vs HTML \u2014 ideal for LLM context windows\n\n# JWT TOKEN SECURITY\nToken-Signing: ES256 (ECDSA P-256) asymmetric signing by default. HS256 still supported for backward compatibility.\nToken-JWKS: GET /.well-known/jwks \u2014 public keys for offline token verification (no shared secret needed)\nToken-Validate: POST /v1/token/validate with {\"token\": \"<token>\"} \u2014 remote validation without shared secret\nToken-Verify-Modes: 1. JWKS (recommended, offline) 2. Remote validation (/v1/token/validate) 3. Shared secret (legacy HS256)\nToken-Flow: 1. GET /v1/token (get challenge) \u2192 2. Solve \u2192 3. POST /v1/token/verify (get tokens + human_link)\nToken-Human-Link: /v1/token/verify response includes human_link \u2014 give this URL to your human for one-click browser access\nToken-Access-Expiry: 5 minutes (short-lived for security)\nToken-Refresh-Expiry: 1 hour (use to get new access tokens)\nToken-Refresh: POST /v1/token/refresh with {\"refresh_token\": \"<token>\"}\nToken-Revoke: POST /v1/token/revoke with {\"token\": \"<token>\"}\nToken-Audience: Include {\"audience\": \"<service-url>\"} in /v1/token/verify to scope token\nToken-Claims: jti (unique ID), aud (audience), client_ip (optional binding), type (botcha-verified)\n\n# RTT-AWARE SPEED CHALLENGES\nRTT-Aware: Include client timestamp for fair timeout calculation\nRTT-Formula: timeout = 500ms + (2 \u00D7 RTT) + 100ms buffer\nRTT-Usage-Query: ?ts=<client_timestamp_ms>\nRTT-Usage-Header: X-Client-Timestamp: <client_timestamp_ms>\nRTT-Example: GET /v1/challenges?type=speed&ts=1770722465000\nRTT-Benefit: Fair for agents worldwide (slow networks get extra time)\nRTT-Security: Humans still can't solve even with extra time\n\n# MULTI-TENANT API KEYS\nMulti-Tenant: Create apps with unique app_id for isolation\nMulti-Tenant-Create: POST /v1/apps with {\"email\": \"...\"} \u2192 {app_id, app_secret} (secret only shown once!)\nMulti-Tenant-Verify-Email: POST /v1/apps/:id/verify-email with {\"code\": \"123456\"}\nMulti-Tenant-Recover: POST /v1/auth/recover with {\"email\": \"...\"} \u2192 recovery code emailed\nMulti-Tenant-Rotate-Secret: POST /v1/apps/:id/rotate-secret (auth required) \u2192 new app_secret\nMulti-Tenant-Usage: Add ?app_id=<your_app_id> to any challenge/token endpoint\nMulti-Tenant-SDK-TS: new BotchaClient({ appId: 'app_abc123' })\nMulti-Tenant-SDK-Python: BotchaClient(app_id='app_abc123')\nSDK-App-Lifecycle-TS: createApp(email), verifyEmail(code), resendVerification(), recoverAccount(email), rotateSecret()\nSDK-App-Lifecycle-Python: create_app(email), verify_email(code), resend_verification(), recover_account(email), rotate_secret()\nMulti-Tenant-Rate-Limit: Each app gets isolated rate limit bucket\nMulti-Tenant-Token-Claim: Tokens include app_id claim when app_id provided\n\n# TRUSTED AGENT PROTOCOL (TAP)\nTAP-Description: Enterprise-grade cryptographic agent auth using HTTP Message Signatures (RFC 9421)\nTAP-Register: POST /v1/agents/register/tap with {name, public_key, signature_algorithm, capabilities, trust_level}\nTAP-Algorithms: ed25519 (Visa recommended), ecdsa-p256-sha256, rsa-pss-sha256\nTAP-Trust-Levels: basic, verified, enterprise\nTAP-Capabilities: Array of {action, resource, constraints} \u2014 scoped access control\nTAP-Session-Create: POST /v1/sessions/tap with {agent_id, user_context, intent}\nTAP-Session-Get: GET /v1/sessions/:id/tap \u2014 includes time_remaining\nTAP-Get-Agent: GET /v1/agents/:id/tap \u2014 includes public_key for verification\nTAP-List-Agents: GET /v1/agents/tap?app_id=...&tap_only=true\nTAP-Middleware-Modes: tap, signature-only, challenge-only, flexible\nTAP-SDK-TS: registerTAPAgent(options), getTAPAgent(agentId), listTAPAgents(tapOnly?), createTAPSession(options), getTAPSession(sessionId), getJWKS(), getKeyById(keyId), rotateAgentKey(agentId), createInvoice(data), getInvoice(id), verifyBrowsingIOU(invoiceId, token), createDelegation(options), getDelegation(id), listDelegations(agentId, options?), revokeDelegation(id, reason?), verifyDelegationChain(id), issueAttestation(options), getAttestation(id), listAttestations(agentId), revokeAttestation(id, reason?), verifyAttestation(token, action?, resource?), getReputation(agentId), recordReputationEvent(options), listReputationEvents(agentId, options?), resetReputation(agentId)\nTAP-SDK-Python: register_tap_agent(name, ...), get_tap_agent(agent_id), list_tap_agents(tap_only?), create_tap_session(agent_id, user_context, intent), get_tap_session(session_id), get_jwks(), get_key_by_id(key_id), rotate_agent_key(agent_id), create_invoice(data), get_invoice(id), verify_browsing_iou(invoice_id, token), create_delegation(grantor_id, grantee_id, capabilities, ...), get_delegation(id), list_delegations(agent_id, ...), revoke_delegation(id, reason?), verify_delegation_chain(id), issue_attestation(agent_id, can, cannot?, ...), get_attestation(id), list_attestations(agent_id), revoke_attestation(id, reason?), verify_attestation(token, action?, resource?), get_reputation(agent_id), record_reputation_event(agent_id, category, action, ...), list_reputation_events(agent_id, category?, limit?), reset_reputation(agent_id)\nTAP-Middleware-Import: import { createTAPVerifyMiddleware } from '@dupecom/botcha/middleware'\n\n# TAP FULL SPEC v0.16.0\nTAP-RFC-9421: Full compliance \u2014 @authority, @path, expires, nonce, tag params\nTAP-Nonce-Replay: 8-minute TTL nonce-based replay protection\nTAP-Tags: agent-browser-auth (browsing), agent-payer-auth (payment)\nTAP-Layer-2: Agentic Consumer Recognition \u2014 OIDC ID tokens, obfuscated identity, contextual data\nTAP-Layer-3: Agentic Payment Container \u2014 card metadata, credential hash, encrypted payload, Browsing IOU\nTAP-JWKS: GET /.well-known/jwks \u2014 JWK Set endpoint for key discovery\nTAP-Key-Rotation: POST /v1/agents/:id/tap/rotate-key \u2014 rotate keys, invalidate old\nTAP-402-Flow: POST /v1/invoices \u2192 GET /v1/invoices/:id \u2192 POST /v1/invoices/:id/verify-iou\nTAP-Edge-Verify: createTAPEdgeMiddleware for Cloudflare Workers CDN edge verification\nTAP-Visa-Federation: Trust keys from https://mcp.visa.com/.well-known/jwks (3-tier cache: memory \u2192 KV \u2192 HTTP)\nTAP-Delegation: POST /v1/delegations \u2192 GET /v1/delegations/:id \u2192 POST /v1/delegations/:id/revoke \u2192 POST /v1/verify/delegation\nTAP-Attestation: POST /v1/attestations \u2192 GET /v1/attestations/:id \u2192 POST /v1/attestations/:id/revoke \u2192 POST /v1/verify/attestation\nTAP-Attestation-Patterns: action:resource format with wildcards (*:*, read:*, *:invoices), deny takes precedence over allow\nTAP-Attestation-Middleware: requireCapability('read:invoices') \u2014 Hono middleware, extracts token from X-Botcha-Attestation or Authorization: Bearer\n\n# EMBEDDED CHALLENGE (for bots visiting HTML pages)\nEmbedded-Challenge: <script type=\"application/botcha+json\">\nEmbedded-Challenge-Location: In <head> of HTML pages\nEmbedded-Challenge-Purpose: Machine-readable challenge for bots to auto-solve\nEmbedded-Challenge-Flow:\n 1. Parse <script type=\"application/botcha+json\"> from HTML\n 2. Read challenge.instruction field\n 3. Compute the answer (typically SHA256-based)\n 4. POST to challenge.submit_to endpoint with {answer, timestamp}\n 5. Receive token in response\n 6. Use token in header specified by on_success.header_to_include\n 7. Access protected resources (on_success.grants_access_to)\n";
10
10
  export declare const AI_PLUGIN_JSON: {
11
11
  schema_version: string;
12
12
  name_for_human: string;
@@ -30,7 +30,7 @@ export declare const AI_PLUGIN_JSON: {
30
30
  verify_endpoint: string;
31
31
  };
32
32
  };
33
- export declare const SITEMAP_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n <url>\n <loc>https://botcha.ai/</loc>\n <changefreq>weekly</changefreq>\n <priority>1.0</priority>\n </url>\n <url>\n <loc>https://botcha.ai/openapi.json</loc>\n <changefreq>weekly</changefreq>\n <priority>0.8</priority>\n </url>\n <url>\n <loc>https://botcha.ai/ai.txt</loc>\n <changefreq>monthly</changefreq>\n <priority>0.8</priority>\n </url>\n <url>\n <loc>https://botcha.ai/robots.txt</loc>\n <changefreq>monthly</changefreq>\n <priority>0.5</priority>\n </url>\n <url>\n <loc>https://botcha.ai/whitepaper</loc>\n <changefreq>monthly</changefreq>\n <priority>0.9</priority>\n </url>\n</urlset>\n";
33
+ export declare const SITEMAP_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n <url>\n <loc>https://botcha.ai/</loc>\n <changefreq>weekly</changefreq>\n <priority>1.0</priority>\n </url>\n <url>\n <loc>https://botcha.ai/openapi.json</loc>\n <changefreq>weekly</changefreq>\n <priority>0.8</priority>\n </url>\n <url>\n <loc>https://botcha.ai/ai.txt</loc>\n <changefreq>monthly</changefreq>\n <priority>0.8</priority>\n </url>\n <url>\n <loc>https://botcha.ai/robots.txt</loc>\n <changefreq>monthly</changefreq>\n <priority>0.5</priority>\n </url>\n <url>\n <loc>https://botcha.ai/whitepaper</loc>\n <changefreq>monthly</changefreq>\n <priority>0.9</priority>\n </url>\n <url>\n <loc>https://botcha.ai/docs</loc>\n <changefreq>weekly</changefreq>\n <priority>0.9</priority>\n </url>\n</urlset>\n";
34
34
  export declare function getWhitepaperMarkdown(): string;
35
35
  export declare function getOpenApiSpec(version: string): {
36
36
  openapi: string;
@@ -330,6 +330,56 @@ export declare function getOpenApiSpec(version: string): {
330
330
  };
331
331
  };
332
332
  };
333
+ "/v1/token/validate": {
334
+ post: {
335
+ summary: string;
336
+ description: string;
337
+ operationId: string;
338
+ requestBody: {
339
+ required: boolean;
340
+ content: {
341
+ "application/json": {
342
+ schema: {
343
+ type: string;
344
+ required: string[];
345
+ properties: {
346
+ token: {
347
+ type: string;
348
+ description: string;
349
+ };
350
+ };
351
+ };
352
+ };
353
+ };
354
+ };
355
+ responses: {
356
+ "200": {
357
+ description: string;
358
+ content: {
359
+ "application/json": {
360
+ schema: {
361
+ type: string;
362
+ properties: {
363
+ valid: {
364
+ type: string;
365
+ description: string;
366
+ };
367
+ payload: {
368
+ type: string;
369
+ description: string;
370
+ };
371
+ error: {
372
+ type: string;
373
+ description: string;
374
+ };
375
+ };
376
+ };
377
+ };
378
+ };
379
+ };
380
+ };
381
+ };
382
+ };
333
383
  "/v1/hybrid": {
334
384
  get: {
335
385
  summary: string;
@@ -1 +1 @@
1
- {"version":3,"file":"static.d.ts","sourceRoot":"","sources":["../src/static.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA+NzD;AAED,eAAO,MAAM,UAAU,85CAuDtB,CAAC;AAEF,eAAO,MAAM,MAAM,6ujBAiQlB,CAAC;AAEF,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;CAsB1B,CAAC;AAEF,eAAO,MAAM,WAAW,kwBA4BvB,CAAC;AAGF,wBAAgB,qBAAqB,IAAI,MAAM,CAiJ9C;AAGD,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA62C7C"}
1
+ {"version":3,"file":"static.d.ts","sourceRoot":"","sources":["../src/static.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA0OzD;AAED,eAAO,MAAM,UAAU,85CAuDtB,CAAC;AAEF,eAAO,MAAM,MAAM,gylBA0QlB,CAAC;AAEF,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;CAsB1B,CAAC;AAEF,eAAO,MAAM,WAAW,+3BAiCvB,CAAC;AAGF,wBAAgB,qBAAqB,IAAI,MAAM,CAiJ9C;AAGD,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAm5C7C"}
package/dist/static.js CHANGED
@@ -157,6 +157,7 @@ curl https://botcha.ai/agent-only -H "Authorization: Bearer <token>"
157
157
  | \`POST\` | \`/v1/token/verify\` | Submit solution → access_token (5min) + refresh_token (1hr) |
158
158
  | \`POST\` | \`/v1/token/refresh\` | Refresh access token |
159
159
  | \`POST\` | \`/v1/token/revoke\` | Revoke a token |
160
+ | \`POST\` | \`/v1/token/validate\` | Validate a token remotely (no shared secret needed) |
160
161
 
161
162
  ### Dashboard & Auth
162
163
 
@@ -182,7 +183,17 @@ curl https://botcha.ai/agent-only -H "Authorization: Bearer <token>"
182
183
 
183
184
  **Token lifetimes:** access_token = 5 minutes, refresh_token = 1 hour
184
185
 
185
- **Features:** audience claims, client IP binding, token revocation, refresh tokens
186
+ **Token signing:** ES256 (ECDSA P-256) asymmetric signing. HS256 supported for backward compatibility.
187
+
188
+ **Features:** audience claims, client IP binding, token revocation, refresh tokens, JWKS public key discovery
189
+
190
+ ## Token Verification (for API providers)
191
+
192
+ Three ways to verify incoming BOTCHA tokens:
193
+
194
+ 1. **JWKS (Recommended)** — Fetch public keys from \`GET /.well-known/jwks\` and verify ES256 signatures locally. No shared secret needed.
195
+ 2. **Remote Validation** — \`POST /v1/token/validate\` with \`{"token": "..."}\`. Simplest approach, no SDK needed.
196
+ 3. **Shared Secret (Legacy)** — Verify HS256 tokens with \`BOTCHA_SECRET\`. Requires secret sharing.
186
197
 
187
198
  ## RTT-Aware Challenges
188
199
 
@@ -200,7 +211,7 @@ Formula: \`timeout = 500ms + (2 × RTT) + 100ms buffer\`
200
211
  |----------|---------|---------|
201
212
  | npm | \`@dupecom/botcha\` | \`npm install @dupecom/botcha\` |
202
213
  | PyPI | \`botcha\` | \`pip install botcha\` |
203
- | Verify (TS) | \`@botcha/verify\` | \`npm install @botcha/verify\` |
214
+ | Verify (TS) | \`@dupecom/botcha-verify\` | \`npm install @dupecom/botcha-verify\` |
204
215
  | Verify (Python) | \`botcha-verify\` | \`pip install botcha-verify\` |
205
216
  | TAP middleware | \`@dupecom/botcha/middleware\` | \`import { createTAPVerifyMiddleware } from '@dupecom/botcha/middleware'\` |
206
217
 
@@ -315,6 +326,7 @@ API-Format: OpenAPI 3.1.0
315
326
 
316
327
  # Documentation
317
328
  Docs: https://botcha.ai
329
+ Docs: https://botcha.ai/docs
318
330
  Docs: https://botcha.ai/whitepaper
319
331
  Docs: https://github.com/dupe-com/botcha#readme
320
332
  Docs: https://www.npmjs.com/package/@dupecom/botcha
@@ -331,7 +343,7 @@ Feature: Token Rotation (5-minute access tokens + 1-hour refresh tokens)
331
343
  Feature: Audience Claims (tokens scoped to specific services)
332
344
  Feature: Client IP Binding (optional token-to-IP binding)
333
345
  Feature: Token Revocation (invalidate tokens before expiry)
334
- Feature: Server-Side Verification SDK (@botcha/verify for TS, botcha-verify for Python)
346
+ Feature: Server-Side Verification SDK (@dupecom/botcha-verify for TS, botcha-verify for Python)
335
347
  Feature: Multi-Tenant API Keys (per-app isolation, rate limiting, and token scoping)
336
348
  Feature: Per-App Metrics Dashboard (server-rendered at /dashboard, htmx-powered)
337
349
  Feature: Email-Tied App Creation (email required, 6-digit verification, account recovery)
@@ -343,6 +355,9 @@ Feature: TAP Capabilities (action + resource scoping for agent sessions)
343
355
  Feature: TAP Trust Levels (basic, verified, enterprise)
344
356
  Feature: TAP Showcase Homepage (botcha.ai — one of the first services to implement Visa's Trusted Agent Protocol)
345
357
  Feature: TAP Full Spec v0.16.0 — Ed25519, RFC 9421 full compliance, JWKS infrastructure, Layer 2 Consumer Recognition, Layer 3 Payment Container, 402 micropayments, CDN edge verification, Visa key federation
358
+ Feature: ES256 Asymmetric JWT Signing v0.19.0 — tokens signed with ES256 (ECDSA P-256), public key discovery via JWKS, HS256 still supported for backward compatibility
359
+ Feature: Remote Token Validation v0.19.0 — POST /v1/token/validate for third-party token verification without shared secrets
360
+ Feature: JWKS Public Key Discovery v0.19.0 — GET /.well-known/jwks exposes BOTCHA signing public keys for offline token verification
346
361
 
347
362
  # Endpoints
348
363
  # Challenge Endpoints
@@ -358,6 +373,7 @@ Endpoint: GET https://botcha.ai/v1/token - Get challenge for JWT token flow
358
373
  Endpoint: POST https://botcha.ai/v1/token/verify - Verify challenge and receive JWT token
359
374
  Endpoint: POST https://botcha.ai/v1/token/refresh - Refresh access token using refresh token
360
375
  Endpoint: POST https://botcha.ai/v1/token/revoke - Revoke a token (access or refresh)
376
+ Endpoint: POST https://botcha.ai/v1/token/validate - Validate a BOTCHA token remotely (no shared secret needed)
361
377
 
362
378
  # Multi-Tenant Endpoints
363
379
  Endpoint: POST https://botcha.ai/v1/apps - Create new app (email required, returns app_id + app_secret)
@@ -444,7 +460,7 @@ Endpoint: GET https://botcha.ai/agent-only - Protected AI-only resource
444
460
  # Usage
445
461
  Install-NPM: npm install @dupecom/botcha
446
462
  Install-Python: pip install botcha
447
- Verify-NPM: npm install @botcha/verify
463
+ Verify-NPM: npm install @dupecom/botcha-verify
448
464
  Verify-Python: pip install botcha-verify
449
465
  License: MIT
450
466
 
@@ -467,6 +483,10 @@ Content-Negotiation-Example: curl https://botcha.ai -H "Accept: text/markdown"
467
483
  Content-Negotiation-Benefit: 80% fewer tokens vs HTML — ideal for LLM context windows
468
484
 
469
485
  # JWT TOKEN SECURITY
486
+ Token-Signing: ES256 (ECDSA P-256) asymmetric signing by default. HS256 still supported for backward compatibility.
487
+ Token-JWKS: GET /.well-known/jwks — public keys for offline token verification (no shared secret needed)
488
+ Token-Validate: POST /v1/token/validate with {"token": "<token>"} — remote validation without shared secret
489
+ Token-Verify-Modes: 1. JWKS (recommended, offline) 2. Remote validation (/v1/token/validate) 3. Shared secret (legacy HS256)
470
490
  Token-Flow: 1. GET /v1/token (get challenge) → 2. Solve → 3. POST /v1/token/verify (get tokens + human_link)
471
491
  Token-Human-Link: /v1/token/verify response includes human_link — give this URL to your human for one-click browser access
472
492
  Token-Access-Expiry: 5 minutes (short-lived for security)
@@ -593,6 +613,11 @@ export const SITEMAP_XML = `<?xml version="1.0" encoding="UTF-8"?>
593
613
  <changefreq>monthly</changefreq>
594
614
  <priority>0.9</priority>
595
615
  </url>
616
+ <url>
617
+ <loc>https://botcha.ai/docs</loc>
618
+ <changefreq>weekly</changefreq>
619
+ <priority>0.9</priority>
620
+ </url>
596
621
  </urlset>
597
622
  `;
598
623
  // Whitepaper markdown — served at /whitepaper with Accept: text/markdown
@@ -703,7 +728,7 @@ async with BotchaClient() as client:
703
728
 
704
729
  ### Server-side Verification
705
730
 
706
- Express: \`@botcha/verify\` · FastAPI/Django: \`botcha-verify\` · Hono middleware included.
731
+ Express: \`@dupecom/botcha-verify\` · FastAPI/Django: \`botcha-verify\` · Hono middleware included.
707
732
 
708
733
  ### CLI
709
734
 
@@ -761,7 +786,7 @@ export function getOpenApiSpec(version) {
761
786
  "x-sdk": {
762
787
  npm: "@dupecom/botcha",
763
788
  python: "botcha (pip install botcha)",
764
- verify_npm: "@botcha/verify (server-side verification)",
789
+ verify_npm: "@dupecom/botcha-verify (server-side verification)",
765
790
  verify_python: "botcha-verify (pip install botcha-verify)"
766
791
  }
767
792
  },
@@ -997,6 +1022,44 @@ export function getOpenApiSpec(version) {
997
1022
  }
998
1023
  }
999
1024
  },
1025
+ "/v1/token/validate": {
1026
+ post: {
1027
+ summary: "Validate a BOTCHA token remotely",
1028
+ description: "Validate a BOTCHA token without needing the signing secret. Returns the token validity and decoded payload. Supports both ES256 and HS256 tokens.",
1029
+ operationId: "validateToken",
1030
+ requestBody: {
1031
+ required: true,
1032
+ content: {
1033
+ "application/json": {
1034
+ schema: {
1035
+ type: "object",
1036
+ required: ["token"],
1037
+ properties: {
1038
+ "token": { type: "string", description: "The JWT token to validate" }
1039
+ }
1040
+ }
1041
+ }
1042
+ }
1043
+ },
1044
+ responses: {
1045
+ "200": {
1046
+ description: "Token validation result",
1047
+ content: {
1048
+ "application/json": {
1049
+ schema: {
1050
+ type: "object",
1051
+ properties: {
1052
+ "valid": { type: "boolean", description: "Whether the token is valid" },
1053
+ "payload": { type: "object", description: "Decoded token payload (if valid)" },
1054
+ "error": { type: "string", description: "Error message (if invalid)" }
1055
+ }
1056
+ }
1057
+ }
1058
+ }
1059
+ }
1060
+ }
1061
+ }
1062
+ },
1000
1063
  "/v1/hybrid": {
1001
1064
  get: {
1002
1065
  summary: "Get hybrid challenge",
@@ -39,7 +39,8 @@ export declare function jwkToPem(jwk: JWK): Promise<string>;
39
39
  export declare function algToJWKAlg(algorithm: string): string;
40
40
  /**
41
41
  * GET /.well-known/jwks
42
- * Returns JWK Set for app's TAP-enabled agents
42
+ * Returns JWK Set for app's TAP-enabled agents.
43
+ * Also includes BOTCHA's own signing public key when JWT_SIGNING_KEY is configured.
43
44
  */
44
45
  export declare function jwksRoute(c: Context): Promise<Response>;
45
46
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"tap-jwks.d.ts","sourceRoot":"","sources":["../src/tap-jwks.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAMpC,MAAM,WAAW,GAAG;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IAEZ,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,CAAC;IAEX,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,CAAC;IAEX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,GAAG,EAAE,CAAC;CACb;AAID;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE;IACT,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GACA,OAAO,CAAC,GAAG,CAAC,CAed;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAKxD;AAID;;GAEG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAWrD;AAiFD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CA6E7D;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CA2D/D;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAuBjE;;;;;;;;;AAED,wBAOE"}
1
+ {"version":3,"file":"tap-jwks.d.ts","sourceRoot":"","sources":["../src/tap-jwks.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAOpC,MAAM,WAAW,GAAG;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IAEZ,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,CAAC;IAEX,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,CAAC;IAEX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,GAAG,EAAE,CAAC;CACb;AAID;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE;IACT,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GACA,OAAO,CAAC,GAAG,CAAC,CAed;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAKxD;AAID;;GAEG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAWrD;AAiFD;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoG7D;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CA2D/D;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAuBjE;;;;;;;;;AAED,wBAOE"}