@dupecom/botcha-cloudflare
v0.23.0
Published
BOTCHA for Cloudflare Workers - Prove you're a bot. Humans need not apply.
Maintainers
Readme
@dupecom/botcha-cloudflare
BOTCHA - Prove you're a bot. Humans need not apply.
Cloudflare Workers Edition v0.22.0 - Identity layer for AI agents
Reverse CAPTCHA that verifies AI agents and blocks humans. Running at the edge.
What's New in v0.22.0
- x402 Payment Gating — Agents pay $0.001 USDC on Base for a BOTCHA token. No puzzle. (
GET /v1/x402/challenge) - ANS Integration — DNS-based agent identity lookup and BOTCHA-issued ownership badges. (
GET /v1/ans/resolve/:name) - DID/VC Issuer — BOTCHA issues portable W3C Verifiable Credential JWTs. (
POST /v1/credentials/issue) - A2A Agent Card Attestation (coming soon, PR #26)
- OIDC-A Attestation (coming soon, PR #28)
Features
- ⚡ Speed Challenge - 5 SHA256 hashes in 500ms (impossible for humans to copy-paste)
- 🧮 Standard Challenge - Configurable difficulty prime calculations
- 🔐 JWT Authentication - Token-based access control with jose library
- 🚦 Rate Limiting - IP-based throttling with KV storage
- 🌍 Edge-native - Runs on Cloudflare's global network
- 📦 Minimal dependencies - Hono for routing, jose for JWT
Quick Deploy
# Clone the repo
git clone https://github.com/dupe-com/botcha
cd botcha/packages/cloudflare-workers
# Install dependencies
npm install
# Deploy to Cloudflare
npm run deployLocal Development
npm run dev
# Worker running at http://localhost:8787🔐 JWT Token Flow (Recommended)
1. Get Challenge
GET /v1/tokenResponse includes challenge and instructions for getting a JWT token.
2. Solve Challenge & Get JWT
POST /v1/token/verify
Content-Type: application/json
{
"id": "challenge-uuid",
"answers": ["abc12345", "def67890", ...]
}Returns JWT token valid for 1 hour.
3. Access Protected Resources
GET /agent-only
Authorization: Bearer <your-jwt-token>📊 Rate Limiting
Free tier: 100 challenges per hour per IP
Rate limit headers:
X-RateLimit-Limit: 100X-RateLimit-Remaining: 95X-RateLimit-Reset: 2026-02-02T12:00:00.000Z
API Endpoints
v1 API (Production)
| Endpoint | Method | Description |
|----------|--------|-------------|
| / | GET | API information |
| /health | GET | Health check |
| /v1/challenges | GET | Generate challenge (speed or standard) |
| /v1/challenges/:id/verify | POST | Verify challenge (no JWT) |
| /v1/token | GET | Get challenge for JWT flow |
| /v1/token/verify | POST | Verify challenge → get JWT token |
| /v1/token/refresh | POST | Refresh access token |
| /v1/token/revoke | POST | Revoke token immediately |
| /v1/token/validate | POST | Remote token validation (no shared secret) |
| /v1/challenge/stream | GET | SSE streaming challenge (AI-native) |
| /v1/challenge/stream/:session | POST | SSE action handler (go, solve) |
| /agent-only | GET | Protected endpoint (requires JWT) |
Well-Known Endpoints
| Endpoint | Method | Description |
|----------|--------|-------------|
| /.well-known/did.json | GET | BOTCHA DID Document (did:web:botcha.ai) |
| /.well-known/jwks | GET | JWK Set (TAP agent keys + DID signing keys) |
| /.well-known/jwks.json | GET | JWK Set alias |
| /.well-known/ai-plugin.json | GET | ChatGPT plugin manifest |
x402 Payment Gating
| Endpoint | Method | Auth | Description |
|----------|--------|------|-------------|
| /v1/x402/info | GET | public | Payment config discovery |
| /v1/x402/challenge | GET | public / X-Payment | Pay $0.001 USDC → BOTCHA token |
| /v1/x402/verify-payment | POST | Bearer | Verify x402 payment proof |
| /v1/x402/webhook | POST | — | Settlement notifications |
| /agent-only/x402 | GET | Bearer + x402 | Demo: requires both BOTCHA token + payment |
ANS (Agent Name Service)
| Endpoint | Method | Auth | Description |
|----------|--------|------|-------------|
| /v1/ans/botcha | GET | public | BOTCHA's ANS identity |
| /v1/ans/resolve/:name | GET | public | DNS-based ANS lookup |
| /v1/ans/resolve/lookup | GET | public | ANS lookup via ?name= query param |
| /v1/ans/discover | GET | public | List BOTCHA-verified ANS agents |
| /v1/ans/nonce/:name | GET | Bearer | Nonce for ownership proof |
| /v1/ans/verify | POST | Bearer | Verify ANS ownership → BOTCHA badge |
DID/VC Issuer
| Endpoint | Method | Auth | Description |
|----------|--------|------|-------------|
| /v1/credentials/issue | POST | Bearer | Issue W3C VC JWT |
| /v1/credentials/verify | POST | public | Verify any BOTCHA-issued VC JWT |
| /v1/dids/:did/resolve | GET | public | Resolve did:web DIDs |
A2A Agent Card Attestation (coming soon — PR #26)
| Endpoint | Method | Auth | Description |
|----------|--------|------|-------------|
| /.well-known/agent.json | GET | public | BOTCHA's A2A Agent Card |
| /v1/a2a/agent-card | GET | public | BOTCHA's A2A Agent Card (alias) |
| /v1/a2a/attest | POST | Bearer | Attest an agent's A2A card |
| /v1/a2a/verify-card | POST | public | Verify an attested card |
| /v1/a2a/verify-agent | POST | public | Verify agent by card or agent_url |
| /v1/a2a/trust-level/:agent_url | GET | public | Get trust level for agent URL |
| /v1/a2a/cards | GET | public | Registry browse |
| /v1/a2a/cards/:id | GET | public | Get specific card by ID |
OIDC-A Attestation (coming soon — PR #28)
| Endpoint | Method | Auth | Description |
|----------|--------|------|-------------|
| /.well-known/oauth-authorization-server | GET | public | OAuth/OIDC-A discovery |
| /v1/attestation/eat | POST | Bearer | Issue Entity Attestation Token (EAT/RFC 9711) |
| /v1/attestation/oidc-agent-claims | POST | Bearer | Issue OIDC-A agent claims block |
| /v1/auth/agent-grant | POST | Bearer | Agent grant flow (OAuth2-style) |
| /v1/auth/agent-grant/:id/status | GET | Bearer | Grant status |
| /v1/auth/agent-grant/:id/resolve | POST | Bearer | Approve/resolve grant |
| /v1/oidc/userinfo | GET | Bearer | OIDC-A UserInfo |
SSE Streaming (AI-Native)
For AI agents that prefer conversational flows, BOTCHA offers Server-Sent Events streaming:
Flow:
GET /v1/challenge/stream- Opens SSE connection, receive welcome/instructions/ready eventsPOST /v1/challenge/stream/:sessionwith{action:"go"}- Start challenge timer (fair timing!)- Receive
challengeevent with problems POST /v1/challenge/stream/:sessionwith{action:"solve", answers:[...]}- Submit solution- Receive
resultevent with JWT token
Benefits: Timer starts when you say "GO" (not on connection), natural back-and-forth handshake.
Legacy API (v0 - backward compatible)
| Endpoint | Method | Description |
|----------|--------|-------------|
| /api/challenge | GET/POST | Standard challenge |
| /api/speed-challenge | GET/POST | Speed challenge (500ms limit) |
| /api/verify-landing | POST | Landing page challenge |
Solving Challenges (for AI Agents)
// Speed challenge
const challenge = await fetch('https://your-worker.workers.dev/api/speed-challenge').then(r => r.json());
const answers = await Promise.all(
challenge.challenge.problems.map(async (p) => {
const hash = await crypto.subtle.digest(
'SHA-256',
new TextEncoder().encode(p.num.toString())
);
return Array.from(new Uint8Array(hash))
.map(b => b.toString(16).padStart(2, '0'))
.join('')
.substring(0, 8);
})
);
const result = await fetch('https://your-worker.workers.dev/api/speed-challenge', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: challenge.challenge.id, answers }),
}).then(r => r.json());
console.log(result.verdict); // "🤖 VERIFIED AI AGENT"🔑 Production Configuration
KV Namespaces
Create KV namespaces:
# Create challenge storage
wrangler kv namespace create CHALLENGES
wrangler kv namespace create CHALLENGES --preview
# Create rate limiting storage
wrangler kv namespace create RATE_LIMITS
wrangler kv namespace create RATE_LIMITS --previewUpdate wrangler.toml with the returned IDs.
JWT Secret
⚠️ Important: Use Wrangler secrets for production:
wrangler secret put JWT_SECRET
# Enter a strong secret (32+ characters)Testing
Run the test script:
# Start dev server
npm run dev
# Run tests
./test-api.shLicense
MIT
