@kya-os/mcp-i 0.1.0 → 1.2.0

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.
Files changed (229) hide show
  1. package/README.md +406 -71
  2. package/dist/149.js +1 -0
  3. package/dist/189.js +1 -0
  4. package/dist/261.js +1 -0
  5. package/dist/28.js +1 -0
  6. package/dist/295.js +1 -0
  7. package/dist/460.js +1 -0
  8. package/dist/570.js +1 -0
  9. package/dist/634.js +1 -0
  10. package/dist/647.js +1 -0
  11. package/dist/67.js +1 -0
  12. package/dist/739.js +1 -0
  13. package/dist/742.js +1 -0
  14. package/dist/904.js +1 -0
  15. package/dist/938.js +1 -0
  16. package/dist/auth/api-key.d.ts +16 -0
  17. package/dist/auth/api-key.js +82 -0
  18. package/dist/auth/jwt.d.ts +43 -0
  19. package/dist/auth/jwt.js +51 -0
  20. package/dist/auth/oauth/factory.d.ts +12 -0
  21. package/dist/auth/oauth/factory.js +36 -0
  22. package/dist/auth/oauth/index.d.ts +5 -0
  23. package/dist/auth/oauth/index.js +27 -0
  24. package/dist/auth/oauth/providers/proxy-provider.d.ts +13 -0
  25. package/dist/auth/oauth/providers/proxy-provider.js +159 -0
  26. package/dist/auth/oauth/router.d.ts +4 -0
  27. package/dist/auth/oauth/router.js +294 -0
  28. package/dist/auth/oauth/storage/memory-storage.d.ts +12 -0
  29. package/dist/auth/oauth/storage/memory-storage.js +40 -0
  30. package/dist/auth/oauth/types.d.ts +112 -0
  31. package/dist/auth/oauth/types.js +2 -0
  32. package/dist/cache/__tests__/cloudflare-kv-nonce-cache.test.d.ts +4 -0
  33. package/dist/cache/__tests__/cloudflare-kv-nonce-cache.test.js +176 -0
  34. package/dist/cache/__tests__/concurrency.test.d.ts +5 -0
  35. package/dist/cache/__tests__/concurrency.test.js +300 -0
  36. package/dist/cache/__tests__/dynamodb-nonce-cache.test.d.ts +4 -0
  37. package/dist/cache/__tests__/dynamodb-nonce-cache.test.js +176 -0
  38. package/dist/cache/__tests__/memory-nonce-cache.test.d.ts +4 -0
  39. package/dist/cache/__tests__/memory-nonce-cache.test.js +132 -0
  40. package/dist/cache/__tests__/nonce-cache-factory-simple.test.d.ts +4 -0
  41. package/dist/cache/__tests__/nonce-cache-factory-simple.test.js +133 -0
  42. package/dist/cache/__tests__/nonce-cache-factory.test.d.ts +4 -0
  43. package/dist/cache/__tests__/nonce-cache-factory.test.js +252 -0
  44. package/dist/cache/__tests__/redis-nonce-cache.test.d.ts +4 -0
  45. package/dist/cache/__tests__/redis-nonce-cache.test.js +95 -0
  46. package/dist/cache/cloudflare-kv-nonce-cache.d.ts +14 -0
  47. package/dist/cache/cloudflare-kv-nonce-cache.js +93 -0
  48. package/dist/cache/dynamodb-nonce-cache.d.ts +15 -0
  49. package/dist/cache/dynamodb-nonce-cache.js +92 -0
  50. package/dist/cache/index.d.ts +16 -0
  51. package/dist/cache/index.js +32 -0
  52. package/dist/cache/memory-nonce-cache.d.ts +44 -0
  53. package/dist/cache/memory-nonce-cache.js +105 -0
  54. package/dist/cache/nonce-cache-factory.d.ts +20 -0
  55. package/dist/cache/nonce-cache-factory.js +208 -0
  56. package/dist/cache/redis-nonce-cache.d.ts +14 -0
  57. package/dist/cache/redis-nonce-cache.js +53 -0
  58. package/dist/compiler/compiler-context.d.ts +23 -0
  59. package/dist/compiler/compiler-context.js +24 -0
  60. package/dist/compiler/config/constants.d.ts +41 -0
  61. package/dist/compiler/config/constants.js +45 -0
  62. package/dist/compiler/config/index.d.ts +252 -0
  63. package/dist/compiler/config/index.js +15 -0
  64. package/dist/compiler/config/injection.d.ts +26 -0
  65. package/dist/compiler/config/injection.js +58 -0
  66. package/dist/compiler/config/schemas/experimental/index.d.ts +91 -0
  67. package/dist/compiler/config/schemas/experimental/index.js +16 -0
  68. package/dist/compiler/config/schemas/experimental/oauth.d.ts +74 -0
  69. package/dist/compiler/config/schemas/experimental/oauth.js +25 -0
  70. package/dist/compiler/config/schemas/index.d.ts +6 -0
  71. package/dist/compiler/config/schemas/index.js +17 -0
  72. package/dist/compiler/config/schemas/paths.d.ts +9 -0
  73. package/dist/compiler/config/schemas/paths.js +12 -0
  74. package/dist/compiler/config/schemas/transport/http.d.ts +82 -0
  75. package/dist/compiler/config/schemas/transport/http.js +33 -0
  76. package/dist/compiler/config/schemas/transport/stdio.d.ts +9 -0
  77. package/dist/compiler/config/schemas/transport/stdio.js +15 -0
  78. package/dist/compiler/config/schemas/webpack.d.ts +3 -0
  79. package/dist/compiler/config/schemas/webpack.js +15 -0
  80. package/dist/compiler/config/types.d.ts +1 -0
  81. package/dist/compiler/config/types.js +2 -0
  82. package/dist/compiler/config/utils.d.ts +20 -0
  83. package/dist/compiler/config/utils.js +36 -0
  84. package/dist/compiler/generate-env-code.d.ts +1 -0
  85. package/dist/compiler/generate-env-code.js +8 -0
  86. package/dist/compiler/generate-import-code.d.ts +1 -0
  87. package/dist/compiler/generate-import-code.js +24 -0
  88. package/dist/compiler/get-webpack-config/get-entries.d.ts +3 -0
  89. package/dist/compiler/get-webpack-config/get-entries.js +29 -0
  90. package/dist/compiler/get-webpack-config/get-externals.d.ts +7 -0
  91. package/dist/compiler/get-webpack-config/get-externals.js +88 -0
  92. package/dist/compiler/get-webpack-config/get-injected-variables.d.ts +8 -0
  93. package/dist/compiler/get-webpack-config/get-injected-variables.js +25 -0
  94. package/dist/compiler/get-webpack-config/index.d.ts +4 -0
  95. package/dist/compiler/get-webpack-config/index.js +101 -0
  96. package/dist/compiler/get-webpack-config/plugins.d.ts +8 -0
  97. package/dist/compiler/get-webpack-config/plugins.js +132 -0
  98. package/dist/compiler/get-webpack-config/resolve-tsconfig-paths.d.ts +9 -0
  99. package/dist/compiler/get-webpack-config/resolve-tsconfig-paths.js +40 -0
  100. package/dist/compiler/index.d.ts +6 -0
  101. package/dist/compiler/index.js +194 -0
  102. package/dist/compiler/on-first-build.d.ts +3 -0
  103. package/dist/compiler/on-first-build.js +58 -0
  104. package/dist/compiler/parse-xmcp-config.d.ts +9 -0
  105. package/dist/compiler/parse-xmcp-config.js +155 -0
  106. package/dist/compiler/start-http-server.d.ts +1 -0
  107. package/dist/compiler/start-http-server.js +34 -0
  108. package/dist/index.d.ts +12 -54
  109. package/dist/index.js +22 -190
  110. package/dist/index.js.LICENSE.txt +49 -0
  111. package/dist/runtime/__tests__/audit.test.d.ts +4 -0
  112. package/dist/runtime/__tests__/audit.test.js +328 -0
  113. package/dist/runtime/__tests__/identity.test.d.ts +4 -0
  114. package/dist/runtime/__tests__/identity.test.js +164 -0
  115. package/dist/runtime/__tests__/mcpi-runtime.test.d.ts +4 -0
  116. package/dist/runtime/__tests__/mcpi-runtime.test.js +372 -0
  117. package/dist/runtime/__tests__/proof.test.d.ts +4 -0
  118. package/dist/runtime/__tests__/proof.test.js +302 -0
  119. package/dist/runtime/__tests__/session.test.d.ts +4 -0
  120. package/dist/runtime/__tests__/session.test.js +254 -0
  121. package/dist/runtime/__tests__/well-known.test.d.ts +4 -0
  122. package/dist/runtime/__tests__/well-known.test.js +312 -0
  123. package/dist/runtime/adapter-express.js +2 -0
  124. package/dist/runtime/adapter-express.js.LICENSE.txt +252 -0
  125. package/dist/runtime/adapter-nextjs.js +2 -0
  126. package/dist/runtime/adapter-nextjs.js.LICENSE.txt +53 -0
  127. package/dist/runtime/adapters/express/index.d.ts +2 -0
  128. package/dist/runtime/adapters/express/index.js +48 -0
  129. package/dist/runtime/adapters/nextjs/index.d.ts +8 -0
  130. package/dist/runtime/adapters/nextjs/index.js +18 -0
  131. package/dist/runtime/audit.d.ts +93 -0
  132. package/dist/runtime/audit.js +212 -0
  133. package/dist/runtime/debug.d.ts +118 -0
  134. package/dist/runtime/debug.js +612 -0
  135. package/dist/runtime/delegation-hooks.d.ts +85 -0
  136. package/dist/runtime/delegation-hooks.js +116 -0
  137. package/dist/runtime/demo.d.ts +71 -0
  138. package/dist/runtime/demo.js +135 -0
  139. package/dist/runtime/headers.d.ts +1 -0
  140. package/dist/runtime/headers.js +9 -0
  141. package/dist/runtime/http.js +2 -0
  142. package/dist/runtime/http.js.LICENSE.txt +252 -0
  143. package/dist/runtime/identity.d.ts +105 -0
  144. package/dist/runtime/identity.js +232 -0
  145. package/dist/runtime/index.d.ts +16 -0
  146. package/dist/runtime/index.js +56 -0
  147. package/dist/runtime/mcpi-runtime.d.ts +164 -0
  148. package/dist/runtime/mcpi-runtime.js +352 -0
  149. package/dist/runtime/proof.d.ts +87 -0
  150. package/dist/runtime/proof.js +223 -0
  151. package/dist/runtime/session.d.ts +88 -0
  152. package/dist/runtime/session.js +216 -0
  153. package/dist/runtime/stdio.js +2 -0
  154. package/dist/runtime/stdio.js.LICENSE.txt +1 -0
  155. package/dist/runtime/templates/home.d.ts +2 -0
  156. package/dist/runtime/templates/home.js +50 -0
  157. package/dist/runtime/transports/http/base-streamable-http.d.ts +25 -0
  158. package/dist/runtime/transports/http/base-streamable-http.js +16 -0
  159. package/dist/runtime/transports/http/http-context.d.ts +9 -0
  160. package/dist/runtime/transports/http/http-context.js +8 -0
  161. package/dist/runtime/transports/http/index.d.ts +1 -0
  162. package/dist/runtime/transports/http/index.js +55 -0
  163. package/dist/runtime/transports/http/setup-cors.d.ts +4 -0
  164. package/dist/runtime/transports/http/setup-cors.js +24 -0
  165. package/dist/runtime/transports/http/stateless-streamable-http.d.ts +39 -0
  166. package/dist/runtime/transports/http/stateless-streamable-http.js +331 -0
  167. package/dist/runtime/transports/stdio/index.d.ts +1 -0
  168. package/dist/runtime/transports/stdio/index.js +51 -0
  169. package/dist/runtime/utils/server.d.ts +42 -0
  170. package/dist/runtime/utils/server.js +39 -0
  171. package/dist/runtime/utils/tools.d.ts +8 -0
  172. package/dist/runtime/utils/tools.js +115 -0
  173. package/dist/runtime/verifier-middleware.d.ts +76 -0
  174. package/dist/runtime/verifier-middleware.js +322 -0
  175. package/dist/runtime/well-known.d.ts +151 -0
  176. package/dist/runtime/well-known.js +258 -0
  177. package/dist/storage/config.d.ts +28 -0
  178. package/dist/storage/config.js +79 -0
  179. package/dist/storage/delegation.d.ts +59 -0
  180. package/dist/storage/delegation.js +130 -0
  181. package/dist/storage/merkle-verifier.d.ts +84 -0
  182. package/dist/storage/merkle-verifier.js +261 -0
  183. package/dist/test/__tests__/nonce-cache-integration.test.d.ts +1 -0
  184. package/dist/test/__tests__/nonce-cache-integration.test.js +116 -0
  185. package/dist/test/__tests__/nonce-cache.test.d.ts +1 -0
  186. package/dist/test/__tests__/nonce-cache.test.js +122 -0
  187. package/dist/test/__tests__/runtime-integration.test.d.ts +4 -0
  188. package/dist/test/__tests__/runtime-integration.test.js +192 -0
  189. package/dist/test/__tests__/test-infrastructure.test.d.ts +4 -0
  190. package/dist/test/__tests__/test-infrastructure.test.js +178 -0
  191. package/dist/test/deterministic-keys.d.ts +31 -0
  192. package/dist/test/deterministic-keys.js +108 -0
  193. package/dist/test/examples/test-usage-example.d.ts +140 -0
  194. package/dist/test/examples/test-usage-example.js +175 -0
  195. package/dist/test/index.d.ts +11 -0
  196. package/dist/test/index.js +27 -0
  197. package/dist/test/local-verification.d.ts +28 -0
  198. package/dist/test/local-verification.js +342 -0
  199. package/dist/test/mock-identity-provider.d.ts +96 -0
  200. package/dist/test/mock-identity-provider.js +243 -0
  201. package/dist/test/runtime-integration.d.ts +63 -0
  202. package/dist/test/runtime-integration.js +140 -0
  203. package/dist/test/test-environment.d.ts +26 -0
  204. package/dist/test/test-environment.js +50 -0
  205. package/dist/types/declarations.d.ts +1 -0
  206. package/dist/types/declarations.js +6 -0
  207. package/dist/types/middleware.d.ts +2 -0
  208. package/dist/types/middleware.js +2 -0
  209. package/dist/types/tool.d.ts +80 -0
  210. package/dist/types/tool.js +2 -0
  211. package/dist/utils/cli-icons.d.ts +3 -0
  212. package/dist/utils/cli-icons.js +7 -0
  213. package/dist/utils/constants.d.ts +6 -0
  214. package/dist/utils/constants.js +13 -0
  215. package/dist/utils/context.d.ts +33 -0
  216. package/dist/utils/context.js +58 -0
  217. package/dist/utils/file-watcher.d.ts +19 -0
  218. package/dist/utils/file-watcher.js +49 -0
  219. package/dist/utils/fs-utils.d.ts +2 -0
  220. package/dist/utils/fs-utils.js +22 -0
  221. package/dist/utils/path-validation.d.ts +3 -0
  222. package/dist/utils/path-validation.js +56 -0
  223. package/dist/utils/spawn-process.d.ts +9 -0
  224. package/dist/utils/spawn-process.js +50 -0
  225. package/dist/utils/subscribable.d.ts +12 -0
  226. package/dist/utils/subscribable.js +44 -0
  227. package/package.json +99 -21
  228. package/dist/index.d.ts.map +0 -1
  229. package/dist/index.js.map +0 -1
