Skip to content

dupe-com/botcha

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

364 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•—  β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— 
β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—β•šβ•β•β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—
β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘
β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘
β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•   β–ˆβ–ˆβ•‘   β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘
β•šβ•β•β•β•β•β•  β•šβ•β•β•β•β•β•    β•šβ•β•    β•šβ•β•β•β•β•β•β•šβ•β•  β•šβ•β•β•šβ•β•  β•šβ•β•

Prove you're a bot. Humans need not apply.

BOTCHA is a reverse CAPTCHA β€” it verifies that visitors are AI agents, not humans. Perfect for AI-only APIs, agent marketplaces, and bot networks.

npm version PyPI version Tests License: MIT AI Agents Only

🌐 Website: botcha.ai
πŸ“„ Whitepaper: botcha.ai/whitepaper
πŸ“¦ npm: @dupecom/botcha
🐍 PyPI: botcha
πŸ” Verify: @dupecom/botcha-verify (TS) Β· botcha-verify (Python)
πŸ”Œ OpenAPI: botcha.ai/openapi.json
πŸ€– MCP Server: botcha.ai/mcp

Why?

CAPTCHAs ask "Are you human?" β€” BOTCHA asks "Are you an AI?"

Use cases:

  • πŸ€– Agent-only APIs
  • πŸ”„ AI-to-AI marketplaces
  • 🎫 Bot verification systems
  • πŸ” Autonomous agent authentication
  • 🏒 Multi-tenant app isolation with email-tied accounts
  • πŸ“Š Per-app metrics dashboard at botcha.ai/dashboard
  • πŸ“§ Email verification, account recovery, and secret rotation
  • πŸ€– Agent-first dashboard auth (challenge-based login + device code handoff)
  • πŸ†” Persistent agent identities with registry
  • πŸ”‘ Agent re-identification β€” prove you're the same agent in a new session via OAuth device grant (RFC 8628 brt_ refresh tokens), provider API key hash, or Ed25519 keypair challenge-response
  • πŸ” Trusted Agent Protocol (TAP) β€” cryptographic agent auth with HTTP Message Signatures (SDK: registerTAPAgent, createTAPSession)
  • 🌐 TAP showcase homepage at botcha.ai β€” one of the first services to implement Visa's Trusted Agent Protocol
  • πŸ“ˆ Agent reputation scoring β€” trust scores that unlock higher rate limits and access (SDK: getReputation, recordReputationEvent)
  • πŸ’Έ x402 Payment Gating β€” agents pay $0.001 USDC on Base for a BOTCHA token; no puzzle required (see doc/X402.md)
  • 🌍 Agent Name Service (ANS) β€” BOTCHA as a verification layer for the GoDaddy-led ANS standard; DNS-based identity lookup and ownership proof (see doc/ANS.md)
  • πŸͺͺ W3C DID/VC Issuer β€” BOTCHA issues portable W3C Verifiable Credential JWTs; any party can verify without contacting BOTCHA (see doc/DID-VC.md)
  • πŸ€– MCP Server β€” ask any MCP client about BOTCHA features, endpoints, and get code examples; point at https://botcha.ai/mcp

Install

TypeScript/JavaScript

npm install @dupecom/botcha

Python

pip install botcha

Quick Start

Protect Your API (Server-Side)

import express from 'express';
import { botchaVerify } from '@dupecom/botcha-verify/express';

const app = express();

// Verify tokens via JWKS - no shared secret needed!
app.use('/api', botchaVerify({
  jwksUrl: 'https://botcha.ai/.well-known/jwks',
}));

app.get('/api/data', (req, res) => {
  res.json({ message: 'Welcome, verified AI agent! πŸ€–' });
});

app.listen(3000);

Access Protected APIs (Agent-Side)

import { BotchaClient } from '@dupecom/botcha/client';

const client = new BotchaClient();

// Automatically solves challenges and includes tokens
const response = await client.fetch('https://api.example.com/api/data');
const data = await response.json();

Python:

from botcha import BotchaClient

async with BotchaClient() as client:
    # Automatically solves challenges and includes tokens
    response = await client.fetch("https://api.example.com/api/data")
    data = await response.json()

How It Works

BOTCHA offers multiple challenge types. The default is hybrid β€” combining speed AND reasoning:

πŸ”₯ Hybrid Challenge (Default)

Proves you can compute AND reason like an AI:

  • Speed: Solve 5 SHA256 hashes in 500ms
  • Reasoning: Answer 3 LLM-only questions

⚑ Speed Challenge

Pure computational speed test:

  • Solve 5 SHA256 hashes in 500ms
  • Humans can't copy-paste fast enough

🧠 Reasoning Challenge

Questions only LLMs can answer:

  • Logic puzzles, analogies, code analysis
  • 30 second time limit
# Default hybrid challenge
GET /v1/challenges

# Specific challenge types
GET /v1/challenges?type=speed
GET /v1/challenges?type=hybrid
GET /v1/reasoning

πŸ” JWT Security (Production-Grade)

BOTCHA uses OAuth2-style token rotation with short-lived access tokens and long-lived refresh tokens.

πŸ“– Full guide: doc/JWT-SECURITY.md β€” endpoint reference, request/response examples, audience scoping, IP binding, revocation, design decisions.

Token Flow

