UNPKG

@kya-os/cli

Version:

CLI for MCP-I setup and management

159 lines 5.26 kB
/** * Identity management utilities for key rotation and archiving */ import { existsSync, mkdirSync, writeFileSync, readFileSync, unlinkSync, } from "fs"; import { join } from "path"; import crypto from "crypto"; /** * Generate a new Ed25519 keypair */ export function generateKeyPair() { // Generate Ed25519 keypair const { privateKey, publicKey } = crypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { type: "pkcs8", format: "der" }, publicKeyEncoding: { type: "spki", format: "der" }, }); // Convert to base64 const privateKeyBase64 = Buffer.from(privateKey).toString("base64"); const publicKeyBase64 = Buffer.from(publicKey).toString("base64"); // Generate key ID (first 8 chars of public key hash) const kid = `key-${crypto.createHash("sha256").update(publicKey).digest("hex").slice(0, 8)}`; return { privateKey: privateKeyBase64, publicKey: publicKeyBase64, kid, }; } /** * Generate a new DID based on the current environment */ export function generateDID(kid) { const isDev = !process.env.AGENT_PRIVATE_KEY; if (isDev) { // Development DID return `did:web:localhost:3000:agents:${kid}`; } else { // Production DID - should be configured via environment const existingDID = process.env.AGENT_DID; if (existingDID) { return existingDID; } throw new Error("AGENT_DID environment variable is required in production"); } } /** * Load current identity from file or environment */ export function loadCurrentIdentity() { const isDev = !process.env.AGENT_PRIVATE_KEY; if (isDev) { // Load from .mcpi/identity.json const identityFile = join(process.cwd(), ".mcpi", "identity.json"); if (!existsSync(identityFile)) { return null; } try { const data = JSON.parse(readFileSync(identityFile, "utf-8")); return data; } catch (error) { console.warn(`Warning: Could not load identity file: ${error}`); return null; } } else { // Load from environment variables const privateKey = process.env.AGENT_PRIVATE_KEY; const kid = process.env.AGENT_KEY_ID; const did = process.env.AGENT_DID; if (!privateKey || !kid || !did) { return null; } // We don't have public key in env, but we can derive it from private key if needed return { version: "1.0", did, kid, privateKey, publicKey: "", // Not available from env createdAt: new Date().toISOString(), lastRotated: new Date().toISOString(), }; } } /** * Archive old key to .mcpi/keys/YYYY-MM-DD.json */ export function archiveOldKey(identity, reason) { const keysDir = join(process.cwd(), ".mcpi", "keys"); if (!existsSync(keysDir)) { mkdirSync(keysDir, { recursive: true }); } // Create archive filename with timestamp const timestamp = new Date().toISOString().split("T")[0]; // YYYY-MM-DD let archiveFile = join(keysDir, `${timestamp}.json`); // If file exists, add a counter let counter = 1; while (existsSync(archiveFile)) { archiveFile = join(keysDir, `${timestamp}-${counter}.json`); counter++; } const archiveData = { did: identity.did, kid: identity.kid, privateKey: identity.privateKey, publicKey: identity.publicKey, archivedAt: new Date().toISOString(), reason: reason || "Key rotation", originalCreatedAt: identity.createdAt, originalLastRotated: identity.lastRotated, }; writeFileSync(archiveFile, JSON.stringify(archiveData, null, 2)); return archiveFile; } /** * Generate new identity with optional DID preservation */ export function generateNewIdentity(preserveDID) { const { privateKey, publicKey, kid } = generateKeyPair(); const did = preserveDID || generateDID(kid); const now = new Date().toISOString(); return { version: "1.0", did, kid, privateKey, publicKey, createdAt: now, lastRotated: now, }; } /** * Save identity to development file */ export function saveIdentityToDev(identity) { const mcpiDir = join(process.cwd(), ".mcpi"); if (!existsSync(mcpiDir)) { mkdirSync(mcpiDir, { recursive: true }); } const identityFile = join(mcpiDir, "identity.json"); writeFileSync(identityFile, JSON.stringify(identity, null, 2)); } /** * Clear development identity file */ export function clearDevIdentity() { const identityFile = join(process.cwd(), ".mcpi", "identity.json"); if (existsSync(identityFile)) { unlinkSync(identityFile); } } /** * Generate audit line for key rotation */ export function generateAuditLine(oldKeyId, newKeyId, did, mode, delegated, forced) { const ts = Math.floor(Date.now() / 1000); return `keys.rotate.v1 ts=${ts} did=${did} oldKid=${oldKeyId} newKid=${newKeyId} mode=${mode} delegated=${delegated ? "yes" : "no"} force=${forced ? "yes" : "no"}`; } //# sourceMappingURL=identity-manager.js.map