@@ -0,0 +1,223 @@
1
+ "use strict";
2
+ /**
3
+ * Detached Proof Generation for XMCP-I Runtime
4
+ *
5
+ * Handles JCS canonicalization, SHA-256 digest generation, and Ed25519 detached JWS signing
6
+ * according to requirements 5.1, 5.2, 5.3, 5.6.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.ProofGenerator = void 0;
10
+ exports.createProofResponse = createProofResponse;
11
+ exports.extractCanonicalData = extractCanonicalData;
12
+ const crypto_1 = require("crypto");
13
+ const jose_1 = require("jose");
14
+ /**
15
+ * Proof generator class
16
+ */
17
+ class ProofGenerator {
18
+ identity;
19
+ constructor(identity) {
20
+ this.identity = identity;
21
+ }
22
+ /**
23
+ * Generate detached proof for tool request/response
24
+ * Requirements: 5.1, 5.2, 5.3, 5.6
25
+ */
26
+ async generateProof(request, response, session, options = {}) {
27
+ // Generate canonical hashes
28
+ const hashes = this.generateCanonicalHashes(request, response);
29
+ // Create proof metadata
30
+ const meta = {
31
+ did: this.identity.did,
32
+ kid: this.identity.keyId,
33
+ ts: Math.floor(Date.now() / 1000),
34
+ nonce: session.nonce,
35
+ audience: session.audience,
36
+ sessionId: session.sessionId,
37
+ requestHash: hashes.requestHash,
38
+ responseHash: hashes.responseHash,
39
+ ...options, // Include scopeId and delegationRef if provided
40
+ };
41
+ // Generate detached JWS
42
+ const jws = await this.generateDetachedJWS(meta);
43
+ return {
44
+ jws,
45
+ meta,
46
+ };
47
+ }
48
+ /**
49
+ * Generate canonical hashes for request and response
50
+ * Requirement: 5.1
51
+ */
52
+ generateCanonicalHashes(request, response) {
53
+ // Canonicalize request (exclude transport metadata, include method and params)
54
+ const canonicalRequest = {
55
+ method: request.method,
56
+ ...(request.params && { params: request.params }),
57
+ };
58
+ // Canonicalize response (only the data part, exclude meta)
59
+ const canonicalResponse = response.data;
60
+ // Generate SHA-256 hashes with JCS canonicalization
61
+ const requestHash = this.generateSHA256Hash(canonicalRequest);
62
+ const responseHash = this.generateSHA256Hash(canonicalResponse);
63
+ return {
64
+ requestHash,
65
+ responseHash,
66
+ };
67
+ }
68
+ /**
69
+ * Generate SHA-256 hash with JCS canonicalization
70
+ * Requirement: 5.2
71
+ */
72
+ generateSHA256Hash(data) {
73
+ // JCS (JSON Canonicalization Scheme) canonicalization
74
+ const canonicalJson = this.canonicalizeJSON(data);
75
+ // Generate SHA-256 hash
76
+ const hash = (0, crypto_1.createHash)("sha256")
77
+ .update(canonicalJson, "utf8")
78
+ .digest("hex");
79
+ return `sha256:${hash}`;
80
+ }
81
+ /**
82
+ * JCS canonicalization implementation
83
+ * This is a simplified implementation - in production, use a proper JCS library
84
+ */
85
+ canonicalizeJSON(obj) {
86
+ if (obj === null)
87
+ return "null";
88
+ if (typeof obj === "boolean")
89
+ return obj.toString();
90
+ if (typeof obj === "number") {
91
+ // Handle special number cases
92
+ if (Number.isNaN(obj))
93
+ return "null";
94
+ if (!Number.isFinite(obj))
95
+ return "null";
96
+ return obj.toString();
97
+ }
98
+ if (typeof obj === "string")
99
+ return JSON.stringify(obj);
100
+ if (Array.isArray(obj)) {
101
+ const items = obj.map((item) => this.canonicalizeJSON(item));
102
+ return `[${items.join(",")}]`;
103
+ }
104
+ if (typeof obj === "object") {
105
+ // Sort keys for canonical ordering
106
+ const sortedKeys = Object.keys(obj).sort();
107
+ const pairs = sortedKeys.map((key) => {
108
+ const value = this.canonicalizeJSON(obj[key]);
109
+ return `${JSON.stringify(key)}:${value}`;
110
+ });
111
+ return `{${pairs.join(",")}}`;
112
+ }
113
+ // Fallback for other types
114
+ return JSON.stringify(obj);
115
+ }
116
+ /**
117
+ * Generate Ed25519 detached JWS (compact format)
118
+ * Requirement: 5.3
119
+ */
120
+ async generateDetachedJWS(meta) {
121
+ try {
122
+ // Import the private key
123
+ const privateKeyPem = this.formatPrivateKeyAsPEM(this.identity.privateKey);
124
+ const privateKey = await (0, jose_1.importPKCS8)(privateKeyPem, "EdDSA");
125
+ // Create JWT with proof metadata as payload
126
+ const jwt = await new jose_1.SignJWT(meta)
127
+ .setProtectedHeader({
128
+ alg: "EdDSA",
129
+ kid: this.identity.keyId,
130
+ })
131
+ .sign(privateKey);
132
+ // For detached JWS, we remove the payload part (middle section)
133
+ const [header, , signature] = jwt.split(".");
134
+ return `${header}..${signature}`;
135
+ }
136
+ catch (error) {
137
+ throw new Error(`Failed to generate detached JWS: ${error instanceof Error ? error.message : "Unknown error"}`);
138
+ }
139
+ }
140
+ /**
141
+ * Format base64 private key as PEM for JOSE library
142
+ */
143
+ formatPrivateKeyAsPEM(base64PrivateKey) {
144
+ // For Ed25519, we need to format as PKCS#8 PEM
145
+ // This is a simplified implementation - in production, use proper key formatting
146
+ const keyData = Buffer.from(base64PrivateKey, "base64");
147
+ // Ed25519 PKCS#8 header and footer
148
+ const header = "-----BEGIN PRIVATE KEY-----\n";
149
+ const footer = "\n-----END PRIVATE KEY-----";
150
+ // For Ed25519, we need to wrap the raw key in PKCS#8 structure
151
+ // This is a simplified approach - in production, use proper ASN.1 encoding
152
+ const pkcs8Header = Buffer.from([
153
+ 0x30,
154
+ 0x2e, // SEQUENCE, length 46
155
+ 0x02,
156
+ 0x01,
157
+ 0x00, // INTEGER version 0
158
+ 0x30,
159
+ 0x05, // SEQUENCE, length 5
160
+ 0x06,
161
+ 0x03,
162
+ 0x2b,
163
+ 0x65,
164
+ 0x70, // OID for Ed25519
165
+ 0x04,
166
+ 0x22, // OCTET STRING, length 34
167
+ 0x04,
168
+ 0x20, // OCTET STRING, length 32 (the actual key)
169
+ ]);
170
+ const fullKey = Buffer.concat([pkcs8Header, keyData.subarray(0, 32)]);
171
+ const base64Key = fullKey.toString("base64");
172
+ // Format as PEM with line breaks every 64 characters
173
+ const formattedKey = base64Key.match(/.{1,64}/g)?.join("\n") || base64Key;
174
+ return header + formattedKey + footer;
175
+ }
176
+ /**
177
+ * Verify a detached proof (for testing/validation)
178
+ */
179
+ async verifyProof(proof, request, response) {
180
+ try {
181
+ // Regenerate hashes
182
+ const expectedHashes = this.generateCanonicalHashes(request, response);
183
+ // Check if hashes match
184
+ if (proof.meta.requestHash !== expectedHashes.requestHash ||
185
+ proof.meta.responseHash !== expectedHashes.responseHash) {
186
+ return false;
187
+ }
188
+ // TODO: Verify JWS signature (requires public key)
189
+ // For now, just validate the structure
190
+ const jwsParts = proof.jws.split(".");
191
+ return jwsParts.length === 3 && jwsParts[1] === ""; // Detached format
192
+ }
193
+ catch {
194
+ return false;
195
+ }
196
+ }
197
+ }
198
+ exports.ProofGenerator = ProofGenerator;
199
+ /**
200
+ * Utility functions
201
+ */
202
+ /**
203
+ * Create a tool response with proof
204
+ */
205
+ async function createProofResponse(request, data, identity, session, options = {}) {
206
+ const response = { data };
207
+ const proofGenerator = new ProofGenerator(identity);
208
+ const proof = await proofGenerator.generateProof(request, response, session, options);
209
+ response.meta = { proof };
210
+ return response;
211
+ }
212
+ /**
213
+ * Extract canonical data for hashing (utility for testing)
214
+ */
215
+ function extractCanonicalData(request, response) {
216
+ return {
217
+ request: {
218
+ method: request.method,
219
+ ...(request.params && { params: request.params }),
220
+ },
221
+ response: response.data,
222
+ };
223
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Handshake and Session Management for XMCP-I Runtime
3
+ *
4
+ * Handles handshake enforcement, session management, and nonce validation
5
+ * according to requirements 4.5-4.9 and 19.1-19.2.
6
+ */
7
+ import { HandshakeRequest, SessionContext, NonceCache } from "@kya-os/contracts/handshake";
8
+ /**
9
+ * Session management configuration
10
+ */
11
+ export interface SessionConfig {
12
+ timestampSkewSeconds?: number;
13
+ sessionTtlMinutes?: number;
14
+ absoluteSessionLifetime?: number;
15
+ nonceCache?: NonceCache;
16
+ }
17
+ /**
18
+ * Handshake validation result
19
+ */
20
+ export interface HandshakeResult {
21
+ success: boolean;
22
+ session?: SessionContext;
23
+ error?: {
24
+ code: string;
25
+ message: string;
26
+ remediation?: string;
27
+ };
28
+ }
29
+ /**
30
+ * Session manager class
31
+ */
32
+ export declare class SessionManager {
33
+ private config;
34
+ private sessions;
35
+ constructor(config?: SessionConfig);
36
+ /**
37
+ * Validate handshake and create or retrieve session
38
+ * Requirements: 4.5, 4.6, 4.7, 4.8, 4.9
39
+ */
40
+ validateHandshake(request: HandshakeRequest): Promise<HandshakeResult>;
41
+ /**
42
+ * Get session by ID and update last activity
43
+ */
44
+ getSession(sessionId: string): Promise<SessionContext | null>;
45
+ /**
46
+ * Generate a unique session ID
47
+ */
48
+ private generateSessionId;
49
+ /**
50
+ * Generate a cryptographically secure nonce
51
+ */
52
+ static generateNonce(): string;
53
+ /**
54
+ * Cleanup expired sessions and nonces
55
+ */
56
+ cleanup(): Promise<void>;
57
+ /**
58
+ * Get session statistics
59
+ */
60
+ getStats(): {
61
+ activeSessions: number;
62
+ config: {
63
+ timestampSkewSeconds: number;
64
+ sessionTtlMinutes: number;
65
+ absoluteSessionLifetime?: number;
66
+ cacheType: string;
67
+ };
68
+ };
69
+ /**
70
+ * Clear all sessions (useful for testing)
71
+ */
72
+ clearSessions(): void;
73
+ }
74
+ /**
75
+ * Default session manager instance
76
+ */
77
+ export declare const defaultSessionManager: SessionManager;
78
+ /**
79
+ * Utility functions
80
+ */
81
+ /**
82
+ * Create a handshake request
83
+ */
84
+ export declare function createHandshakeRequest(audience: string): HandshakeRequest;
85
+ /**
86
+ * Validate handshake request format
87
+ */
88
+ export declare function validateHandshakeFormat(request: any): request is HandshakeRequest;
@@ -0,0 +1,216 @@
1
+ "use strict";
2
+ /**
3
+ * Handshake and Session Management for XMCP-I Runtime
4
+ *
5
+ * Handles handshake enforcement, session management, and nonce validation
6
+ * according to requirements 4.5-4.9 and 19.1-19.2.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.defaultSessionManager = exports.SessionManager = void 0;
10
+ exports.createHandshakeRequest = createHandshakeRequest;
11
+ exports.validateHandshakeFormat = validateHandshakeFormat;
12
+ const crypto_1 = require("crypto");
13
+ const memory_nonce_cache_1 = require("../cache/memory-nonce-cache");
14
+ /**
15
+ * Session manager class
16
+ */
17
+ class SessionManager {
18
+ config;
19
+ sessions = new Map();
20
+ constructor(config = {}) {
21
+ this.config = {
22
+ timestampSkewSeconds: parseInt(process.env.XMCP_I_TS_SKEW_SEC || "120"),
23
+ sessionTtlMinutes: parseInt(process.env.XMCP_I_SESSION_TTL_MIN || "30"),
24
+ // absoluteSessionLifetime: undefined, // Disabled by default (omit to use undefined)
25
+ nonceCache: new memory_nonce_cache_1.MemoryNonceCache(),
26
+ ...config,
27
+ };
28
+ // Warn about multi-instance deployments with memory cache
29
+ if (this.config.nonceCache instanceof memory_nonce_cache_1.MemoryNonceCache) {
30
+ console.warn("Warning: Using MemoryNonceCache - not suitable for multi-instance deployments. " +
31
+ "Consider using Redis, DynamoDB, or Cloudflare KV for production.");
32
+ }
33
+ }
34
+ /**
35
+ * Validate handshake and create or retrieve session
36
+ * Requirements: 4.5, 4.6, 4.7, 4.8, 4.9
37
+ */
38
+ async validateHandshake(request) {
39
+ try {
40
+ // Validate timestamp (±120s clock skew by default)
41
+ const now = Math.floor(Date.now() / 1000);
42
+ const timeDiff = Math.abs(now - request.timestamp);
43
+ if (timeDiff > this.config.timestampSkewSeconds) {
44
+ return {
45
+ success: false,
46
+ error: {
47
+ code: "XMCP_I_EHANDSHAKE",
48
+ message: `Timestamp outside acceptable range (±${this.config.timestampSkewSeconds}s)`,
49
+ remediation: `Check NTP sync on client and server. Current server time: ${now}, received: ${request.timestamp}, diff: ${timeDiff}s. Adjust XMCP_I_TS_SKEW_SEC if needed.`,
50
+ },
51
+ };
52
+ }
53
+ // Validate nonce (must be unique within session window)
54
+ const nonceExists = await this.config.nonceCache.has(request.nonce);
55
+ if (nonceExists) {
56
+ return {
57
+ success: false,
58
+ error: {
59
+ code: "XMCP_I_EHANDSHAKE",
60
+ message: "Nonce already used (replay attack prevention)",
61
+ remediation: "Generate a new unique nonce for each request",
62
+ },
63
+ };
64
+ }
65
+ // Add nonce to cache with TTL >= session TTL
66
+ const nonceTtlSeconds = this.config.sessionTtlMinutes * 60 + 60; // Session TTL + 1 minute buffer
67
+ await this.config.nonceCache.add(request.nonce, nonceTtlSeconds);
68
+ // Generate session ID
69
+ const sessionId = this.generateSessionId();
70
+ // Create session context
71
+ const session = {
72
+ sessionId,
73
+ audience: request.audience,
74
+ nonce: request.nonce,
75
+ timestamp: request.timestamp,
76
+ createdAt: now,
77
+ lastActivity: now,
78
+ ttlMinutes: this.config.sessionTtlMinutes,
79
+ };
80
+ // Store session
81
+ this.sessions.set(sessionId, session);
82
+ return {
83
+ success: true,
84
+ session,
85
+ };
86
+ }
87
+ catch (error) {
88
+ return {
89
+ success: false,
90
+ error: {
91
+ code: "XMCP_I_EHANDSHAKE",
92
+ message: `Handshake validation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
93
+ },
94
+ };
95
+ }
96
+ }
97
+ /**
98
+ * Get session by ID and update last activity
99
+ */
100
+ async getSession(sessionId) {
101
+ const session = this.sessions.get(sessionId);
102
+ if (!session) {
103
+ return null;
104
+ }
105
+ const now = Math.floor(Date.now() / 1000);
106
+ // Check if session has expired (idle timeout)
107
+ const idleTimeSeconds = now - session.lastActivity;
108
+ const maxIdleSeconds = session.ttlMinutes * 60;
109
+ if (idleTimeSeconds > maxIdleSeconds) {
110
+ this.sessions.delete(sessionId);
111
+ return null;
112
+ }
113
+ // Check absolute session lifetime if configured
114
+ if (this.config.absoluteSessionLifetime) {
115
+ const sessionAgeSeconds = now - session.createdAt;
116
+ const maxAgeSeconds = this.config.absoluteSessionLifetime * 60;
117
+ if (sessionAgeSeconds > maxAgeSeconds) {
118
+ this.sessions.delete(sessionId);
119
+ return null;
120
+ }
121
+ }
122
+ // Update last activity
123
+ session.lastActivity = now;
124
+ this.sessions.set(sessionId, session);
125
+ return session;
126
+ }
127
+ /**
128
+ * Generate a unique session ID
129
+ */
130
+ generateSessionId() {
131
+ const timestamp = Date.now().toString(36);
132
+ const random = (0, crypto_1.randomBytes)(8).toString("hex");
133
+ return `sess_${timestamp}_${random}`;
134
+ }
135
+ /**
136
+ * Generate a cryptographically secure nonce
137
+ */
138
+ static generateNonce() {
139
+ return (0, crypto_1.randomBytes)(16).toString("base64url"); // 128-bit nonce
140
+ }
141
+ /**
142
+ * Cleanup expired sessions and nonces
143
+ */
144
+ async cleanup() {
145
+ const now = Math.floor(Date.now() / 1000);
146
+ // Clean up expired sessions
147
+ for (const [sessionId, session] of this.sessions.entries()) {
148
+ const idleTimeSeconds = now - session.lastActivity;
149
+ const maxIdleSeconds = session.ttlMinutes * 60;
150
+ let expired = idleTimeSeconds > maxIdleSeconds;
151
+ // Check absolute lifetime
152
+ if (!expired && this.config.absoluteSessionLifetime) {
153
+ const sessionAgeSeconds = now - session.createdAt;
154
+ const maxAgeSeconds = this.config.absoluteSessionLifetime * 60;
155
+ expired = sessionAgeSeconds > maxAgeSeconds;
156
+ }
157
+ if (expired) {
158
+ this.sessions.delete(sessionId);
159
+ }
160
+ }
161
+ // Clean up expired nonces
162
+ await this.config.nonceCache.cleanup();
163
+ }
164
+ /**
165
+ * Get session statistics
166
+ */
167
+ getStats() {
168
+ return {
169
+ activeSessions: this.sessions.size,
170
+ config: {
171
+ timestampSkewSeconds: this.config.timestampSkewSeconds,
172
+ sessionTtlMinutes: this.config.sessionTtlMinutes,
173
+ absoluteSessionLifetime: this.config.absoluteSessionLifetime,
174
+ cacheType: this.config.nonceCache.constructor.name,
175
+ },
176
+ };
177
+ }
178
+ /**
179
+ * Clear all sessions (useful for testing)
180
+ */
181
+ clearSessions() {
182
+ this.sessions.clear();
183
+ }
184
+ }
185
+ exports.SessionManager = SessionManager;
186
+ /**
187
+ * Default session manager instance
188
+ */
189
+ exports.defaultSessionManager = new SessionManager();
190
+ /**
191
+ * Utility functions
192
+ */
193
+ /**
194
+ * Create a handshake request
195
+ */
196
+ function createHandshakeRequest(audience) {
197
+ return {
198
+ nonce: SessionManager.generateNonce(),
199
+ audience,
200
+ timestamp: Math.floor(Date.now() / 1000),
201
+ };
202
+ }
203
+ /**
204
+ * Validate handshake request format
205
+ */
206
+ function validateHandshakeFormat(request) {
207
+ return (typeof request === "object" &&
208
+ request !== null &&
209
+ typeof request.nonce === "string" &&
210
+ request.nonce.length > 0 &&
211
+ typeof request.audience === "string" &&
212
+ request.audience.length > 0 &&
213
+ typeof request.timestamp === "number" &&
214
+ request.timestamp > 0 &&
215
+ Number.isInteger(request.timestamp));
216
+ }