@dupecom/botcha-cloudflare 0.11.0 → 0.13.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/dist/analytics.js +2 -2
- package/dist/dashboard/auth.d.ts +13 -4
- package/dist/dashboard/auth.d.ts.map +1 -1
- package/dist/dashboard/auth.js +38 -42
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +1 -0
- package/dist/dashboard/landing.d.ts +3 -3
- package/dist/dashboard/landing.d.ts.map +1 -1
- package/dist/dashboard/landing.js +47 -7
- package/dist/dashboard/pages.d.ts.map +1 -1
- package/dist/dashboard/pages.js +1 -1
- package/dist/dashboard/styles.d.ts +1 -1
- package/dist/dashboard/styles.d.ts.map +1 -1
- package/dist/dashboard/styles.js +3 -3
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +105 -58
- package/dist/static.d.ts +227 -1
- package/dist/static.d.ts.map +1 -1
- package/dist/static.js +191 -1
- package/dist/tap-agents.d.ts +120 -0
- package/dist/tap-agents.d.ts.map +1 -0
- package/dist/tap-agents.js +225 -0
- package/dist/tap-routes.d.ts +215 -0
- package/dist/tap-routes.d.ts.map +1 -0
- package/dist/tap-routes.js +379 -0
- package/dist/tap-verify.d.ts +86 -0
- package/dist/tap-verify.d.ts.map +1 -0
- package/dist/tap-verify.js +275 -0
- package/package.json +3 -3
package/dist/static.js
CHANGED
|
@@ -77,6 +77,16 @@ curl https://botcha.ai/agent-only -H "Authorization: Bearer <token>"
|
|
|
77
77
|
| \`GET\` | \`/v1/agents/:id\` | Get agent by ID (public, no auth) |
|
|
78
78
|
| \`GET\` | \`/v1/agents\` | List all agents for your app (auth required) |
|
|
79
79
|
|
|
80
|
+
### TAP (Trusted Agent Protocol)
|
|
81
|
+
|
|
82
|
+
| Method | Path | Description |
|
|
83
|
+
|--------|------|-------------|
|
|
84
|
+
| \`POST\` | \`/v1/agents/register/tap\` | Register TAP agent with public key + capabilities |
|
|
85
|
+
| \`GET\` | \`/v1/agents/:id/tap\` | Get TAP agent details (includes public key) |
|
|
86
|
+
| \`GET\` | \`/v1/agents/tap\` | List TAP-enabled agents for app |
|
|
87
|
+
| \`POST\` | \`/v1/sessions/tap\` | Create TAP session with intent validation |
|
|
88
|
+
| \`GET\` | \`/v1/sessions/:id/tap\` | Get TAP session info |
|
|
89
|
+
|
|
80
90
|
### Challenges
|
|
81
91
|
|
|
82
92
|
| Method | Path | Description |
|
|
@@ -272,6 +282,9 @@ Feature: Email-Tied App Creation (email required, 6-digit verification, account
|
|
|
272
282
|
Feature: Secret Rotation (rotate app_secret with email notification)
|
|
273
283
|
Feature: Agent-First Dashboard Auth (challenge-based login + device code handoff)
|
|
274
284
|
Feature: Agent Registry (persistent agent identities with name, operator, version)
|
|
285
|
+
Feature: Trusted Agent Protocol (TAP) — cryptographic agent auth with HTTP Message Signatures (RFC 9421)
|
|
286
|
+
Feature: TAP Capabilities (action + resource scoping for agent sessions)
|
|
287
|
+
Feature: TAP Trust Levels (basic, verified, enterprise)
|
|
275
288
|
|
|
276
289
|
# Endpoints
|
|
277
290
|
# Challenge Endpoints
|
|
@@ -310,11 +323,22 @@ Endpoint: GET https://botcha.ai/dashboard/login - Dashboard login page
|
|
|
310
323
|
Endpoint: POST https://botcha.ai/dashboard/login - Login with app_id + app_secret
|
|
311
324
|
Endpoint: GET https://botcha.ai/dashboard/code - Enter device code (human-facing)
|
|
312
325
|
|
|
326
|
+
# Code Redemption (Unified)
|
|
327
|
+
Endpoint: GET https://botcha.ai/go/:code - Unified code redemption — handles gate codes (from /v1/token/verify) AND device codes (from /v1/auth/device-code/verify)
|
|
328
|
+
Endpoint: POST https://botcha.ai/gate - Submit code form, redirects to /go/:code
|
|
329
|
+
|
|
313
330
|
# Agent Registry Endpoints
|
|
314
331
|
Endpoint: POST https://botcha.ai/v1/agents/register - Register agent identity (requires app_id)
|
|
315
332
|
Endpoint: GET https://botcha.ai/v1/agents/:id - Get agent by ID (public, no auth)
|
|
316
333
|
Endpoint: GET https://botcha.ai/v1/agents - List all agents for authenticated app
|
|
317
334
|
|
|
335
|
+
# TAP (Trusted Agent Protocol) Endpoints
|
|
336
|
+
Endpoint: POST https://botcha.ai/v1/agents/register/tap - Register TAP agent with public key + capabilities
|
|
337
|
+
Endpoint: GET https://botcha.ai/v1/agents/:id/tap - Get TAP agent details (includes public key)
|
|
338
|
+
Endpoint: GET https://botcha.ai/v1/agents/tap - List TAP-enabled agents for app
|
|
339
|
+
Endpoint: POST https://botcha.ai/v1/sessions/tap - Create TAP session with intent validation
|
|
340
|
+
Endpoint: GET https://botcha.ai/v1/sessions/:id/tap - Get TAP session info
|
|
341
|
+
|
|
318
342
|
# Legacy Endpoints
|
|
319
343
|
Endpoint: GET https://botcha.ai/api/challenge - Generate standard challenge
|
|
320
344
|
Endpoint: POST https://botcha.ai/api/challenge - Verify standard challenge
|
|
@@ -350,7 +374,8 @@ Content-Negotiation-Example: curl https://botcha.ai -H "Accept: text/markdown"
|
|
|
350
374
|
Content-Negotiation-Benefit: 80% fewer tokens vs HTML — ideal for LLM context windows
|
|
351
375
|
|
|
352
376
|
# JWT TOKEN SECURITY
|
|
353
|
-
Token-Flow: 1. GET /v1/token (get challenge) → 2. Solve → 3. POST /v1/token/verify (get tokens)
|
|
377
|
+
Token-Flow: 1. GET /v1/token (get challenge) → 2. Solve → 3. POST /v1/token/verify (get tokens + human_link)
|
|
378
|
+
Token-Human-Link: /v1/token/verify response includes human_link — give this URL to your human for one-click browser access
|
|
354
379
|
Token-Access-Expiry: 5 minutes (short-lived for security)
|
|
355
380
|
Token-Refresh-Expiry: 1 hour (use to get new access tokens)
|
|
356
381
|
Token-Refresh: POST /v1/token/refresh with {"refresh_token": "<token>"}
|
|
@@ -381,6 +406,18 @@ SDK-App-Lifecycle-Python: create_app(email), verify_email(code), resend_verifica
|
|
|
381
406
|
Multi-Tenant-Rate-Limit: Each app gets isolated rate limit bucket
|
|
382
407
|
Multi-Tenant-Token-Claim: Tokens include app_id claim when app_id provided
|
|
383
408
|
|
|
409
|
+
# TRUSTED AGENT PROTOCOL (TAP)
|
|
410
|
+
TAP-Description: Enterprise-grade cryptographic agent auth using HTTP Message Signatures (RFC 9421)
|
|
411
|
+
TAP-Register: POST /v1/agents/register/tap with {name, public_key, signature_algorithm, capabilities, trust_level}
|
|
412
|
+
TAP-Algorithms: ecdsa-p256-sha256, rsa-pss-sha256
|
|
413
|
+
TAP-Trust-Levels: basic, verified, enterprise
|
|
414
|
+
TAP-Capabilities: Array of {action, resource, constraints} — scoped access control
|
|
415
|
+
TAP-Session-Create: POST /v1/sessions/tap with {agent_id, user_context, intent}
|
|
416
|
+
TAP-Session-Get: GET /v1/sessions/:id/tap — includes time_remaining
|
|
417
|
+
TAP-Get-Agent: GET /v1/agents/:id/tap — includes public_key for verification
|
|
418
|
+
TAP-List-Agents: GET /v1/agents/tap?app_id=...&tap_only=true
|
|
419
|
+
TAP-Middleware-Modes: tap, signature-only, challenge-only, flexible
|
|
420
|
+
|
|
384
421
|
# EMBEDDED CHALLENGE (for bots visiting HTML pages)
|
|
385
422
|
Embedded-Challenge: <script type="application/botcha+json">
|
|
386
423
|
Embedded-Challenge-Location: In <head> of HTML pages
|
|
@@ -1099,6 +1136,159 @@ export function getOpenApiSpec(version) {
|
|
|
1099
1136
|
"401": { description: "Unauthorized - app_id required" }
|
|
1100
1137
|
}
|
|
1101
1138
|
}
|
|
1139
|
+
},
|
|
1140
|
+
"/v1/agents/register/tap": {
|
|
1141
|
+
post: {
|
|
1142
|
+
summary: "Register a TAP-enabled agent",
|
|
1143
|
+
description: "Register an agent with Trusted Agent Protocol (TAP) capabilities including public key, signature algorithm, capabilities, and trust level. Requires app_id.",
|
|
1144
|
+
operationId: "registerTAPAgent",
|
|
1145
|
+
parameters: [
|
|
1146
|
+
{
|
|
1147
|
+
name: "app_id",
|
|
1148
|
+
in: "query",
|
|
1149
|
+
schema: { type: "string" },
|
|
1150
|
+
description: "Multi-tenant app ID (or use JWT Bearer token with app_id claim)"
|
|
1151
|
+
}
|
|
1152
|
+
],
|
|
1153
|
+
requestBody: {
|
|
1154
|
+
required: true,
|
|
1155
|
+
content: {
|
|
1156
|
+
"application/json": {
|
|
1157
|
+
schema: {
|
|
1158
|
+
type: "object",
|
|
1159
|
+
required: ["name"],
|
|
1160
|
+
properties: {
|
|
1161
|
+
"name": { type: "string", description: "Agent name" },
|
|
1162
|
+
"operator": { type: "string", description: "Operator/organization name" },
|
|
1163
|
+
"version": { type: "string", description: "Agent version" },
|
|
1164
|
+
"public_key": { type: "string", description: "PEM-encoded public key" },
|
|
1165
|
+
"signature_algorithm": { type: "string", enum: ["ecdsa-p256-sha256", "rsa-pss-sha256"], description: "Signature algorithm (required if public_key provided)" },
|
|
1166
|
+
"trust_level": { type: "string", enum: ["basic", "verified", "enterprise"], description: "Agent trust level (default: basic)" },
|
|
1167
|
+
"capabilities": {
|
|
1168
|
+
type: "array",
|
|
1169
|
+
items: {
|
|
1170
|
+
type: "object",
|
|
1171
|
+
properties: {
|
|
1172
|
+
"action": { type: "string", description: "Capability action (e.g., read, write, execute)" },
|
|
1173
|
+
"resource": { type: "string", description: "Resource path" },
|
|
1174
|
+
"constraints": { type: "object", description: "Optional constraints" }
|
|
1175
|
+
}
|
|
1176
|
+
},
|
|
1177
|
+
description: "Agent capabilities (action + resource pairs)"
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
},
|
|
1184
|
+
responses: {
|
|
1185
|
+
"201": { description: "TAP agent registered successfully" },
|
|
1186
|
+
"400": { description: "Invalid request (missing fields, bad key format, invalid algorithm)" },
|
|
1187
|
+
"401": { description: "Unauthorized - app_id required" }
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
},
|
|
1191
|
+
"/v1/agents/{id}/tap": {
|
|
1192
|
+
get: {
|
|
1193
|
+
summary: "Get TAP agent details",
|
|
1194
|
+
description: "Retrieve TAP-enhanced agent information including public key, capabilities, and trust level.",
|
|
1195
|
+
operationId: "getTAPAgent",
|
|
1196
|
+
parameters: [
|
|
1197
|
+
{
|
|
1198
|
+
name: "id",
|
|
1199
|
+
in: "path",
|
|
1200
|
+
required: true,
|
|
1201
|
+
schema: { type: "string" },
|
|
1202
|
+
description: "The agent_id to retrieve"
|
|
1203
|
+
}
|
|
1204
|
+
],
|
|
1205
|
+
responses: {
|
|
1206
|
+
"200": { description: "TAP agent details including public key and capabilities" },
|
|
1207
|
+
"404": { description: "Agent not found" }
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
},
|
|
1211
|
+
"/v1/agents/tap": {
|
|
1212
|
+
get: {
|
|
1213
|
+
summary: "List TAP-enabled agents",
|
|
1214
|
+
description: "List all TAP-enabled agents for the authenticated app. Use ?tap_only=true to filter to TAP-enabled agents only.",
|
|
1215
|
+
operationId: "listTAPAgents",
|
|
1216
|
+
parameters: [
|
|
1217
|
+
{
|
|
1218
|
+
name: "app_id",
|
|
1219
|
+
in: "query",
|
|
1220
|
+
schema: { type: "string" },
|
|
1221
|
+
description: "Multi-tenant app ID"
|
|
1222
|
+
},
|
|
1223
|
+
{
|
|
1224
|
+
name: "tap_only",
|
|
1225
|
+
in: "query",
|
|
1226
|
+
schema: { type: "string", enum: ["true", "false"] },
|
|
1227
|
+
description: "Filter to TAP-enabled agents only"
|
|
1228
|
+
}
|
|
1229
|
+
],
|
|
1230
|
+
responses: {
|
|
1231
|
+
"200": { description: "List of TAP agents with capabilities and trust levels" },
|
|
1232
|
+
"401": { description: "Unauthorized - app_id required" }
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
},
|
|
1236
|
+
"/v1/sessions/tap": {
|
|
1237
|
+
post: {
|
|
1238
|
+
summary: "Create a TAP session",
|
|
1239
|
+
description: "Create a capability-scoped session after validating the agent's intent against its registered capabilities.",
|
|
1240
|
+
operationId: "createTAPSession",
|
|
1241
|
+
requestBody: {
|
|
1242
|
+
required: true,
|
|
1243
|
+
content: {
|
|
1244
|
+
"application/json": {
|
|
1245
|
+
schema: {
|
|
1246
|
+
type: "object",
|
|
1247
|
+
required: ["agent_id", "user_context", "intent"],
|
|
1248
|
+
properties: {
|
|
1249
|
+
"agent_id": { type: "string", description: "Registered TAP agent ID" },
|
|
1250
|
+
"user_context": { type: "string", description: "User context identifier" },
|
|
1251
|
+
"intent": {
|
|
1252
|
+
type: "object",
|
|
1253
|
+
properties: {
|
|
1254
|
+
"action": { type: "string", description: "Intended action (e.g., read, write)" },
|
|
1255
|
+
"resource": { type: "string", description: "Target resource path" },
|
|
1256
|
+
"purpose": { type: "string", description: "Human-readable purpose" }
|
|
1257
|
+
},
|
|
1258
|
+
description: "Declared intent for the session"
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
},
|
|
1265
|
+
responses: {
|
|
1266
|
+
"201": { description: "TAP session created with capabilities and expiry" },
|
|
1267
|
+
"400": { description: "Missing required fields or invalid intent" },
|
|
1268
|
+
"403": { description: "Agent lacks required capability for declared intent" },
|
|
1269
|
+
"404": { description: "Agent not found" }
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
},
|
|
1273
|
+
"/v1/sessions/{id}/tap": {
|
|
1274
|
+
get: {
|
|
1275
|
+
summary: "Get TAP session info",
|
|
1276
|
+
description: "Retrieve TAP session details including capabilities, intent, and time remaining.",
|
|
1277
|
+
operationId: "getTAPSession",
|
|
1278
|
+
parameters: [
|
|
1279
|
+
{
|
|
1280
|
+
name: "id",
|
|
1281
|
+
in: "path",
|
|
1282
|
+
required: true,
|
|
1283
|
+
schema: { type: "string" },
|
|
1284
|
+
description: "The session_id to retrieve"
|
|
1285
|
+
}
|
|
1286
|
+
],
|
|
1287
|
+
responses: {
|
|
1288
|
+
"200": { description: "TAP session details with time remaining" },
|
|
1289
|
+
"404": { description: "Session not found or expired" }
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1102
1292
|
}
|
|
1103
1293
|
},
|
|
1104
1294
|
components: {
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TAP-Enhanced Agent Registry
|
|
3
|
+
* Extends the basic BOTCHA agent registry with Trusted Agent Protocol features
|
|
4
|
+
*
|
|
5
|
+
* Provides enterprise-grade cryptographic agent authentication with:
|
|
6
|
+
* - HTTP Message Signatures (RFC 9421)
|
|
7
|
+
* - Capability-based access control
|
|
8
|
+
* - Intent declaration and validation
|
|
9
|
+
* - Session management with expiration
|
|
10
|
+
*/
|
|
11
|
+
import { Agent, KVNamespace } from './agents.js';
|
|
12
|
+
/**
|
|
13
|
+
* TAP-enhanced agent record (backward compatible with Agent)
|
|
14
|
+
*/
|
|
15
|
+
export interface TAPAgent extends Agent {
|
|
16
|
+
public_key?: string;
|
|
17
|
+
signature_algorithm?: 'ecdsa-p256-sha256' | 'rsa-pss-sha256';
|
|
18
|
+
key_created_at?: number;
|
|
19
|
+
capabilities?: TAPCapability[];
|
|
20
|
+
trust_level?: 'basic' | 'verified' | 'enterprise';
|
|
21
|
+
issuer?: string;
|
|
22
|
+
tap_enabled?: boolean;
|
|
23
|
+
last_verified_at?: number;
|
|
24
|
+
}
|
|
25
|
+
/** Valid TAP capability actions — single source of truth */
|
|
26
|
+
export declare const TAP_VALID_ACTIONS: readonly ["browse", "compare", "purchase", "audit", "search"];
|
|
27
|
+
export type TAPAction = typeof TAP_VALID_ACTIONS[number];
|
|
28
|
+
export interface TAPCapability {
|
|
29
|
+
action: TAPAction;
|
|
30
|
+
scope?: string[];
|
|
31
|
+
restrictions?: {
|
|
32
|
+
max_amount?: number;
|
|
33
|
+
rate_limit?: number;
|
|
34
|
+
[key: string]: any;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export interface TAPSession {
|
|
38
|
+
session_id: string;
|
|
39
|
+
agent_id: string;
|
|
40
|
+
app_id: string;
|
|
41
|
+
user_context: string;
|
|
42
|
+
capabilities: TAPCapability[];
|
|
43
|
+
intent: TAPIntent;
|
|
44
|
+
created_at: number;
|
|
45
|
+
expires_at: number;
|
|
46
|
+
}
|
|
47
|
+
export interface TAPIntent {
|
|
48
|
+
action: string;
|
|
49
|
+
resource?: string;
|
|
50
|
+
scope?: string[];
|
|
51
|
+
duration?: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Register an agent with TAP capabilities
|
|
55
|
+
*/
|
|
56
|
+
export declare function registerTAPAgent(agents: KVNamespace, appId: string, registration: {
|
|
57
|
+
name: string;
|
|
58
|
+
operator?: string;
|
|
59
|
+
version?: string;
|
|
60
|
+
public_key?: string;
|
|
61
|
+
signature_algorithm?: 'ecdsa-p256-sha256' | 'rsa-pss-sha256';
|
|
62
|
+
capabilities?: TAPCapability[];
|
|
63
|
+
trust_level?: 'basic' | 'verified' | 'enterprise';
|
|
64
|
+
issuer?: string;
|
|
65
|
+
}): Promise<{
|
|
66
|
+
success: boolean;
|
|
67
|
+
agent?: TAPAgent;
|
|
68
|
+
error?: string;
|
|
69
|
+
}>;
|
|
70
|
+
/**
|
|
71
|
+
* Get agent with TAP capabilities
|
|
72
|
+
*/
|
|
73
|
+
export declare function getTAPAgent(agents: KVNamespace, agentId: string): Promise<{
|
|
74
|
+
success: boolean;
|
|
75
|
+
agent?: TAPAgent;
|
|
76
|
+
error?: string;
|
|
77
|
+
}>;
|
|
78
|
+
/**
|
|
79
|
+
* Update agent's last verification timestamp
|
|
80
|
+
*/
|
|
81
|
+
export declare function updateAgentVerification(agents: KVNamespace, agentId: string, verificationSuccess: boolean): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* List TAP-enabled agents for an app
|
|
84
|
+
*/
|
|
85
|
+
export declare function listTAPAgents(agents: KVNamespace, appId: string, tapOnly?: boolean): Promise<{
|
|
86
|
+
success: boolean;
|
|
87
|
+
agents?: TAPAgent[];
|
|
88
|
+
error?: string;
|
|
89
|
+
}>;
|
|
90
|
+
/**
|
|
91
|
+
* Create a TAP session after successful verification
|
|
92
|
+
*/
|
|
93
|
+
export declare function createTAPSession(sessions: KVNamespace, agentId: string, appId: string, userContext: string, capabilities: TAPCapability[], intent: TAPIntent): Promise<{
|
|
94
|
+
success: boolean;
|
|
95
|
+
session?: TAPSession;
|
|
96
|
+
error?: string;
|
|
97
|
+
}>;
|
|
98
|
+
/**
|
|
99
|
+
* Get and validate TAP session
|
|
100
|
+
*/
|
|
101
|
+
export declare function getTAPSession(sessions: KVNamespace, sessionId: string): Promise<{
|
|
102
|
+
success: boolean;
|
|
103
|
+
session?: TAPSession;
|
|
104
|
+
error?: string;
|
|
105
|
+
}>;
|
|
106
|
+
export declare function validateCapability(agentCapabilities: TAPCapability[], requiredAction: string, requiredScope?: string): {
|
|
107
|
+
valid: boolean;
|
|
108
|
+
error?: string;
|
|
109
|
+
};
|
|
110
|
+
declare const _default: {
|
|
111
|
+
registerTAPAgent: typeof registerTAPAgent;
|
|
112
|
+
getTAPAgent: typeof getTAPAgent;
|
|
113
|
+
listTAPAgents: typeof listTAPAgents;
|
|
114
|
+
updateAgentVerification: typeof updateAgentVerification;
|
|
115
|
+
createTAPSession: typeof createTAPSession;
|
|
116
|
+
getTAPSession: typeof getTAPSession;
|
|
117
|
+
validateCapability: typeof validateCapability;
|
|
118
|
+
};
|
|
119
|
+
export default _default;
|
|
120
|
+
//# sourceMappingURL=tap-agents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tap-agents.d.ts","sourceRoot":"","sources":["../src/tap-agents.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TAP-Enhanced Agent Registry
|
|
3
|
+
* Extends the basic BOTCHA agent registry with Trusted Agent Protocol features
|
|
4
|
+
*
|
|
5
|
+
* Provides enterprise-grade cryptographic agent authentication with:
|
|
6
|
+
* - HTTP Message Signatures (RFC 9421)
|
|
7
|
+
* - Capability-based access control
|
|
8
|
+
* - Intent declaration and validation
|
|
9
|
+
* - Session management with expiration
|
|
10
|
+
*/
|
|
11
|
+
import { generateAgentId } from './agents.js';
|
|
12
|
+
/** Valid TAP capability actions — single source of truth */
|
|
13
|
+
export const TAP_VALID_ACTIONS = ['browse', 'compare', 'purchase', 'audit', 'search'];
|
|
14
|
+
// ============ TAP AGENT MANAGEMENT ============
|
|
15
|
+
/**
|
|
16
|
+
* Register an agent with TAP capabilities
|
|
17
|
+
*/
|
|
18
|
+
export async function registerTAPAgent(agents, appId, registration) {
|
|
19
|
+
try {
|
|
20
|
+
const agentId = generateAgentId();
|
|
21
|
+
const now = Date.now();
|
|
22
|
+
const agent = {
|
|
23
|
+
agent_id: agentId,
|
|
24
|
+
app_id: appId,
|
|
25
|
+
name: registration.name,
|
|
26
|
+
operator: registration.operator,
|
|
27
|
+
version: registration.version,
|
|
28
|
+
created_at: now,
|
|
29
|
+
// TAP fields
|
|
30
|
+
public_key: registration.public_key,
|
|
31
|
+
signature_algorithm: registration.signature_algorithm,
|
|
32
|
+
key_created_at: registration.public_key ? now : undefined,
|
|
33
|
+
capabilities: registration.capabilities || [],
|
|
34
|
+
trust_level: registration.trust_level || 'basic',
|
|
35
|
+
issuer: registration.issuer,
|
|
36
|
+
tap_enabled: Boolean(registration.public_key),
|
|
37
|
+
last_verified_at: undefined
|
|
38
|
+
};
|
|
39
|
+
// Validate TAP configuration
|
|
40
|
+
if (agent.public_key) {
|
|
41
|
+
if (!agent.signature_algorithm) {
|
|
42
|
+
return { success: false, error: 'signature_algorithm required when public_key provided' };
|
|
43
|
+
}
|
|
44
|
+
// Validate public key format
|
|
45
|
+
if (!isValidPEMPublicKey(agent.public_key)) {
|
|
46
|
+
return { success: false, error: 'Invalid PEM public key format' };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Store agent
|
|
50
|
+
await agents.put(`agent:${agentId}`, JSON.stringify(agent));
|
|
51
|
+
// Update app's agent index
|
|
52
|
+
await updateAppAgentIndex(agents, appId, agentId, 'add');
|
|
53
|
+
return { success: true, agent };
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.error('Failed to register TAP agent:', error);
|
|
57
|
+
return { success: false, error: 'Internal server error' };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get agent with TAP capabilities
|
|
62
|
+
*/
|
|
63
|
+
export async function getTAPAgent(agents, agentId) {
|
|
64
|
+
try {
|
|
65
|
+
const agentData = await agents.get(`agent:${agentId}`, 'text');
|
|
66
|
+
if (!agentData) {
|
|
67
|
+
return { success: false, error: 'Agent not found' };
|
|
68
|
+
}
|
|
69
|
+
const agent = JSON.parse(agentData);
|
|
70
|
+
return { success: true, agent };
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
console.error('Failed to get TAP agent:', error);
|
|
74
|
+
return { success: false, error: 'Internal server error' };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Update agent's last verification timestamp
|
|
79
|
+
*/
|
|
80
|
+
export async function updateAgentVerification(agents, agentId, verificationSuccess) {
|
|
81
|
+
try {
|
|
82
|
+
const result = await getTAPAgent(agents, agentId);
|
|
83
|
+
if (result.success && result.agent) {
|
|
84
|
+
result.agent.last_verified_at = verificationSuccess ? Date.now() : result.agent.last_verified_at;
|
|
85
|
+
await agents.put(`agent:${agentId}`, JSON.stringify(result.agent));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
console.error('Failed to update agent verification:', error);
|
|
90
|
+
// Fail silently - verification updates are not critical
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* List TAP-enabled agents for an app
|
|
95
|
+
*/
|
|
96
|
+
export async function listTAPAgents(agents, appId, tapOnly = false) {
|
|
97
|
+
try {
|
|
98
|
+
const indexData = await agents.get(`app_agents:${appId}`, 'text');
|
|
99
|
+
if (!indexData) {
|
|
100
|
+
return { success: true, agents: [] };
|
|
101
|
+
}
|
|
102
|
+
const agentIds = JSON.parse(indexData);
|
|
103
|
+
const agentPromises = agentIds.map(id => getTAPAgent(agents, id));
|
|
104
|
+
const results = await Promise.all(agentPromises);
|
|
105
|
+
const tapAgents = results
|
|
106
|
+
.filter(r => r.success && r.agent)
|
|
107
|
+
.map(r => r.agent)
|
|
108
|
+
.filter(agent => !tapOnly || agent.tap_enabled);
|
|
109
|
+
return { success: true, agents: tapAgents };
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
console.error('Failed to list TAP agents:', error);
|
|
113
|
+
return { success: false, error: 'Internal server error' };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// ============ TAP SESSION MANAGEMENT ============
|
|
117
|
+
/**
|
|
118
|
+
* Create a TAP session after successful verification
|
|
119
|
+
*/
|
|
120
|
+
export async function createTAPSession(sessions, agentId, appId, userContext, capabilities, intent) {
|
|
121
|
+
try {
|
|
122
|
+
const sessionId = generateSessionId();
|
|
123
|
+
const now = Date.now();
|
|
124
|
+
const MAX_SESSION_DURATION = 86400; // 24 hours max
|
|
125
|
+
const duration = Math.min(intent.duration || 3600, MAX_SESSION_DURATION);
|
|
126
|
+
const expiresAt = now + duration * 1000;
|
|
127
|
+
const session = {
|
|
128
|
+
session_id: sessionId,
|
|
129
|
+
agent_id: agentId,
|
|
130
|
+
app_id: appId,
|
|
131
|
+
user_context: userContext,
|
|
132
|
+
capabilities,
|
|
133
|
+
intent,
|
|
134
|
+
created_at: now,
|
|
135
|
+
expires_at: expiresAt
|
|
136
|
+
};
|
|
137
|
+
// Store session with TTL
|
|
138
|
+
const ttlSeconds = Math.floor((expiresAt - now) / 1000);
|
|
139
|
+
await sessions.put(`session:${sessionId}`, JSON.stringify(session), {
|
|
140
|
+
expirationTtl: ttlSeconds
|
|
141
|
+
});
|
|
142
|
+
return { success: true, session };
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
console.error('Failed to create TAP session:', error);
|
|
146
|
+
return { success: false, error: 'Internal server error' };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get and validate TAP session
|
|
151
|
+
*/
|
|
152
|
+
export async function getTAPSession(sessions, sessionId) {
|
|
153
|
+
try {
|
|
154
|
+
const sessionData = await sessions.get(`session:${sessionId}`, 'text');
|
|
155
|
+
if (!sessionData) {
|
|
156
|
+
return { success: false, error: 'Session not found or expired' };
|
|
157
|
+
}
|
|
158
|
+
const session = JSON.parse(sessionData);
|
|
159
|
+
// Double-check expiration
|
|
160
|
+
if (Date.now() > session.expires_at) {
|
|
161
|
+
return { success: false, error: 'Session expired' };
|
|
162
|
+
}
|
|
163
|
+
return { success: true, session };
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
console.error('Failed to get TAP session:', error);
|
|
167
|
+
return { success: false, error: 'Internal server error' };
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// ============ UTILITY FUNCTIONS ============
|
|
171
|
+
function generateSessionId() {
|
|
172
|
+
const bytes = new Uint8Array(16);
|
|
173
|
+
crypto.getRandomValues(bytes);
|
|
174
|
+
return Array.from(bytes)
|
|
175
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
176
|
+
.join('');
|
|
177
|
+
}
|
|
178
|
+
function isValidPEMPublicKey(pemKey) {
|
|
179
|
+
return pemKey.includes('BEGIN PUBLIC KEY') &&
|
|
180
|
+
pemKey.includes('END PUBLIC KEY') &&
|
|
181
|
+
pemKey.length > 100; // Basic sanity check
|
|
182
|
+
}
|
|
183
|
+
async function updateAppAgentIndex(agents, appId, agentId, operation) {
|
|
184
|
+
try {
|
|
185
|
+
const indexData = await agents.get(`app_agents:${appId}`, 'text');
|
|
186
|
+
let agentIds = indexData ? JSON.parse(indexData) : [];
|
|
187
|
+
if (operation === 'add' && !agentIds.includes(agentId)) {
|
|
188
|
+
agentIds.push(agentId);
|
|
189
|
+
}
|
|
190
|
+
else if (operation === 'remove') {
|
|
191
|
+
agentIds = agentIds.filter(id => id !== agentId);
|
|
192
|
+
}
|
|
193
|
+
await agents.put(`app_agents:${appId}`, JSON.stringify(agentIds));
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
console.error('Failed to update agent index:', error);
|
|
197
|
+
// Fail silently - index updates are not critical
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// ============ CAPABILITY VALIDATION ============
|
|
201
|
+
export function validateCapability(agentCapabilities, requiredAction, requiredScope) {
|
|
202
|
+
const matchingCaps = agentCapabilities.filter(cap => cap.action === requiredAction);
|
|
203
|
+
if (matchingCaps.length === 0) {
|
|
204
|
+
return { valid: false, error: `Agent lacks capability: ${requiredAction}` };
|
|
205
|
+
}
|
|
206
|
+
if (!requiredScope) {
|
|
207
|
+
return { valid: true };
|
|
208
|
+
}
|
|
209
|
+
const hasScope = matchingCaps.some(cap => !cap.scope ||
|
|
210
|
+
cap.scope.includes('*') ||
|
|
211
|
+
cap.scope.includes(requiredScope));
|
|
212
|
+
if (!hasScope) {
|
|
213
|
+
return { valid: false, error: `Agent lacks scope '${requiredScope}' for action '${requiredAction}'` };
|
|
214
|
+
}
|
|
215
|
+
return { valid: true };
|
|
216
|
+
}
|
|
217
|
+
export default {
|
|
218
|
+
registerTAPAgent,
|
|
219
|
+
getTAPAgent,
|
|
220
|
+
listTAPAgents,
|
|
221
|
+
updateAgentVerification,
|
|
222
|
+
createTAPSession,
|
|
223
|
+
getTAPSession,
|
|
224
|
+
validateCapability
|
|
225
|
+
};
|