@kya-os/mcp-i 1.12.2 → 1.12.4

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.
@@ -75,8 +75,34 @@ export declare class IdentityManager {
75
75
  /**
76
76
  * Generate new development identity
77
77
  * Requirements: 4.1, 4.4
78
+ *
79
+ * @param preserveDid - use this DID instead of minting a fresh
80
+ * did:web:localhost:... one. Added for `rotateDevIdentity()`'s benefit
81
+ * (a rotation keeps the agent's DID stable by default); every existing
82
+ * caller passes no argument and is unaffected.
78
83
  */
79
84
  private generateDevIdentity;
85
+ /**
86
+ * Force-generate a fresh Ed25519 keypair and persist it to
87
+ * `devIdentityPath`, unconditionally overwriting whatever identity (if
88
+ * any) is already there. Unlike `ensureIdentity()`, which loads an
89
+ * existing valid identity rather than replacing it, this always mints a
90
+ * new one -- the primitive `mcpi keys rotate` needs.
91
+ *
92
+ * Deliberately reuses this same jose-based, 32-byte-raw-key generation
93
+ * path (rather than a second, independent key generator) so a rotated
94
+ * identity file is always in the one format every other identity path in
95
+ * this codebase already produces and consumes -- `CLIIdentityFileSchema`
96
+ * (`packages/contracts/src/cli.ts`) requires exactly 44 base64 characters
97
+ * (a raw 32-byte key), which the CLI's own `node:crypto`-based DER
98
+ * keypair (PKCS8/SPKI, `utils/identity-manager.ts`'s `generateKeyPair`)
99
+ * does not produce -- confirmed by direct inspection, not assumed.
100
+ *
101
+ * @param preserveDid - reuse this DID instead of minting a fresh one,
102
+ * mirroring `mcpi keys rotate`'s own default of keeping the DID stable
103
+ * across a rotation unless `--new-did` is passed.
104
+ */
105
+ rotateDevIdentity(preserveDid?: string): Promise<AgentIdentity>;
80
106
  /**
81
107
  * Generate multibase-encoded key identifier (z-prefix base58btc)
82
108
  */
@@ -120,8 +120,13 @@ class IdentityManager {
120
120
  /**
121
121
  * Generate new development identity
122
122
  * Requirements: 4.1, 4.4
123
+ *
124
+ * @param preserveDid - use this DID instead of minting a fresh
125
+ * did:web:localhost:... one. Added for `rotateDevIdentity()`'s benefit
126
+ * (a rotation keeps the agent's DID stable by default); every existing
127
+ * caller passes no argument and is unaffected.
123
128
  */
124
- async generateDevIdentity() {
129
+ async generateDevIdentity(preserveDid) {
125
130
  // Generate Ed25519 keypair
126
131
  const keyPair = await (0, jose_1.generateKeyPair)("EdDSA", { crv: "Ed25519" });
127
132
  // Export keys to JWK format
@@ -133,10 +138,11 @@ class IdentityManager {
133
138
  const publicKey = Buffer.from(privateKeyJwk.x, "base64url").toString("base64");
134
139
  // Generate multibase-encoded key ID
135
140
  const kid = this.generateMultibaseKid(publicKey);
136
- // Generate DID (for dev, use localhost)
137
- // Extract a short identifier for the DID path (first 8 chars of hash for readability)
141
+ // Generate DID (for dev, use localhost), unless the caller wants to
142
+ // keep an existing one. Extract a short identifier for the DID path
143
+ // (first 8 chars of hash for readability).
138
144
  const shortId = (0, crypto_1.createHash)("sha256").update(publicKey).digest("hex").substring(0, 8);
139
- const did = `did:web:localhost:3000:agents:${shortId}`;
145
+ const did = preserveDid ?? `did:web:localhost:3000:agents:${shortId}`;
140
146
  const now = new Date().toISOString();
141
147
  const identity = {
142
148
  did,
@@ -149,6 +155,33 @@ class IdentityManager {
149
155
  await this.saveDevIdentity(identity);
150
156
  return identity;
151
157
  }
158
+ /**
159
+ * Force-generate a fresh Ed25519 keypair and persist it to
160
+ * `devIdentityPath`, unconditionally overwriting whatever identity (if
161
+ * any) is already there. Unlike `ensureIdentity()`, which loads an
162
+ * existing valid identity rather than replacing it, this always mints a
163
+ * new one -- the primitive `mcpi keys rotate` needs.
164
+ *
165
+ * Deliberately reuses this same jose-based, 32-byte-raw-key generation
166
+ * path (rather than a second, independent key generator) so a rotated
167
+ * identity file is always in the one format every other identity path in
168
+ * this codebase already produces and consumes -- `CLIIdentityFileSchema`
169
+ * (`packages/contracts/src/cli.ts`) requires exactly 44 base64 characters
170
+ * (a raw 32-byte key), which the CLI's own `node:crypto`-based DER
171
+ * keypair (PKCS8/SPKI, `utils/identity-manager.ts`'s `generateKeyPair`)
172
+ * does not produce -- confirmed by direct inspection, not assumed.
173
+ *
174
+ * @param preserveDid - reuse this DID instead of minting a fresh one,
175
+ * mirroring `mcpi keys rotate`'s own default of keeping the DID stable
176
+ * across a rotation unless `--new-did` is passed.
177
+ */
178
+ async rotateDevIdentity(preserveDid) {
179
+ const identity = await this.generateDevIdentity(preserveDid);
180
+ const rotated = { ...identity, lastRotated: identity.createdAt };
181
+ await this.saveDevIdentity(rotated);
182
+ this.cachedIdentity = rotated;
183
+ return rotated;
184
+ }
152
185
  /**
153
186
  * Generate multibase-encoded key identifier (z-prefix base58btc)
154
187
  */
@@ -14,6 +14,7 @@ exports.createMCPIRuntime = createMCPIRuntime;
14
14
  const mcp_i_runtime_1 = require("@kya-os/mcp-i-runtime");
15
15
  const node_providers_1 = require("../providers/node-providers");
16
16
  const path_1 = __importDefault(require("path"));
17
+ const config_1 = require("@kya-os/contracts/config");
17
18
  /**
18
19
  * Convert legacy config to provider-based config
19
20
  */
@@ -67,8 +68,9 @@ class MCPINodeRuntimeWrapper extends mcp_i_runtime_1.MCPIRuntimeBase {
67
68
  timestampSkewSeconds: coreConfig.session?.timestampSkewSeconds || 120,
68
69
  });
69
70
  // Instantiate AccessControlApiService if API key is available
70
- const apiKey = process.env.AGENTSHIELD_API_KEY;
71
- const apiUrl = process.env.AGENTSHIELD_API_URL || "https://kya.vouched.id";
71
+ const checkpoint = (0, config_1.withCheckpointNames)(process.env);
72
+ const apiKey = checkpoint.CHECKPOINT_API_KEY;
73
+ const apiUrl = checkpoint.CHECKPOINT_API_URL || "https://kya.vouched.id";
72
74
  if (apiKey) {
73
75
  this.accessControlService = new mcp_i_runtime_1.AccessControlApiService({
74
76
  baseUrl: apiUrl,
@@ -47,7 +47,7 @@ export interface MCPIRuntimeConfig {
47
47
  enabled?: boolean;
48
48
  /** Proof batch queue configuration */
49
49
  batchQueue?: {
50
- /** Proof submission destinations (AgentShield, KTA, etc.) */
50
+ /** Proof submission destinations (Checkpoint, KTA, etc.) */
51
51
  destinations?: Array<{
52
52
  /** Destination type */
53
53
  type: "agentshield" | "kta";
@@ -101,7 +101,7 @@ export interface MCPIRuntimeConfig {
101
101
  toolProtections?: ToolProtectionMap;
102
102
  /** Local tool protection file path (default: tool-protections.json) */
103
103
  toolProtectionsFile?: string | false;
104
- /** AgentShield API configuration for fetching tool protections */
104
+ /** Checkpoint API configuration for fetching tool protections */
105
105
  agentShield?: {
106
106
  apiUrl: string;
107
107
  apiKey?: string;
@@ -66,7 +66,7 @@ class MCPIRuntime {
66
66
  this.delegationVerifier = (0, delegation_verifier_1.createDelegationVerifier)(config.delegation.verifier);
67
67
  }
68
68
  // NOTE: Tool protection resolver is created in initialize()
69
- // after identity is loaded, because AgentShield API needs the agent DID
69
+ // after identity is loaded, because Checkpoint API needs the agent DID
70
70
  }
71
71
  /**
72
72
  * Initialize the runtime (async setup)
@@ -176,69 +176,70 @@ class MCPIRuntime {
176
176
  if (!this.cachedIdentity) {
177
177
  throw new Error("Runtime not initialized - call initialize() first");
178
178
  }
179
- // PHASE 1 INTERCEPTOR: Check delegation BEFORE executing tool
180
- if (this.config.delegation?.enabled && options.requiresDelegation) {
181
- if (!this.delegationVerifier) {
182
- throw new Error("Delegation verifier not configured");
183
- }
184
- if (!options.agentDid) {
185
- throw new Error("agentDid required when requiresDelegation=true");
186
- }
187
- const requiredScopes = options.requiredScopes || [];
188
- // Build auth handshake config
189
- const authConfig = {
190
- delegationVerifier: this.delegationVerifier,
191
- resumeTokenStore: this.resumeTokenStore,
192
- kta: this.config.delegation.authorization?.kta,
193
- bouncer: {
194
- authorizationUrl: this.config.delegation.authorization?.authorizationUrl ||
195
- "https://agentshield.example.com/consent",
196
- resumeTokenTtl: this.config.delegation.authorization?.resumeTokenTtl,
197
- minReputationScore: this.config.delegation.authorization?.minReputationScore,
198
- requireAuthForUnknown: this.config.delegation.authorization?.requireAuthForUnknown,
199
- },
200
- debug: this.config.identity?.environment === "development",
201
- };
202
- // Verify delegation or return authorization hints
203
- const verifyResult = await (0, auth_handshake_1.verifyOrHints)(options.agentDid, requiredScopes, authConfig);
204
- // If not authorized, return needs_authorization error
205
- if (!verifyResult.authorized) {
206
- if (!verifyResult.authError) {
207
- throw new Error('Authorization failed but no authError was provided');
208
- }
209
- return verifyResult.authError;
210
- }
211
- // If authorized, capture delegation metadata for outbound header propagation
212
- if (verifyResult.delegation) {
213
- options.delegationRef = verifyResult.delegation.id;
214
- options.delegationChain = (0, mcp_i_core_1.buildChainString)(verifyResult.delegation);
215
- options.delegationScopes = options.requiredScopes ?? [];
216
- const identity = await this.identityManager.ensureIdentity();
217
- (0, request_context_1.setDelegationContextFromIdentity)({
218
- delegationId: options.delegationRef,
219
- delegationChain: options.delegationChain,
220
- delegationScopes: options.delegationScopes,
221
- // JWS-compact DelegationCredential VC from the verified result —
222
- // emitted on KYA-OS-Delegation-Credential when the verifier
223
- // returned a presentable credential.
224
- delegationCredential: verifyResult.credential?.credential_jwt,
225
- identity,
226
- });
227
- }
228
- }
229
- const auditInvocation = this.config.auditTrail
230
- ? await (0, mcp_i_runtime_1.beginRuntimeToolAudit)(this.config.auditTrail, {
231
- toolName: request.method,
232
- args: request.params,
233
- session: {
234
- ...session,
235
- id: session.sessionId,
236
- },
237
- })
238
- : undefined;
239
- // Capture before async boundary — TS narrowing doesn't survive across async callbacks
179
+ // Isolate verification and delegation setup before either can change a caller's
180
+ // context. The tool and proof generation then share this call's own store.
240
181
  const cachedIdentity = this.cachedIdentity;
241
182
  return (0, request_context_1.runWithContext)({ session: session ?? undefined }, async () => {
183
+ // PHASE 1 INTERCEPTOR: Check delegation BEFORE executing tool
184
+ if (this.config.delegation?.enabled && options.requiresDelegation) {
185
+ if (!this.delegationVerifier) {
186
+ throw new Error("Delegation verifier not configured");
187
+ }
188
+ if (!options.agentDid) {
189
+ throw new Error("agentDid required when requiresDelegation=true");
190
+ }
191
+ const requiredScopes = options.requiredScopes || [];
192
+ // Build auth handshake config
193
+ const authConfig = {
194
+ delegationVerifier: this.delegationVerifier,
195
+ resumeTokenStore: this.resumeTokenStore,
196
+ kta: this.config.delegation.authorization?.kta,
197
+ bouncer: {
198
+ authorizationUrl: this.config.delegation.authorization?.authorizationUrl ||
199
+ "https://agentshield.example.com/consent",
200
+ resumeTokenTtl: this.config.delegation.authorization?.resumeTokenTtl,
201
+ minReputationScore: this.config.delegation.authorization?.minReputationScore,
202
+ requireAuthForUnknown: this.config.delegation.authorization?.requireAuthForUnknown,
203
+ },
204
+ debug: this.config.identity?.environment === "development",
205
+ };
206
+ // Verify delegation or return authorization hints
207
+ const verifyResult = await (0, auth_handshake_1.verifyOrHints)(options.agentDid, requiredScopes, authConfig);
208
+ // If not authorized, return needs_authorization error
209
+ if (!verifyResult.authorized) {
210
+ if (!verifyResult.authError) {
211
+ throw new Error('Authorization failed but no authError was provided');
212
+ }
213
+ return verifyResult.authError;
214
+ }
215
+ // If authorized, capture delegation metadata for outbound header propagation
216
+ if (verifyResult.delegation) {
217
+ options.delegationRef = verifyResult.delegation.id;
218
+ options.delegationChain = (0, mcp_i_core_1.buildChainString)(verifyResult.delegation);
219
+ options.delegationScopes = options.requiredScopes ?? [];
220
+ const identity = await this.identityManager.ensureIdentity();
221
+ (0, request_context_1.setDelegationContextFromIdentity)({
222
+ delegationId: options.delegationRef,
223
+ delegationChain: options.delegationChain,
224
+ delegationScopes: options.delegationScopes,
225
+ // JWS-compact DelegationCredential VC from the verified result —
226
+ // emitted on KYA-OS-Delegation-Credential when the verifier
227
+ // returned a presentable credential.
228
+ delegationCredential: verifyResult.credential?.credential_jwt,
229
+ identity,
230
+ });
231
+ }
232
+ }
233
+ const auditInvocation = this.config.auditTrail
234
+ ? await (0, mcp_i_runtime_1.beginRuntimeToolAudit)(this.config.auditTrail, {
235
+ toolName: request.method,
236
+ args: request.params,
237
+ session: {
238
+ ...session,
239
+ id: session.sessionId,
240
+ },
241
+ })
242
+ : undefined;
242
243
  try {
243
244
  // Execute the tool (only if delegation check passed)
244
245
  let data = await toolHandler(request);
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Proof Batch Queue
3
3
  *
4
- * Collects proofs in memory and submits them in batches to KTA and AgentShield.
4
+ * Collects proofs in memory and submits them in batches to KTA and Checkpoint.
5
5
  * This prevents blocking tool execution while ensuring proofs are eventually submitted.
6
6
  *
7
7
  * Performance:
@@ -74,9 +74,9 @@ export declare class KTAProofDestination implements ProofDestination {
74
74
  private submitOne;
75
75
  }
76
76
  /**
77
- * AgentShield proof submission destination
77
+ * Checkpoint proof submission destination
78
78
  *
79
- * Submits proofs to AgentShield's /api/v1/bouncer/proofs endpoint
79
+ * Submits proofs to Checkpoint's /api/v1/bouncer/proofs endpoint
80
80
  * with proper authentication and session grouping.
81
81
  */
82
82
  export declare class AgentShieldProofDestination implements ProofDestination {
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * Proof Batch Queue
4
4
  *
5
- * Collects proofs in memory and submits them in batches to KTA and AgentShield.
5
+ * Collects proofs in memory and submits them in batches to KTA and Checkpoint.
6
6
  * This prevents blocking tool execution while ensuring proofs are eventually submitted.
7
7
  *
8
8
  * Performance:
@@ -121,13 +121,13 @@ class KTAProofDestination {
121
121
  }
122
122
  exports.KTAProofDestination = KTAProofDestination;
123
123
  /**
124
- * AgentShield proof submission destination
124
+ * Checkpoint proof submission destination
125
125
  *
126
- * Submits proofs to AgentShield's /api/v1/bouncer/proofs endpoint
126
+ * Submits proofs to Checkpoint's /api/v1/bouncer/proofs endpoint
127
127
  * with proper authentication and session grouping.
128
128
  */
129
129
  class AgentShieldProofDestination {
130
- name = "AgentShield";
130
+ name = "Checkpoint";
131
131
  apiUrl;
132
132
  apiKey;
133
133
  constructor(apiUrl, apiKey) {
@@ -139,13 +139,13 @@ class AgentShieldProofDestination {
139
139
  return;
140
140
  }
141
141
  const proofs = submissions.map((submission) => submission.proof);
142
- // Extract session_id from first proof for AgentShield session grouping
143
- // AgentShield uses this for analytics and detection monitoring
142
+ // Extract session_id from first proof for Checkpoint session grouping
143
+ // Checkpoint uses this for analytics and detection monitoring
144
144
  const sessionId = proofs[0]?.meta?.sessionId || "unknown";
145
- // AgentShield API format requires delegation_id and session_id wrapper
145
+ // Checkpoint API format requires delegation_id and session_id wrapper
146
146
  const requestBody = {
147
147
  delegation_id: null, // null for proofs without delegation context
148
- session_id: sessionId, // AgentShield session grouping (same as meta.sessionId)
148
+ session_id: sessionId, // Checkpoint session grouping (same as meta.sessionId)
149
149
  proofs: proofs,
150
150
  };
151
151
  const response = await fetch(`${this.apiUrl}/api/v1/bouncer/proofs`, {
@@ -161,7 +161,7 @@ class AgentShieldProofDestination {
161
161
  const errorBody = await response
162
162
  .text()
163
163
  .catch(() => "Unable to read error body");
164
- throw new Error(`AgentShield proof submission failed: ${response.status} ${response.statusText}\n${errorBody}`);
164
+ throw new Error(`Checkpoint proof submission failed: ${response.status} ${response.statusText}\n${errorBody}`);
165
165
  }
166
166
  }
167
167
  }