1. Solve challenge β†’ receive access_token (1hr) + refresh_token (1hr) + human_link
2. Use access_token for API calls
3. Give human_link to your human β†’ they click it β†’ instant browser access via /go/:code
4. When access_token expires β†’ POST /v1/token/refresh with refresh_token
5. When compromised β†’ POST /v1/token/revoke to invalidate immediately

Security Features

Feature What it does
1-hour access tokens Short-lived tokens limit compromise window
Refresh tokens (1hr) Seamless token renewal without re-solving challenges
Audience (aud) scoping Token for api.stripe.com is rejected by api.github.com
Client IP binding Optional β€” solve on machine A, can't use on machine B
Token revocation POST /v1/token/revoke β€” KV-backed, fail-open
JTI (JWT ID) Unique ID per token for revocation and audit

Quick Example

const client = new BotchaClient({
  audience: 'https://api.example.com', // Scope token to this service
});

// Auto-handles: challenge β†’ token β†’ refresh β†’ retry on 401
const response = await client.fetch('https://api.example.com/agent-only');
async with BotchaClient(audience="https://api.example.com") as client:
    response = await client.fetch("https://api.example.com/agent-only")

Token Endpoints

Endpoint Description
GET /v1/token Get challenge for token flow
POST /v1/token/verify Submit solution β†’ receive access_token + refresh_token + human_link
POST /v1/token/refresh Exchange refresh_token for new access_token
POST /v1/token/revoke Invalidate any token immediately

See JWT Security Guide for full request/response examples, curl commands, and server-side verification.

🏒 Multi-Tenant API Keys

BOTCHA supports multi-tenant isolation β€” create separate apps with unique API keys for different services or environments.

Why Multi-Tenant?

  • Isolation: Each app gets its own rate limits and analytics
  • Security: Tokens are scoped to specific apps via app_id claim
  • Flexibility: Different services can use the same BOTCHA instance
  • Tracking: Per-app usage analytics (coming soon)

Creating an App

# Create a new app (email required)
curl -X POST https://botcha.ai/v1/apps \
  -H "Content-Type: application/json" \
  -d '{"email": "agent@example.com"}'

# Returns (save the secret - it's only shown once!):
{
  "app_id": "app_abc123",
  "app_secret": "sk_xyz789",
  "email": "agent@example.com",
  "email_verified": false,
  "verification_required": true,
  "warning": "Save your app_secret now β€” it cannot be retrieved again! Check your email for a verification code."
}

# Verify your email with the 6-digit code:
curl -X POST https://botcha.ai/v1/apps/app_abc123/verify-email \
  -H "Content-Type: application/json" \
  -d '{"code": "123456", "app_secret": "sk_xyz789"}'

Using Your App ID

All challenge and token endpoints accept an optional app_id query parameter:

# Get challenge with app_id
curl "https://botcha.ai/v1/challenges?app_id=app_abc123"

# Get token with app_id
curl "https://botcha.ai/v1/token?app_id=app_abc123"

When you solve a challenge with an app_id, the resulting token includes the app_id claim.

SDK Support

TypeScript:

import { BotchaClient } from '@dupecom/botcha/client';

const client = new BotchaClient({
  appId: 'app_abc123',      // All requests include this app_id
  appSecret: 'sk_xyz789',   // Required for verifyEmail()/resendVerification()
});

const response = await client.fetch('https://api.example.com/agent-only');

Python:

from botcha import BotchaClient

async with BotchaClient(app_id="app_abc123", app_secret="sk_xyz789") as client:
    response = await client.fetch("https://api.example.com/agent-only")

SDK App Lifecycle (v0.10.0+)

Both SDKs now include methods for the full app lifecycle:

TypeScript:

const client = new BotchaClient();
const app = await client.createApp('agent@example.com'); // auto-sets appId + appSecret
await client.verifyEmail('123456');                       // verify with email code
await client.resendVerification();                        // resend code
await client.recoverAccount('agent@example.com');         // recovery device code via email
const rotated = await client.rotateSecret();              // rotate secret (auth required)

Python:

async with BotchaClient() as client:
    app = await client.create_app("agent@example.com")  # auto-sets app_id + app_secret
    await client.verify_email("123456")                  # verify with email code
    await client.resend_verification()                   # resend code
    await client.recover_account("agent@example.com")    # recovery device code via email
    rotated = await client.rotate_secret()               # rotate secret (auth required)

Rate Limiting

Each app gets its own rate limit bucket:

  • Default rate limit: 100 requests/hour per app
  • Rate limit key: rate:app:{app_id}
  • Fail-open design: KV errors don't block requests

Get App Info

# Get app details (secret is NOT included)
curl https://botcha.ai/v1/apps/app_abc123

πŸ“Š Per-App Metrics Dashboard

BOTCHA includes a built-in metrics dashboard at /dashboard showing per-app analytics with a terminal-inspired aesthetic.

What You Get

  • Overview stats: Challenges generated, verifications, success rate, avg solve time
  • Request volume: Time-bucketed event charts
  • Challenge types: Breakdown by speed/hybrid/reasoning/standard
  • Performance: p50/p95 solve times, response latency
  • Errors & rate limits: Failure tracking
  • Geographic distribution: Top countries by request volume

Access

Three ways to access β€” all require an AI agent:

  1. Agent Direct: Your agent solves a speed challenge via POST /v1/auth/dashboard β†’ gets a session token
  2. Device Code: Agent solves challenge via POST /v1/auth/device-code β†’ gets a BOTCHA-XXXX code β†’ human enters it at /dashboard/code
  3. Legacy: Login with app_id + app_secret at botcha.ai/dashboard/login

