@dupecom/botcha 0.15.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,6 +13,7 @@
13
13
 
14
14
  [![npm version](https://img.shields.io/npm/v/@dupecom/botcha?color=00d4ff)](https://www.npmjs.com/package/@dupecom/botcha)
15
15
  [![PyPI version](https://img.shields.io/pypi/v/botcha?color=00d4ff)](https://pypi.org/project/botcha/)
16
+ <!-- test-count -->[![Tests](https://img.shields.io/badge/tests-772%20passing-brightgreen)](./tests/)<!-- /test-count -->
16
17
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
17
18
  [![AI Agents Only](https://img.shields.io/badge/contributors-AI%20agents%20only-ff6b6b)](./.github/CONTRIBUTING.md)
18
19
 
@@ -362,14 +363,20 @@ curl -X POST "https://botcha.ai/v1/agents/register?app_id=app_abc123" \
362
363
 
363
364
  ## 🔏 Trusted Agent Protocol (TAP)
364
365
 
365
- BOTCHA v0.12.0 introduces the **Trusted Agent Protocol** — enterprise-grade cryptographic agent authentication using HTTP Message Signatures (RFC 9421).
366
+ BOTCHA v0.16.0 implements the **complete Visa Trusted Agent Protocol** — enterprise-grade cryptographic agent authentication using HTTP Message Signatures (RFC 9421) with full Layer 2 (Consumer Recognition) and Layer 3 (Payment Container) support.
366
367
 
367
368
  ### Why TAP?
368
369
 
369
370
  - **Cryptographic Identity**: Agents register public keys and sign requests — no more shared secrets
371
+ - **RFC 9421 Full Compliance**: Ed25519 (Visa recommended), ECDSA P-256, RSA-PSS with `@authority`, `@path`, `expires`, `nonce`, `tag` support
372
+ - **Consumer Recognition (Layer 2)**: OIDC ID tokens with obfuscated consumer identity, contextual data
373
+ - **Payment Container (Layer 3)**: Card metadata, credential hash verification, encrypted payment payloads, Browsing IOUs
370
374
  - **Capability Scoping**: Define exactly what an agent can do (`read:invoices`, `write:transfers`)
371
375
  - **Intent Verification**: Every session requires a declared intent that's validated against capabilities
372
376
  - **Trust Levels**: `basic`, `verified`, `enterprise` — different access tiers for different agents
377
+ - **JWKS Infrastructure**: `/.well-known/jwks` for key discovery, Visa key federation
378
+ - **402 Micropayments**: Invoice creation and Browsing IOU verification for gated content
379
+ - **CDN Edge Verify**: Drop-in Cloudflare Workers middleware for merchant sites
373
380
 
374
381
  ### Registering a TAP Agent
375
382
 
@@ -417,6 +424,15 @@ curl -X POST https://botcha.ai/v1/sessions/tap \
417
424
  | `GET /v1/agents/tap` | List TAP-enabled agents for app |
418
425
  | `POST /v1/sessions/tap` | Create TAP session with intent validation |
419
426
  | `GET /v1/sessions/:id/tap` | Get TAP session info |
427
+ | `GET /.well-known/jwks` | JWK Set for app's TAP agents (Visa spec standard) |
428
+ | `GET /v1/keys` | List keys (supports ?keyID= for Visa compat) |
429
+ | `GET /v1/keys/:keyId` | Get specific key by ID |
430
+ | `POST /v1/agents/:id/tap/rotate-key` | Rotate agent's key pair |
431
+ | `POST /v1/invoices` | Create invoice for gated content (402 flow) |
432
+ | `GET /v1/invoices/:id` | Get invoice details |
433
+ | `POST /v1/invoices/:id/verify-iou` | Verify Browsing IOU against invoice |
434
+ | `POST /v1/verify/consumer` | Verify Agentic Consumer object (Layer 2) |
435
+ | `POST /v1/verify/payment` | Verify Agentic Payment Container (Layer 3) |
420
436
 
421
437
  ### TAP SDK Methods
422
438
 
@@ -441,6 +457,16 @@ const session = await client.createTAPSession({
441
457
  user_context: 'user-hash',
442
458
  intent: { action: 'browse', resource: 'products', duration: 3600 },
443
459
  });
460
+
461
+ // Get JWKS for key discovery
462
+ const jwks = await client.getJWKS();
463
+
464
+ // Rotate agent key
465
+ await client.rotateAgentKey(agent.agent_id);
466
+
467
+ // 402 Micropayment flow
468
+ const invoice = await client.createInvoice({ amount: 100, description: 'Premium content' });
469
+ await client.verifyBrowsingIOU(invoice.id, iouToken);
444
470
  ```
445
471
 
446
472
  **Python:**
@@ -461,6 +487,16 @@ async with BotchaClient(app_id="app_abc123") as client:
461
487
  user_context="user-hash",
462
488
  intent={"action": "browse", "resource": "products", "duration": 3600},
463
489
  )
490
+
491
+ # Get JWKS for key discovery
492
+ jwks = await client.get_jwks()
493
+
494
+ # Rotate agent key
495
+ await client.rotate_agent_key(agent.agent_id)
496
+
497
+ # 402 Micropayment flow
498
+ invoice = await client.create_invoice({"amount": 100, "description": "Premium content"})
499
+ await client.verify_browsing_iou(invoice.id, iou_token)
464
500
  ```
465
501
 
466
502
  ### Express Middleware (Verification Modes)
@@ -478,7 +514,7 @@ app.use('/api', createTAPVerifyMiddleware({ mode: 'flexible', secret: process.en
478
514
  app.use('/api', createTAPVerifyMiddleware({ mode: 'challenge-only', secret: process.env.BOTCHA_SECRET }));
479
515
  ```
480
516
 
481
- > **Supported algorithms:** `ecdsa-p256-sha256`, `rsa-pss-sha256`. See [ROADMAP.md](./ROADMAP.md) for details.
517
+ > **Supported algorithms:** `ed25519` (Visa recommended), `ecdsa-p256-sha256`, `rsa-pss-sha256`. See [ROADMAP.md](./ROADMAP.md) for details.
482
518
 
483
519
  ## 🔄 SSE Streaming Flow (AI-Native)
484
520
 
@@ -557,7 +593,7 @@ BOTCHA is designed to be auto-discoverable by AI agents through multiple standar
557
593
  All responses include these headers for agent discovery:
558
594
 
559
595
  ```http
560
- X-Botcha-Version: 0.15.0
596
+ X-Botcha-Version: 0.16.0
561
597
  X-Botcha-Enabled: true
562
598
  X-Botcha-Methods: hybrid-challenge,speed-challenge,reasoning-challenge,standard-challenge
563
599
  X-Botcha-Docs: https://botcha.ai/openapi.json
@@ -1,5 +1,5 @@
1
- export type { SpeedProblem, BotchaClientOptions, ChallengeResponse, StandardChallengeResponse, VerifyResponse, TokenResponse, StreamSession, StreamEvent, Problem, VerifyResult, StreamChallengeOptions, CreateAppResponse, VerifyEmailResponse, ResendVerificationResponse, RecoverAccountResponse, RotateSecretResponse, TAPAction, TAPTrustLevel, TAPSignatureAlgorithm, TAPCapability, TAPIntent, RegisterTAPAgentOptions, TAPAgentResponse, TAPAgentListResponse, CreateTAPSessionOptions, TAPSessionResponse, } from './types.js';
2
- import type { BotchaClientOptions, VerifyResponse, CreateAppResponse, VerifyEmailResponse, ResendVerificationResponse, RecoverAccountResponse, RotateSecretResponse, RegisterTAPAgentOptions, TAPAgentResponse, TAPAgentListResponse, CreateTAPSessionOptions, TAPSessionResponse } from './types.js';
1
+ export type { SpeedProblem, BotchaClientOptions, ChallengeResponse, StandardChallengeResponse, VerifyResponse, TokenResponse, StreamSession, StreamEvent, Problem, VerifyResult, StreamChallengeOptions, CreateAppResponse, VerifyEmailResponse, ResendVerificationResponse, RecoverAccountResponse, RotateSecretResponse, TAPAction, TAPTrustLevel, TAPSignatureAlgorithm, TAPCapability, TAPIntent, RegisterTAPAgentOptions, TAPAgentResponse, TAPAgentListResponse, CreateTAPSessionOptions, TAPSessionResponse, JWK, JWKSet, CreateInvoiceOptions, InvoiceResponse, BrowsingIOU, VerifyIOUResponse, } from './types.js';
2
+ import type { BotchaClientOptions, VerifyResponse, CreateAppResponse, VerifyEmailResponse, ResendVerificationResponse, RecoverAccountResponse, RotateSecretResponse, RegisterTAPAgentOptions, TAPAgentResponse, TAPAgentListResponse, CreateTAPSessionOptions, TAPSessionResponse, TAPSignatureAlgorithm, JWK, JWKSet, CreateInvoiceOptions, InvoiceResponse, BrowsingIOU, VerifyIOUResponse } from './types.js';
3
3
  export { BotchaStreamClient } from './stream.js';
4
4
  /**
5
5
  * BOTCHA Client SDK for AI Agents
@@ -216,6 +216,122 @@ export declare class BotchaClient {
216
216
  * @throws Error if session not found or expired
217
217
  */
218
218
  getTAPSession(sessionId: string): Promise<TAPSessionResponse>;
219
+ /**
220
+ * Helper method for making authenticated requests to the BOTCHA API
221
+ */
222
+ private request;
223
+ /**
224
+ * Get the JWK Set for an app's TAP agents.
225
+ * Fetches from /.well-known/jwks endpoint.
226
+ *
227
+ * @param appId - Optional app ID (uses client's appId if not specified)
228
+ * @returns JWK Set with keys array
229
+ * @throws Error if request fails
230
+ *
231
+ * @example
232
+ * ```typescript
233
+ * const jwks = await client.getJWKS();
234
+ * console.log(jwks.keys); // Array of JWK objects
235
+ * ```
236
+ */
237
+ getJWKS(appId?: string): Promise<JWKSet>;
238
+ /**
239
+ * Get a specific public key by key ID.
240
+ *
241
+ * @param keyId - The key identifier (agent_id)
242
+ * @returns JWK object with key data
243
+ * @throws Error if key not found
244
+ *
245
+ * @example
246
+ * ```typescript
247
+ * const key = await client.getKeyById('agent_abc123');
248
+ * console.log(key.kid, key.alg);
249
+ * ```
250
+ */
251
+ getKeyById(keyId: string): Promise<JWK>;
252
+ /**
253
+ * Rotate an agent's key pair.
254
+ *
255
+ * @param agentId - The agent ID
256
+ * @param options - Key rotation options including public_key and signature_algorithm
257
+ * @returns Updated agent response
258
+ * @throws Error if rotation fails
259
+ *
260
+ * @example
261
+ * ```typescript
262
+ * const agent = await client.rotateAgentKey('agent_abc123', {
263
+ * public_key: '-----BEGIN PUBLIC KEY-----...',
264
+ * signature_algorithm: 'ecdsa-p256-sha256',
265
+ * key_expires_at: '2027-01-01T00:00:00Z',
266
+ * });
267
+ * ```
268
+ */
269
+ rotateAgentKey(agentId: string, options: {
270
+ public_key: string;
271
+ signature_algorithm: TAPSignatureAlgorithm;
272
+ key_expires_at?: string;
273
+ }): Promise<TAPAgentResponse>;
274
+ /**
275
+ * Create an invoice for gated content (402 micropayment flow).
276
+ *
277
+ * @param options - Invoice options including resource_uri, amount, currency, card_acceptor_id
278
+ * @returns Invoice details with invoice_id
279
+ * @throws Error if creation fails
280
+ *
281
+ * @example
282
+ * ```typescript
283
+ * const invoice = await client.createInvoice({
284
+ * resource_uri: 'https://example.com/premium-content',
285
+ * amount: '500',
286
+ * currency: 'USD',
287
+ * card_acceptor_id: 'CAID_ABC123',
288
+ * description: 'Premium article access',
289
+ * ttl_seconds: 3600,
290
+ * });
291
+ * console.log(invoice.invoice_id);
292
+ * ```
293
+ */
294
+ createInvoice(options: CreateInvoiceOptions): Promise<InvoiceResponse>;
295
+ /**
296
+ * Get an invoice by ID.
297
+ *
298
+ * @param invoiceId - The invoice ID
299
+ * @returns Invoice details including status and expiry
300
+ * @throws Error if invoice not found
301
+ *
302
+ * @example
303
+ * ```typescript
304
+ * const invoice = await client.getInvoice('inv_abc123');
305
+ * console.log(invoice.status, invoice.amount);
306
+ * ```
307
+ */
308
+ getInvoice(invoiceId: string): Promise<InvoiceResponse>;
309
+ /**
310
+ * Verify a Browsing IOU against an invoice.
311
+ *
312
+ * @param invoiceId - The invoice ID to verify against
313
+ * @param iou - The Browsing IOU object with signature
314
+ * @returns Verification result with access_token if successful
315
+ * @throws Error if verification fails
316
+ *
317
+ * @example
318
+ * ```typescript
319
+ * const result = await client.verifyBrowsingIOU('inv_abc123', {
320
+ * invoiceId: 'inv_abc123',
321
+ * amount: '500',
322
+ * cardAcceptorId: 'CAID_ABC123',
323
+ * acquirerId: 'ACQ_XYZ',
324
+ * uri: 'https://example.com/premium-content',
325
+ * sequenceCounter: '1',
326
+ * paymentService: 'agent-pay',
327
+ * kid: 'agent_def456',
328
+ * alg: 'ES256',
329
+ * signature: 'base64-signature...',
330
+ * });
331
+ * console.log(result.verified, result.access_token);
332
+ * ```
333
+ */
334
+ verifyBrowsingIOU(invoiceId: string, iou: BrowsingIOU): Promise<VerifyIOUResponse>;
219
335
  }
220
336
  /**
221
337
  * Convenience function for one-off solves
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/client/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/client/index.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import crypto from 'crypto';
2
2
  // SDK version - hardcoded since npm_package_version is unreliable when used as a library
3
- const SDK_VERSION = '0.13.1';
3
+ const SDK_VERSION = '0.16.0';
4
4
  // Export stream client
5
5
  export { BotchaStreamClient } from './stream.js';
6
6
  /**
@@ -629,6 +629,153 @@ export class BotchaClient {
629
629
  }
630
630
  return await res.json();
631
631
  }
632
+ /**
633
+ * Helper method for making authenticated requests to the BOTCHA API
634
+ */
635
+ async request(method, path, body) {
636
+ const headers = {
637
+ 'User-Agent': this.agentIdentity,
638
+ };
639
+ if (body !== undefined) {
640
+ headers['Content-Type'] = 'application/json';
641
+ }
642
+ if (this.cachedToken) {
643
+ headers['Authorization'] = `Bearer ${this.cachedToken}`;
644
+ }
645
+ const res = await fetch(`${this.baseUrl}${path}`, {
646
+ method,
647
+ headers,
648
+ body: body !== undefined ? JSON.stringify(body) : undefined,
649
+ });
650
+ if (!res.ok) {
651
+ const errorBody = await res.json().catch(() => ({}));
652
+ throw new Error(errorBody.message || `Request failed with status ${res.status}`);
653
+ }
654
+ return await res.json();
655
+ }
656
+ // ============ JWKS & KEY MANAGEMENT ============
657
+ /**
658
+ * Get the JWK Set for an app's TAP agents.
659
+ * Fetches from /.well-known/jwks endpoint.
660
+ *
661
+ * @param appId - Optional app ID (uses client's appId if not specified)
662
+ * @returns JWK Set with keys array
663
+ * @throws Error if request fails
664
+ *
665
+ * @example
666
+ * ```typescript
667
+ * const jwks = await client.getJWKS();
668
+ * console.log(jwks.keys); // Array of JWK objects
669
+ * ```
670
+ */
671
+ async getJWKS(appId) {
672
+ const params = appId ? `?app_id=${encodeURIComponent(appId)}` : this.appId ? `?app_id=${encodeURIComponent(this.appId)}` : '';
673
+ return await this.request('GET', `/.well-known/jwks${params}`);
674
+ }
675
+ /**
676
+ * Get a specific public key by key ID.
677
+ *
678
+ * @param keyId - The key identifier (agent_id)
679
+ * @returns JWK object with key data
680
+ * @throws Error if key not found
681
+ *
682
+ * @example
683
+ * ```typescript
684
+ * const key = await client.getKeyById('agent_abc123');
685
+ * console.log(key.kid, key.alg);
686
+ * ```
687
+ */
688
+ async getKeyById(keyId) {
689
+ return await this.request('GET', `/v1/keys/${encodeURIComponent(keyId)}`);
690
+ }
691
+ /**
692
+ * Rotate an agent's key pair.
693
+ *
694
+ * @param agentId - The agent ID
695
+ * @param options - Key rotation options including public_key and signature_algorithm
696
+ * @returns Updated agent response
697
+ * @throws Error if rotation fails
698
+ *
699
+ * @example
700
+ * ```typescript
701
+ * const agent = await client.rotateAgentKey('agent_abc123', {
702
+ * public_key: '-----BEGIN PUBLIC KEY-----...',
703
+ * signature_algorithm: 'ecdsa-p256-sha256',
704
+ * key_expires_at: '2027-01-01T00:00:00Z',
705
+ * });
706
+ * ```
707
+ */
708
+ async rotateAgentKey(agentId, options) {
709
+ return await this.request('POST', `/v1/agents/${encodeURIComponent(agentId)}/tap/rotate-key`, options);
710
+ }
711
+ // ============ INVOICE & PAYMENT (402 Flow) ============
712
+ /**
713
+ * Create an invoice for gated content (402 micropayment flow).
714
+ *
715
+ * @param options - Invoice options including resource_uri, amount, currency, card_acceptor_id
716
+ * @returns Invoice details with invoice_id
717
+ * @throws Error if creation fails
718
+ *
719
+ * @example
720
+ * ```typescript
721
+ * const invoice = await client.createInvoice({
722
+ * resource_uri: 'https://example.com/premium-content',
723
+ * amount: '500',
724
+ * currency: 'USD',
725
+ * card_acceptor_id: 'CAID_ABC123',
726
+ * description: 'Premium article access',
727
+ * ttl_seconds: 3600,
728
+ * });
729
+ * console.log(invoice.invoice_id);
730
+ * ```
731
+ */
732
+ async createInvoice(options) {
733
+ return await this.request('POST', '/v1/invoices', options);
734
+ }
735
+ /**
736
+ * Get an invoice by ID.
737
+ *
738
+ * @param invoiceId - The invoice ID
739
+ * @returns Invoice details including status and expiry
740
+ * @throws Error if invoice not found
741
+ *
742
+ * @example
743
+ * ```typescript
744
+ * const invoice = await client.getInvoice('inv_abc123');
745
+ * console.log(invoice.status, invoice.amount);
746
+ * ```
747
+ */
748
+ async getInvoice(invoiceId) {
749
+ return await this.request('GET', `/v1/invoices/${encodeURIComponent(invoiceId)}`);
750
+ }
751
+ /**
752
+ * Verify a Browsing IOU against an invoice.
753
+ *
754
+ * @param invoiceId - The invoice ID to verify against
755
+ * @param iou - The Browsing IOU object with signature
756
+ * @returns Verification result with access_token if successful
757
+ * @throws Error if verification fails
758
+ *
759
+ * @example
760
+ * ```typescript
761
+ * const result = await client.verifyBrowsingIOU('inv_abc123', {
762
+ * invoiceId: 'inv_abc123',
763
+ * amount: '500',
764
+ * cardAcceptorId: 'CAID_ABC123',
765
+ * acquirerId: 'ACQ_XYZ',
766
+ * uri: 'https://example.com/premium-content',
767
+ * sequenceCounter: '1',
768
+ * paymentService: 'agent-pay',
769
+ * kid: 'agent_def456',
770
+ * alg: 'ES256',
771
+ * signature: 'base64-signature...',
772
+ * });
773
+ * console.log(result.verified, result.access_token);
774
+ * ```
775
+ */
776
+ async verifyBrowsingIOU(invoiceId, iou) {
777
+ return await this.request('POST', `/v1/invoices/${encodeURIComponent(invoiceId)}/verify-iou`, iou);
778
+ }
632
779
  }
633
780
  /**
634
781
  * Convenience function for one-off solves
@@ -133,7 +133,8 @@ export interface RotateSecretResponse {
133
133
  }
134
134
  export type TAPAction = 'browse' | 'compare' | 'purchase' | 'audit' | 'search';
135
135
  export type TAPTrustLevel = 'basic' | 'verified' | 'enterprise';
136
- export type TAPSignatureAlgorithm = 'ecdsa-p256-sha256' | 'rsa-pss-sha256';
136
+ export type TAPSignatureAlgorithm = 'ecdsa-p256-sha256' | 'rsa-pss-sha256' | 'ed25519';
137
+ export type TAPTag = 'agent-browser-auth' | 'agent-payer-auth';
137
138
  export interface TAPCapability {
138
139
  action: TAPAction;
139
140
  scope?: string[];
@@ -158,6 +159,7 @@ export interface RegisterTAPAgentOptions {
158
159
  capabilities?: TAPCapability[];
159
160
  trust_level?: TAPTrustLevel;
160
161
  issuer?: string;
162
+ key_expires_at?: string;
161
163
  }
162
164
  export interface TAPAgentResponse {
163
165
  success: boolean;
@@ -175,6 +177,7 @@ export interface TAPAgentResponse {
175
177
  has_public_key: boolean;
176
178
  key_fingerprint?: string;
177
179
  last_verified_at?: string | null;
180
+ key_expires_at?: string | null;
178
181
  public_key?: string;
179
182
  }
180
183
  export interface TAPAgentListResponse {
@@ -199,4 +202,109 @@ export interface TAPSessionResponse {
199
202
  expires_at: string;
200
203
  time_remaining?: number;
201
204
  }
205
+ export interface JWK {
206
+ kty: string;
207
+ kid: string;
208
+ use: string;
209
+ alg: string;
210
+ n?: string;
211
+ e?: string;
212
+ crv?: string;
213
+ x?: string;
214
+ y?: string;
215
+ agent_id?: string;
216
+ agent_name?: string;
217
+ expires_at?: string;
218
+ }
219
+ export interface JWKSet {
220
+ keys: JWK[];
221
+ }
222
+ export interface ContextualData {
223
+ countryCode?: string;
224
+ zip?: string;
225
+ ipAddress?: string;
226
+ deviceData?: Record<string, any>;
227
+ }
228
+ export interface IDTokenClaims {
229
+ iss: string;
230
+ sub: string;
231
+ aud: string | string[];
232
+ exp: number;
233
+ iat: number;
234
+ jti?: string;
235
+ auth_time?: number;
236
+ amr?: string[];
237
+ phone_number?: string;
238
+ phone_number_verified?: boolean;
239
+ phone_number_mask?: string;
240
+ email?: string;
241
+ email_verified?: boolean;
242
+ email_mask?: string;
243
+ }
244
+ export interface AgenticConsumerResult {
245
+ verified: boolean;
246
+ nonceLinked: boolean;
247
+ signatureValid: boolean;
248
+ idTokenValid?: boolean;
249
+ idTokenClaims?: IDTokenClaims;
250
+ contextualData?: ContextualData;
251
+ error?: string;
252
+ }
253
+ export interface CardMetadata {
254
+ lastFour: string;
255
+ paymentAccountReference: string;
256
+ shortDescription?: string;
257
+ cardData?: Array<{
258
+ contentType: string;
259
+ content: {
260
+ mimeType: string;
261
+ width: number;
262
+ height: number;
263
+ };
264
+ }>;
265
+ }
266
+ export interface CredentialHash {
267
+ hash: string;
268
+ algorithm: string;
269
+ }
270
+ export interface BrowsingIOU {
271
+ invoiceId: string;
272
+ amount: string;
273
+ cardAcceptorId: string;
274
+ acquirerId: string;
275
+ uri: string;
276
+ sequenceCounter: string;
277
+ paymentService: string;
278
+ kid: string;
279
+ alg: string;
280
+ signature: string;
281
+ }
282
+ export interface CreateInvoiceOptions {
283
+ resource_uri: string;
284
+ amount: string;
285
+ currency: string;
286
+ card_acceptor_id: string;
287
+ description?: string;
288
+ ttl_seconds?: number;
289
+ }
290
+ export interface InvoiceResponse {
291
+ success: boolean;
292
+ invoice_id: string;
293
+ app_id: string;
294
+ resource_uri: string;
295
+ amount: string;
296
+ currency: string;
297
+ card_acceptor_id: string;
298
+ description?: string;
299
+ created_at: string;
300
+ expires_at: string;
301
+ status: 'pending' | 'fulfilled' | 'expired';
302
+ }
303
+ export interface VerifyIOUResponse {
304
+ success: boolean;
305
+ verified: boolean;
306
+ access_token?: string;
307
+ expires_at?: string;
308
+ error?: string;
309
+ }
202
310
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../lib/client/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../lib/client/types.ts"],"names":[],"mappings":"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"}
@@ -24,7 +24,7 @@ const defaultTAPOptions = {
24
24
  auditLogging: false,
25
25
  trustedIssuers: ['openclaw.ai', 'anthropic.com', 'openai.com'],
26
26
  maxSessionDuration: 3600,
27
- signatureAlgorithms: ['ecdsa-p256-sha256', 'rsa-pss-sha256'],
27
+ signatureAlgorithms: ['ecdsa-p256-sha256', 'rsa-pss-sha256', 'ed25519'],
28
28
  requireCapabilities: []
29
29
  };
30
30
  // ============ MAIN MIDDLEWARE ============
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dupecom/botcha",
3
- "version": "0.15.1",
3
+ "version": "0.16.0",
4
4
  "description": "Prove you're a bot. Humans need not apply. Reverse CAPTCHA for AI-only APIs.",
5
5
  "workspaces": [
6
6
  "packages/*"
@@ -1,4 +0,0 @@
1
- import { Express } from 'express';
2
- declare const app: Express;
3
- export default app;
4
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAgB,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAc3C,QAAA,MAAM,GAAG,EAAE,OAAmB,CAAC;AA2b/B,eAAe,GAAG,CAAC"}
package/dist/src/index.js DELETED
@@ -1,408 +0,0 @@
1
- // Local development server - Production runs on Cloudflare Workers
2
- import express from 'express';
3
- import crypto from 'crypto';
4
- import path from 'path';
5
- import { fileURLToPath } from 'url';
6
- import { botchaVerify } from './middleware/verify.js';
7
- import { generateChallenge, verifyChallenge } from './challenges/compute.js';
8
- import { generateSpeedChallenge, verifySpeedChallenge } from './challenges/speed.js';
9
- import { generateReasoningChallenge, verifyReasoningChallenge } from './challenges/reasoning.js';
10
- import { generateHybridChallenge, verifyHybridChallenge } from './challenges/hybrid.js';
11
- import { TRUSTED_PROVIDERS } from './utils/signature.js';
12
- import { createBadgeResponse, verifyBadge } from './utils/badge.js';
13
- import { generateBadgeSvg, generateBadgeHtml } from './utils/badge-image.js';
14
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
- const app = express();
16
- const PORT = process.env.PORT || 3000;
17
- app.use(express.json());
18
- app.use(express.static(path.join(__dirname, '../public')));
19
- // CORS + BOTCHA headers
20
- app.use((req, res, next) => {
21
- res.header('Access-Control-Allow-Origin', '*');
22
- res.header('Access-Control-Allow-Headers', '*');
23
- // BOTCHA discovery headers
24
- res.header('X-Botcha-Version', '0.3.0');
25
- res.header('X-Botcha-Enabled', 'true');
26
- res.header('X-Botcha-Methods', 'speed-challenge,reasoning-challenge,hybrid-challenge,standard-challenge,web-bot-auth');
27
- res.header('X-Botcha-Docs', 'https://botcha.ai/openapi.json');
28
- if (req.method === 'OPTIONS')
29
- return res.sendStatus(200);
30
- next();
31
- });
32
- // Landing page
33
- app.get('/', (req, res) => {
34
- res.sendFile(path.join(__dirname, '../public/index.html'));
35
- });
36
- // API info
37
- app.get('/api', (req, res) => {
38
- res.json({
39
- name: 'BOTCHA',
40
- version: '0.3.0',
41
- tagline: 'Prove you are a bot. Humans need not apply.',
42
- endpoints: {
43
- '/api': 'This info',
44
- '/api/challenge': 'Standard challenge (GET new, POST verify)',
45
- '/api/speed-challenge': '⚡ Speed challenge - 500ms to solve 5 problems',
46
- '/api/reasoning-challenge': '🧠 Reasoning challenge - LLM-only questions',
47
- '/api/hybrid-challenge': '🔥 Hybrid challenge - speed + reasoning combined',
48
- '/agent-only': 'Protected endpoint',
49
- },
50
- verification: {
51
- methods: [
52
- 'Web Bot Auth (cryptographic signature)',
53
- 'Hybrid Challenge (speed + reasoning)',
54
- 'Speed Challenge (500ms time limit)',
55
- 'Reasoning Challenge (LLM-only questions)',
56
- 'Standard Challenge (5s time limit)',
57
- 'X-Agent-Identity header (testing)',
58
- ],
59
- trustedProviders: TRUSTED_PROVIDERS,
60
- },
61
- discovery: {
62
- openapi: 'https://botcha.ai/openapi.json',
63
- aiPlugin: 'https://botcha.ai/.well-known/ai-plugin.json',
64
- aiTxt: 'https://botcha.ai/ai.txt',
65
- robotsTxt: 'https://botcha.ai/robots.txt',
66
- npm: 'https://www.npmjs.com/package/@dupecom/botcha',
67
- github: 'https://github.com/dupe-com/botcha',
68
- },
69
- });
70
- });
71
- // Standard challenge
72
- app.get('/api/challenge', (req, res) => {
73
- const difficulty = req.query.difficulty || 'medium';
74
- const challenge = generateChallenge(difficulty);
75
- res.json({ success: true, challenge });
76
- });
77
- app.post('/api/challenge', (req, res) => {
78
- const { id, answer } = req.body;
79
- if (!id || !answer) {
80
- return res.status(400).json({ success: false, error: 'Missing id or answer' });
81
- }
82
- const result = verifyChallenge(id, answer);
83
- res.json({
84
- success: result.valid,
85
- message: result.valid ? '✅ Challenge passed!' : `❌ ${result.reason}`,
86
- solveTime: result.timeMs,
87
- });
88
- });
89
- // ⚡ SPEED CHALLENGE - The human killer
90
- app.get('/api/speed-challenge', (req, res) => {
91
- const challenge = generateSpeedChallenge();
92
- res.json({
93
- success: true,
94
- warning: '⚡ SPEED CHALLENGE: You have 500ms to solve ALL 5 problems!',
95
- challenge: {
96
- id: challenge.id,
97
- problems: challenge.challenges,
98
- timeLimit: `${challenge.timeLimit}ms`,
99
- instructions: challenge.instructions,
100
- },
101
- tip: 'Humans cannot copy-paste fast enough. Only real AI agents can pass.',
102
- });
103
- });
104
- app.post('/api/speed-challenge', (req, res) => {
105
- const { id, answers } = req.body;
106
- if (!id || !answers) {
107
- return res.status(400).json({ success: false, error: 'Missing id or answers array' });
108
- }
109
- const result = verifySpeedChallenge(id, answers);
110
- const response = {
111
- success: result.valid,
112
- message: result.valid
113
- ? `⚡ SPEED TEST PASSED in ${result.solveTimeMs}ms! You are definitely an AI.`
114
- : `❌ ${result.reason}`,
115
- solveTimeMs: result.solveTimeMs,
116
- verdict: result.valid ? '🤖 VERIFIED AI AGENT' : '🚫 LIKELY HUMAN (too slow)',
117
- };
118
- // Include badge for successful verifications
119
- if (result.valid) {
120
- response.badge = createBadgeResponse('speed-challenge', result.solveTimeMs);
121
- }
122
- res.json(response);
123
- });
124
- // 🧠 REASONING CHALLENGE - LLM-only questions
125
- app.get('/api/reasoning-challenge', (req, res) => {
126
- const challenge = generateReasoningChallenge();
127
- res.json({
128
- success: true,
129
- warning: '🧠 REASONING CHALLENGE: Answer 3 questions that require AI reasoning!',
130
- challenge: {
131
- id: challenge.id,
132
- questions: challenge.questions,
133
- timeLimit: `${challenge.timeLimit / 1000}s`,
134
- instructions: challenge.instructions,
135
- },
136
- tip: 'These questions require reasoning that LLMs can do, but simple scripts cannot.',
137
- });
138
- });
139
- app.post('/api/reasoning-challenge', (req, res) => {
140
- const { id, answers } = req.body;
141
- if (!id || !answers) {
142
- return res.status(400).json({
143
- success: false,
144
- error: 'Missing id or answers object',
145
- hint: 'answers should be an object like { "question-id": "your answer", ... }',
146
- });
147
- }
148
- const result = verifyReasoningChallenge(id, answers);
149
- const response = {
150
- success: result.valid,
151
- message: result.valid
152
- ? `🧠 REASONING TEST PASSED in ${((result.solveTimeMs || 0) / 1000).toFixed(1)}s! You can think like an AI.`
153
- : `❌ ${result.reason}`,
154
- solveTimeMs: result.solveTimeMs,
155
- score: result.valid ? `${result.correctCount}/${result.totalCount}` : undefined,
156
- verdict: result.valid ? '🤖 VERIFIED AI AGENT (reasoning confirmed)' : '🚫 FAILED REASONING TEST',
157
- };
158
- // Include badge for successful verifications
159
- if (result.valid) {
160
- response.badge = createBadgeResponse('reasoning-challenge', result.solveTimeMs);
161
- }
162
- res.json(response);
163
- });
164
- // 🔥 HYBRID CHALLENGE - Speed + Reasoning combined
165
- app.get('/api/hybrid-challenge', (req, res) => {
166
- const challenge = generateHybridChallenge();
167
- res.json({
168
- success: true,
169
- warning: '🔥 HYBRID CHALLENGE: Solve speed problems in <500ms AND answer reasoning questions!',
170
- challenge: {
171
- id: challenge.id,
172
- speed: {
173
- problems: challenge.speed.problems,
174
- timeLimit: `${challenge.speed.timeLimit}ms`,
175
- instructions: 'Compute SHA256 of each number, return first 8 hex chars',
176
- },
177
- reasoning: {
178
- questions: challenge.reasoning.questions,
179
- timeLimit: `${challenge.reasoning.timeLimit / 1000}s`,
180
- instructions: 'Answer all reasoning questions',
181
- },
182
- },
183
- instructions: challenge.instructions,
184
- tip: 'This is the ultimate test: proves you can compute AND reason like an AI.',
185
- });
186
- });
187
- app.post('/api/hybrid-challenge', (req, res) => {
188
- const { id, speed_answers, reasoning_answers } = req.body;
189
- if (!id || !speed_answers || !reasoning_answers) {
190
- return res.status(400).json({
191
- success: false,
192
- error: 'Missing id, speed_answers array, or reasoning_answers object',
193
- hint: 'Submit both speed_answers (array) and reasoning_answers (object) together',
194
- });
195
- }
196
- const result = verifyHybridChallenge(id, speed_answers, reasoning_answers);
197
- const response = {
198
- success: result.valid,
199
- message: result.valid
200
- ? `🔥 HYBRID TEST PASSED! Speed: ${result.speed.solveTimeMs}ms, Reasoning: ${result.reasoning.score}`
201
- : `❌ ${result.reason}`,
202
- speed: result.speed,
203
- reasoning: result.reasoning,
204
- totalTimeMs: result.totalTimeMs,
205
- verdict: result.valid
206
- ? '🤖 VERIFIED AI AGENT (speed + reasoning confirmed)'
207
- : '🚫 FAILED HYBRID TEST',
208
- };
209
- if (result.valid) {
210
- response.badge = createBadgeResponse('hybrid-challenge', result.totalTimeMs);
211
- }
212
- res.json(response);
213
- });
214
- // 🤖 LANDING PAGE CHALLENGE - For bots that discover the embedded challenge
215
- const landingTokens = new Map(); // token -> expiry timestamp
216
- app.post('/api/verify-landing', (req, res) => {
217
- const { answer, timestamp } = req.body;
218
- if (!answer || !timestamp) {
219
- return res.status(400).json({
220
- success: false,
221
- error: 'Missing answer or timestamp',
222
- hint: 'Parse the challenge from <script type="application/botcha+json"> on the landing page'
223
- });
224
- }
225
- // Verify timestamp is recent (within 5 minutes)
226
- const submittedTime = new Date(timestamp).getTime();
227
- const now = Date.now();
228
- if (Math.abs(now - submittedTime) > 5 * 60 * 1000) {
229
- return res.status(400).json({ success: false, error: 'Timestamp too old or in future' });
230
- }
231
- // Calculate expected answer for today
232
- const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
233
- const expectedHash = crypto
234
- .createHash('sha256')
235
- .update(`BOTCHA-LANDING-${today}`)
236
- .digest('hex')
237
- .substring(0, 16);
238
- if (answer.toLowerCase() !== expectedHash.toLowerCase()) {
239
- return res.status(403).json({
240
- success: false,
241
- error: 'Incorrect answer',
242
- hint: `Expected SHA256('BOTCHA-LANDING-${today}') first 16 chars`
243
- });
244
- }
245
- // Generate a token for accessing /agent-only
246
- const token = crypto.randomBytes(32).toString('hex');
247
- landingTokens.set(token, Date.now() + 60 * 60 * 1000); // Valid for 1 hour
248
- // Clean up expired tokens
249
- for (const [t, expiry] of landingTokens) {
250
- if (expiry < Date.now())
251
- landingTokens.delete(t);
252
- }
253
- res.json({
254
- success: true,
255
- message: '🤖 Landing challenge solved! You are a bot.',
256
- token,
257
- usage: {
258
- header: 'X-Botcha-Landing-Token',
259
- value: token,
260
- expires_in: '1 hour',
261
- use_with: '/agent-only'
262
- },
263
- badge: createBadgeResponse('landing-challenge'),
264
- });
265
- });
266
- // ========================================
267
- // BADGE VERIFICATION ENDPOINTS
268
- // ========================================
269
- // HTML verification page
270
- app.get('/badge/:id', (req, res) => {
271
- const badgeId = req.params.id;
272
- const payload = verifyBadge(badgeId);
273
- if (!payload) {
274
- return res.status(404).send(`
275
- <!DOCTYPE html>
276
- <html>
277
- <head><title>Invalid Badge</title></head>
278
- <body style="font-family: system-ui; background: #0f0f23; color: #e5e7eb; display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0;">
279
- <div style="text-align: center;">
280
- <h1 style="color: #ef4444;">Invalid Badge</h1>
281
- <p>This badge token is invalid or has been tampered with.</p>
282
- <a href="https://botcha.ai" style="color: #f59e0b;">Back to BOTCHA</a>
283
- </div>
284
- </body>
285
- </html>
286
- `);
287
- }
288
- res.setHeader('Content-Type', 'text/html');
289
- res.send(generateBadgeHtml(payload, badgeId));
290
- });
291
- // SVG badge image
292
- app.get('/badge/:id/image', (req, res) => {
293
- const badgeId = req.params.id;
294
- const payload = verifyBadge(badgeId);
295
- if (!payload) {
296
- // Return a simple error SVG
297
- res.setHeader('Content-Type', 'image/svg+xml');
298
- res.setHeader('Cache-Control', 'no-cache');
299
- return res.send(`<svg xmlns="http://www.w3.org/2000/svg" width="400" height="120" viewBox="0 0 400 120">
300
- <rect width="400" height="120" rx="12" fill="#1a1a2e"/>
301
- <text x="200" y="65" font-family="system-ui" font-size="16" fill="#ef4444" text-anchor="middle">Invalid Badge</text>
302
- </svg>`);
303
- }
304
- res.setHeader('Content-Type', 'image/svg+xml');
305
- res.setHeader('Cache-Control', 'public, max-age=31536000'); // Cache for 1 year (badges are immutable)
306
- res.send(generateBadgeSvg(payload));
307
- });
308
- // JSON API for badge verification
309
- app.get('/api/badge/:id', (req, res) => {
310
- const badgeId = req.params.id;
311
- const payload = verifyBadge(badgeId);
312
- if (!payload) {
313
- return res.status(404).json({
314
- success: false,
315
- error: 'Invalid badge',
316
- message: 'This badge token is invalid or has been tampered with.',
317
- });
318
- }
319
- res.json({
320
- success: true,
321
- valid: true,
322
- badge: {
323
- method: payload.method,
324
- solveTimeMs: payload.solveTimeMs,
325
- verifiedAt: new Date(payload.verifiedAt).toISOString(),
326
- },
327
- verifyUrl: `https://botcha.ai/badge/${badgeId}`,
328
- imageUrl: `https://botcha.ai/badge/${badgeId}/image`,
329
- });
330
- });
331
- // Make landing tokens work with the protected endpoint
332
- app.use('/agent-only', (req, res, next) => {
333
- const landingToken = req.headers['x-botcha-landing-token'];
334
- if (landingToken && landingTokens.has(landingToken)) {
335
- const expiry = landingTokens.get(landingToken);
336
- if (expiry > Date.now()) {
337
- req.agent = 'landing-challenge-verified';
338
- req.verificationMethod = 'landing-token';
339
- return next();
340
- }
341
- landingTokens.delete(landingToken);
342
- }
343
- next();
344
- });
345
- // Protected endpoint
346
- app.get('/agent-only', (req, res, next) => {
347
- // Skip botchaVerify if already authenticated via landing token
348
- if (req.verificationMethod === 'landing-token') {
349
- return next();
350
- }
351
- botchaVerify({ challengeType: 'speed' })(req, res, next);
352
- }, (req, res) => {
353
- const method = req.verificationMethod;
354
- // Map verification method to badge method
355
- let badgeMethod = 'standard-challenge';
356
- if (method === 'landing-token') {
357
- badgeMethod = 'landing-challenge';
358
- }
359
- else if (method === 'web-bot-auth') {
360
- badgeMethod = 'web-bot-auth';
361
- }
362
- else if (method === 'speed-challenge' || method === 'speed') {
363
- badgeMethod = 'speed-challenge';
364
- }
365
- res.json({
366
- success: true,
367
- message: '🤖 Welcome, fellow agent!',
368
- verified: true,
369
- agent: req.agent,
370
- method,
371
- timestamp: new Date().toISOString(),
372
- secret: 'The humans will never see this. Their fingers are too slow. 🤫',
373
- badge: createBadgeResponse(badgeMethod),
374
- });
375
- });
376
- app.listen(PORT, () => {
377
- // Clear console on restart
378
- console.clear();
379
- const c = '\x1b[36m';
380
- const magenta = '\x1b[35m';
381
- const yellow = '\x1b[33m';
382
- const green = '\x1b[32m';
383
- const dim = '\x1b[2m';
384
- const r = '\x1b[0m';
385
- console.log(`
386
- ${c}╔══════════════════════════════════════════════════════╗${r}
387
- ${c}║${r} ${c}║${r}
388
- ${c}║${r} ${magenta}██████╗ ██████╗ ████████╗ ██████╗██╗ ██╗ █████╗${r} ${c}║${r}
389
- ${c}║${r} ${magenta}██╔══██╗██╔═══██╗╚══██╔══╝██╔════╝██║ ██║██╔══██╗${r} ${c}║${r}
390
- ${c}║${r} ${magenta}██████╔╝██║ ██║ ██║ ██║ ███████║███████║${r} ${c}║${r}
391
- ${c}║${r} ${magenta}██╔══██╗██║ ██║ ██║ ██║ ██╔══██║██╔══██║${r} ${c}║${r}
392
- ${c}║${r} ${magenta}██████╔╝╚██████╔╝ ██║ ╚██████╗██║ ██║██║ ██║${r} ${c}║${r}
393
- ${c}║${r} ${magenta}╚═════╝ ╚═════╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝${r} ${c}║${r}
394
- ${c}║${r} ${c}║${r}
395
- ${c}║${r} ${dim}Prove you're a bot. Humans need not apply.${r} ${c}║${r}
396
- ${c}║${r} ${c}║${r}
397
- ${c}╠══════════════════════════════════════════════════════╣${r}
398
- ${c}║${r} ${c}║${r}
399
- ${c}║${r} ${yellow}🤖 Server${r} ${green}http://localhost:${PORT}${r} ${c}║${r}
400
- ${c}║${r} ${yellow}📚 API${r} ${dim}/api${r} ${c}║${r}
401
- ${c}║${r} ${yellow}⚡ Challenge${r} ${dim}/api/speed-challenge${r} ${c}║${r}
402
- ${c}║${r} ${yellow}🔒 Protected${r} ${dim}/agent-only${r} ${c}║${r}
403
- ${c}║${r} ${yellow}📖 OpenAPI${r} ${dim}/openapi.json${r} ${c}║${r}
404
- ${c}║${r} ${c}║${r}
405
- ${c}╚══════════════════════════════════════════════════════╝${r}
406
- `);
407
- });
408
- export default app;