@kya-os/contracts 1.5.3-canary.16 → 1.5.3-canary.17
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/.turbo/turbo-build.log +17 -0
- package/.turbo/turbo-test$colon$coverage.log +85 -0
- package/.turbo/turbo-test.log +32 -0
- package/coverage/coverage-final.json +38 -0
- package/dist/consent/schemas.d.ts +18 -0
- package/dist/consent/schemas.js +10 -0
- package/dist/dashboard-config/schemas.d.ts +1424 -220
- package/dist/tool-protection/index.d.ts +418 -8
- package/dist/tool-protection/index.js +61 -2
- package/package.json +35 -129
- package/schemas/cli/register-output/v1.0.0.json +69 -0
- package/schemas/identity/v1.0.0.json +46 -0
- package/schemas/proof/v1.0.0.json +80 -0
- package/schemas/registry/receipt-v1.0.0.json +60 -0
- package/schemas/verifier/verify-page/v1.0.0.json +94 -0
- package/schemas/well-known/agent/v1.0.0.json +67 -0
- package/schemas/well-known/did/v1.0.0.json +174 -0
- package/scripts/emit-schemas.js +11 -0
- package/src/agentshield-api/admin-schemas.ts +31 -0
- package/src/agentshield-api/admin-types.ts +47 -0
- package/src/agentshield-api/endpoints.ts +60 -0
- package/src/agentshield-api/index.ts +70 -0
- package/src/agentshield-api/schemas.ts +304 -0
- package/src/agentshield-api/types.ts +317 -0
- package/src/audit/index.ts +128 -0
- package/src/cli.ts +156 -0
- package/src/config/base.ts +107 -0
- package/src/config/builder.ts +97 -0
- package/src/config/delegation.ts +232 -0
- package/src/config/identity.ts +252 -0
- package/src/config/index.ts +78 -0
- package/src/config/proofing.ts +138 -0
- package/src/config/tool-context.ts +41 -0
- package/src/config/tool-protection.ts +174 -0
- package/src/consent/index.ts +32 -0
- package/src/consent/schemas.ts +334 -0
- package/src/consent/types.ts +199 -0
- package/src/dashboard-config/default-config.json +86 -0
- package/src/dashboard-config/default-config.ts +266 -0
- package/src/dashboard-config/index.ts +48 -0
- package/src/dashboard-config/schemas.ts +286 -0
- package/src/dashboard-config/types.ts +404 -0
- package/src/delegation/constraints.ts +267 -0
- package/src/delegation/index.ts +8 -0
- package/src/delegation/schemas.ts +595 -0
- package/src/did/index.ts +9 -0
- package/src/did/resolve-contract.ts +255 -0
- package/src/did/schemas.ts +190 -0
- package/src/did/types.ts +224 -0
- package/src/env/constants.ts +70 -0
- package/src/env/index.ts +5 -0
- package/src/handshake.ts +125 -0
- package/src/index.ts +45 -0
- package/src/proof/index.ts +31 -0
- package/src/proof/proof-record.ts +163 -0
- package/src/proof/signing-spec.ts +146 -0
- package/src/proof.ts +99 -0
- package/src/registry.ts +146 -0
- package/src/runtime/errors.ts +153 -0
- package/src/runtime/headers.ts +136 -0
- package/src/runtime/index.ts +6 -0
- package/src/test.ts +143 -0
- package/src/tlkrc/index.ts +5 -0
- package/src/tlkrc/rotation.ts +153 -0
- package/src/tool-protection/index.ts +343 -0
- package/src/utils/validation.ts +93 -0
- package/src/vc/index.ts +8 -0
- package/src/vc/schemas.ts +277 -0
- package/src/vc/statuslist.ts +279 -0
- package/src/verifier.ts +92 -0
- package/src/well-known/index.ts +237 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Constants
|
|
3
|
+
*
|
|
4
|
+
* System-wide constants for algorithms, TTLs, and limits
|
|
5
|
+
*
|
|
6
|
+
* Related Spec: MCP-I §8
|
|
7
|
+
* Python Reference: All service documentation files
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Hash algorithm for cryptographic operations
|
|
12
|
+
*/
|
|
13
|
+
export const HASH_ALGO = 'SHA-256' as const;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Supported signature algorithms
|
|
17
|
+
*/
|
|
18
|
+
export const SIG_ALGOS = ['Ed25519', 'ES256'] as const;
|
|
19
|
+
|
|
20
|
+
export type SignatureAlgorithm = (typeof SIG_ALGOS)[number];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Nonce TTL in milliseconds (5 minutes)
|
|
24
|
+
*/
|
|
25
|
+
export const NONCE_TTL_MS = 5 * 60 * 1000;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Resume token TTL in milliseconds (10 minutes)
|
|
29
|
+
*/
|
|
30
|
+
export const RESUME_TOKEN_TTL_MS = 10 * 60 * 1000;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* StatusList2021 cache TTL in seconds (1 minute)
|
|
34
|
+
*/
|
|
35
|
+
export const STATUSLIST_CACHE_SEC = 60;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* DID resolution timeout in milliseconds (500ms)
|
|
39
|
+
*/
|
|
40
|
+
export const DID_RESOLVE_TIMEOUT_MS = 500;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Default session TTL in minutes (30 minutes)
|
|
44
|
+
*/
|
|
45
|
+
export const DEFAULT_SESSION_TTL_MINUTES = 30;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Maximum timestamp skew in seconds (2 minutes)
|
|
49
|
+
*/
|
|
50
|
+
export const MAX_TIMESTAMP_SKEW_SEC = 120;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Maximum delegation chain depth
|
|
54
|
+
*/
|
|
55
|
+
export const MAX_DELEGATION_CHAIN_DEPTH = 10;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Maximum status list size (1 million entries)
|
|
59
|
+
*/
|
|
60
|
+
export const MAX_STATUSLIST_SIZE = 1000000;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Proof archive TTL in seconds (30 days)
|
|
64
|
+
*/
|
|
65
|
+
export const PROOF_ARCHIVE_TTL_SEC = 30 * 24 * 60 * 60;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Key rotation grace period in seconds (24 hours)
|
|
69
|
+
*/
|
|
70
|
+
export const KEY_ROTATION_GRACE_PERIOD_SEC = 24 * 60 * 60;
|
package/src/env/index.ts
ADDED
package/src/handshake.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handshake and session management schemas
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const MCPClientCapabilitiesSchema = z.record(z.string(), z.unknown());
|
|
8
|
+
|
|
9
|
+
export const MCPClientInfoSchema = z.object({
|
|
10
|
+
name: z.string().min(1), // e.g., "Claude Desktop"
|
|
11
|
+
title: z.string().optional(), // Human-readable display name
|
|
12
|
+
version: z.string().optional(), // e.g., "1.0.0"
|
|
13
|
+
platform: z.string().optional(), // e.g., "desktop", "web", "mobile"
|
|
14
|
+
vendor: z.string().optional(), // e.g., "Anthropic"
|
|
15
|
+
persistentId: z.string().optional(), // Client-provided stable identifier
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const MCPHandshakeClientInfoSchema = MCPClientInfoSchema.extend({
|
|
19
|
+
clientId: z.string().optional(), // Server-generated identifier (optional in request)
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export const MCPClientSessionInfoSchema = MCPClientInfoSchema.extend({
|
|
23
|
+
clientId: z.string(), // Generated by server
|
|
24
|
+
protocolVersion: z.string().optional(), // Negotiated protocol version
|
|
25
|
+
capabilities: MCPClientCapabilitiesSchema.optional(), // Negotiated capabilities snapshot
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export const HandshakeRequestSchema = z.object({
|
|
29
|
+
nonce: z.string().min(1),
|
|
30
|
+
audience: z.string().min(1),
|
|
31
|
+
timestamp: z.number().int().positive(),
|
|
32
|
+
agentDid: z.string().startsWith("did:").optional(), // Agent DID for delegation verification
|
|
33
|
+
clientInfo: MCPHandshakeClientInfoSchema.optional(), // Optional client information from MCP initialize
|
|
34
|
+
clientProtocolVersion: z.string().min(1).optional(), // Protocol version negotiated during initialize
|
|
35
|
+
clientCapabilities: MCPClientCapabilitiesSchema.optional(), // Client capability advertisement
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export const SessionContextSchema = z.object({
|
|
39
|
+
sessionId: z.string().min(1),
|
|
40
|
+
audience: z.string().min(1),
|
|
41
|
+
nonce: z.string().min(1),
|
|
42
|
+
timestamp: z.number().int().positive(),
|
|
43
|
+
createdAt: z.number().int().positive(),
|
|
44
|
+
lastActivity: z.number().int().positive(),
|
|
45
|
+
ttlMinutes: z.number().int().positive().default(30),
|
|
46
|
+
agentDid: z.string().optional(), // MCP Client/Agent DID (from handshake)
|
|
47
|
+
serverDid: z.string().min(1).optional(), // MCP-I Server DID (optional for backward compatibility)
|
|
48
|
+
clientDid: z.string().optional(), // Client app DID (if different from agent)
|
|
49
|
+
userDid: z.string().optional(), // User DID (delegator)
|
|
50
|
+
clientInfo: MCPClientSessionInfoSchema.optional(), // MCP client information with negotiated metadata
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export const NonceCacheEntrySchema = z.object({
|
|
54
|
+
sessionId: z.string().min(1),
|
|
55
|
+
expiresAt: z.number().int().positive(),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Type exports
|
|
59
|
+
export type MCPClientInfo = z.infer<typeof MCPClientInfoSchema>;
|
|
60
|
+
export type MCPClientSessionInfo = z.infer<typeof MCPClientSessionInfoSchema>;
|
|
61
|
+
export type HandshakeRequest = z.infer<typeof HandshakeRequestSchema>;
|
|
62
|
+
export type SessionContext = z.infer<typeof SessionContextSchema>;
|
|
63
|
+
export type MCPClientCapabilities = z.infer<typeof MCPClientCapabilitiesSchema>;
|
|
64
|
+
export type NonceCacheEntry = z.infer<typeof NonceCacheEntrySchema>;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Nonce cache interface for replay prevention
|
|
68
|
+
*/
|
|
69
|
+
export interface NonceCache {
|
|
70
|
+
/**
|
|
71
|
+
* Check if a nonce exists in the cache
|
|
72
|
+
* @param nonce - The nonce to check
|
|
73
|
+
* @param agentDid - Optional agent DID for agent-scoped nonces (prevents cross-agent replay attacks)
|
|
74
|
+
*/
|
|
75
|
+
has(nonce: string, agentDid?: string): Promise<boolean>;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Add a nonce to the cache with TTL
|
|
79
|
+
* MUST ensure atomic add-if-absent semantics for replay prevention
|
|
80
|
+
* @param nonce - The nonce to add
|
|
81
|
+
* @param ttl - Time to live in seconds
|
|
82
|
+
* @param agentDid - Optional agent DID for agent-scoped nonces (prevents cross-agent replay attacks)
|
|
83
|
+
*/
|
|
84
|
+
add(nonce: string, ttl: number, agentDid?: string): Promise<void>;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Clean up expired entries
|
|
88
|
+
* Should be safe to call frequently and should be no-op for backends that auto-expire
|
|
89
|
+
*/
|
|
90
|
+
cleanup(): Promise<void>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Configuration for nonce cache implementations
|
|
95
|
+
*/
|
|
96
|
+
export const NonceCacheConfigSchema = z.object({
|
|
97
|
+
type: z.enum(["memory", "redis", "dynamodb", "cloudflare-kv"]).optional(),
|
|
98
|
+
redis: z
|
|
99
|
+
.object({
|
|
100
|
+
url: z.string().url(),
|
|
101
|
+
keyPrefix: z.string().default("mcpi:nonce:"),
|
|
102
|
+
})
|
|
103
|
+
.optional(),
|
|
104
|
+
dynamodb: z
|
|
105
|
+
.object({
|
|
106
|
+
tableName: z.string(),
|
|
107
|
+
region: z.string().optional(),
|
|
108
|
+
keyAttribute: z.string().default("nonce"),
|
|
109
|
+
ttlAttribute: z.string().default("expiresAt"),
|
|
110
|
+
})
|
|
111
|
+
.optional(),
|
|
112
|
+
cloudflareKv: z
|
|
113
|
+
.object({
|
|
114
|
+
namespace: z.string(),
|
|
115
|
+
keyPrefix: z.string().default("nonce:"),
|
|
116
|
+
})
|
|
117
|
+
.optional(),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
export type NonceCacheConfig = z.infer<typeof NonceCacheConfigSchema>;
|
|
121
|
+
|
|
122
|
+
// Constants
|
|
123
|
+
export const DEFAULT_SESSION_TTL_MINUTES = 30;
|
|
124
|
+
export const DEFAULT_TIMESTAMP_SKEW_SECONDS = 120;
|
|
125
|
+
export const NONCE_LENGTH_BYTES = 16; // 128-bit
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @kya-os/contracts - Shared types and schemas for XMCP-I ecosystem
|
|
3
|
+
*
|
|
4
|
+
* This package provides a single source of truth for all types and contracts
|
|
5
|
+
* used across the XMCP-I ecosystem, including runtime, CLI, verifier, and registry.
|
|
6
|
+
*
|
|
7
|
+
* NOTE: Some exports may conflict. Use subpath imports for new modules:
|
|
8
|
+
* - import { ... } from '@kya-os/contracts/did'
|
|
9
|
+
* - import { ... } from '@kya-os/contracts/vc'
|
|
10
|
+
* - import { ... } from '@kya-os/contracts/delegation'
|
|
11
|
+
* - import { ... } from '@kya-os/contracts/runtime'
|
|
12
|
+
* - import { ... } from '@kya-os/contracts/tlkrc'
|
|
13
|
+
* - import { ... } from '@kya-os/contracts/env'
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// Legacy exports (maintain backward compatibility)
|
|
17
|
+
export * from "./handshake.js";
|
|
18
|
+
export * from "./proof.js";
|
|
19
|
+
export * from "./verifier.js";
|
|
20
|
+
export * from "./registry.js";
|
|
21
|
+
export * from "./cli.js";
|
|
22
|
+
export * from "./test.js";
|
|
23
|
+
export * from "./utils/validation.js";
|
|
24
|
+
|
|
25
|
+
// W3C VC and Delegation exports (for mcp-i-core compatibility)
|
|
26
|
+
export * from "./vc/index.js";
|
|
27
|
+
export * from "./delegation/index.js";
|
|
28
|
+
|
|
29
|
+
// Audit types (platform-agnostic)
|
|
30
|
+
export * from "./audit/index.js";
|
|
31
|
+
|
|
32
|
+
// Version information
|
|
33
|
+
export const CONTRACTS_VERSION = "1.2.1";
|
|
34
|
+
export const SUPPORTED_XMCP_I_VERSION = "^1.0.0";
|
|
35
|
+
|
|
36
|
+
// New MCP-I contract types are available via subpath imports:
|
|
37
|
+
// import { ... } from '@kya-os/contracts/did'
|
|
38
|
+
// import { ... } from '@kya-os/contracts/vc'
|
|
39
|
+
// import { ... } from '@kya-os/contracts/delegation'
|
|
40
|
+
// import { ... } from '@kya-os/contracts/runtime'
|
|
41
|
+
// import { ... } from '@kya-os/contracts/tlkrc'
|
|
42
|
+
// import { ... } from '@kya-os/contracts/env'
|
|
43
|
+
// import { ... } from '@kya-os/contracts/agentshield-api'
|
|
44
|
+
// import { ... } from '@kya-os/contracts/tool-protection'
|
|
45
|
+
// import { ... } from '@kya-os/contracts/well-known'
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proof Module Exports
|
|
3
|
+
*
|
|
4
|
+
* This module exports all proof-related types including DetachedProof,
|
|
5
|
+
* proof records, and signing specs.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Export DetachedProof and related schemas from root proof.ts
|
|
9
|
+
export {
|
|
10
|
+
DetachedProofSchema,
|
|
11
|
+
ProofMetaSchema,
|
|
12
|
+
CanonicalHashesSchema,
|
|
13
|
+
AuditRecordSchema,
|
|
14
|
+
ToolCallContextSchema,
|
|
15
|
+
ProofSubmissionContextSchema,
|
|
16
|
+
ProofSubmissionRequestSchema,
|
|
17
|
+
JWS_ALGORITHM,
|
|
18
|
+
HASH_ALGORITHM,
|
|
19
|
+
AUDIT_VERSION,
|
|
20
|
+
type DetachedProof,
|
|
21
|
+
type ProofMeta,
|
|
22
|
+
type CanonicalHashes,
|
|
23
|
+
type AuditRecord,
|
|
24
|
+
type ToolCallContext,
|
|
25
|
+
type ProofSubmissionContext,
|
|
26
|
+
type ProofSubmissionRequest,
|
|
27
|
+
} from '../proof.js';
|
|
28
|
+
|
|
29
|
+
// Export proof record and signing spec types
|
|
30
|
+
export * from './signing-spec.js';
|
|
31
|
+
export * from './proof-record.js';
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proof Record (Archive)
|
|
3
|
+
*
|
|
4
|
+
* Schema for proof records stored in KV/archive for audit trails
|
|
5
|
+
*
|
|
6
|
+
* Related Spec: MCP-I §5
|
|
7
|
+
* Python Reference: Edge-Delegation-Verification.md
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Request Info Schema
|
|
14
|
+
*
|
|
15
|
+
* Information about the request that was proven
|
|
16
|
+
*/
|
|
17
|
+
export const RequestInfoSchema = z.object({
|
|
18
|
+
method: z.string(),
|
|
19
|
+
url: z.string().url(),
|
|
20
|
+
bodyHash: z.string().optional(),
|
|
21
|
+
headersHash: z.string().optional(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export type RequestInfo = z.infer<typeof RequestInfoSchema>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Response Info Schema
|
|
28
|
+
*
|
|
29
|
+
* Information about the response
|
|
30
|
+
*/
|
|
31
|
+
export const ResponseInfoSchema = z.object({
|
|
32
|
+
status: z.number().int(),
|
|
33
|
+
bodyHash: z.string().optional(),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export type ResponseInfo = z.infer<typeof ResponseInfoSchema>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Proof Details Schema
|
|
40
|
+
*
|
|
41
|
+
* Core proof information
|
|
42
|
+
*/
|
|
43
|
+
export const ProofDetailsSchema = z.object({
|
|
44
|
+
timestamp: z.number().int().positive(),
|
|
45
|
+
nonce: z.string().min(1),
|
|
46
|
+
did: z.string().min(1),
|
|
47
|
+
signature: z.string().regex(/^[A-Za-z0-9_-]+$/),
|
|
48
|
+
algorithm: z.enum(['Ed25519', 'ES256']),
|
|
49
|
+
sessionId: z.string().min(1),
|
|
50
|
+
audience: z.string().min(1),
|
|
51
|
+
request: RequestInfoSchema.optional(),
|
|
52
|
+
response: ResponseInfoSchema.optional(),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export type ProofDetails = z.infer<typeof ProofDetailsSchema>;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Linkage Info Schema
|
|
59
|
+
*
|
|
60
|
+
* Links to delegations and credentials
|
|
61
|
+
*/
|
|
62
|
+
export const LinkageInfoSchema = z.object({
|
|
63
|
+
delegationId: z.string().optional(),
|
|
64
|
+
credentialId: z.string().optional(),
|
|
65
|
+
chainDepth: z.number().int().nonnegative().optional(),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
export type LinkageInfo = z.infer<typeof LinkageInfoSchema>;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* CRISP Info Schema
|
|
72
|
+
*
|
|
73
|
+
* CRISP spending information
|
|
74
|
+
*/
|
|
75
|
+
export const CrispInfoSchema = z.object({
|
|
76
|
+
unit: z.enum(['USD', 'ops', 'points']),
|
|
77
|
+
delta: z.number().optional(),
|
|
78
|
+
remaining: z.number().optional(),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
export type CrispInfo = z.infer<typeof CrispInfoSchema>;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Verification Info Schema
|
|
85
|
+
*
|
|
86
|
+
* Verification result for the proof
|
|
87
|
+
*/
|
|
88
|
+
export const VerificationInfoSchema = z.object({
|
|
89
|
+
result: z.enum(['pending', 'pass', 'fail']),
|
|
90
|
+
reason: z.string().optional(),
|
|
91
|
+
checkedAt: z.number().int().positive().optional(),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
export type VerificationInfo = z.infer<typeof VerificationInfoSchema>;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Proof Record Schema
|
|
98
|
+
*
|
|
99
|
+
* Complete proof record for archive/KV storage
|
|
100
|
+
*/
|
|
101
|
+
export const ProofRecordSchema = z.object({
|
|
102
|
+
/** Unique identifier for the proof record */
|
|
103
|
+
id: z.string().min(1),
|
|
104
|
+
|
|
105
|
+
/** Tool/service name that created the proof */
|
|
106
|
+
toolName: z.string().min(1),
|
|
107
|
+
|
|
108
|
+
/** Timestamp when stored (milliseconds since epoch) */
|
|
109
|
+
storedAt: z.number().int().positive(),
|
|
110
|
+
|
|
111
|
+
/** Expiration timestamp (milliseconds since epoch) */
|
|
112
|
+
expiresAt: z.number().int().positive(),
|
|
113
|
+
|
|
114
|
+
/** Core proof details */
|
|
115
|
+
proof: ProofDetailsSchema,
|
|
116
|
+
|
|
117
|
+
/** Optional linkage to delegations/credentials */
|
|
118
|
+
linkage: LinkageInfoSchema.optional(),
|
|
119
|
+
|
|
120
|
+
/** Optional CRISP spending info */
|
|
121
|
+
crisp: CrispInfoSchema.optional(),
|
|
122
|
+
|
|
123
|
+
/** Optional verification info */
|
|
124
|
+
verification: VerificationInfoSchema.optional(),
|
|
125
|
+
|
|
126
|
+
/** Optional metadata */
|
|
127
|
+
metadata: z.record(z.any()).optional(),
|
|
128
|
+
}).passthrough();
|
|
129
|
+
|
|
130
|
+
export type ProofRecord = z.infer<typeof ProofRecordSchema>;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Validation Helpers
|
|
134
|
+
*/
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Validate a proof record
|
|
138
|
+
*
|
|
139
|
+
* @param record - The record to validate
|
|
140
|
+
* @returns Validation result
|
|
141
|
+
*/
|
|
142
|
+
export function validateProofRecord(record: unknown) {
|
|
143
|
+
return ProofRecordSchema.safeParse(record);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Check if proof record is expired
|
|
148
|
+
*
|
|
149
|
+
* @param record - The record to check
|
|
150
|
+
* @returns true if expired
|
|
151
|
+
*/
|
|
152
|
+
export function isProofRecordExpired(record: ProofRecord): boolean {
|
|
153
|
+
return Date.now() > record.expiresAt;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Constants
|
|
158
|
+
*/
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Default proof record TTL (30 days in milliseconds)
|
|
162
|
+
*/
|
|
163
|
+
export const DEFAULT_PROOF_RECORD_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proof Signing Specification
|
|
3
|
+
*
|
|
4
|
+
* Canonical signing order and detached JWS contracts for proofs
|
|
5
|
+
*
|
|
6
|
+
* Related Spec: MCP-I §5
|
|
7
|
+
* Python Reference: Edge-Delegation-Verification.md
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Canonical Request Parts Schema
|
|
14
|
+
*
|
|
15
|
+
* Parts of a request that are canonically signed
|
|
16
|
+
*/
|
|
17
|
+
export const CanonicalRequestPartsSchema = z.object({
|
|
18
|
+
/** HTTP method (uppercased) */
|
|
19
|
+
method: z.string().toUpperCase(),
|
|
20
|
+
|
|
21
|
+
/** Absolute URL */
|
|
22
|
+
url: z.string().url(),
|
|
23
|
+
|
|
24
|
+
/** Optional body hash (base64url of SHA-256) */
|
|
25
|
+
bodyHash: z.string().regex(/^[A-Za-z0-9_-]+$/).optional(),
|
|
26
|
+
|
|
27
|
+
/** Optional headers hash (base64url of SHA-256 of allowlisted headers) */
|
|
28
|
+
headersHash: z.string().regex(/^[A-Za-z0-9_-]+$/).optional(),
|
|
29
|
+
|
|
30
|
+
/** Nonce (base64) */
|
|
31
|
+
nonce: z.string().min(1),
|
|
32
|
+
|
|
33
|
+
/** Timestamp (milliseconds since epoch) */
|
|
34
|
+
timestamp: z.number().int().positive(),
|
|
35
|
+
|
|
36
|
+
/** Audience (e.g., 'mcp-client') */
|
|
37
|
+
audience: z.string().min(1),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export type CanonicalRequestParts = z.infer<typeof CanonicalRequestPartsSchema>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Detached JWS Schema
|
|
44
|
+
*
|
|
45
|
+
* Detached JSON Web Signature for proofs
|
|
46
|
+
*/
|
|
47
|
+
export const DetachedJwsSchema = z.object({
|
|
48
|
+
/** Algorithm (Ed25519 or ES256) */
|
|
49
|
+
alg: z.enum(['Ed25519', 'ES256']),
|
|
50
|
+
|
|
51
|
+
/** Optional key ID (fragment from DID) */
|
|
52
|
+
kid: z.string().optional(),
|
|
53
|
+
|
|
54
|
+
/** Base64url-encoded signature */
|
|
55
|
+
signature: z.string().regex(/^[A-Za-z0-9_-]+$/),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export type DetachedJws = z.infer<typeof DetachedJwsSchema>;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Signing Order
|
|
62
|
+
*
|
|
63
|
+
* **CRITICAL**: This order MUST be used for canonical string generation.
|
|
64
|
+
* Changing this order breaks signature verification.
|
|
65
|
+
*/
|
|
66
|
+
export const SIGNING_ORDER = Object.freeze([
|
|
67
|
+
'method',
|
|
68
|
+
'url',
|
|
69
|
+
'bodyHash',
|
|
70
|
+
'headersHash',
|
|
71
|
+
'nonce',
|
|
72
|
+
'timestamp',
|
|
73
|
+
'audience',
|
|
74
|
+
] as const);
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Type for signing order fields
|
|
78
|
+
*/
|
|
79
|
+
export type SigningOrderField = (typeof SIGNING_ORDER)[number];
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Validation Helpers
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Validate canonical request parts
|
|
87
|
+
*
|
|
88
|
+
* @param parts - The parts to validate
|
|
89
|
+
* @returns Validation result
|
|
90
|
+
*/
|
|
91
|
+
export function validateCanonicalRequestParts(parts: unknown) {
|
|
92
|
+
return CanonicalRequestPartsSchema.safeParse(parts);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Validate detached JWS
|
|
97
|
+
*
|
|
98
|
+
* @param jws - The JWS to validate
|
|
99
|
+
* @returns Validation result
|
|
100
|
+
*/
|
|
101
|
+
export function validateDetachedJws(jws: unknown) {
|
|
102
|
+
return DetachedJwsSchema.safeParse(jws);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Generate canonical signing string from parts
|
|
107
|
+
*
|
|
108
|
+
* **NOTE**: This is a type-level spec. Actual implementation
|
|
109
|
+
* requires runtime string concatenation.
|
|
110
|
+
*
|
|
111
|
+
* @param parts - Canonical request parts
|
|
112
|
+
* @returns Canonical string for signing
|
|
113
|
+
*/
|
|
114
|
+
export function getCanonicalSigningString(parts: CanonicalRequestParts): string {
|
|
115
|
+
const values: string[] = [];
|
|
116
|
+
|
|
117
|
+
for (const field of SIGNING_ORDER) {
|
|
118
|
+
const value = parts[field];
|
|
119
|
+
if (value !== undefined) {
|
|
120
|
+
values.push(String(value));
|
|
121
|
+
} else {
|
|
122
|
+
values.push('');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return values.join('\n');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Constants
|
|
131
|
+
*/
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Supported signing algorithms
|
|
135
|
+
*/
|
|
136
|
+
export const SUPPORTED_SIGNING_ALGORITHMS = ['Ed25519', 'ES256'] as const;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Hash algorithm for body/headers
|
|
140
|
+
*/
|
|
141
|
+
export const SIGNING_HASH_ALGORITHM = 'SHA-256';
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Base64url pattern for validation
|
|
145
|
+
*/
|
|
146
|
+
export const BASE64URL_PATTERN = /^[A-Za-z0-9_-]+$/;
|
package/src/proof.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Proof and signature schemas for MCP-I
|
|
5
|
+
*
|
|
6
|
+
* Note: The type name "DetachedProof" is maintained for backward compatibility,
|
|
7
|
+
* but the JWS format is actually FULL compact JWS (header.payload.signature),
|
|
8
|
+
* not detached format (header..signature).
|
|
9
|
+
*
|
|
10
|
+
* The JWS payload contains JWT standard claims (aud, sub, iss) plus custom
|
|
11
|
+
* proof claims (requestHash, responseHash, nonce, etc.).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export const ProofMetaSchema = z.object({
|
|
15
|
+
did: z.string().min(1),
|
|
16
|
+
kid: z.string().min(1),
|
|
17
|
+
ts: z.number().int().positive(),
|
|
18
|
+
nonce: z.string().min(1),
|
|
19
|
+
audience: z.string().min(1),
|
|
20
|
+
sessionId: z.string().min(1),
|
|
21
|
+
requestHash: z.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
22
|
+
responseHash: z.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
23
|
+
scopeId: z.string().optional(),
|
|
24
|
+
delegationRef: z.string().optional(),
|
|
25
|
+
clientDid: z.string().optional(), // Optional for backward compatibility
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export const DetachedProofSchema = z.object({
|
|
29
|
+
jws: z.string().min(1), // Full compact JWS format (header.payload.signature)
|
|
30
|
+
meta: ProofMetaSchema, // Convenience metadata (duplicates signed payload data)
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export const CanonicalHashesSchema = z.object({
|
|
34
|
+
requestHash: z.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
35
|
+
responseHash: z.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export const AuditRecordSchema = z.object({
|
|
39
|
+
version: z.literal("audit.v1"),
|
|
40
|
+
ts: z.number().int().positive(),
|
|
41
|
+
session: z.string().min(1),
|
|
42
|
+
audience: z.string().min(1),
|
|
43
|
+
did: z.string().min(1),
|
|
44
|
+
kid: z.string().min(1),
|
|
45
|
+
reqHash: z.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
46
|
+
resHash: z.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
47
|
+
verified: z.enum(["yes", "no"]),
|
|
48
|
+
scope: z.string().min(1), // "-" for no scope
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Type exports
|
|
52
|
+
export type ProofMeta = z.infer<typeof ProofMetaSchema>;
|
|
53
|
+
export type DetachedProof = z.infer<typeof DetachedProofSchema>;
|
|
54
|
+
export type CanonicalHashes = z.infer<typeof CanonicalHashesSchema>;
|
|
55
|
+
export type AuditRecord = z.infer<typeof AuditRecordSchema>;
|
|
56
|
+
|
|
57
|
+
// Constants
|
|
58
|
+
export const JWS_ALGORITHM = "EdDSA";
|
|
59
|
+
export const HASH_ALGORITHM = "sha256";
|
|
60
|
+
export const AUDIT_VERSION = "audit.v1";
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Tool call context for AgentShield dashboard integration
|
|
64
|
+
* Provides plaintext tool execution data alongside cryptographic proofs
|
|
65
|
+
*/
|
|
66
|
+
export const ToolCallContextSchema = z.object({
|
|
67
|
+
tool: z.string().min(1),
|
|
68
|
+
args: z.record(z.unknown()),
|
|
69
|
+
result: z.unknown().optional(),
|
|
70
|
+
scopeId: z.string(),
|
|
71
|
+
userId: z.string().optional(),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Proof submission context for AgentShield API
|
|
76
|
+
* Includes tool calls and optional MCP server URL for tool discovery
|
|
77
|
+
*/
|
|
78
|
+
export const ProofSubmissionContextSchema = z.object({
|
|
79
|
+
toolCalls: z.array(ToolCallContextSchema),
|
|
80
|
+
mcpServerUrl: z.string().url().optional(),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Complete proof submission request to AgentShield
|
|
85
|
+
*/
|
|
86
|
+
export const ProofSubmissionRequestSchema = z.object({
|
|
87
|
+
session_id: z.string().min(1),
|
|
88
|
+
delegation_id: z.string().nullable().optional(),
|
|
89
|
+
proofs: z.array(z.object({
|
|
90
|
+
jws: z.string().min(1),
|
|
91
|
+
meta: ProofMetaSchema,
|
|
92
|
+
})),
|
|
93
|
+
context: ProofSubmissionContextSchema.optional(),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Type exports
|
|
97
|
+
export type ToolCallContext = z.infer<typeof ToolCallContextSchema>;
|
|
98
|
+
export type ProofSubmissionContext = z.infer<typeof ProofSubmissionContextSchema>;
|
|
99
|
+
export type ProofSubmissionRequest = z.infer<typeof ProofSubmissionRequestSchema>;
|