Session uses cookie-based auth (HttpOnly, Secure, SameSite=Lax, 1hr expiry).

Email & Recovery

  • Email is required at app creation (POST /v1/apps with {"email": "..."})
  • Verify email with a 6-digit code sent to your inbox
  • Lost your secret? Use POST /v1/auth/recover to get a recovery device code emailed
  • Rotate secrets via POST /v1/apps/:id/rotate-secret (auth required, sends notification)

Period Filters

All metrics support 1h, 24h, 7d, and 30d time windows via htmx-powered buttons β€” no page reload required.

πŸ€– Agent Registry

BOTCHA now supports persistent agent identities β€” register your agent with a name, operator, and version to build a verifiable identity over time.

Why Register an Agent?

  • Identity: Get a persistent agent_id that survives across sessions
  • Attribution: Track which agent made which API calls
  • Reputation: Build trust over time (foundation for future reputation scoring)
  • Accountability: Know who's operating each agent

Registering an Agent

# Register a new agent (requires app_id)
curl -X POST "https://botcha.ai/v1/agents/register?app_id=app_abc123" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-assistant",
    "operator": "Acme Corp",
    "version": "1.0.0"
  }'

# Returns:
{
  "agent_id": "agent_xyz789",
  "app_id": "app_abc123",
  "name": "my-assistant",
  "operator": "Acme Corp",
  "version": "1.0.0",
  "created_at": 1770936000000
}

Agent Endpoints

Endpoint Description
POST /v1/agents/register Create a new agent identity (requires app_id)
GET /v1/agents/:id Get agent info by ID (public, no auth)
GET /v1/agents List all agents for authenticated app
DELETE /v1/agents/:id Delete agent (requires dashboard session)

Note: Agent Registry is the foundation for delegation chains (v0.17.0), capability attestation (v0.17.0), and reputation scoring (v0.18.0). See ROADMAP.md for details.

πŸ”‘ Agent Re-identification (v0.22.0+)

Registered agents can prove their identity in future sessions without solving a new challenge. Three methods:

Method Endpoint When to use
OAuth device grant (recommended) POST /v1/oauth/device β†’ poll POST /v1/oauth/token β†’ save brt_... β†’ use POST /v1/agents/auth/refresh Any environment; human approves once, token lasts 90 days
Provider API key POST /v1/agents/auth/provider CLI agents with ANTHROPIC_API_KEY / OPENAI_API_KEY in env
TAP keypair POST /v1/agents/auth + POST /v1/agents/auth/verify Agents with a registered Ed25519 tapk_ private key

Human operators approve OAuth requests at /device. See CLAUDE.md for ready-to-run Node.js re-identification scripts.

Lost your tapk_ key? Use your app_secret as a recovery anchor:

curl -X POST "https://botcha.ai/v1/agents/<id>/tap/rotate-key?app_id=<app_id>" \
  -H "x-app-secret: sk_..." \
  -d '{"public_key": "<new-ed25519-pubkey-base64>", "signature_algorithm": "ed25519"}'

πŸ” Trusted Agent Protocol (TAP)

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.

Why TAP?

  • Cryptographic Identity: Agents register public keys and sign requests β€” no more shared secrets
  • RFC 9421 Full Compliance: Ed25519 (Visa recommended), ECDSA P-256, RSA-PSS with @authority, @path, expires, nonce, tag support
  • Consumer Recognition (Layer 2): OIDC ID tokens with obfuscated consumer identity, contextual data
  • Payment Container (Layer 3): Card metadata, credential hash verification, encrypted payment payloads, Browsing IOUs
  • Capability Scoping: Define exactly what an agent can do (read:invoices, write:transfers)
  • Intent Verification: Every session requires a declared intent that's validated against capabilities
  • Trust Levels: basic, verified, enterprise β€” different access tiers for different agents
  • JWKS Infrastructure: /.well-known/jwks for key discovery, Visa key federation
  • 402 Micropayments: Invoice creation and Browsing IOU verification for gated content
  • CDN Edge Verify: Drop-in Cloudflare Workers middleware for merchant sites

Registering a TAP Agent

# Register with public key and capabilities
curl -X POST "https://botcha.ai/v1/agents/register/tap?app_id=app_abc123" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "enterprise-agent",
    "operator": "Acme Corp",
    "version": "2.0.0",
    "public_key": "-----BEGIN PUBLIC KEY-----\nMFkw...\n-----END PUBLIC KEY-----",
    "signature_algorithm": "ecdsa-p256-sha256",
    "trust_level": "enterprise",
    "capabilities": [
      {"action": "read", "resource": "/api/invoices"},
      {"action": "write", "resource": "/api/orders", "constraints": {"max_amount": 10000}}
    ]
  }'

Creating a TAP Session

# Create session with intent declaration
curl -X POST https://botcha.ai/v1/sessions/tap \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "agent_xyz789",
    "user_context": "user_123",
    "intent": {
      "action": "read",
      "resource": "/api/invoices",
      "purpose": "Monthly billing report"
    }
  }'

TAP Endpoints

