@kya-os/contracts 1.7.20 → 1.7.22

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.
@@ -0,0 +1,424 @@
1
+ "use strict";
2
+ /**
3
+ * Policy Configuration Schemas
4
+ *
5
+ * Zod schemas for agent detection policy configuration.
6
+ * These schemas define how MCP-I servers and AgentShield enforce
7
+ * access control based on detected agent characteristics.
8
+ *
9
+ * @module @kya-os/contracts/policy
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.EXAMPLE_CHALLENGE_LOW_REP = exports.EXAMPLE_ALLOW_CHATGPT = exports.EXAMPLE_BLOCK_AI_AGENTS = exports.DEFAULT_POLICY_CONFIG = exports.UpdatePolicyResponseSchema = exports.UpdatePolicyRequestSchema = exports.GetPolicyResponseSchema = exports.GetPolicyRequestSchema = exports.PolicyConfigPartialSchema = exports.PolicyConfigSchema = exports.PolicyRuleSchema = exports.PolicyConditionSchema = exports.AllowListEntrySchema = exports.DenyListEntrySchema = exports.ThresholdConfigSchema = exports.EnforcementActionSchema = void 0;
13
+ exports.validatePolicyConfig = validatePolicyConfig;
14
+ exports.safeParsePolicyConfig = safeParsePolicyConfig;
15
+ exports.validateDenyListEntry = validateDenyListEntry;
16
+ exports.validatePolicyRule = validatePolicyRule;
17
+ const zod_1 = require("zod");
18
+ // ============================================================================
19
+ // Enforcement Actions
20
+ // ============================================================================
21
+ /**
22
+ * Available enforcement actions
23
+ *
24
+ * - `allow` - Allow the request to proceed
25
+ * - `block` - Block the request (return 403)
26
+ * - `redirect` - Redirect to a configured URL
27
+ * - `challenge` - Present a challenge (CAPTCHA, proof-of-work, etc.)
28
+ * - `log` - Allow but log for monitoring
29
+ */
30
+ exports.EnforcementActionSchema = zod_1.z.enum([
31
+ 'allow',
32
+ 'block',
33
+ 'redirect',
34
+ 'challenge',
35
+ 'log',
36
+ ]);
37
+ // ============================================================================
38
+ // Threshold Configuration
39
+ // ============================================================================
40
+ /**
41
+ * Threshold-based enforcement settings
42
+ *
43
+ * These thresholds determine automatic enforcement based on
44
+ * detection confidence and reputation scores.
45
+ */
46
+ exports.ThresholdConfigSchema = zod_1.z.object({
47
+ /**
48
+ * Detection confidence threshold (0-100)
49
+ * Agents detected with confidence >= this value trigger the configured action
50
+ * @default 80
51
+ */
52
+ confidenceThreshold: zod_1.z.number().min(0).max(100).default(80),
53
+ /**
54
+ * Action to take when confidence threshold is exceeded
55
+ * @default 'block'
56
+ */
57
+ confidenceAction: exports.EnforcementActionSchema.default('block'),
58
+ /**
59
+ * Minimum reputation score (0-100) required to allow access
60
+ * Agents with reputation < this value may be blocked/challenged
61
+ * Set to 0 to disable reputation-based enforcement
62
+ * @default 0
63
+ */
64
+ minReputationScore: zod_1.z.number().min(0).max(100).default(0),
65
+ /**
66
+ * Action to take when reputation is below threshold
67
+ * @default 'challenge'
68
+ */
69
+ lowReputationAction: exports.EnforcementActionSchema.default('challenge'),
70
+ /**
71
+ * High reputation score threshold (0-100)
72
+ * Agents with reputation >= this value are trusted and bypass other checks
73
+ * @default 90
74
+ */
75
+ trustedReputationThreshold: zod_1.z.number().min(0).max(100).default(90),
76
+ /**
77
+ * Whether cryptographically signed requests (MCP-I verified) bypass thresholds
78
+ * @default true
79
+ */
80
+ trustSignedRequests: zod_1.z.boolean().default(true),
81
+ });
82
+ // ============================================================================
83
+ // Deny List Entry
84
+ // ============================================================================
85
+ /**
86
+ * Entry in the deny list for blocking specific agents
87
+ *
88
+ * At least one identifier must be provided: clientDid, agentDid, or clientName
89
+ */
90
+ exports.DenyListEntrySchema = zod_1.z
91
+ .object({
92
+ /** MCP-I client DID (e.g., "did:key:z6Mk...") */
93
+ clientDid: zod_1.z.string().optional(),
94
+ /** Agent DID from MCP-I identity */
95
+ agentDid: zod_1.z.string().optional(),
96
+ /** Human-readable client name (e.g., "Perplexity", "ChatGPT") */
97
+ clientName: zod_1.z.string().optional(),
98
+ /** Agent type to block (e.g., "ai_agent", "bot", "scraper") */
99
+ agentType: zod_1.z.string().optional(),
100
+ /** Reason for denial (shown to user/logged) */
101
+ reason: zod_1.z.string().optional(),
102
+ /** ISO 8601 timestamp when entry was added */
103
+ blockedAt: zod_1.z.string().datetime().optional(),
104
+ /** Who added this entry (user ID or "system") */
105
+ addedBy: zod_1.z.string().optional(),
106
+ /** Whether this entry is currently active */
107
+ active: zod_1.z.boolean().default(true),
108
+ /** Optional expiration date for temporary blocks */
109
+ expiresAt: zod_1.z.string().datetime().optional(),
110
+ })
111
+ .refine((data) => data.clientDid || data.agentDid || data.clientName || data.agentType, 'At least one identifier (clientDid, agentDid, clientName, or agentType) must be provided');
112
+ // ============================================================================
113
+ // Allow List Entry
114
+ // ============================================================================
115
+ /**
116
+ * Entry in the allow list for exempting specific agents from all checks
117
+ *
118
+ * At least one identifier must be provided: clientDid, agentDid, clientName, or agentType
119
+ */
120
+ exports.AllowListEntrySchema = zod_1.z
121
+ .object({
122
+ /** MCP-I client DID (e.g., "did:key:z6Mk...") */
123
+ clientDid: zod_1.z.string().optional(),
124
+ /** Agent DID from MCP-I identity */
125
+ agentDid: zod_1.z.string().optional(),
126
+ /** Human-readable client name (e.g., "Perplexity", "ChatGPT") */
127
+ clientName: zod_1.z.string().optional(),
128
+ /** Agent type to allow (e.g., "ai_agent", "verified_bot") */
129
+ agentType: zod_1.z.string().optional(),
130
+ /** Reason for allowing (documentation) */
131
+ reason: zod_1.z.string().optional(),
132
+ /** ISO 8601 timestamp when entry was added */
133
+ addedAt: zod_1.z.string().datetime().optional(),
134
+ /** Who added this entry */
135
+ addedBy: zod_1.z.string().optional(),
136
+ })
137
+ .refine((data) => data.clientDid || data.agentDid || data.clientName || data.agentType, 'At least one identifier (clientDid, agentDid, clientName, or agentType) must be provided');
138
+ // ============================================================================
139
+ // Policy Rule Condition
140
+ // ============================================================================
141
+ /**
142
+ * Condition for matching requests in a policy rule
143
+ */
144
+ exports.PolicyConditionSchema = zod_1.z.object({
145
+ /** Field to match against */
146
+ field: zod_1.z.enum([
147
+ // Agent identification
148
+ 'agentType', // 'ai_agent', 'bot', 'automation', 'scraper', etc.
149
+ 'agentName', // Detected agent name (e.g., "ChatGPT", "Claude")
150
+ 'agentVendor', // Agent vendor (e.g., "OpenAI", "Anthropic")
151
+ 'clientDid', // MCP-I client DID
152
+ 'agentDid', // MCP-I agent DID
153
+ // Detection metrics
154
+ 'confidence', // Detection confidence (0-100)
155
+ 'reputationScore', // Agent reputation score (0-100)
156
+ 'riskLevel', // 'low', 'medium', 'high', 'critical'
157
+ // Request context
158
+ 'path', // Request path
159
+ 'method', // HTTP method
160
+ 'origin', // Request origin header
161
+ 'userAgent', // User-Agent string
162
+ // Verification status
163
+ 'signatureVerified', // Whether MCP-I signature was verified (boolean)
164
+ 'isAuthenticated', // Whether agent is authenticated via MCP-I (boolean)
165
+ 'hasValidDelegation', // Whether agent has valid delegation chain (boolean)
166
+ ]),
167
+ /** Comparison operator */
168
+ operator: zod_1.z.enum([
169
+ 'equals',
170
+ 'notEquals',
171
+ 'contains',
172
+ 'notContains',
173
+ 'startsWith',
174
+ 'endsWith',
175
+ 'greaterThan',
176
+ 'lessThan',
177
+ 'greaterThanOrEquals',
178
+ 'lessThanOrEquals',
179
+ 'matches', // Regex match
180
+ 'in', // Value in list
181
+ 'notIn', // Value not in list
182
+ ]),
183
+ /** Value to compare against */
184
+ value: zod_1.z.union([zod_1.z.string(), zod_1.z.number(), zod_1.z.boolean(), zod_1.z.array(zod_1.z.string())]),
185
+ /** Whether this condition is case-insensitive (for string comparisons) */
186
+ caseInsensitive: zod_1.z.boolean().optional(),
187
+ });
188
+ // ============================================================================
189
+ // Policy Rule
190
+ // ============================================================================
191
+ /**
192
+ * A single policy rule that maps conditions to an action
193
+ */
194
+ exports.PolicyRuleSchema = zod_1.z
195
+ .object({
196
+ /** Unique identifier for this rule */
197
+ id: zod_1.z.string(),
198
+ /** Human-readable name */
199
+ name: zod_1.z.string(),
200
+ /** Description of what this rule does */
201
+ description: zod_1.z.string().optional(),
202
+ /** Priority (lower = higher priority, evaluated first) */
203
+ priority: zod_1.z.number().int().min(0).max(1000).default(100),
204
+ /**
205
+ * Conditions that must ALL match for this rule to apply (AND logic)
206
+ * For OR logic, create multiple rules with same action
207
+ */
208
+ conditions: zod_1.z.array(exports.PolicyConditionSchema).min(1),
209
+ /** Action to take when conditions match */
210
+ action: exports.EnforcementActionSchema,
211
+ /** Redirect URL (required if action is 'redirect') */
212
+ redirectUrl: zod_1.z.string().url().optional(),
213
+ /** Custom message to show/log */
214
+ message: zod_1.z.string().optional(),
215
+ /** Whether this rule is currently active */
216
+ enabled: zod_1.z.boolean().default(true),
217
+ /** Metadata for auditing */
218
+ metadata: zod_1.z
219
+ .object({
220
+ createdAt: zod_1.z.string().datetime().optional(),
221
+ updatedAt: zod_1.z.string().datetime().optional(),
222
+ createdBy: zod_1.z.string().optional(),
223
+ })
224
+ .optional(),
225
+ })
226
+ .refine((data) => data.action !== 'redirect' || data.redirectUrl, 'redirectUrl is required when action is "redirect"');
227
+ // ============================================================================
228
+ // Policy Configuration
229
+ // ============================================================================
230
+ /**
231
+ * Base policy configuration object (without refinements)
232
+ * Used for partial updates where full validation happens after merge
233
+ */
234
+ const PolicyConfigBaseSchema = zod_1.z.object({
235
+ /** Schema version for migrations */
236
+ version: zod_1.z.string().default('1.0.0'),
237
+ /** Whether policy enforcement is enabled */
238
+ enabled: zod_1.z.boolean().default(true),
239
+ /** Default action when no rules match */
240
+ defaultAction: exports.EnforcementActionSchema.default('allow'),
241
+ /** Default redirect URL for redirect actions */
242
+ redirectUrl: zod_1.z.string().url().optional(),
243
+ /** Threshold-based enforcement settings */
244
+ thresholds: exports.ThresholdConfigSchema.default({
245
+ confidenceThreshold: 80,
246
+ confidenceAction: 'block',
247
+ minReputationScore: 0,
248
+ lowReputationAction: 'challenge',
249
+ trustedReputationThreshold: 90,
250
+ trustSignedRequests: true,
251
+ }),
252
+ /** Deny list entries (checked after allow list, before rules) */
253
+ denyList: zod_1.z.array(exports.DenyListEntrySchema).default([]),
254
+ /** Allow list entries (exempt from all checks) */
255
+ allowList: zod_1.z.array(exports.AllowListEntrySchema).default([]),
256
+ /** Custom policy rules (evaluated by priority after threshold checks) */
257
+ rules: zod_1.z.array(exports.PolicyRuleSchema).default([]),
258
+ /** Paths to exclude from policy enforcement (glob patterns supported) */
259
+ excludedPaths: zod_1.z.array(zod_1.z.string()).default([]),
260
+ /** Paths to include (if set, only these paths are enforced) */
261
+ includedPaths: zod_1.z.array(zod_1.z.string()).optional(),
262
+ /** Metadata for the policy configuration */
263
+ metadata: zod_1.z
264
+ .object({
265
+ lastUpdated: zod_1.z.string().datetime().optional(),
266
+ updatedBy: zod_1.z.string().optional(),
267
+ policyId: zod_1.z.string().optional(),
268
+ })
269
+ .optional(),
270
+ });
271
+ /**
272
+ * Complete policy configuration for a project
273
+ *
274
+ * Policy evaluation order:
275
+ * 1. Allow list (exempt agents, always allowed)
276
+ * 2. Deny list (blocked agents, always blocked)
277
+ * 3. Threshold checks (confidence, reputation)
278
+ * 4. Custom rules (evaluated by priority, first match wins)
279
+ * 5. Default action (if no rules match)
280
+ */
281
+ exports.PolicyConfigSchema = PolicyConfigBaseSchema.refine((data) => data.defaultAction !== 'redirect' || data.redirectUrl, 'redirectUrl is required when defaultAction is "redirect"');
282
+ /** Partial policy config for updates (refinements applied after merge) */
283
+ exports.PolicyConfigPartialSchema = PolicyConfigBaseSchema.partial();
284
+ // ============================================================================
285
+ // API Request/Response Schemas
286
+ // ============================================================================
287
+ /**
288
+ * Request to get policy configuration
289
+ */
290
+ exports.GetPolicyRequestSchema = zod_1.z.object({
291
+ projectId: zod_1.z.string(),
292
+ });
293
+ /**
294
+ * Response with policy configuration
295
+ */
296
+ exports.GetPolicyResponseSchema = zod_1.z.object({
297
+ success: zod_1.z.boolean(),
298
+ data: zod_1.z.object({
299
+ policy: exports.PolicyConfigSchema,
300
+ }),
301
+ metadata: zod_1.z
302
+ .object({
303
+ requestId: zod_1.z.string().optional(),
304
+ timestamp: zod_1.z.string().datetime().optional(),
305
+ cachedUntil: zod_1.z.string().datetime().optional(),
306
+ })
307
+ .optional(),
308
+ });
309
+ /**
310
+ * Request to update policy configuration
311
+ */
312
+ exports.UpdatePolicyRequestSchema = zod_1.z.object({
313
+ projectId: zod_1.z.string(),
314
+ policy: exports.PolicyConfigPartialSchema,
315
+ });
316
+ /**
317
+ * Response after updating policy
318
+ */
319
+ exports.UpdatePolicyResponseSchema = zod_1.z.object({
320
+ success: zod_1.z.boolean(),
321
+ data: zod_1.z.object({
322
+ policy: exports.PolicyConfigSchema,
323
+ changes: zod_1.z.array(zod_1.z.string()).optional(),
324
+ }),
325
+ metadata: zod_1.z
326
+ .object({
327
+ requestId: zod_1.z.string().optional(),
328
+ timestamp: zod_1.z.string().datetime().optional(),
329
+ })
330
+ .optional(),
331
+ });
332
+ // ============================================================================
333
+ // Validation Helpers
334
+ // ============================================================================
335
+ /**
336
+ * Validate a policy configuration
337
+ */
338
+ function validatePolicyConfig(data) {
339
+ return exports.PolicyConfigSchema.parse(data);
340
+ }
341
+ /**
342
+ * Safely validate a policy configuration (returns result object)
343
+ */
344
+ function safeParsePolicyConfig(data) {
345
+ return exports.PolicyConfigSchema.safeParse(data);
346
+ }
347
+ /**
348
+ * Validate a deny list entry
349
+ */
350
+ function validateDenyListEntry(data) {
351
+ return exports.DenyListEntrySchema.parse(data);
352
+ }
353
+ /**
354
+ * Validate a policy rule
355
+ */
356
+ function validatePolicyRule(data) {
357
+ return exports.PolicyRuleSchema.parse(data);
358
+ }
359
+ // ============================================================================
360
+ // Default Policy
361
+ // ============================================================================
362
+ /**
363
+ * Default policy configuration for new projects
364
+ */
365
+ exports.DEFAULT_POLICY_CONFIG = {
366
+ version: '1.0.0',
367
+ enabled: true,
368
+ defaultAction: 'allow',
369
+ thresholds: {
370
+ confidenceThreshold: 80,
371
+ confidenceAction: 'block',
372
+ minReputationScore: 0,
373
+ lowReputationAction: 'challenge',
374
+ trustedReputationThreshold: 90,
375
+ trustSignedRequests: true,
376
+ },
377
+ denyList: [],
378
+ allowList: [],
379
+ rules: [],
380
+ excludedPaths: [],
381
+ };
382
+ // ============================================================================
383
+ // Example Policies (for documentation)
384
+ // ============================================================================
385
+ /**
386
+ * Example: Block high-confidence AI agents
387
+ */
388
+ exports.EXAMPLE_BLOCK_AI_AGENTS = {
389
+ id: 'block-ai-agents',
390
+ name: 'Block AI Agents',
391
+ description: 'Block requests from AI agents detected with high confidence',
392
+ priority: 10,
393
+ conditions: [
394
+ { field: 'agentType', operator: 'equals', value: 'ai_agent' },
395
+ { field: 'confidence', operator: 'greaterThanOrEquals', value: 85 },
396
+ ],
397
+ action: 'block',
398
+ message: 'AI agent access is not permitted',
399
+ enabled: true,
400
+ };
401
+ /**
402
+ * Example: Allow verified ChatGPT
403
+ */
404
+ exports.EXAMPLE_ALLOW_CHATGPT = {
405
+ clientName: 'ChatGPT',
406
+ reason: 'Verified OpenAI integration',
407
+ addedAt: new Date().toISOString(),
408
+ };
409
+ /**
410
+ * Example: Challenge low-reputation agents
411
+ */
412
+ exports.EXAMPLE_CHALLENGE_LOW_REP = {
413
+ id: 'challenge-low-rep',
414
+ name: 'Challenge Low Reputation',
415
+ description: 'Present challenge to agents with low reputation scores',
416
+ priority: 50,
417
+ conditions: [
418
+ { field: 'reputationScore', operator: 'lessThan', value: 30 },
419
+ { field: 'signatureVerified', operator: 'equals', value: false },
420
+ ],
421
+ action: 'challenge',
422
+ message: 'Please verify your identity',
423
+ enabled: true,
424
+ };