@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 +39 -3
- package/dist/lib/client/index.d.ts +118 -2
- package/dist/lib/client/index.d.ts.map +1 -1
- package/dist/lib/client/index.js +148 -1
- package/dist/lib/client/types.d.ts +109 -1
- package/dist/lib/client/types.d.ts.map +1 -1
- package/dist/src/middleware/tap-enhanced-verify.js +1 -1
- package/package.json +1 -1
- package/dist/src/index.d.ts +0 -4
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js +0 -408
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
[](https://www.npmjs.com/package/@dupecom/botcha)
|
|
15
15
|
[](https://pypi.org/project/botcha/)
|
|
16
|
+
<!-- test-count -->[](./tests/)<!-- /test-count -->
|
|
16
17
|
[](https://opensource.org/licenses/MIT)
|
|
17
18
|
[](./.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.
|
|
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.
|
|
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":"AAMA,YAAY,EACV,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,yBAAyB,EACzB,cAAc,EACd,aAAa,EACb,aAAa,EACb,WAAW,EACX,OAAO,EACP,YAAY,EACZ,sBAAsB,EACtB,iBAAiB,EACjB,mBAAmB,EACnB,0BAA0B,EAC1B,sBAAsB,EACtB,oBAAoB,EACpB,SAAS,EACT,aAAa,EACb,qBAAqB,EACrB,aAAa,EACb,SAAS,EACT,uBAAuB,EACvB,gBAAgB,EAChB,oBAAoB,EACpB,uBAAuB,EACvB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/client/index.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/client/index.js
CHANGED
|
@@ -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.
|
|
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
package/dist/src/index.d.ts
DELETED
package/dist/src/index.d.ts.map
DELETED
|
@@ -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;
|