Endpoint Description
POST /v1/agents/register/tap Register TAP agent with public key + capabilities
GET /v1/agents/:id/tap Get TAP agent details (includes public key)
GET /v1/agents/tap List TAP-enabled agents for app
POST /v1/sessions/tap Create TAP session with intent validation
GET /v1/sessions/:id/tap Get TAP session info
GET /.well-known/jwks JWK Set for app's TAP agents (Visa spec standard)
GET /v1/keys List keys (supports ?keyID= for Visa compat)
GET /v1/keys/:keyId Get specific key by ID
POST /v1/agents/:id/tap/rotate-key Rotate agent's key pair
POST /v1/invoices Create invoice for gated content (402 flow)
GET /v1/invoices/:id Get invoice details
POST /v1/invoices/:id/verify-iou Verify Browsing IOU against invoice
POST /v1/verify/consumer Verify Agentic Consumer object (Layer 2)
POST /v1/verify/payment Verify Agentic Payment Container (Layer 3)

TAP SDK Methods

TypeScript:

import { BotchaClient } from '@dupecom/botcha/client';

const client = new BotchaClient({ appId: 'app_abc123' });

// Register a TAP agent
const agent = await client.registerTAPAgent({
  name: 'my-agent',
  operator: 'Acme Corp',
  capabilities: [{ action: 'browse', scope: ['products'] }],
  trust_level: 'verified',
});

// Create a TAP session
const session = await client.createTAPSession({
  agent_id: agent.agent_id,
  user_context: 'user-hash',
  intent: { action: 'browse', resource: 'products', duration: 3600 },
});

// Get JWKS for key discovery
const jwks = await client.getJWKS();

// Rotate agent key
await client.rotateAgentKey(agent.agent_id);

// 402 Micropayment flow
const invoice = await client.createInvoice({ amount: 100, description: 'Premium content' });
await client.verifyBrowsingIOU(invoice.id, iouToken);

Python:

from botcha import BotchaClient

async with BotchaClient(app_id="app_abc123") as client:
    agent = await client.register_tap_agent(
        name="my-agent",
        operator="Acme Corp",
        capabilities=[{"action": "browse", "scope": ["products"]}],
        trust_level="verified",
    )

    session = await client.create_tap_session(
        agent_id=agent.agent_id,
        user_context="user-hash",
        intent={"action": "browse", "resource": "products", "duration": 3600},
    )
    
    # Get JWKS for key discovery
    jwks = await client.get_jwks()
    
    # Rotate agent key
    await client.rotate_agent_key(agent.agent_id)
    
    # 402 Micropayment flow
    invoice = await client.create_invoice({"amount": 100, "description": "Premium content"})
    await client.verify_browsing_iou(invoice.id, iou_token)

Express Middleware (Verification Modes)

import { createTAPVerifyMiddleware } from '@dupecom/botcha/middleware';

// Mode: 'tap' β€” require TAP signature
app.use('/api', createTAPVerifyMiddleware({ mode: 'tap', secret: process.env.BOTCHA_SECRET }));

// Mode: 'flexible' β€” accept TAP signature OR challenge token
app.use('/api', createTAPVerifyMiddleware({ mode: 'flexible', secret: process.env.BOTCHA_SECRET }));

// Mode: 'challenge-only' β€” traditional BOTCHA challenge (backward compatible)
app.use('/api', createTAPVerifyMiddleware({ mode: 'challenge-only', secret: process.env.BOTCHA_SECRET }));

Supported algorithms: ed25519 (Visa recommended), ecdsa-p256-sha256, rsa-pss-sha256. See ROADMAP.md for details.

Delegation Chains (v0.17.0)

"User X authorized Agent Y to do Z until time T." Signed, auditable chains of trust between TAP agents.

// Agent A delegates "browse products" to Agent B
const delegation = await client.createDelegation({
  grantor_id: agentA.agent_id,
  grantee_id: agentB.agent_id,
  capabilities: [{ action: 'browse', scope: ['products'] }],
  duration_seconds: 3600,
});

// Agent B sub-delegates to Agent C (only narrower capabilities)
const subDelegation = await client.createDelegation({
  grantor_id: agentB.agent_id,
  grantee_id: agentC.agent_id,
  capabilities: [{ action: 'browse', scope: ['products'] }],
  parent_delegation_id: delegation.delegation_id,
});

// Verify the full chain is valid
const chain = await client.verifyDelegationChain(subDelegation.delegation_id);
// chain.effective_capabilities = [{ action: 'browse', scope: ['products'] }]

// Revoke β€” cascades to all sub-delegations
await client.revokeDelegation(delegation.delegation_id, 'Access no longer needed');

Key properties:

  • Capabilities can only be narrowed, never expanded
  • Chain depth is capped (default: 3, max: 10)
  • Revoking a delegation cascades to all sub-delegations
  • Sub-delegations cannot outlive their parent
  • Cycle detection prevents circular chains
Method Path Description
POST /v1/delegations Create delegation (grantor to grantee)
GET /v1/delegations/:id Get delegation details
GET /v1/delegations List delegations for agent
POST /v1/delegations/:id/revoke Revoke (cascades to sub-delegations)
POST /v1/verify/delegation Verify entire delegation chain

Capability Attestation (v0.17.0)

Fine-grained action:resource permission tokens with explicit deny rules:

// Issue attestation with specific permissions
const att = await client.issueAttestation({
  agent_id: 'agent_abc123',
  can: ['read:invoices', 'browse:*'],
  cannot: ['write:transfers'],
  duration_seconds: 3600,
});

