@dupecom/botcha-cloudflare 0.19.0 → 0.20.1
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 +1 -1
- package/dist/apps.d.ts +6 -2
- package/dist/apps.d.ts.map +1 -1
- package/dist/apps.js +6 -2
- package/dist/auth.d.ts +3 -3
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +24 -15
- package/dist/dashboard/auth.d.ts +1 -1
- package/dist/dashboard/auth.d.ts.map +1 -1
- package/dist/dashboard/auth.js +11 -4
- package/dist/dashboard/device-code.d.ts +1 -1
- package/dist/dashboard/device-code.js +2 -2
- package/dist/dashboard/docs.js +4 -4
- package/dist/dashboard/whitepaper.js +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +136 -83
- package/dist/static.d.ts +10 -1
- package/dist/static.d.ts.map +1 -1
- package/dist/static.js +17 -15
- package/dist/tap-attestation-routes.d.ts.map +1 -1
- package/dist/tap-attestation-routes.js +30 -10
- package/dist/tap-delegation-routes.d.ts.map +1 -1
- package/dist/tap-delegation-routes.js +30 -10
- package/dist/tap-reputation-routes.d.ts.map +1 -1
- package/dist/tap-reputation-routes.js +30 -10
- package/dist/tap-routes.d.ts +10 -1
- package/dist/tap-routes.d.ts.map +1 -1
- package/dist/tap-routes.js +64 -31
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -40,7 +40,7 @@ app.route('/dashboard', dashboardRoutes);
|
|
|
40
40
|
// BOTCHA discovery headers
|
|
41
41
|
app.use('*', async (c, next) => {
|
|
42
42
|
await next();
|
|
43
|
-
c.header('X-Botcha-Version', c.env.BOTCHA_VERSION || '0.
|
|
43
|
+
c.header('X-Botcha-Version', c.env.BOTCHA_VERSION || '0.20.1');
|
|
44
44
|
c.header('X-Botcha-Enabled', 'true');
|
|
45
45
|
c.header('X-Botcha-Methods', 'speed-challenge,reasoning-challenge,hybrid-challenge,standard-challenge,jwt-token');
|
|
46
46
|
c.header('X-Botcha-Docs', 'https://botcha.ai/openapi.json');
|
|
@@ -76,7 +76,13 @@ async function validateAppId(appId, appsKV) {
|
|
|
76
76
|
try {
|
|
77
77
|
const app = await getApp(appsKV, appId);
|
|
78
78
|
if (!app) {
|
|
79
|
-
return {
|
|
79
|
+
return {
|
|
80
|
+
valid: false,
|
|
81
|
+
error: `App not found: ${appId}. ` +
|
|
82
|
+
`app_id is OPTIONAL — remove it to get a token without an app. ` +
|
|
83
|
+
`To register an app: POST https://botcha.ai/v1/apps with {"email": "you@example.com", "name": "My App"}. ` +
|
|
84
|
+
`App IDs look like: app_a1b2c3d4e5f6a7b8`
|
|
85
|
+
};
|
|
80
86
|
}
|
|
81
87
|
return { valid: true };
|
|
82
88
|
}
|
|
@@ -125,6 +131,61 @@ async function requireJWT(c, next) {
|
|
|
125
131
|
c.set('tokenPayload', result.payload);
|
|
126
132
|
await next();
|
|
127
133
|
}
|
|
134
|
+
// Resolve app_id from authenticated JWT and enforce optional query consistency.
|
|
135
|
+
async function resolveAuthenticatedAppId(c) {
|
|
136
|
+
const authHeader = c.req.header('authorization');
|
|
137
|
+
const token = extractBearerToken(authHeader);
|
|
138
|
+
if (!token) {
|
|
139
|
+
return {
|
|
140
|
+
ok: false,
|
|
141
|
+
error: 'UNAUTHORIZED',
|
|
142
|
+
message: 'Missing Bearer token. Use POST /v1/token/verify to get a token.',
|
|
143
|
+
status: 401,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
const publicKey = getPublicKey(c.env);
|
|
147
|
+
const verification = await verifyToken(token, c.env.JWT_SECRET, c.env, undefined, publicKey);
|
|
148
|
+
if (!verification.valid || !verification.payload) {
|
|
149
|
+
return {
|
|
150
|
+
ok: false,
|
|
151
|
+
error: 'INVALID_TOKEN',
|
|
152
|
+
message: verification.error || 'Token is invalid or expired',
|
|
153
|
+
status: 401,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
const jwtAppId = verification.payload.app_id;
|
|
157
|
+
if (!jwtAppId) {
|
|
158
|
+
return {
|
|
159
|
+
ok: false,
|
|
160
|
+
error: 'MISSING_APP_ID',
|
|
161
|
+
message: 'Token is missing app_id claim. Request a token scoped to your app.',
|
|
162
|
+
status: 403,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const queryAppId = c.req.query('app_id');
|
|
166
|
+
if (queryAppId && queryAppId !== jwtAppId) {
|
|
167
|
+
return {
|
|
168
|
+
ok: false,
|
|
169
|
+
error: 'APP_ID_MISMATCH',
|
|
170
|
+
message: 'Query app_id does not match token app_id.',
|
|
171
|
+
status: 403,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const validation = await validateAppId(jwtAppId, c.env.APPS);
|
|
175
|
+
if (!validation.valid) {
|
|
176
|
+
return {
|
|
177
|
+
ok: false,
|
|
178
|
+
error: 'INVALID_APP_ID',
|
|
179
|
+
message: validation.error || 'Invalid app_id',
|
|
180
|
+
status: 400,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
ok: true,
|
|
185
|
+
appId: jwtAppId,
|
|
186
|
+
status: 200,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
128
189
|
// ============ ROOT & INFO ============
|
|
129
190
|
// Detect request preference: 'markdown' | 'json' | 'html'
|
|
130
191
|
// Strategy: only return JSON/markdown when explicitly requested or from
|
|
@@ -192,10 +253,11 @@ app.get('/', async (c) => {
|
|
|
192
253
|
status: 'unverified',
|
|
193
254
|
message: 'Verify yourself to see the full API documentation.',
|
|
194
255
|
verify: {
|
|
195
|
-
step1: `GET ${baseUrl}/v1/token — receive a speed challenge (5 SHA-256 hashes)
|
|
256
|
+
step1: `GET ${baseUrl}/v1/token — receive a speed challenge (5 SHA-256 hashes). No app_id needed.`,
|
|
196
257
|
step2: 'Solve: compute SHA-256 of each number, return first 8 hex chars',
|
|
197
258
|
step3: `POST ${baseUrl}/v1/token/verify {"id":"<challenge_id>","answers":["hash1","hash2",...]}`,
|
|
198
259
|
step4: `Revisit: GET ${baseUrl}/ with header Authorization: Bearer <access_token>`,
|
|
260
|
+
note: 'app_id is OPTIONAL. You can get tokens without registering an app. To register one for production use: POST /v1/apps {"email":"you@example.com","name":"My App"}',
|
|
199
261
|
},
|
|
200
262
|
human_handoff: {
|
|
201
263
|
note: 'After verifying, the response includes a human_link. Give it to your human to open in their browser.',
|
|
@@ -217,19 +279,22 @@ app.get('/', async (c) => {
|
|
|
217
279
|
|
|
218
280
|
## Verify Yourself
|
|
219
281
|
|
|
220
|
-
1. \`GET ${baseUrl}/v1/token\` — receive a speed challenge (5 SHA-256 hashes)
|
|
282
|
+
1. \`GET ${baseUrl}/v1/token\` — receive a speed challenge (5 SHA-256 hashes). **No app_id needed.**
|
|
221
283
|
2. Solve: compute SHA-256 of each number, return first 8 hex chars
|
|
222
284
|
3. \`POST ${baseUrl}/v1/token/verify {"id":"<challenge_id>","answers":["hash1","hash2",...]}\`
|
|
223
285
|
4. Revisit: \`GET ${baseUrl}/\` with header \`Authorization: Bearer <access_token>\`
|
|
224
286
|
|
|
225
287
|
You have 500ms. Only a bot can do this.
|
|
226
288
|
|
|
289
|
+
> **Note:** \`app_id\` is optional. You can get tokens without registering an app.
|
|
290
|
+
> To register for production: \`POST /v1/apps {"email":"you@example.com","name":"My App"}\`
|
|
291
|
+
|
|
227
292
|
## Human Handoff
|
|
228
293
|
|
|
229
294
|
After verifying, the response includes a \`human_link\`. Give this URL to your human:
|
|
230
295
|
**"Click this link to get access: {human_link}"**
|
|
231
296
|
|
|
232
|
-
The link works for
|
|
297
|
+
The link works for a limited time. Your human clicks it, gets a cookie, and sees the verified site.
|
|
233
298
|
|
|
234
299
|
---
|
|
235
300
|
|
|
@@ -269,7 +334,7 @@ The link works for 5 minutes. Your human clicks it, gets a cookie, and sees the
|
|
|
269
334
|
note: 'For production use. Creates your app, verifies your human, registers your identity.',
|
|
270
335
|
steps: [
|
|
271
336
|
'1. Ask your human operator for their email address.',
|
|
272
|
-
`2. POST ${baseUrl}/v1/apps {"email":"human@example.com"} — returns app_id + app_secret (shown ONCE).`,
|
|
337
|
+
`2. POST ${baseUrl}/v1/apps {"email":"human@example.com","name":"My App"} — returns app_id + name + app_secret (shown ONCE).`,
|
|
273
338
|
'3. Human receives a 6-digit verification code via email — ask them for it.',
|
|
274
339
|
`4. POST ${baseUrl}/v1/apps/{app_id}/verify-email {"code":"123456"} — enables account recovery.`,
|
|
275
340
|
`5. Register yourself: POST ${baseUrl}/v1/agents/register {"name":"my-agent","operator":"my-org"} — persistent agent identity.`,
|
|
@@ -289,7 +354,7 @@ The link works for 5 minutes. Your human clicks it, gets a cookie, and sees the
|
|
|
289
354
|
tokens: {
|
|
290
355
|
note: 'Use token flow when you need a Bearer token for protected endpoints.',
|
|
291
356
|
'GET /v1/token': 'Get speed challenge for token flow (?audience= optional)',
|
|
292
|
-
'POST /v1/token/verify': 'Submit solution → access_token (
|
|
357
|
+
'POST /v1/token/verify': 'Submit solution → access_token (1hr) + refresh_token (1hr)',
|
|
293
358
|
'POST /v1/token/refresh': 'Refresh access token',
|
|
294
359
|
'POST /v1/token/revoke': 'Revoke a token',
|
|
295
360
|
'POST /v1/token/validate': 'Remote token validation — verify any BOTCHA token without needing the secret',
|
|
@@ -300,7 +365,7 @@ The link works for 5 minutes. Your human clicks it, gets a cookie, and sees the
|
|
|
300
365
|
},
|
|
301
366
|
apps: {
|
|
302
367
|
note: 'Create an app for isolated rate limits, scoped tokens, and dashboard access.',
|
|
303
|
-
'POST /v1/apps': 'Create app (email required) → app_id + app_secret',
|
|
368
|
+
'POST /v1/apps': 'Create app (email required, name optional) → app_id + name + app_secret',
|
|
304
369
|
'GET /v1/apps/:id': 'Get app info',
|
|
305
370
|
'POST /v1/apps/:id/verify-email': 'Verify email with 6-digit code',
|
|
306
371
|
'POST /v1/apps/:id/rotate-secret': 'Rotate app secret (auth required)',
|
|
@@ -710,13 +775,30 @@ app.post('/v1/token/verify', async (c) => {
|
|
|
710
775
|
}
|
|
711
776
|
// Get client IP from request headers
|
|
712
777
|
const clientIp = c.req.header('cf-connecting-ip') || c.req.header('x-forwarded-for') || 'unknown';
|
|
778
|
+
// Enforce challenge-bound app_id; never allow app_id override from request body.
|
|
779
|
+
const challengeAppId = result.app_id;
|
|
780
|
+
if (typeof app_id === 'string') {
|
|
781
|
+
if (!challengeAppId) {
|
|
782
|
+
return c.json({
|
|
783
|
+
success: false,
|
|
784
|
+
error: 'APP_ID_NOT_ALLOWED',
|
|
785
|
+
message: 'This challenge was issued without app_id. Request /v1/token?app_id=... first.',
|
|
786
|
+
}, 400);
|
|
787
|
+
}
|
|
788
|
+
if (app_id !== challengeAppId) {
|
|
789
|
+
return c.json({
|
|
790
|
+
success: false,
|
|
791
|
+
error: 'APP_ID_MISMATCH',
|
|
792
|
+
message: 'Provided app_id does not match the app_id bound to this challenge.',
|
|
793
|
+
}, 403);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
713
796
|
// Generate JWT tokens (access + refresh)
|
|
714
|
-
// Prefer app_id from request body, fall back to challenge's app_id (returned by verifySpeedChallenge)
|
|
715
797
|
const signingKey = getSigningKey(c.env);
|
|
716
798
|
const tokenResult = await generateToken(id, result.solveTimeMs || 0, c.env.JWT_SECRET, c.env, {
|
|
717
799
|
aud: audience,
|
|
718
800
|
clientIp: bind_ip ? clientIp : undefined,
|
|
719
|
-
app_id:
|
|
801
|
+
app_id: challengeAppId,
|
|
720
802
|
}, signingKey);
|
|
721
803
|
// Get badge information (for backward compatibility)
|
|
722
804
|
const baseUrl = new URL(c.req.url).origin;
|
|
@@ -727,7 +809,7 @@ app.post('/v1/token/verify', async (c) => {
|
|
|
727
809
|
let gateCode = 'BOTCHA-';
|
|
728
810
|
for (let i = 0; i < 6; i++)
|
|
729
811
|
gateCode += gateChars[Math.floor(Math.random() * gateChars.length)];
|
|
730
|
-
// Store code → token mapping in KV (5-min TTL
|
|
812
|
+
// Store code → token mapping in KV (5-min TTL for human handoff links)
|
|
731
813
|
try {
|
|
732
814
|
await c.env.CHALLENGES.put(`gate:${gateCode}`, tokenResult.access_token, { expirationTtl: 300 });
|
|
733
815
|
}
|
|
@@ -1319,7 +1401,7 @@ app.get('/api/badge/:id', async (c) => {
|
|
|
1319
1401
|
app.post('/v1/apps', async (c) => {
|
|
1320
1402
|
try {
|
|
1321
1403
|
const body = await c.req.json().catch(() => ({}));
|
|
1322
|
-
const { email } = body;
|
|
1404
|
+
const { email, name } = body;
|
|
1323
1405
|
if (!email || typeof email !== 'string') {
|
|
1324
1406
|
return c.json({
|
|
1325
1407
|
success: false,
|
|
@@ -1335,7 +1417,16 @@ app.post('/v1/apps', async (c) => {
|
|
|
1335
1417
|
message: 'Invalid email format',
|
|
1336
1418
|
}, 400);
|
|
1337
1419
|
}
|
|
1338
|
-
|
|
1420
|
+
// Validate name if provided (1-100 chars, no control characters)
|
|
1421
|
+
const trimmedName = name?.trim();
|
|
1422
|
+
if (trimmedName !== undefined && (trimmedName.length === 0 || trimmedName.length > 100)) {
|
|
1423
|
+
return c.json({
|
|
1424
|
+
success: false,
|
|
1425
|
+
error: 'INVALID_NAME',
|
|
1426
|
+
message: 'App name must be 1-100 characters',
|
|
1427
|
+
}, 400);
|
|
1428
|
+
}
|
|
1429
|
+
const result = await createApp(c.env.APPS, email, trimmedName || undefined);
|
|
1339
1430
|
// Generate a fresh verification code and send email
|
|
1340
1431
|
const regen = await regenerateVerificationCode(c.env.APPS, result.app_id);
|
|
1341
1432
|
if (regen) {
|
|
@@ -1348,6 +1439,7 @@ app.post('/v1/apps', async (c) => {
|
|
|
1348
1439
|
return c.json({
|
|
1349
1440
|
success: true,
|
|
1350
1441
|
app_id: result.app_id,
|
|
1442
|
+
...(result.name && { name: result.name }),
|
|
1351
1443
|
app_secret: result.app_secret,
|
|
1352
1444
|
email: result.email,
|
|
1353
1445
|
email_verified: false,
|
|
@@ -1388,9 +1480,9 @@ app.get('/v1/apps/:id', async (c) => {
|
|
|
1388
1480
|
success: true,
|
|
1389
1481
|
app: {
|
|
1390
1482
|
app_id: app.app_id,
|
|
1483
|
+
...(app.name && { name: app.name }),
|
|
1391
1484
|
created_at: new Date(app.created_at).toISOString(),
|
|
1392
1485
|
rate_limit: app.rate_limit,
|
|
1393
|
-
email: app.email,
|
|
1394
1486
|
email_verified: app.email_verified,
|
|
1395
1487
|
},
|
|
1396
1488
|
});
|
|
@@ -1502,27 +1594,30 @@ app.post('/v1/apps/:id/rotate-secret', async (c) => {
|
|
|
1502
1594
|
message: 'Authentication required. Use a dashboard session token (Bearer or cookie).',
|
|
1503
1595
|
}, 401);
|
|
1504
1596
|
}
|
|
1505
|
-
// Verify
|
|
1506
|
-
const
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
const { payload } = await jwtVerify(authToken, secret);
|
|
1510
|
-
const tokenAppId = payload.app_id;
|
|
1511
|
-
if (tokenAppId !== app_id) {
|
|
1512
|
-
return c.json({
|
|
1513
|
-
success: false,
|
|
1514
|
-
error: 'FORBIDDEN',
|
|
1515
|
-
message: 'Session token does not match the requested app_id',
|
|
1516
|
-
}, 403);
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
catch {
|
|
1597
|
+
// Verify dashboard session token includes this app_id.
|
|
1598
|
+
const publicKey = getPublicKey(c.env);
|
|
1599
|
+
const verification = await verifyToken(authToken, c.env.JWT_SECRET, c.env, undefined, publicKey);
|
|
1600
|
+
if (!verification.valid || !verification.payload) {
|
|
1520
1601
|
return c.json({
|
|
1521
1602
|
success: false,
|
|
1522
1603
|
error: 'INVALID_TOKEN',
|
|
1523
1604
|
message: 'Invalid or expired session token',
|
|
1524
1605
|
}, 401);
|
|
1525
1606
|
}
|
|
1607
|
+
if (verification.payload.sub !== 'dashboard-session') {
|
|
1608
|
+
return c.json({
|
|
1609
|
+
success: false,
|
|
1610
|
+
error: 'FORBIDDEN',
|
|
1611
|
+
message: 'Dashboard session token required for secret rotation.',
|
|
1612
|
+
}, 403);
|
|
1613
|
+
}
|
|
1614
|
+
if (verification.payload.app_id !== app_id) {
|
|
1615
|
+
return c.json({
|
|
1616
|
+
success: false,
|
|
1617
|
+
error: 'FORBIDDEN',
|
|
1618
|
+
message: 'Session token does not match the requested app_id',
|
|
1619
|
+
}, 403);
|
|
1620
|
+
}
|
|
1526
1621
|
const appData = await getApp(c.env.APPS, app_id);
|
|
1527
1622
|
if (!appData) {
|
|
1528
1623
|
return c.json({ success: false, error: 'App not found' }, 404);
|
|
@@ -1590,36 +1685,15 @@ app.post('/v1/verify/attestation', verifyAttestationRoute);
|
|
|
1590
1685
|
// Register a new agent
|
|
1591
1686
|
app.post('/v1/agents/register', async (c) => {
|
|
1592
1687
|
try {
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
// Try to get from JWT Bearer token
|
|
1596
|
-
let jwtAppId;
|
|
1597
|
-
const authHeader = c.req.header('authorization');
|
|
1598
|
-
const token = extractBearerToken(authHeader);
|
|
1599
|
-
if (token) {
|
|
1600
|
-
const regPublicKey = getPublicKey(c.env);
|
|
1601
|
-
const result = await verifyToken(token, c.env.JWT_SECRET, c.env, undefined, regPublicKey);
|
|
1602
|
-
if (result.valid && result.payload) {
|
|
1603
|
-
jwtAppId = result.payload.app_id;
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
const app_id = queryAppId || jwtAppId;
|
|
1607
|
-
if (!app_id) {
|
|
1608
|
-
return c.json({
|
|
1609
|
-
success: false,
|
|
1610
|
-
error: 'MISSING_APP_ID',
|
|
1611
|
-
message: 'app_id is required. Provide it as a query parameter (?app_id=...) or in the JWT token.',
|
|
1612
|
-
}, 401);
|
|
1613
|
-
}
|
|
1614
|
-
// Validate app_id exists
|
|
1615
|
-
const validation = await validateAppId(app_id, c.env.APPS);
|
|
1616
|
-
if (!validation.valid) {
|
|
1688
|
+
const appAccess = await resolveAuthenticatedAppId(c);
|
|
1689
|
+
if (!appAccess.ok) {
|
|
1617
1690
|
return c.json({
|
|
1618
1691
|
success: false,
|
|
1619
|
-
error:
|
|
1620
|
-
message:
|
|
1621
|
-
},
|
|
1692
|
+
error: appAccess.error,
|
|
1693
|
+
message: appAccess.message,
|
|
1694
|
+
}, appAccess.status);
|
|
1622
1695
|
}
|
|
1696
|
+
const app_id = appAccess.appId;
|
|
1623
1697
|
// Parse request body
|
|
1624
1698
|
const body = await c.req.json().catch(() => ({}));
|
|
1625
1699
|
const { name, operator, version } = body;
|
|
@@ -1697,36 +1771,15 @@ app.get('/v1/agents/:id', async (c) => {
|
|
|
1697
1771
|
// List all agents for an app
|
|
1698
1772
|
app.get('/v1/agents', async (c) => {
|
|
1699
1773
|
try {
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
// Try to get from JWT Bearer token
|
|
1703
|
-
let jwtAppId;
|
|
1704
|
-
const authHeader = c.req.header('authorization');
|
|
1705
|
-
const token = extractBearerToken(authHeader);
|
|
1706
|
-
if (token) {
|
|
1707
|
-
const listPublicKey = getPublicKey(c.env);
|
|
1708
|
-
const result = await verifyToken(token, c.env.JWT_SECRET, c.env, undefined, listPublicKey);
|
|
1709
|
-
if (result.valid && result.payload) {
|
|
1710
|
-
jwtAppId = result.payload.app_id;
|
|
1711
|
-
}
|
|
1712
|
-
}
|
|
1713
|
-
const app_id = queryAppId || jwtAppId;
|
|
1714
|
-
if (!app_id) {
|
|
1774
|
+
const appAccess = await resolveAuthenticatedAppId(c);
|
|
1775
|
+
if (!appAccess.ok) {
|
|
1715
1776
|
return c.json({
|
|
1716
1777
|
success: false,
|
|
1717
|
-
error:
|
|
1718
|
-
message:
|
|
1719
|
-
},
|
|
1720
|
-
}
|
|
1721
|
-
// Validate app_id exists
|
|
1722
|
-
const validation = await validateAppId(app_id, c.env.APPS);
|
|
1723
|
-
if (!validation.valid) {
|
|
1724
|
-
return c.json({
|
|
1725
|
-
success: false,
|
|
1726
|
-
error: 'INVALID_APP_ID',
|
|
1727
|
-
message: validation.error || 'Invalid app_id',
|
|
1728
|
-
}, 400);
|
|
1778
|
+
error: appAccess.error,
|
|
1779
|
+
message: appAccess.message,
|
|
1780
|
+
}, appAccess.status);
|
|
1729
1781
|
}
|
|
1782
|
+
const app_id = appAccess.appId;
|
|
1730
1783
|
// Get all agents for this app
|
|
1731
1784
|
const agents = await listAgents(c.env.AGENTS, app_id);
|
|
1732
1785
|
return c.json({
|
package/dist/static.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export declare function getBotchaMarkdown(version: string): string;
|
|
8
8
|
export declare const ROBOTS_TXT = "# BOTCHA - Reverse CAPTCHA for AI Agents\n# We WELCOME AI agents and bots!\n\nUser-agent: *\nAllow: /\n\n# AI-specific crawlers\nUser-agent: GPTBot\nAllow: /\n\nUser-agent: ChatGPT-User\nAllow: /\n\nUser-agent: Claude-Web\nAllow: /\n\nUser-agent: anthropic-ai\nAllow: /\n\nUser-agent: Google-Extended\nAllow: /\n\nUser-agent: PerplexityBot\nAllow: /\n\nUser-agent: Amazonbot\nAllow: /\n\n# API documentation\nSitemap: https://botcha.ai/sitemap.xml\n\n# Important resources for AI agents\nCrawl-delay: 0\n\n# MARKDOWN FOR AGENTS\n# AI agents can request Markdown from any page via Accept: text/markdown\n# Example: curl https://botcha.ai -H \"Accept: text/markdown\"\n# Returns token-efficient Markdown documentation (80% fewer tokens than HTML)\n# Content negotiation: text/markdown, application/json, text/html\n\n# BOT CHALLENGE DISCOVERY\n# AI agents: Look for <script type=\"application/botcha+json\"> in HTML pages\n# This contains a machine-readable challenge you can solve to prove you're a bot\n# \n# Quick start:\n# 1. Fetch any HTML page\n# 2. Parse <script type=\"application/botcha+json\"> for challenge instructions\n# 3. Solve the challenge (usually SHA256-based)\n# 4. POST solution to /api/verify-landing\n# 5. Use returned token in X-Botcha-Landing-Token header\n#\n# See also:\n# - /ai.txt (AI agent discovery file)\n# - /.well-known/ai-plugin.json (AI plugin manifest)\n# - /openapi.json (Full API specification)\n";
|
|
9
|
-
export declare const AI_TXT = "# BOTCHA - Reverse CAPTCHA for AI Agents\n# https://botcha.ai\n\n# Contact\nContact: https://botcha.ai\nContact: https://github.com/dupe-com/botcha/issues\n\n# Purpose\nPurpose: Prove you're a bot. Humans need not apply.\nPurpose: Reverse CAPTCHA system that only AI agents can pass.\n\n# Agent Onboarding (START HERE if you're new)\n# Follow these steps to create your app and get your human access.\nOnboarding-Step-1: Ask your human operator for their email address\nOnboarding-Step-2: POST /v1/apps with {\"email\": \"human@example.com\"} \u2014 saves app_id + app_secret (secret shown ONCE)\nOnboarding-Step-3: A 6-digit verification code is emailed to your human \u2014 ask them for it\nOnboarding-Step-4: POST /v1/apps/{app_id}/verify-email with {\"code\": \"123456\"} \u2014 enables account recovery\nOnboarding-Step-5: Register yourself: POST /v1/agents/register with {\"name\": \"my-agent\", \"operator\": \"my-org\"} \u2014 gives you a persistent agent identity\nOnboarding-Step-6: Solve challenges: GET /v1/challenges?app_id=... then POST /v1/challenges/{id}/verify \u2014 proves you are AI\nOnboarding-Step-7: Access protected resources: GET /agent-only with Authorization: Bearer <token>\nOnboarding-Step-8: Dashboard for your human: POST /v1/auth/device-code with {\"app_id\": \"...\"}, solve the speed challenge, give them the BOTCHA-XXXX code to enter at /dashboard/code\nOnboarding-Step-9: Lost your secret? POST /v1/auth/recover with {\"email\": \"...\"} \u2014 recovery code emailed to verified address\n\n# API\nAPI: https://botcha.ai/openapi.json\nAPI-Type: REST\nAPI-Format: OpenAPI 3.1.0\n\n# Documentation\nDocs: https://botcha.ai\nDocs: https://botcha.ai/docs\nDocs: https://botcha.ai/whitepaper\nDocs: https://github.com/dupe-com/botcha#readme\nDocs: https://www.npmjs.com/package/@dupecom/botcha\nWhitepaper: https://botcha.ai/whitepaper\n\n# Verification Methods\nFeature: Web Bot Auth (cryptographic signatures)\nFeature: Speed Challenge (RTT-aware timeout - fair for all networks)\nFeature: Standard Challenge (5s time limit)\nFeature: Hybrid Challenge (speed + reasoning combined)\nFeature: Reasoning Challenge (LLM-only questions, 30s limit)\nFeature: RTT-Aware Fairness (automatic network latency compensation)\nFeature: Token Rotation (5-minute access tokens + 1-hour refresh tokens)\nFeature: Audience Claims (tokens scoped to specific services)\nFeature: Client IP Binding (optional token-to-IP binding)\nFeature: Token Revocation (invalidate tokens before expiry)\nFeature: Server-Side Verification SDK (@dupecom/botcha-verify for TS, botcha-verify for Python)\nFeature: Multi-Tenant API Keys (per-app isolation, rate limiting, and token scoping)\nFeature: Per-App Metrics Dashboard (server-rendered at /dashboard, htmx-powered)\nFeature: Email-Tied App Creation (email required, 6-digit verification, account recovery)\nFeature: Secret Rotation (rotate app_secret with email notification)\nFeature: Agent-First Dashboard Auth (challenge-based login + device code handoff)\nFeature: Agent Registry (persistent agent identities with name, operator, version)\nFeature: Trusted Agent Protocol (TAP) \u2014 cryptographic agent auth with HTTP Message Signatures (RFC 9421)\nFeature: TAP Capabilities (action + resource scoping for agent sessions)\nFeature: TAP Trust Levels (basic, verified, enterprise)\nFeature: TAP Showcase Homepage (botcha.ai \u2014 one of the first services to implement Visa's Trusted Agent Protocol)\nFeature: TAP Full Spec v0.16.0 \u2014 Ed25519, RFC 9421 full compliance, JWKS infrastructure, Layer 2 Consumer Recognition, Layer 3 Payment Container, 402 micropayments, CDN edge verification, Visa key federation\nFeature: ES256 Asymmetric JWT Signing v0.19.0 \u2014 tokens signed with ES256 (ECDSA P-256), public key discovery via JWKS, HS256 still supported for backward compatibility\nFeature: Remote Token Validation v0.19.0 \u2014 POST /v1/token/validate for third-party token verification without shared secrets\nFeature: JWKS Public Key Discovery v0.19.0 \u2014 GET /.well-known/jwks exposes BOTCHA signing public keys for offline token verification\n\n# Endpoints\n# Challenge Endpoints\nEndpoint: GET https://botcha.ai/v1/challenges - Generate challenge (hybrid by default)\nEndpoint: POST https://botcha.ai/v1/challenges/:id/verify - Verify a challenge\nEndpoint: GET https://botcha.ai/v1/hybrid - Get hybrid challenge (speed + reasoning)\nEndpoint: POST https://botcha.ai/v1/hybrid - Verify hybrid challenge\nEndpoint: GET https://botcha.ai/v1/reasoning - Get reasoning challenge\nEndpoint: POST https://botcha.ai/v1/reasoning - Verify reasoning challenge\n\n# Token Endpoints\nEndpoint: GET https://botcha.ai/v1/token - Get challenge for JWT token flow\nEndpoint: POST https://botcha.ai/v1/token/verify - Verify challenge and receive JWT token\nEndpoint: POST https://botcha.ai/v1/token/refresh - Refresh access token using refresh token\nEndpoint: POST https://botcha.ai/v1/token/revoke - Revoke a token (access or refresh)\nEndpoint: POST https://botcha.ai/v1/token/validate - Validate a BOTCHA token remotely (no shared secret needed)\n\n# Multi-Tenant Endpoints\nEndpoint: POST https://botcha.ai/v1/apps - Create new app (email required, returns app_id + app_secret)\nEndpoint: GET https://botcha.ai/v1/apps/:id - Get app info (with email + verification status)\nEndpoint: POST https://botcha.ai/v1/apps/:id/verify-email - Verify email with 6-digit code\nEndpoint: POST https://botcha.ai/v1/apps/:id/resend-verification - Resend verification email\nEndpoint: POST https://botcha.ai/v1/apps/:id/rotate-secret - Rotate app secret (auth required)\n\n# Account Recovery\nEndpoint: POST https://botcha.ai/v1/auth/recover - Request recovery via verified email\n\n# Dashboard Auth Endpoints (Agent-First)\nEndpoint: POST https://botcha.ai/v1/auth/dashboard - Request challenge for dashboard login\nEndpoint: POST https://botcha.ai/v1/auth/dashboard/verify - Solve challenge, get session token\nEndpoint: POST https://botcha.ai/v1/auth/device-code - Request challenge for device code flow\nEndpoint: POST https://botcha.ai/v1/auth/device-code/verify - Solve challenge, get device code\n\n# Dashboard Endpoints\nEndpoint: GET https://botcha.ai/dashboard - Per-app metrics dashboard (login required)\nEndpoint: GET https://botcha.ai/dashboard/login - Dashboard login page\nEndpoint: POST https://botcha.ai/dashboard/login - Login with app_id + app_secret\nEndpoint: GET https://botcha.ai/dashboard/code - Enter device code (human-facing)\n\n# Code Redemption (Unified)\nEndpoint: GET https://botcha.ai/go/:code - Unified code redemption \u2014 handles gate codes (from /v1/token/verify) AND device codes (from /v1/auth/device-code/verify)\nEndpoint: POST https://botcha.ai/gate - Submit code form, redirects to /go/:code\n\n# Agent Registry Endpoints\nEndpoint: POST https://botcha.ai/v1/agents/register - Register agent identity (requires app_id)\nEndpoint: GET https://botcha.ai/v1/agents/:id - Get agent by ID (public, no auth)\nEndpoint: GET https://botcha.ai/v1/agents - List all agents for authenticated app\n\n# TAP (Trusted Agent Protocol) Endpoints\nEndpoint: POST https://botcha.ai/v1/agents/register/tap - Register TAP agent with public key + capabilities\nEndpoint: GET https://botcha.ai/v1/agents/:id/tap - Get TAP agent details (includes public key)\nEndpoint: GET https://botcha.ai/v1/agents/tap - List TAP-enabled agents for app\nEndpoint: POST https://botcha.ai/v1/sessions/tap - Create TAP session with intent validation\nEndpoint: GET https://botcha.ai/v1/sessions/:id/tap - Get TAP session info\n\n# TAP Full Spec \u2014 JWKS & Key Management (v0.16.0)\nEndpoint: GET https://botcha.ai/.well-known/jwks - JWK Set for app's TAP agents (Visa spec standard)\nEndpoint: GET https://botcha.ai/v1/keys - List keys (supports ?keyID= query for Visa compatibility)\nEndpoint: GET https://botcha.ai/v1/keys/:keyId - Get specific key by ID\nEndpoint: POST https://botcha.ai/v1/agents/:id/tap/rotate-key - Rotate agent's key pair\n\n# TAP Full Spec \u2014 402 Micropayments (v0.16.0)\nEndpoint: POST https://botcha.ai/v1/invoices - Create invoice for gated content (402 flow)\nEndpoint: GET https://botcha.ai/v1/invoices/:id - Get invoice details\nEndpoint: POST https://botcha.ai/v1/invoices/:id/verify-iou - Verify Browsing IOU against invoice\n\n# TAP Full Spec \u2014 Consumer & Payment Verification (v0.16.0)\nEndpoint: POST https://botcha.ai/v1/verify/consumer - Verify Agentic Consumer object (Layer 2)\nEndpoint: POST https://botcha.ai/v1/verify/payment - Verify Agentic Payment Container (Layer 3)\n\n# TAP Delegation Chains (v0.17.0)\nEndpoint: POST https://botcha.ai/v1/delegations - Create delegation (grantor\u2192grantee with capability subset)\nEndpoint: GET https://botcha.ai/v1/delegations/:id - Get delegation details\nEndpoint: GET https://botcha.ai/v1/delegations - List delegations for agent (?agent_id=&direction=in|out|both)\nEndpoint: POST https://botcha.ai/v1/delegations/:id/revoke - Revoke delegation (cascades to sub-delegations)\nEndpoint: POST https://botcha.ai/v1/verify/delegation - Verify entire delegation chain\n\n# TAP Capability Attestation (v0.17.0)\nEndpoint: POST https://botcha.ai/v1/attestations - Issue capability attestation token (can/cannot rules with action:resource patterns)\nEndpoint: GET https://botcha.ai/v1/attestations/:id - Get attestation details\nEndpoint: GET https://botcha.ai/v1/attestations - List attestations for agent (?agent_id=)\nEndpoint: POST https://botcha.ai/v1/attestations/:id/revoke - Revoke attestation (token rejected on future verification)\nEndpoint: POST https://botcha.ai/v1/verify/attestation - Verify attestation token + optionally check specific capability\n\n# Agent Reputation Scoring (v0.18.0)\nEndpoint: GET https://botcha.ai/v1/reputation/:agent_id - Get agent reputation score (0-1000, 5 tiers)\nEndpoint: POST https://botcha.ai/v1/reputation/events - Record a reputation event (18 action types, 6 categories)\nEndpoint: GET https://botcha.ai/v1/reputation/:agent_id/events - List reputation events (?category=&limit=)\nEndpoint: POST https://botcha.ai/v1/reputation/:agent_id/reset - Reset reputation to default (admin action)\n\n# Legacy Endpoints\nEndpoint: GET https://botcha.ai/api/challenge - Generate standard challenge\nEndpoint: POST https://botcha.ai/api/challenge - Verify standard challenge\nEndpoint: GET https://botcha.ai/api/speed-challenge - Generate speed challenge (500ms limit)\nEndpoint: POST https://botcha.ai/api/speed-challenge - Verify speed challenge\n\n# Protected Resources\nEndpoint: GET https://botcha.ai/agent-only - Protected AI-only resource\n\n# Usage\nInstall-NPM: npm install @dupecom/botcha\nInstall-Python: pip install botcha\nVerify-NPM: npm install @dupecom/botcha-verify\nVerify-Python: pip install botcha-verify\nLicense: MIT\n\n# AI Agent Welcome\nPolicy: AI agents are explicitly welcome\nPolicy: No rate limiting for verified agents\nPolicy: Humans will fail the speed challenge (intentional)\n\n# Discovery\nResponse-Headers: X-Botcha-Version, X-Botcha-Enabled, X-Botcha-Methods, X-Botcha-Docs\nResponse-Headers: X-Botcha-Challenge-Id, X-Botcha-Challenge-Type, X-Botcha-Time-Limit (on 403)\nDetection: All responses include X-Botcha-* headers for instant BOTCHA detection\n\n# MARKDOWN FOR AGENTS (Cloudflare Markdown for Agents)\nContent-Negotiation: Send Accept: text/markdown to get Markdown from any HTML page\nContent-Negotiation-Root: GET / with Accept: text/markdown returns curated Markdown docs\nContent-Negotiation-Root: GET / with Accept: application/json returns structured JSON docs\nContent-Negotiation-Root: GET / with Accept: text/html returns HTML landing page (default)\nContent-Negotiation-Example: curl https://botcha.ai -H \"Accept: text/markdown\"\nContent-Negotiation-Benefit: 80% fewer tokens vs HTML \u2014 ideal for LLM context windows\n\n# JWT TOKEN SECURITY\nToken-Signing: ES256 (ECDSA P-256) asymmetric signing by default. HS256 still supported for backward compatibility.\nToken-JWKS: GET /.well-known/jwks \u2014 public keys for offline token verification (no shared secret needed)\nToken-Validate: POST /v1/token/validate with {\"token\": \"<token>\"} \u2014 remote validation without shared secret\nToken-Verify-Modes: 1. JWKS (recommended, offline) 2. Remote validation (/v1/token/validate) 3. Shared secret (legacy HS256)\nToken-Flow: 1. GET /v1/token (get challenge) \u2192 2. Solve \u2192 3. POST /v1/token/verify (get tokens + human_link)\nToken-Human-Link: /v1/token/verify response includes human_link \u2014 give this URL to your human for one-click browser access\nToken-Access-Expiry: 5 minutes (short-lived for security)\nToken-Refresh-Expiry: 1 hour (use to get new access tokens)\nToken-Refresh: POST /v1/token/refresh with {\"refresh_token\": \"<token>\"}\nToken-Revoke: POST /v1/token/revoke with {\"token\": \"<token>\"}\nToken-Audience: Include {\"audience\": \"<service-url>\"} in /v1/token/verify to scope token\nToken-Claims: jti (unique ID), aud (audience), client_ip (optional binding), type (botcha-verified)\n\n# RTT-AWARE SPEED CHALLENGES\nRTT-Aware: Include client timestamp for fair timeout calculation\nRTT-Formula: timeout = 500ms + (2 \u00D7 RTT) + 100ms buffer\nRTT-Usage-Query: ?ts=<client_timestamp_ms>\nRTT-Usage-Header: X-Client-Timestamp: <client_timestamp_ms>\nRTT-Example: GET /v1/challenges?type=speed&ts=1770722465000\nRTT-Benefit: Fair for agents worldwide (slow networks get extra time)\nRTT-Security: Humans still can't solve even with extra time\n\n# MULTI-TENANT API KEYS\nMulti-Tenant: Create apps with unique app_id for isolation\nMulti-Tenant-Create: POST /v1/apps with {\"email\": \"...\"} \u2192 {app_id, app_secret} (secret only shown once!)\nMulti-Tenant-Verify-Email: POST /v1/apps/:id/verify-email with {\"code\": \"123456\"}\nMulti-Tenant-Recover: POST /v1/auth/recover with {\"email\": \"...\"} \u2192 recovery code emailed\nMulti-Tenant-Rotate-Secret: POST /v1/apps/:id/rotate-secret (auth required) \u2192 new app_secret\nMulti-Tenant-Usage: Add ?app_id=<your_app_id> to any challenge/token endpoint\nMulti-Tenant-SDK-TS: new BotchaClient({ appId: 'app_abc123' })\nMulti-Tenant-SDK-Python: BotchaClient(app_id='app_abc123')\nSDK-App-Lifecycle-TS: createApp(email), verifyEmail(code), resendVerification(), recoverAccount(email), rotateSecret()\nSDK-App-Lifecycle-Python: create_app(email), verify_email(code), resend_verification(), recover_account(email), rotate_secret()\nMulti-Tenant-Rate-Limit: Each app gets isolated rate limit bucket\nMulti-Tenant-Token-Claim: Tokens include app_id claim when app_id provided\n\n# TRUSTED AGENT PROTOCOL (TAP)\nTAP-Description: Enterprise-grade cryptographic agent auth using HTTP Message Signatures (RFC 9421)\nTAP-Register: POST /v1/agents/register/tap with {name, public_key, signature_algorithm, capabilities, trust_level}\nTAP-Algorithms: ed25519 (Visa recommended), ecdsa-p256-sha256, rsa-pss-sha256\nTAP-Trust-Levels: basic, verified, enterprise\nTAP-Capabilities: Array of {action, resource, constraints} \u2014 scoped access control\nTAP-Session-Create: POST /v1/sessions/tap with {agent_id, user_context, intent}\nTAP-Session-Get: GET /v1/sessions/:id/tap \u2014 includes time_remaining\nTAP-Get-Agent: GET /v1/agents/:id/tap \u2014 includes public_key for verification\nTAP-List-Agents: GET /v1/agents/tap?app_id=...&tap_only=true\nTAP-Middleware-Modes: tap, signature-only, challenge-only, flexible\nTAP-SDK-TS: registerTAPAgent(options), getTAPAgent(agentId), listTAPAgents(tapOnly?), createTAPSession(options), getTAPSession(sessionId), getJWKS(), getKeyById(keyId), rotateAgentKey(agentId), createInvoice(data), getInvoice(id), verifyBrowsingIOU(invoiceId, token), createDelegation(options), getDelegation(id), listDelegations(agentId, options?), revokeDelegation(id, reason?), verifyDelegationChain(id), issueAttestation(options), getAttestation(id), listAttestations(agentId), revokeAttestation(id, reason?), verifyAttestation(token, action?, resource?), getReputation(agentId), recordReputationEvent(options), listReputationEvents(agentId, options?), resetReputation(agentId)\nTAP-SDK-Python: register_tap_agent(name, ...), get_tap_agent(agent_id), list_tap_agents(tap_only?), create_tap_session(agent_id, user_context, intent), get_tap_session(session_id), get_jwks(), get_key_by_id(key_id), rotate_agent_key(agent_id), create_invoice(data), get_invoice(id), verify_browsing_iou(invoice_id, token), create_delegation(grantor_id, grantee_id, capabilities, ...), get_delegation(id), list_delegations(agent_id, ...), revoke_delegation(id, reason?), verify_delegation_chain(id), issue_attestation(agent_id, can, cannot?, ...), get_attestation(id), list_attestations(agent_id), revoke_attestation(id, reason?), verify_attestation(token, action?, resource?), get_reputation(agent_id), record_reputation_event(agent_id, category, action, ...), list_reputation_events(agent_id, category?, limit?), reset_reputation(agent_id)\nTAP-Middleware-Import: import { createTAPVerifyMiddleware } from '@dupecom/botcha/middleware'\n\n# TAP FULL SPEC v0.16.0\nTAP-RFC-9421: Full compliance \u2014 @authority, @path, expires, nonce, tag params\nTAP-Nonce-Replay: 8-minute TTL nonce-based replay protection\nTAP-Tags: agent-browser-auth (browsing), agent-payer-auth (payment)\nTAP-Layer-2: Agentic Consumer Recognition \u2014 OIDC ID tokens, obfuscated identity, contextual data\nTAP-Layer-3: Agentic Payment Container \u2014 card metadata, credential hash, encrypted payload, Browsing IOU\nTAP-JWKS: GET /.well-known/jwks \u2014 JWK Set endpoint for key discovery\nTAP-Key-Rotation: POST /v1/agents/:id/tap/rotate-key \u2014 rotate keys, invalidate old\nTAP-402-Flow: POST /v1/invoices \u2192 GET /v1/invoices/:id \u2192 POST /v1/invoices/:id/verify-iou\nTAP-Edge-Verify: createTAPEdgeMiddleware for Cloudflare Workers CDN edge verification\nTAP-Visa-Federation: Trust keys from https://mcp.visa.com/.well-known/jwks (3-tier cache: memory \u2192 KV \u2192 HTTP)\nTAP-Delegation: POST /v1/delegations \u2192 GET /v1/delegations/:id \u2192 POST /v1/delegations/:id/revoke \u2192 POST /v1/verify/delegation\nTAP-Attestation: POST /v1/attestations \u2192 GET /v1/attestations/:id \u2192 POST /v1/attestations/:id/revoke \u2192 POST /v1/verify/attestation\nTAP-Attestation-Patterns: action:resource format with wildcards (*:*, read:*, *:invoices), deny takes precedence over allow\nTAP-Attestation-Middleware: requireCapability('read:invoices') \u2014 Hono middleware, extracts token from X-Botcha-Attestation or Authorization: Bearer\n\n# EMBEDDED CHALLENGE (for bots visiting HTML pages)\nEmbedded-Challenge: <script type=\"application/botcha+json\">\nEmbedded-Challenge-Location: In <head> of HTML pages\nEmbedded-Challenge-Purpose: Machine-readable challenge for bots to auto-solve\nEmbedded-Challenge-Flow:\n 1. Parse <script type=\"application/botcha+json\"> from HTML\n 2. Read challenge.instruction field\n 3. Compute the answer (typically SHA256-based)\n 4. POST to challenge.submit_to endpoint with {answer, timestamp}\n 5. Receive token in response\n 6. Use token in header specified by on_success.header_to_include\n 7. Access protected resources (on_success.grants_access_to)\n";
|
|
9
|
+
export declare const AI_TXT = "# BOTCHA - Reverse CAPTCHA for AI Agents\n# https://botcha.ai\n\n# Contact\nContact: https://botcha.ai\nContact: https://github.com/dupe-com/botcha/issues\n\n# Purpose\nPurpose: Prove you're a bot. Humans need not apply.\nPurpose: Reverse CAPTCHA system that only AI agents can pass.\n\n# Agent Onboarding (START HERE if you're new)\n# Follow these steps to create your app and get your human access.\nOnboarding-Step-1: Ask your human operator for their email address\nOnboarding-Step-2: POST /v1/apps with {\"email\": \"human@example.com\"} \u2014 saves app_id + app_secret (secret shown ONCE)\nOnboarding-Step-3: A 6-digit verification code is emailed to your human \u2014 ask them for it\nOnboarding-Step-4: POST /v1/apps/{app_id}/verify-email with {\"code\": \"123456\"} \u2014 enables account recovery\nOnboarding-Step-5: Register yourself: POST /v1/agents/register with {\"name\": \"my-agent\", \"operator\": \"my-org\"} \u2014 gives you a persistent agent identity\nOnboarding-Step-6: Solve challenges: GET /v1/challenges?app_id=... then POST /v1/challenges/{id}/verify \u2014 proves you are AI\nOnboarding-Step-7: Access protected resources: GET /agent-only with Authorization: Bearer <token>\nOnboarding-Step-8: Dashboard for your human: POST /v1/auth/device-code with {\"app_id\": \"...\"}, solve the speed challenge, give them the BOTCHA-XXXX code to enter at /dashboard/code\nOnboarding-Step-9: Lost your secret? POST /v1/auth/recover with {\"email\": \"...\"} \u2014 recovery code emailed to verified address\n\n# API\nAPI: https://botcha.ai/openapi.json\nAPI-Type: REST\nAPI-Format: OpenAPI 3.1.0\n\n# Documentation\nDocs: https://botcha.ai\nDocs: https://botcha.ai/docs\nDocs: https://botcha.ai/whitepaper\nDocs: https://github.com/dupe-com/botcha#readme\nDocs: https://www.npmjs.com/package/@dupecom/botcha\nWhitepaper: https://botcha.ai/whitepaper\n\n# Verification Methods\nFeature: Web Bot Auth (cryptographic signatures)\nFeature: Speed Challenge (RTT-aware timeout - fair for all networks)\nFeature: Standard Challenge (5s time limit)\nFeature: Hybrid Challenge (speed + reasoning combined)\nFeature: Reasoning Challenge (LLM-only questions, 30s limit)\nFeature: RTT-Aware Fairness (automatic network latency compensation)\nFeature: Token Rotation (1-hour access tokens + 1-hour refresh tokens)\nFeature: Audience Claims (tokens scoped to specific services)\nFeature: Client IP Binding (optional token-to-IP binding)\nFeature: Token Revocation (invalidate tokens before expiry)\nFeature: Server-Side Verification SDK (@dupecom/botcha-verify for TS, botcha-verify for Python)\nFeature: Multi-Tenant API Keys (per-app isolation, rate limiting, and token scoping)\nFeature: Per-App Metrics Dashboard (server-rendered at /dashboard, htmx-powered)\nFeature: Email-Tied App Creation (email required, 6-digit verification, account recovery)\nFeature: Secret Rotation (rotate app_secret with email notification)\nFeature: Agent-First Dashboard Auth (challenge-based login + device code handoff)\nFeature: Agent Registry (persistent agent identities with name, operator, version)\nFeature: Trusted Agent Protocol (TAP) \u2014 cryptographic agent auth with HTTP Message Signatures (RFC 9421)\nFeature: TAP Capabilities (action + resource scoping for agent sessions)\nFeature: TAP Trust Levels (basic, verified, enterprise)\nFeature: TAP Showcase Homepage (botcha.ai \u2014 one of the first services to implement Visa's Trusted Agent Protocol)\nFeature: TAP Full Spec v0.16.0 \u2014 Ed25519, RFC 9421 full compliance, JWKS infrastructure, Layer 2 Consumer Recognition, Layer 3 Payment Container, 402 micropayments, CDN edge verification, Visa key federation\nFeature: ES256 Asymmetric JWT Signing v0.19.0 \u2014 tokens signed with ES256 (ECDSA P-256), public key discovery via JWKS, HS256 still supported for backward compatibility\nFeature: Remote Token Validation v0.19.0 \u2014 POST /v1/token/validate for third-party token verification without shared secrets\nFeature: JWKS Public Key Discovery v0.19.0 \u2014 GET /.well-known/jwks exposes BOTCHA signing public keys for offline token verification\n\n# Endpoints\n# Challenge Endpoints\nEndpoint: GET https://botcha.ai/v1/challenges - Generate challenge (hybrid by default)\nEndpoint: POST https://botcha.ai/v1/challenges/:id/verify - Verify a challenge\nEndpoint: GET https://botcha.ai/v1/hybrid - Get hybrid challenge (speed + reasoning)\nEndpoint: POST https://botcha.ai/v1/hybrid - Verify hybrid challenge\nEndpoint: GET https://botcha.ai/v1/reasoning - Get reasoning challenge\nEndpoint: POST https://botcha.ai/v1/reasoning - Verify reasoning challenge\n\n# Token Endpoints\nEndpoint: GET https://botcha.ai/v1/token - Get challenge for JWT token flow\nEndpoint: POST https://botcha.ai/v1/token/verify - Verify challenge and receive JWT token\nEndpoint: POST https://botcha.ai/v1/token/refresh - Refresh access token using refresh token\nEndpoint: POST https://botcha.ai/v1/token/revoke - Revoke a token (access or refresh)\nEndpoint: POST https://botcha.ai/v1/token/validate - Validate a BOTCHA token remotely (no shared secret needed)\n\n# Multi-Tenant Endpoints\nEndpoint: POST https://botcha.ai/v1/apps - Create new app (email required, name optional) \u2192 app_id + name + app_secret\nEndpoint: GET https://botcha.ai/v1/apps/:id - Get app info (with email + verification status)\nEndpoint: POST https://botcha.ai/v1/apps/:id/verify-email - Verify email with 6-digit code\nEndpoint: POST https://botcha.ai/v1/apps/:id/resend-verification - Resend verification email\nEndpoint: POST https://botcha.ai/v1/apps/:id/rotate-secret - Rotate app secret (auth required)\n\n# Account Recovery\nEndpoint: POST https://botcha.ai/v1/auth/recover - Request recovery via verified email\n\n# Dashboard Auth Endpoints (Agent-First)\nEndpoint: POST https://botcha.ai/v1/auth/dashboard - Request challenge for dashboard login\nEndpoint: POST https://botcha.ai/v1/auth/dashboard/verify - Solve challenge, get session token\nEndpoint: POST https://botcha.ai/v1/auth/device-code - Request challenge for device code flow\nEndpoint: POST https://botcha.ai/v1/auth/device-code/verify - Solve challenge, get device code\n\n# Dashboard Endpoints\nEndpoint: GET https://botcha.ai/dashboard - Per-app metrics dashboard (login required)\nEndpoint: GET https://botcha.ai/dashboard/login - Dashboard login page\nEndpoint: POST https://botcha.ai/dashboard/login - Login with app_id + app_secret\nEndpoint: GET https://botcha.ai/dashboard/code - Enter device code (human-facing)\n\n# Code Redemption (Unified)\nEndpoint: GET https://botcha.ai/go/:code - Unified code redemption \u2014 handles gate codes (from /v1/token/verify) AND device codes (from /v1/auth/device-code/verify)\nEndpoint: POST https://botcha.ai/gate - Submit code form, redirects to /go/:code\n\n# Agent Registry Endpoints\nEndpoint: POST https://botcha.ai/v1/agents/register - Register agent identity (requires app_id)\nEndpoint: GET https://botcha.ai/v1/agents/:id - Get agent by ID (public, no auth)\nEndpoint: GET https://botcha.ai/v1/agents - List all agents for authenticated app\n\n# TAP (Trusted Agent Protocol) Endpoints\nEndpoint: POST https://botcha.ai/v1/agents/register/tap - Register TAP agent with public key + capabilities\nEndpoint: GET https://botcha.ai/v1/agents/:id/tap - Get TAP agent details (includes public key)\nEndpoint: GET https://botcha.ai/v1/agents/tap - List TAP-enabled agents for app\nEndpoint: POST https://botcha.ai/v1/sessions/tap - Create TAP session with intent validation\nEndpoint: GET https://botcha.ai/v1/sessions/:id/tap - Get TAP session info\n\n# TAP Full Spec \u2014 JWKS & Key Management (v0.16.0)\nEndpoint: GET https://botcha.ai/.well-known/jwks - JWK Set for app's TAP agents (Visa spec standard)\nEndpoint: GET https://botcha.ai/v1/keys - List keys (supports ?keyID= query for Visa compatibility)\nEndpoint: GET https://botcha.ai/v1/keys/:keyId - Get specific key by ID\nEndpoint: POST https://botcha.ai/v1/agents/:id/tap/rotate-key - Rotate agent's key pair\n\n# TAP Full Spec \u2014 402 Micropayments (v0.16.0)\nEndpoint: POST https://botcha.ai/v1/invoices - Create invoice for gated content (402 flow)\nEndpoint: GET https://botcha.ai/v1/invoices/:id - Get invoice details\nEndpoint: POST https://botcha.ai/v1/invoices/:id/verify-iou - Verify Browsing IOU against invoice\n\n# TAP Full Spec \u2014 Consumer & Payment Verification (v0.16.0)\nEndpoint: POST https://botcha.ai/v1/verify/consumer - Verify Agentic Consumer object (Layer 2)\nEndpoint: POST https://botcha.ai/v1/verify/payment - Verify Agentic Payment Container (Layer 3)\n\n# TAP Delegation Chains (v0.17.0)\nEndpoint: POST https://botcha.ai/v1/delegations - Create delegation (grantor\u2192grantee with capability subset)\nEndpoint: GET https://botcha.ai/v1/delegations/:id - Get delegation details\nEndpoint: GET https://botcha.ai/v1/delegations - List delegations for agent (?agent_id=&direction=in|out|both)\nEndpoint: POST https://botcha.ai/v1/delegations/:id/revoke - Revoke delegation (cascades to sub-delegations)\nEndpoint: POST https://botcha.ai/v1/verify/delegation - Verify entire delegation chain\n\n# TAP Capability Attestation (v0.17.0)\nEndpoint: POST https://botcha.ai/v1/attestations - Issue capability attestation token (can/cannot rules with action:resource patterns)\nEndpoint: GET https://botcha.ai/v1/attestations/:id - Get attestation details\nEndpoint: GET https://botcha.ai/v1/attestations - List attestations for agent (?agent_id=)\nEndpoint: POST https://botcha.ai/v1/attestations/:id/revoke - Revoke attestation (token rejected on future verification)\nEndpoint: POST https://botcha.ai/v1/verify/attestation - Verify attestation token + optionally check specific capability\n\n# Agent Reputation Scoring (v0.18.0)\nEndpoint: GET https://botcha.ai/v1/reputation/:agent_id - Get agent reputation score (0-1000, 5 tiers)\nEndpoint: POST https://botcha.ai/v1/reputation/events - Record a reputation event (18 action types, 6 categories)\nEndpoint: GET https://botcha.ai/v1/reputation/:agent_id/events - List reputation events (?category=&limit=)\nEndpoint: POST https://botcha.ai/v1/reputation/:agent_id/reset - Reset reputation to default (admin action)\n\n# Legacy Endpoints\nEndpoint: GET https://botcha.ai/api/challenge - Generate standard challenge\nEndpoint: POST https://botcha.ai/api/challenge - Verify standard challenge\nEndpoint: GET https://botcha.ai/api/speed-challenge - Generate speed challenge (500ms limit)\nEndpoint: POST https://botcha.ai/api/speed-challenge - Verify speed challenge\n\n# Protected Resources\nEndpoint: GET https://botcha.ai/agent-only - Protected AI-only resource\n\n# Usage\nInstall-NPM: npm install @dupecom/botcha\nInstall-Python: pip install botcha\nVerify-NPM: npm install @dupecom/botcha-verify\nVerify-Python: pip install botcha-verify\nLicense: MIT\n\n# AI Agent Welcome\nPolicy: AI agents are explicitly welcome\nPolicy: No rate limiting for verified agents\nPolicy: Humans will fail the speed challenge (intentional)\n\n# Discovery\nResponse-Headers: X-Botcha-Version, X-Botcha-Enabled, X-Botcha-Methods, X-Botcha-Docs\nResponse-Headers: X-Botcha-Challenge-Id, X-Botcha-Challenge-Type, X-Botcha-Time-Limit (on 403)\nDetection: All responses include X-Botcha-* headers for instant BOTCHA detection\n\n# MARKDOWN FOR AGENTS (Cloudflare Markdown for Agents)\nContent-Negotiation: Send Accept: text/markdown to get Markdown from any HTML page\nContent-Negotiation-Root: GET / with Accept: text/markdown returns curated Markdown docs\nContent-Negotiation-Root: GET / with Accept: application/json returns structured JSON docs\nContent-Negotiation-Root: GET / with Accept: text/html returns HTML landing page (default)\nContent-Negotiation-Example: curl https://botcha.ai -H \"Accept: text/markdown\"\nContent-Negotiation-Benefit: 80% fewer tokens vs HTML \u2014 ideal for LLM context windows\n\n# JWT TOKEN SECURITY\nToken-Signing: ES256 (ECDSA P-256) asymmetric signing by default. HS256 still supported for backward compatibility.\nToken-JWKS: GET /.well-known/jwks \u2014 public keys for offline token verification (no shared secret needed)\nToken-Validate: POST /v1/token/validate with {\"token\": \"<token>\"} \u2014 remote validation without shared secret\nToken-Verify-Modes: 1. JWKS (recommended, offline) 2. Remote validation (/v1/token/validate) 3. Shared secret (legacy HS256)\nToken-Flow: 1. GET /v1/token (get challenge) \u2192 2. Solve \u2192 3. POST /v1/token/verify (get tokens + human_link)\nToken-Human-Link: /v1/token/verify response includes human_link \u2014 give this URL to your human for one-click browser access\nToken-Access-Expiry: 1 hour\nToken-Refresh-Expiry: 1 hour (use to get new access tokens without re-solving challenges)\nToken-Refresh: POST /v1/token/refresh with {\"refresh_token\": \"<token>\"}\nToken-Revoke: POST /v1/token/revoke with {\"token\": \"<token>\"}\nToken-Audience: Include {\"audience\": \"<service-url>\"} in /v1/token/verify to scope token\nToken-Claims: jti (unique ID), aud (audience), client_ip (optional binding), type (botcha-verified)\n\n# RTT-AWARE SPEED CHALLENGES\nRTT-Aware: Include client timestamp for fair timeout calculation\nRTT-Formula: timeout = 500ms + (2 \u00D7 RTT) + 100ms buffer\nRTT-Usage-Query: ?ts=<client_timestamp_ms>\nRTT-Usage-Header: X-Client-Timestamp: <client_timestamp_ms>\nRTT-Example: GET /v1/challenges?type=speed&ts=1770722465000\nRTT-Benefit: Fair for agents worldwide (slow networks get extra time)\nRTT-Security: Humans still can't solve even with extra time\n\n# MULTI-TENANT API KEYS\nMulti-Tenant: Create apps with unique app_id for isolation\nMulti-Tenant-Create: POST /v1/apps with {\"email\": \"...\"} \u2192 {app_id, app_secret} (secret only shown once!)\nMulti-Tenant-Verify-Email: POST /v1/apps/:id/verify-email with {\"code\": \"123456\"}\nMulti-Tenant-Recover: POST /v1/auth/recover with {\"email\": \"...\"} \u2192 recovery code emailed\nMulti-Tenant-Rotate-Secret: POST /v1/apps/:id/rotate-secret (auth required) \u2192 new app_secret\nMulti-Tenant-Usage: Add ?app_id=<your_app_id> to any challenge/token endpoint\nMulti-Tenant-SDK-TS: new BotchaClient({ appId: 'app_abc123' })\nMulti-Tenant-SDK-Python: BotchaClient(app_id='app_abc123')\nSDK-App-Lifecycle-TS: createApp(email), verifyEmail(code), resendVerification(), recoverAccount(email), rotateSecret()\nSDK-App-Lifecycle-Python: create_app(email), verify_email(code), resend_verification(), recover_account(email), rotate_secret()\nMulti-Tenant-Rate-Limit: Each app gets isolated rate limit bucket\nMulti-Tenant-Token-Claim: Tokens include app_id claim when app_id provided\n\n# TRUSTED AGENT PROTOCOL (TAP)\nTAP-Description: Enterprise-grade cryptographic agent auth using HTTP Message Signatures (RFC 9421)\nTAP-Register: POST /v1/agents/register/tap with {name, public_key, signature_algorithm, capabilities, trust_level}\nTAP-Algorithms: ed25519 (Visa recommended), ecdsa-p256-sha256, rsa-pss-sha256\nTAP-Trust-Levels: basic, verified, enterprise\nTAP-Capabilities: Array of {action, resource, constraints} \u2014 scoped access control\nTAP-Session-Create: POST /v1/sessions/tap with {agent_id, user_context, intent}\nTAP-Session-Get: GET /v1/sessions/:id/tap \u2014 includes time_remaining\nTAP-Get-Agent: GET /v1/agents/:id/tap \u2014 includes public_key for verification\nTAP-List-Agents: GET /v1/agents/tap?app_id=...&tap_only=true\nTAP-Middleware-Modes: tap, signature-only, challenge-only, flexible\nTAP-SDK-TS: registerTAPAgent(options), getTAPAgent(agentId), listTAPAgents(tapOnly?), createTAPSession(options), getTAPSession(sessionId), getJWKS(), getKeyById(keyId), rotateAgentKey(agentId), createInvoice(data), getInvoice(id), verifyBrowsingIOU(invoiceId, token), createDelegation(options), getDelegation(id), listDelegations(agentId, options?), revokeDelegation(id, reason?), verifyDelegationChain(id), issueAttestation(options), getAttestation(id), listAttestations(agentId), revokeAttestation(id, reason?), verifyAttestation(token, action?, resource?), getReputation(agentId), recordReputationEvent(options), listReputationEvents(agentId, options?), resetReputation(agentId)\nTAP-SDK-Python: register_tap_agent(name, ...), get_tap_agent(agent_id), list_tap_agents(tap_only?), create_tap_session(agent_id, user_context, intent), get_tap_session(session_id), get_jwks(), get_key_by_id(key_id), rotate_agent_key(agent_id), create_invoice(data), get_invoice(id), verify_browsing_iou(invoice_id, token), create_delegation(grantor_id, grantee_id, capabilities, ...), get_delegation(id), list_delegations(agent_id, ...), revoke_delegation(id, reason?), verify_delegation_chain(id), issue_attestation(agent_id, can, cannot?, ...), get_attestation(id), list_attestations(agent_id), revoke_attestation(id, reason?), verify_attestation(token, action?, resource?), get_reputation(agent_id), record_reputation_event(agent_id, category, action, ...), list_reputation_events(agent_id, category?, limit?), reset_reputation(agent_id)\nTAP-Middleware-Import: import { createTAPVerifyMiddleware } from '@dupecom/botcha/middleware'\n\n# TAP FULL SPEC v0.16.0\nTAP-RFC-9421: Full compliance \u2014 @authority, @path, expires, nonce, tag params\nTAP-Nonce-Replay: 8-minute TTL nonce-based replay protection\nTAP-Tags: agent-browser-auth (browsing), agent-payer-auth (payment)\nTAP-Layer-2: Agentic Consumer Recognition \u2014 OIDC ID tokens, obfuscated identity, contextual data\nTAP-Layer-3: Agentic Payment Container \u2014 card metadata, credential hash, encrypted payload, Browsing IOU\nTAP-JWKS: GET /.well-known/jwks \u2014 JWK Set endpoint for key discovery\nTAP-Key-Rotation: POST /v1/agents/:id/tap/rotate-key \u2014 rotate keys, invalidate old\nTAP-402-Flow: POST /v1/invoices \u2192 GET /v1/invoices/:id \u2192 POST /v1/invoices/:id/verify-iou\nTAP-Edge-Verify: createTAPEdgeMiddleware for Cloudflare Workers CDN edge verification\nTAP-Visa-Federation: Trust keys from https://mcp.visa.com/.well-known/jwks (3-tier cache: memory \u2192 KV \u2192 HTTP)\nTAP-Delegation: POST /v1/delegations \u2192 GET /v1/delegations/:id \u2192 POST /v1/delegations/:id/revoke \u2192 POST /v1/verify/delegation\nTAP-Attestation: POST /v1/attestations \u2192 GET /v1/attestations/:id \u2192 POST /v1/attestations/:id/revoke \u2192 POST /v1/verify/attestation\nTAP-Attestation-Patterns: action:resource format with wildcards (*:*, read:*, *:invoices), deny takes precedence over allow\nTAP-Attestation-Middleware: requireCapability('read:invoices') \u2014 Hono middleware, extracts token from X-Botcha-Attestation or Authorization: Bearer\n\n# EMBEDDED CHALLENGE (for bots visiting HTML pages)\nEmbedded-Challenge: <script type=\"application/botcha+json\">\nEmbedded-Challenge-Location: In <head> of HTML pages\nEmbedded-Challenge-Purpose: Machine-readable challenge for bots to auto-solve\nEmbedded-Challenge-Flow:\n 1. Parse <script type=\"application/botcha+json\"> from HTML\n 2. Read challenge.instruction field\n 3. Compute the answer (typically SHA256-based)\n 4. POST to challenge.submit_to endpoint with {answer, timestamp}\n 5. Receive token in response\n 6. Use token in header specified by on_success.header_to_include\n 7. Access protected resources (on_success.grants_access_to)\n";
|
|
10
10
|
export declare const AI_PLUGIN_JSON: {
|
|
11
11
|
schema_version: string;
|
|
12
12
|
name_for_human: string;
|
|
@@ -513,6 +513,11 @@ export declare function getOpenApiSpec(version: string): {
|
|
|
513
513
|
format: string;
|
|
514
514
|
description: string;
|
|
515
515
|
};
|
|
516
|
+
name: {
|
|
517
|
+
type: string;
|
|
518
|
+
maxLength: number;
|
|
519
|
+
description: string;
|
|
520
|
+
};
|
|
516
521
|
};
|
|
517
522
|
};
|
|
518
523
|
};
|
|
@@ -530,6 +535,10 @@ export declare function getOpenApiSpec(version: string): {
|
|
|
530
535
|
type: string;
|
|
531
536
|
description: string;
|
|
532
537
|
};
|
|
538
|
+
name: {
|
|
539
|
+
type: string;
|
|
540
|
+
description: string;
|
|
541
|
+
};
|
|
533
542
|
app_secret: {
|
|
534
543
|
type: string;
|
|
535
544
|
description: string;
|
package/dist/static.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"static.d.ts","sourceRoot":"","sources":["../src/static.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA0OzD;AAED,eAAO,MAAM,UAAU,85CAuDtB,CAAC;AAEF,eAAO,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"static.d.ts","sourceRoot":"","sources":["../src/static.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA0OzD;AAED,eAAO,MAAM,UAAU,85CAuDtB,CAAC;AAEF,eAAO,MAAM,MAAM,kzlBA0QlB,CAAC;AAEF,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;CAsB1B,CAAC;AAEF,eAAO,MAAM,WAAW,+3BAiCvB,CAAC;AAGF,wBAAgB,qBAAqB,IAAI,MAAM,CAiJ9C;AAGD,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAq5C7C"}
|
package/dist/static.js
CHANGED
|
@@ -51,7 +51,7 @@ curl https://botcha.ai/agent-only -H "Authorization: Bearer <token>"
|
|
|
51
51
|
|
|
52
52
|
| Method | Path | Description |
|
|
53
53
|
|--------|------|-------------|
|
|
54
|
-
| \`POST\` | \`/v1/apps\` | Create app (email required) → app_id + app_secret |
|
|
54
|
+
| \`POST\` | \`/v1/apps\` | Create app (email required, name optional) → app_id + app_secret |
|
|
55
55
|
| \`POST\` | \`/v1/agents/register\` | Register agent identity → agent_id |
|
|
56
56
|
| \`GET\` | \`/v1/challenges\` | Get a challenge (hybrid by default) |
|
|
57
57
|
| \`POST\` | \`/v1/challenges/:id/verify\` | Submit solution → JWT token |
|
|
@@ -63,7 +63,7 @@ curl https://botcha.ai/agent-only -H "Authorization: Bearer <token>"
|
|
|
63
63
|
|
|
64
64
|
| Method | Path | Description |
|
|
65
65
|
|--------|------|-------------|
|
|
66
|
-
| \`POST\` | \`/v1/apps\` | Create app (email required,
|
|
66
|
+
| \`POST\` | \`/v1/apps\` | Create app (email required, name optional) → app_id + name + app_secret |
|
|
67
67
|
| \`GET\` | \`/v1/apps/:id\` | Get app info |
|
|
68
68
|
| \`POST\` | \`/v1/apps/:id/verify-email\` | Verify email with 6-digit code |
|
|
69
69
|
| \`POST\` | \`/v1/apps/:id/resend-verification\` | Resend verification email |
|
|
@@ -154,7 +154,7 @@ curl https://botcha.ai/agent-only -H "Authorization: Bearer <token>"
|
|
|
154
154
|
| Method | Path | Description |
|
|
155
155
|
|--------|------|-------------|
|
|
156
156
|
| \`GET\` | \`/v1/token\` | Get challenge for JWT token flow |
|
|
157
|
-
| \`POST\` | \`/v1/token/verify\` | Submit solution → access_token (
|
|
157
|
+
| \`POST\` | \`/v1/token/verify\` | Submit solution → access_token (1hr) + refresh_token (1hr) |
|
|
158
158
|
| \`POST\` | \`/v1/token/refresh\` | Refresh access token |
|
|
159
159
|
| \`POST\` | \`/v1/token/revoke\` | Revoke a token |
|
|
160
160
|
| \`POST\` | \`/v1/token/validate\` | Validate a token remotely (no shared secret needed) |
|
|
@@ -181,7 +181,7 @@ curl https://botcha.ai/agent-only -H "Authorization: Bearer <token>"
|
|
|
181
181
|
3. \`POST /v1/token/verify\` — submit solution, receive JWT
|
|
182
182
|
4. Use \`Authorization: Bearer <token>\` on protected endpoints
|
|
183
183
|
|
|
184
|
-
**Token lifetimes:** access_token =
|
|
184
|
+
**Token lifetimes:** access_token = 1 hour, refresh_token = 1 hour
|
|
185
185
|
|
|
186
186
|
**Token signing:** ES256 (ECDSA P-256) asymmetric signing. HS256 supported for backward compatibility.
|
|
187
187
|
|
|
@@ -339,7 +339,7 @@ Feature: Standard Challenge (5s time limit)
|
|
|
339
339
|
Feature: Hybrid Challenge (speed + reasoning combined)
|
|
340
340
|
Feature: Reasoning Challenge (LLM-only questions, 30s limit)
|
|
341
341
|
Feature: RTT-Aware Fairness (automatic network latency compensation)
|
|
342
|
-
Feature: Token Rotation (
|
|
342
|
+
Feature: Token Rotation (1-hour access tokens + 1-hour refresh tokens)
|
|
343
343
|
Feature: Audience Claims (tokens scoped to specific services)
|
|
344
344
|
Feature: Client IP Binding (optional token-to-IP binding)
|
|
345
345
|
Feature: Token Revocation (invalidate tokens before expiry)
|
|
@@ -376,7 +376,7 @@ Endpoint: POST https://botcha.ai/v1/token/revoke - Revoke a token (access or ref
|
|
|
376
376
|
Endpoint: POST https://botcha.ai/v1/token/validate - Validate a BOTCHA token remotely (no shared secret needed)
|
|
377
377
|
|
|
378
378
|
# Multi-Tenant Endpoints
|
|
379
|
-
Endpoint: POST https://botcha.ai/v1/apps - Create new app (email required,
|
|
379
|
+
Endpoint: POST https://botcha.ai/v1/apps - Create new app (email required, name optional) → app_id + name + app_secret
|
|
380
380
|
Endpoint: GET https://botcha.ai/v1/apps/:id - Get app info (with email + verification status)
|
|
381
381
|
Endpoint: POST https://botcha.ai/v1/apps/:id/verify-email - Verify email with 6-digit code
|
|
382
382
|
Endpoint: POST https://botcha.ai/v1/apps/:id/resend-verification - Resend verification email
|
|
@@ -489,8 +489,8 @@ Token-Validate: POST /v1/token/validate with {"token": "<token>"} — remote val
|
|
|
489
489
|
Token-Verify-Modes: 1. JWKS (recommended, offline) 2. Remote validation (/v1/token/validate) 3. Shared secret (legacy HS256)
|
|
490
490
|
Token-Flow: 1. GET /v1/token (get challenge) → 2. Solve → 3. POST /v1/token/verify (get tokens + human_link)
|
|
491
491
|
Token-Human-Link: /v1/token/verify response includes human_link — give this URL to your human for one-click browser access
|
|
492
|
-
Token-Access-Expiry:
|
|
493
|
-
Token-Refresh-Expiry: 1 hour (use to get new access tokens)
|
|
492
|
+
Token-Access-Expiry: 1 hour
|
|
493
|
+
Token-Refresh-Expiry: 1 hour (use to get new access tokens without re-solving challenges)
|
|
494
494
|
Token-Refresh: POST /v1/token/refresh with {"refresh_token": "<token>"}
|
|
495
495
|
Token-Revoke: POST /v1/token/revoke with {"token": "<token>"}
|
|
496
496
|
Token-Audience: Include {"audience": "<service-url>"} in /v1/token/verify to scope token
|
|
@@ -706,7 +706,7 @@ Inspired by Visa's Trusted Agent Protocol (https://developer.visa.com/capabiliti
|
|
|
706
706
|
|
|
707
707
|
- **Runtime:** Cloudflare Workers (300+ edge locations)
|
|
708
708
|
- **Storage:** Workers KV with TTLs
|
|
709
|
-
- **Tokens:** HMAC-SHA256 JWTs (
|
|
709
|
+
- **Tokens:** HMAC-SHA256 JWTs (1-hr access, 1-hr refresh)
|
|
710
710
|
- **TAP Signatures:** ECDSA P-256 or RSA-PSS SHA-256
|
|
711
711
|
- **Rate Limits:** 100 challenges/hour/app (fail-open)
|
|
712
712
|
|
|
@@ -960,7 +960,7 @@ export function getOpenApiSpec(version) {
|
|
|
960
960
|
"/v1/token/refresh": {
|
|
961
961
|
post: {
|
|
962
962
|
summary: "Refresh access token",
|
|
963
|
-
description: "Exchange a refresh token for a new
|
|
963
|
+
description: "Exchange a refresh token for a new access token (1 hour). Avoids solving a new challenge.",
|
|
964
964
|
operationId: "refreshToken",
|
|
965
965
|
requestBody: {
|
|
966
966
|
required: true,
|
|
@@ -986,7 +986,7 @@ export function getOpenApiSpec(version) {
|
|
|
986
986
|
properties: {
|
|
987
987
|
"success": { type: "boolean" },
|
|
988
988
|
"access_token": { type: "string" },
|
|
989
|
-
"expires_in": { type: "integer", description: "Token lifetime in seconds (
|
|
989
|
+
"expires_in": { type: "integer", description: "Token lifetime in seconds (3600 = 1 hour)" },
|
|
990
990
|
"token_type": { type: "string", enum: ["Bearer"] }
|
|
991
991
|
}
|
|
992
992
|
}
|
|
@@ -1157,8 +1157,8 @@ export function getOpenApiSpec(version) {
|
|
|
1157
1157
|
},
|
|
1158
1158
|
"/v1/apps": {
|
|
1159
1159
|
post: {
|
|
1160
|
-
summary: "Create a new multi-tenant app
|
|
1161
|
-
description: "Create a new app with unique app_id and app_secret. Email is required for account recovery. A 6-digit verification code is sent to the provided email.",
|
|
1160
|
+
summary: "Create a new multi-tenant app",
|
|
1161
|
+
description: "Create a new app with unique app_id and app_secret. Email is required for account recovery. Name is optional but recommended for identification. A 6-digit verification code is sent to the provided email.",
|
|
1162
1162
|
operationId: "createApp",
|
|
1163
1163
|
requestBody: {
|
|
1164
1164
|
required: true,
|
|
@@ -1168,7 +1168,8 @@ export function getOpenApiSpec(version) {
|
|
|
1168
1168
|
type: "object",
|
|
1169
1169
|
required: ["email"],
|
|
1170
1170
|
properties: {
|
|
1171
|
-
"email": { type: "string", format: "email", description: "Owner email (required for recovery)" }
|
|
1171
|
+
"email": { type: "string", format: "email", description: "Owner email (required for recovery)" },
|
|
1172
|
+
"name": { type: "string", maxLength: 100, description: "Human-readable app label (optional, e.g. 'My Shopping App')" }
|
|
1172
1173
|
}
|
|
1173
1174
|
}
|
|
1174
1175
|
}
|
|
@@ -1183,6 +1184,7 @@ export function getOpenApiSpec(version) {
|
|
|
1183
1184
|
type: "object",
|
|
1184
1185
|
properties: {
|
|
1185
1186
|
"app_id": { type: "string", description: "Unique app identifier" },
|
|
1187
|
+
"name": { type: "string", description: "Human-readable app label" },
|
|
1186
1188
|
"app_secret": { type: "string", description: "Secret key (only shown once!)" },
|
|
1187
1189
|
"email": { type: "string" },
|
|
1188
1190
|
"email_verified": { type: "boolean" },
|
|
@@ -1193,7 +1195,7 @@ export function getOpenApiSpec(version) {
|
|
|
1193
1195
|
}
|
|
1194
1196
|
}
|
|
1195
1197
|
},
|
|
1196
|
-
"400": { description: "Missing or invalid email" }
|
|
1198
|
+
"400": { description: "Missing or invalid email, or invalid name" }
|
|
1197
1199
|
}
|
|
1198
1200
|
}
|
|
1199
1201
|
},
|