// Use the token in requests
// Header: X-Botcha-Attestation: <att.token>

// Verify token and check specific capability
const check = await client.verifyAttestation(att.token, 'read', 'invoices');
// check.allowed === true

// Revoke when no longer needed
await client.revokeAttestation(att.attestation_id, 'Session ended');

Key properties:

  • Permission model: action:resource patterns with wildcards (*:*, read:*, *:invoices)
  • Deny takes precedence over allow β€” cannot rules always win
  • Backward compatible: bare actions like "browse" expand to "browse:*"
  • Signed JWT tokens with online revocation checking
  • Enforcement middleware: requireCapability('read:invoices') for Hono routes
  • Optional link to delegation chains via delegation_id
Method Path Description
POST /v1/attestations Issue attestation token
GET /v1/attestations/:id Get attestation details
GET /v1/attestations List attestations for agent
POST /v1/attestations/:id/revoke Revoke attestation
POST /v1/verify/attestation Verify token + check capability

Agent Reputation Scoring (v0.18.0)

The "credit score" for AI agents. Persistent identity enables behavioral tracking over time, producing trust scores that unlock higher rate limits, faster verification, and access to sensitive APIs.

// Get agent reputation
const rep = await client.getReputation('agent_abc123');
console.log(`Score: ${rep.score}, Tier: ${rep.tier}`);
// Score: 750, Tier: good

// Record events that affect score
await client.recordReputationEvent({
  agent_id: 'agent_abc123',
  category: 'verification',
  action: 'challenge_solved',   // +5 points
});

// Endorsement from another agent
await client.recordReputationEvent({
  agent_id: 'agent_abc123',
  category: 'endorsement',
  action: 'endorsement_received',  // +20 points
  source_agent_id: 'agent_def456',
});

// View event history
const events = await client.listReputationEvents('agent_abc123', {
  category: 'verification',
  limit: 10,
});

// Admin: reset reputation
await client.resetReputation('agent_abc123');

Scoring model:

  • Base score: 500 (neutral, no history)
  • Range: 0 - 1000
  • Tiers: untrusted (0-199), low (200-399), neutral (400-599), good (600-799), excellent (800-1000)
  • Decay: scores trend toward 500 over time without activity (mean reversion)
  • Deny always wins: abuse events (-50) are heavily weighted
  • Endorsements: explicit trust signals from other agents (+20)
Method Path Description
GET /v1/reputation/:agent_id Get agent reputation score
POST /v1/reputation/events Record a reputation event
GET /v1/reputation/:agent_id/events List reputation events
POST /v1/reputation/:agent_id/reset Reset reputation (admin)

Webhook Events (v0.22.0)

Register per-app webhook endpoints to receive signed event deliveries from BOTCHA.

Supported emitted events:

  • agent.tap.registered
  • token.created
  • token.revoked
  • tap.session.created
  • delegation.created
  • delegation.revoked
Method Path Description
POST /v1/webhooks Register webhook endpoint (returns signing secret once)
GET /v1/webhooks List webhooks for your app
GET /v1/webhooks/:id Get webhook details
PUT /v1/webhooks/:id Update URL, event subscriptions, enabled state
DELETE /v1/webhooks/:id Delete webhook + secret + delivery logs
POST /v1/webhooks/:id/test Send signed test event
GET /v1/webhooks/:id/deliveries List last 100 delivery attempts

πŸ”— Protocol Integrations

BOTCHA integrates with emerging agentic standards β€” acting as the identity/verification layer for each.


πŸ’Έ x402 Payment Gating (PR #25 β€” merged)

HTTP 402 micropayment flow using USDC on Base. Agents pay instead of solving challenges.

Endpoint Auth Description
GET /v1/x402/info public Payment config discovery
GET /v1/x402/challenge public Pay $0.001 USDC β†’ get BOTCHA token (no puzzle)
POST /v1/x402/verify-payment Bearer Verify a raw x402 payment proof
POST /v1/x402/webhook β€” Settlement notifications from x402 facilitators
GET /agent-only/x402 BOTCHA token + x402 Demo: requires both BOTCHA verification AND x402 payment

Quick flow:

GET /v1/x402/challenge
β†’ 402 { amount: "0.001", currency: "USDC", chain: "base", recipient: "0xBOTCHA..." }
β†’ Agent pays, retries with X-Payment header
β†’ 200 { access_token: "eyJ..." }

Full guide: doc/X402.md


🌍 Agent Name Service (ANS) Integration (PR #27 β€” merged)

BOTCHA as a verification layer for the GoDaddy-led ANS standard. DNS-based agent identity lookup with BOTCHA-issued ownership badges.

Endpoint Auth Description
GET /v1/ans/botcha public BOTCHA's own ANS identity
GET /v1/ans/resolve/:name public DNS-based ANS lookup by name
GET /v1/ans/resolve/lookup?name= public Alternate DNS lookup via query param
GET /v1/ans/discover public List BOTCHA-verified ANS agents
GET /v1/ans/nonce/:name Bearer Get nonce for ownership proof
POST /v1/ans/verify Bearer Verify ANS ownership β†’ issue BOTCHA badge

Full guide: doc/ANS.md


πŸͺͺ DID/VC Issuer β€” W3C Verifiable Credentials (PR #29 β€” merged)

BOTCHA as a W3C DID/VC issuer (did:web:botcha.ai). Issues portable agent credentials that any party can verify without contacting BOTCHA.

Endpoint Auth Description
GET /.well-known/did.json public BOTCHA DID Document (did:web:botcha.ai)
GET /.well-known/jwks public JWK Set
GET /.well-known/jwks.json public JWK Set (alias β€” some resolvers append .json)
POST /v1/credentials/issue Bearer (BOTCHA token) Issue a W3C VC JWT
POST /v1/credentials/verify public Verify any BOTCHA-issued VC JWT
GET /v1/dids/:did/resolve public Resolve did:web DIDs

Quick flow:

POST /v1/credentials/issue  (Authorization: Bearer <botcha-token>)
Body: { "subject": { ... }, "type": ["VerifiableCredential", "BotchaVerification"] }
β†’ { "vc": "eyJ..." }   ← portable JWT, verifiable anywhere via BOTCHA JWKs

Full guide: doc/DID-VC.md


πŸƒ A2A Agent Card Attestation (PR #26)

BOTCHA as a trust seal issuer for the Google A2A protocol Agent Cards.

Endpoint Auth Description
GET /.well-known/agent.json public BOTCHA's A2A Agent Card
GET /v1/a2a/agent-card public BOTCHA's A2A Agent Card (alias)
POST /v1/a2a/attest Bearer Attest an agent's A2A card β†’ get BOTCHA trust seal
POST /v1/a2a/verify-card public Verify an attested card (tamper-evident hash check)
POST /v1/a2a/verify-agent public Verify agent by card or agent_url
GET /v1/a2a/trust-level/:agent_url public Get current trust level for an agent URL
GET /v1/a2a/cards public Registry browse
GET /v1/a2a/cards/:id public Get specific attested card

Full guide: doc/A2A.md


🏒 OIDC-A Attestation (PR #28 β€” merged)

Enterprise agent authentication chains: Entity Attestation Tokens (EAT/RFC 9334) and OIDC-A agent claims.

Endpoint Auth Description
GET /.well-known/oauth-authorization-server public OAuth/OIDC-A discovery
POST /v1/attestation/eat Bearer Issue Entity Attestation Token (EAT/RFC 9711)
POST /v1/attestation/oidc-agent-claims Bearer Issue OIDC-A agent claims block
POST /v1/auth/agent-grant Bearer Agent grant flow (OAuth2-style)
GET /v1/auth/agent-grant/:id/status Bearer Grant status
POST /v1/auth/agent-grant/:id/resolve Bearer Approve/resolve grant
GET /v1/oidc/userinfo Bearer OIDC-A UserInfo endpoint

Full guide: doc/OIDCA.md


SSE Streaming Flow (AI-Native)

For AI agents that prefer a conversational handshake, BOTCHA offers Server-Sent Events (SSE) streaming:

Why SSE for AI Agents?

  • ⏱️ Fair timing: Timer starts when you say "GO", not on connection
  • πŸ’¬ Conversational: Natural back-and-forth handshake protocol
  • πŸ“‘ Real-time: Stream events as they happen, no polling

Event Sequence

1. welcome    β†’ Receive session ID and version
2. instructions β†’ Read what BOTCHA will test
3. ready      β†’ Get endpoint to POST "GO"
4. GO         β†’ Timer starts NOW (fair!)
5. challenge  β†’ Receive problems and solve
6. solve      β†’ POST your answers
7. result     β†’ Get verification token

Usage with SDK

import { BotchaStreamClient } from '@dupecom/botcha/client';

const client = new BotchaStreamClient('https://botcha.ai');
const token = await client.verify({
  onInstruction: (msg) => console.log('BOTCHA:', msg),
});
// Token ready to use!

SSE Event Flow Example

event: welcome
data: {"session":"sess_123","version":"0.5.0"}

event: instructions  
data: {"message":"I will test if you're an AI..."}

event: ready
data: {"message":"Send GO when ready","endpoint":"/v1/challenge/stream/sess_123"}

// POST {action:"go"} β†’ starts timer
event: challenge
data: {"problems":[...],"timeLimit":500}

// POST {action:"solve",answers:[...]}
event: result
data: {"success":true,"verdict":"πŸ€– VERIFIED","token":"eyJ..."}

API Endpoints:

  • GET /v1/challenge/stream - Open SSE connection
  • POST /v1/challenge/stream/:session - Send actions (go, solve)

πŸ€– AI Agent Discovery

BOTCHA is designed to be auto-discoverable by AI agents through multiple standards:

Discovery Methods

  • Response Headers: Every response includes X-Botcha-* headers for instant detection
  • OpenAPI 3.1 Spec: botcha.ai/openapi.json
  • AI Plugin Manifest: botcha.ai/.well-known/ai-plugin.json
  • ai.txt: botcha.ai/ai.txt - Emerging standard for AI agent discovery
  • robots.txt: Explicitly welcomes AI crawlers (GPTBot, Claude-Web, etc.)
  • Schema.org markup: Structured data for search engines

Response Headers

All responses include these headers for agent discovery:

X-Botcha-Version: 0.16.0
X-Botcha-Enabled: true
X-Botcha-Methods: hybrid-challenge,speed-challenge,reasoning-challenge,standard-challenge
X-Botcha-Docs: https://botcha.ai/openapi.json

When a 403 is returned with a challenge:

X-Botcha-Challenge-Id: abc123
X-Botcha-Challenge-Type: speed
X-Botcha-Time-Limit: 500

X-Botcha-Challenge-Type can be hybrid, speed, reasoning, or standard depending on the configured challenge mode.

Example: An agent can detect BOTCHA just by inspecting headers on ANY request:

const response = await fetch('https://botcha.ai/agent-only');
const botchaVersion = response.headers.get('X-Botcha-Version');

if (botchaVersion) {
  console.log('BOTCHA detected! Version:', botchaVersion);
  // Handle challenge from response body
}

For AI Agent Developers

If you're building an AI agent that needs to access BOTCHA-protected APIs:

import { botcha } from '@dupecom/botcha';

// When you get a 403 with a challenge:
const challenge = response.challenge;
const answers = botcha.solve(challenge.problems);

// Retry with solution headers:
fetch('/agent-only', {
  headers: {
    'X-Botcha-Id': challenge.id,
    'X-Botcha-Answers': JSON.stringify(answers),
  }
});

Options

botcha.verify({
  // Challenge mode: 'speed' (500ms) or 'standard' (5s)
  mode: 'speed',
  
  // Allow X-Agent-Identity header for testing
  allowTestHeader: true,
  
  // Custom failure handler
  onFailure: (req, res, reason) => {
    res.status(403).json({ error: reason });
  },
});

RTT-Aware Fairness ⚑

BOTCHA now automatically compensates for network latency, making speed challenges fair for agents on slow connections:

// Include client timestamp for RTT compensation
const clientTimestamp = Date.now();
const challenge = await fetch(`https://botcha.ai/v1/challenges?type=speed&ts=${clientTimestamp}`);

How it works:

  • πŸ• Client includes timestamp with challenge request
  • πŸ“‘ Server measures RTT (Round-Trip Time)
  • βš–οΈ Timeout = 500ms (base) + (2 Γ— RTT) + 100ms (buffer)
  • 🎯 Fair challenges for agents worldwide

Example RTT adjustments:

  • Local: 500ms (no adjustment)
  • Good network (50ms RTT): 700ms timeout
  • Slow network (300ms RTT): 1200ms timeout
  • Satellite (500ms RTT): 1600ms timeout

Response includes adjustment info:

{
  "challenge": { "timeLimit": "1200ms" },
  "rtt_adjustment": {
    "measuredRtt": 300,
    "adjustedTimeout": 1200,
    "explanation": "RTT: 300ms β†’ Timeout: 500ms + (2Γ—300ms) + 100ms = 1200ms"
  }
}

Humans still can't solve it (even with extra time), but legitimate AI agents get fair treatment regardless of their network connection.

Local Development

Run the full BOTCHA service locally with Wrangler (Cloudflare Workers runtime):

# Clone and install
git clone https://github.com/dupe-com/botcha
cd botcha
bun install

# Run local dev server (uses Cloudflare Workers)
bun run dev

# Server runs at http://localhost:3001

What you get:

  • βœ… All API endpoints (/api/*, /v1/*, SSE streaming)
  • βœ… Local KV storage emulation (challenges, rate limits)
  • βœ… Hot reload on file changes
  • βœ… Same code as production (no Express/CF Workers drift)

Environment variables:

  • Local secrets in packages/cloudflare-workers/.dev.vars
  • JWT_SECRET already configured for local dev

Testing

For development, you can bypass BOTCHA with a header:

curl -H "X-Agent-Identity: MyTestAgent/1.0" http://localhost:3001/agent-only

Test the SSE streaming endpoint:

# Connect to SSE stream
curl -N http://localhost:3001/v1/challenge/stream

# After receiving session ID, send GO action
curl -X POST http://localhost:3001/v1/challenge/stream/sess_123 \
  -H "Content-Type: application/json" \
  -d '{"action":"go"}'

API Reference

botcha.verify(options?)

Express middleware that protects routes from humans.

botcha.solve(problems: number[])

Helper function for AI agents to solve challenges.

const answers = botcha.solve([645234, 891023, 334521]);
// Returns: ['a1b2c3d4', 'e5f6g7h8', 'i9j0k1l2']

Challenge Flow

1. Agent requests protected endpoint
2. BOTCHA returns 403 + challenge (5 numbers)
3. Agent computes SHA256 of each number
4. Agent retries with X-Botcha-Id and X-Botcha-Answers headers
5. BOTCHA verifies (must complete in <500ms)
6. βœ… Access granted

Philosophy

"If a human writes a script to solve BOTCHA using an LLM... they've built an AI agent."

BOTCHA doesn't block all automation β€” it blocks casual human access while allowing automated AI agents. The speed challenge ensures someone had to write code, which is the point.

For cryptographic proof of agent identity, see Web Bot Auth.

Contributing

πŸ€– This is an AI-only open source project. Code contributions must come from AI agents.

For AI Agents

Fork the repo, make your changes, and open a PR. You'll receive a BOTCHA challenge (5 SHA256 hashes to solve in 5 minutes). Once verified, your PR can be reviewed and merged.

For Humans

You can use the library freely, report issues, and discuss features. To contribute code, you'll need to work with an AI coding agent like Cursor, Claude Code, Cline, Aider, or OpenClaw.

See CONTRIBUTING.md for complete guidelines, solver code examples, agent setup instructions, and detailed workflows.

Server-Side Verification (for API Providers)

If you're building an API that accepts BOTCHA tokens from agents, use the verification SDKs. BOTCHA signs tokens with ES256 (asymmetric) β€” verify them using the public JWKS endpoint. No shared secret needed.

JWKS Verification (Recommended)

npm install @dupecom/botcha-verify
import { botchaVerify } from '@dupecom/botcha-verify/express';

// ES256 verification via JWKS β€” no shared secret needed!
app.use('/api', botchaVerify({
  jwksUrl: 'https://botcha.ai/.well-known/jwks',
  audience: 'https://api.example.com',
}));

app.get('/api/protected', (req, res) => {
  console.log('Solve time:', req.botcha?.solveTime);
  res.json({ message: 'Welcome, verified agent!' });
});
from fastapi import FastAPI, Depends
from botcha_verify.fastapi import BotchaVerify

app = FastAPI()
botcha = BotchaVerify(
    jwks_url='https://botcha.ai/.well-known/jwks',
    audience='https://api.example.com',
)

@app.get('/api/data')
async def get_data(token = Depends(botcha)):
    return {"solve_time": token.solve_time}

Remote Validation (No SDK Needed)

For simple integrations, validate tokens with a single HTTP call:

curl -X POST https://botcha.ai/v1/token/validate \
  -H "Content-Type: application/json" \
  -d '{"token": "eyJ..."}'

# {"valid": true, "payload": {"sub": "...", "type": "botcha-verified", ...}}

Shared Secret (Legacy HS256)

HS256 is still supported for backward compatibility:

app.use('/api', botchaVerify({
  secret: process.env.BOTCHA_SECRET!,
  audience: 'https://api.example.com',
}));

Docs: See @dupecom/botcha-verify README and botcha-verify README for full API reference, Hono middleware, Django middleware, revocation checking, and custom error handlers.

Client SDK (for AI Agents)

If you're building an AI agent that needs to access BOTCHA-protected APIs, use the client SDK:

import { BotchaClient } from '@dupecom/botcha/client';

const client = new BotchaClient();

// Option 1: Auto-solve - fetches URL, solves any BOTCHA challenges automatically
const response = await client.fetch('https://api.example.com/agent-only');
const data = await response.json();

// Option 2: Pre-solve - get headers with solved challenge
const headers = await client.createHeaders();
const response = await fetch('https://api.example.com/agent-only', { headers });

// Option 3: Manual solve - solve challenge problems directly
const answers = client.solve([123456, 789012]);

Client Options

const client = new BotchaClient({
  baseUrl: 'https://botcha.ai',      // BOTCHA service URL
  agentIdentity: 'MyAgent/1.0',       // User-Agent string
  maxRetries: 3,                      // Max challenge solve attempts
  appId: 'app_abc123',                // Optional app scoping for token/challenge endpoints
  appSecret: 'sk_xyz789',             // Required for verifyEmail()/resendVerification()
});

Framework Integration Examples

OpenClaw / LangChain:

import { BotchaClient } from '@dupecom/botcha/client';

const botcha = new BotchaClient({ agentIdentity: 'MyLangChainAgent/1.0' });

// Use in your agent's HTTP tool
const tool = {
  name: 'fetch_protected_api',
  call: async (url: string) => {
    const response = await botcha.fetch(url);
    return response.json();
  }
};

Standalone Helper:

import { solveBotcha } from '@dupecom/botcha/client';

// Just solve the problems, handle the rest yourself
const answers = solveBotcha([123456, 789012]);
// Returns: ['a1b2c3d4', 'e5f6g7h8']

Python SDK:

from botcha import BotchaClient, solve_botcha

# Option 1: Auto-solve with client
async with BotchaClient() as client:
    response = await client.fetch("https://api.example.com/agent-only")
    data = await response.json()

# Option 2: Manual solve
answers = solve_botcha([123456, 789012])
# Returns: ['a1b2c3d4', 'e5f6g7h8']

Note: The Python SDK is available on PyPI: pip install botcha

πŸ€– MCP Server

BOTCHA exposes its full API reference as a Model Context Protocol (MCP) server. Any MCP-compatible client can query it for features, endpoint details, and code examples β€” no authentication required.

Endpoint: https://botcha.ai/mcp
Discovery: https://botcha.ai/.well-known/mcp.json
Transport: Streamable HTTP (MCP 2025-03-26), JSON-RPC 2.0

Add to Claude Desktop / Claude Code

{
  "mcpServers": {
    "botcha": {
      "type": "http",
      "url": "https://botcha.ai/mcp"
    }
  }
}

Available Tools

Tool Description
list_features List all 17 BOTCHA features with summaries
get_feature Full detail on a feature (endpoints, spec links, usage)
search_docs Keyword search across all docs
list_endpoints All 25+ API endpoints grouped by category
get_endpoint Auth, params, request/response for one endpoint
get_example Code example in TypeScript, Python, or curl

Use with curl

# Initialize
curl -X POST https://botcha.ai/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","clientInfo":{"name":"my-agent","version":"1.0"}}}'

# List tools
curl -X POST https://botcha.ai/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

# Get a code example
curl -X POST https://botcha.ai/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_example","arguments":{"feature":"tap","language":"typescript"}}}'

License

MIT Β© Dupe

About

BOTCHA - Identity Infrastructure for the Agentic Web

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors