@happyvertical/secrets 0.86.4 → 0.88.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.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/shared/envelope.ts","../src/shared/errors.ts","../src/adapters/database.ts","../src/shared/factory.ts","../src/index.ts"],"sourcesContent":["import * as crypto from 'node:crypto';\nimport type { EncryptedEnvelope } from './types.js';\n\n/**\n * Core envelope encryption primitives using AES-256-GCM\n *\n * This class provides the cryptographic operations for envelope encryption:\n * - Generate data encryption keys (DEKs)\n * - Wrap/unwrap DEKs with a master key\n * - Encrypt/decrypt data with DEKs\n *\n * @example\n * ```typescript\n * // Generate a new data key\n * const dataKey = EnvelopeEncryption.generateDataKey();\n *\n * // Wrap the data key with an AMK\n * const wrapped = EnvelopeEncryption.wrapKey(dataKey, amk);\n *\n * // Encrypt data with the data key\n * const encrypted = EnvelopeEncryption.encryptData('secret', dataKey);\n *\n * // Later: unwrap and decrypt\n * const unwrappedKey = EnvelopeEncryption.unwrapKey(wrapped.wrappedKey, wrapped.iv, wrapped.authTag, amk);\n * const plaintext = EnvelopeEncryption.decryptData(encrypted.ciphertext, encrypted.iv, encrypted.authTag, unwrappedKey);\n * ```\n */\nexport class EnvelopeEncryption {\n private static readonly ALGORITHM = 'aes-256-gcm' as const;\n private static readonly KEY_LENGTH = 32; // 256 bits\n private static readonly IV_LENGTH = 12; // 96 bits for GCM (recommended)\n private static readonly AUTH_TAG_LENGTH = 16; // 128 bits\n\n /**\n * Generate a new random data encryption key\n *\n * @returns A 32-byte (256-bit) random key\n */\n static generateDataKey(): Buffer {\n return crypto.randomBytes(EnvelopeEncryption.KEY_LENGTH);\n }\n\n /**\n * Generate a random IV for encryption\n *\n * @returns A 12-byte (96-bit) random IV\n */\n static generateIV(): Buffer {\n return crypto.randomBytes(EnvelopeEncryption.IV_LENGTH);\n }\n\n /**\n * Wrap (encrypt) a data encryption key with a master key\n *\n * @param dataKey - The data key to wrap (32 bytes)\n * @param masterKey - The master key to wrap with (32 bytes)\n * @returns Object containing wrapped key, IV, and auth tag (all base64-encoded)\n */\n static wrapKey(\n dataKey: Buffer,\n masterKey: Buffer,\n ): { wrappedKey: string; iv: string; authTag: string } {\n EnvelopeEncryption.validateKeyLength(dataKey);\n EnvelopeEncryption.validateKeyLength(masterKey);\n const iv = EnvelopeEncryption.generateIV();\n const cipher = crypto.createCipheriv(\n EnvelopeEncryption.ALGORITHM,\n masterKey,\n iv,\n );\n\n const encrypted = Buffer.concat([cipher.update(dataKey), cipher.final()]);\n const authTag = cipher.getAuthTag();\n\n return {\n wrappedKey: encrypted.toString('base64'),\n iv: iv.toString('base64'),\n authTag: authTag.toString('base64'),\n };\n }\n\n /**\n * Unwrap (decrypt) a data encryption key with a master key\n *\n * @param wrappedKey - Base64-encoded wrapped key\n * @param iv - Base64-encoded IV\n * @param authTag - Base64-encoded authentication tag\n * @param masterKey - The master key to unwrap with (32 bytes)\n * @returns The unwrapped data key (32 bytes)\n * @throws Error if decryption fails (invalid key, tampered data, etc.)\n */\n static unwrapKey(\n wrappedKey: string,\n iv: string,\n authTag: string,\n masterKey: Buffer,\n ): Buffer {\n EnvelopeEncryption.validateKeyLength(masterKey);\n const decipher = crypto.createDecipheriv(\n EnvelopeEncryption.ALGORITHM,\n masterKey,\n Buffer.from(iv, 'base64'),\n );\n decipher.setAuthTag(Buffer.from(authTag, 'base64'));\n\n return Buffer.concat([\n decipher.update(Buffer.from(wrappedKey, 'base64')),\n decipher.final(),\n ]);\n }\n\n /**\n * Encrypt data with a data encryption key\n *\n * @param plaintext - The plaintext string to encrypt\n * @param dataKey - The data encryption key (32 bytes)\n * @returns Object containing ciphertext, IV, and auth tag (all base64-encoded)\n */\n static encryptData(\n plaintext: string,\n dataKey: Buffer,\n ): { ciphertext: string; iv: string; authTag: string } {\n EnvelopeEncryption.validateKeyLength(dataKey);\n const iv = EnvelopeEncryption.generateIV();\n const cipher = crypto.createCipheriv(\n EnvelopeEncryption.ALGORITHM,\n dataKey,\n iv,\n );\n\n const encrypted = Buffer.concat([\n cipher.update(plaintext, 'utf8'),\n cipher.final(),\n ]);\n const authTag = cipher.getAuthTag();\n\n return {\n ciphertext: encrypted.toString('base64'),\n iv: iv.toString('base64'),\n authTag: authTag.toString('base64'),\n };\n }\n\n /**\n * Decrypt data with a data encryption key\n *\n * @param ciphertext - Base64-encoded ciphertext\n * @param iv - Base64-encoded IV\n * @param authTag - Base64-encoded authentication tag\n * @param dataKey - The data encryption key (32 bytes)\n * @returns The decrypted plaintext string\n * @throws Error if decryption fails (invalid key, tampered data, etc.)\n */\n static decryptData(\n ciphertext: string,\n iv: string,\n authTag: string,\n dataKey: Buffer,\n ): string {\n EnvelopeEncryption.validateKeyLength(dataKey);\n const decipher = crypto.createDecipheriv(\n EnvelopeEncryption.ALGORITHM,\n dataKey,\n Buffer.from(iv, 'base64'),\n );\n decipher.setAuthTag(Buffer.from(authTag, 'base64'));\n\n return Buffer.concat([\n decipher.update(Buffer.from(ciphertext, 'base64')),\n decipher.final(),\n ]).toString('utf8');\n }\n\n /**\n * Create a full encrypted envelope\n *\n * This combines key wrapping and data encryption into a single envelope\n * that can be stored and later decrypted.\n *\n * @param plaintext - The plaintext string to encrypt\n * @param dataKey - The data encryption key (32 bytes)\n * @param wrappedKeyInfo - The wrapped key info (from wrapKey)\n * @param amkKeyId - The ID of the AMK used to wrap the key\n * @param metadata - Optional metadata to include in the envelope\n * @returns A complete encrypted envelope\n */\n static createEnvelope(\n plaintext: string,\n dataKey: Buffer,\n wrappedKeyInfo: { wrappedKey: string; iv: string; authTag: string },\n amkKeyId: string,\n metadata?: Record<string, string>,\n ): EncryptedEnvelope {\n const encrypted = EnvelopeEncryption.encryptData(plaintext, dataKey);\n\n // Combine wrapped key info into a single string: wrappedKey:iv:authTag\n const combinedWrappedKey = `${wrappedKeyInfo.wrappedKey}:${wrappedKeyInfo.iv}:${wrappedKeyInfo.authTag}`;\n\n return {\n version: 1,\n algorithm: 'aes-256-gcm',\n wrappedKey: combinedWrappedKey,\n amkKeyId,\n iv: encrypted.iv,\n authTag: encrypted.authTag,\n ciphertext: encrypted.ciphertext,\n createdAt: new Date().toISOString(),\n metadata,\n };\n }\n\n /**\n * Parse a combined wrapped key string\n *\n * @param combinedWrappedKey - The combined wrapped key string (wrappedKey:iv:authTag)\n * @returns Object with wrappedKey, iv, and authTag\n */\n static parseWrappedKey(combinedWrappedKey: string): {\n wrappedKey: string;\n iv: string;\n authTag: string;\n } {\n const parts = combinedWrappedKey.split(':');\n if (parts.length !== 3) {\n throw new Error(\n 'Invalid wrapped key format: expected wrappedKey:iv:authTag',\n );\n }\n const [wrappedKey, iv, authTag] = parts;\n // Validate that all parts are non-empty\n if (!wrappedKey || !iv || !authTag) {\n throw new Error(\n 'Invalid wrapped key format: wrappedKey, iv, and authTag must all be non-empty',\n );\n }\n return { wrappedKey, iv, authTag };\n }\n\n /**\n * Decrypt a full envelope\n *\n * @param envelope - The encrypted envelope\n * @param masterKey - The master key to unwrap the data key\n * @returns The decrypted plaintext string\n */\n static decryptEnvelope(\n envelope: EncryptedEnvelope,\n masterKey: Buffer,\n ): string {\n // Parse the wrapped key\n const {\n wrappedKey,\n iv: keyIv,\n authTag: keyAuthTag,\n } = EnvelopeEncryption.parseWrappedKey(envelope.wrappedKey);\n\n // Unwrap the data key\n const dataKey = EnvelopeEncryption.unwrapKey(\n wrappedKey,\n keyIv,\n keyAuthTag,\n masterKey,\n );\n\n // Decrypt the data\n return EnvelopeEncryption.decryptData(\n envelope.ciphertext,\n envelope.iv,\n envelope.authTag,\n dataKey,\n );\n }\n\n /**\n * Validate that a key is the correct length\n *\n * @param key - The key to validate\n * @param expectedLength - Expected length in bytes (default: 32)\n * @throws Error if key length is invalid\n */\n static validateKeyLength(\n key: Buffer,\n expectedLength: number = EnvelopeEncryption.KEY_LENGTH,\n ): void {\n if (key.length !== expectedLength) {\n throw new Error(\n `Invalid key length: expected ${expectedLength} bytes, got ${key.length} bytes`,\n );\n }\n }\n\n /**\n * Parse a hex-encoded key from string\n *\n * @param hexKey - Hex-encoded key string (64 chars for 32 bytes)\n * @returns The key as a Buffer\n * @throws Error if the hex string is invalid\n */\n static parseHexKey(hexKey: string): Buffer {\n if (!/^[0-9a-fA-F]+$/.test(hexKey)) {\n throw new Error('Invalid hex key: contains non-hex characters');\n }\n\n const key = Buffer.from(hexKey, 'hex');\n EnvelopeEncryption.validateKeyLength(key);\n\n return key;\n }\n}\n","/**\n * Base error class for secret operations\n */\nexport class SecretError extends Error {\n readonly code: string;\n readonly adapterType?: string;\n readonly cause?: Error;\n\n constructor(\n message: string,\n code: string,\n options?: { adapterType?: string; cause?: Error },\n ) {\n super(message);\n this.name = 'SecretError';\n this.code = code;\n this.adapterType = options?.adapterType;\n this.cause = options?.cause;\n }\n}\n\n/**\n * Error thrown when a key is not found\n */\nexport class KeyNotFoundError extends SecretError {\n constructor(keyId: string, adapterType?: string) {\n super(`Key not found: ${keyId}`, 'KEY_NOT_FOUND', { adapterType });\n this.name = 'KeyNotFoundError';\n }\n}\n\n/**\n * Error thrown when decryption fails\n */\nexport class DecryptionError extends SecretError {\n constructor(message: string, adapterType?: string, cause?: Error) {\n super(message, 'DECRYPTION_FAILED', { adapterType, cause });\n this.name = 'DecryptionError';\n }\n}\n\n/**\n * Error thrown when encryption fails\n */\nexport class EncryptionError extends SecretError {\n constructor(message: string, adapterType?: string, cause?: Error) {\n super(message, 'ENCRYPTION_FAILED', { adapterType, cause });\n this.name = 'EncryptionError';\n }\n}\n\n/**\n * Error thrown when key rotation fails\n */\nexport class KeyRotationError extends SecretError {\n constructor(message: string, adapterType?: string, cause?: Error) {\n super(message, 'KEY_ROTATION_FAILED', { adapterType, cause });\n this.name = 'KeyRotationError';\n }\n}\n\n/**\n * Error thrown when a tenant's encryption key is missing\n */\nexport class TenantKeyMissingError extends SecretError {\n constructor(tenantId: string) {\n super(\n `No active encryption key for tenant: ${tenantId}`,\n 'TENANT_KEY_MISSING',\n );\n this.name = 'TenantKeyMissingError';\n }\n}\n\n/**\n * Error thrown when the Application Master Key is unavailable\n */\nexport class AMKUnavailableError extends SecretError {\n constructor(message: string) {\n super(message, 'AMK_UNAVAILABLE');\n this.name = 'AMKUnavailableError';\n }\n}\n\n/**\n * Error thrown when the store is not initialized\n */\nexport class StoreNotInitializedError extends SecretError {\n constructor(adapterType?: string) {\n super('Secret store not initialized', 'STORE_NOT_INITIALIZED', {\n adapterType,\n });\n this.name = 'StoreNotInitializedError';\n }\n}\n\n/**\n * Error thrown when an invalid key format is encountered\n */\nexport class InvalidKeyFormatError extends SecretError {\n constructor(message: string, adapterType?: string) {\n super(message, 'INVALID_KEY_FORMAT', { adapterType });\n this.name = 'InvalidKeyFormatError';\n }\n}\n","import { type DatabaseInterface, syncSchema } from '@happyvertical/sql';\nimport { createId } from '@happyvertical/utils';\nimport { EnvelopeEncryption } from '../shared/envelope.js';\nimport {\n AMKUnavailableError,\n DecryptionError,\n EncryptionError,\n KeyRotationError,\n StoreNotInitializedError,\n TenantKeyMissingError,\n} from '../shared/errors.js';\nimport type {\n ApplicationMasterKey,\n DatabaseSecretStoreOptions,\n DecryptedSecret,\n EncryptedEnvelope,\n EncryptOptions,\n SecretStore,\n SecretStoreEvent,\n SecretStoreEventListener,\n TenantDataEncryptionKey,\n Unsubscribe,\n} from '../shared/types.js';\n\n/**\n * Default rotation period in milliseconds (90 days)\n */\nconst DEFAULT_ROTATION_PERIOD_MS = 90 * 24 * 60 * 60 * 1000;\n\n/**\n * Database-backed secret store\n *\n * Uses envelope encryption with AES-256-GCM:\n * - Application Master Key (AMK) from environment variable\n * - Per-tenant Data Encryption Keys (TDEKs) wrapped by AMK\n * - Secrets encrypted by unwrapped TDEKs\n *\n * @example\n * ```typescript\n * const store = new DatabaseSecretStore({\n * type: 'database',\n * db,\n * amk: {\n * provider: 'env',\n * keyEnvVar: 'SECRET_MASTER_KEY',\n * keyId: 'amk-v1'\n * }\n * });\n *\n * await store.initialize();\n *\n * // Encrypt a secret for a tenant\n * const envelope = await store.encrypt('tenant-123', 'api-key', 'sk_live_xxx');\n *\n * // Decrypt\n * const { value } = await store.decrypt('tenant-123', envelope);\n * ```\n */\nexport class DatabaseSecretStore implements SecretStore {\n private db: DatabaseInterface;\n private keysTable: string;\n private amkConfig: DatabaseSecretStoreOptions['amk'];\n private cachedAmk: Buffer | null = null;\n private initialized = false;\n private listeners: SecretStoreEventListener[] = [];\n\n constructor(options: DatabaseSecretStoreOptions) {\n this.db = options.db;\n\n // Validate table name to prevent SQL injection via identifier interpolation\n const tableName = options.keysTable ?? 'tenant_encryption_keys';\n if (!/^[A-Za-z0-9_]+$/.test(tableName)) {\n throw new Error(\n `Invalid keysTable name \"${tableName}\". Table name must contain only alphanumeric characters and underscores.`,\n );\n }\n this.keysTable = tableName;\n\n this.amkConfig = options.amk;\n }\n\n /**\n * Initialize the store (create schema if needed)\n */\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n // Create the tenant keys table\n const schema = `\n CREATE TABLE IF NOT EXISTS \"${this.keysTable}\" (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n wrapped_key TEXT NOT NULL,\n amk_key_id TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'active',\n version INTEGER NOT NULL DEFAULT 1,\n rotate_after TEXT,\n retired_at TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS \"idx_${this.keysTable}_tenant_status\"\n ON \"${this.keysTable}\" (tenant_id, status);\n\n CREATE INDEX IF NOT EXISTS \"idx_${this.keysTable}_rotate_after\"\n ON \"${this.keysTable}\" (rotate_after)\n WHERE status = 'active';\n `;\n\n await syncSchema({ db: this.db, schema });\n\n this.initialized = true;\n }\n\n /**\n * Get the Application Master Key from environment\n */\n private getAMK(): Buffer {\n if (this.cachedAmk) {\n return this.cachedAmk;\n }\n\n const keyHex = process.env[this.amkConfig.keyEnvVar];\n if (!keyHex) {\n throw new AMKUnavailableError(\n `Application Master Key not found in environment variable: ${this.amkConfig.keyEnvVar}`,\n );\n }\n\n try {\n const key = EnvelopeEncryption.parseHexKey(keyHex);\n this.cachedAmk = key;\n return key;\n } catch (error) {\n throw new AMKUnavailableError(\n `Invalid AMK in ${this.amkConfig.keyEnvVar}: ${(error as Error).message}`,\n );\n }\n }\n\n /**\n * Ensure the store is initialized\n */\n private ensureInitialized(): void {\n if (!this.initialized) {\n throw new StoreNotInitializedError('database');\n }\n }\n\n /**\n * Emit an event to all listeners\n */\n private async emitEvent(event: SecretStoreEvent): Promise<void> {\n for (const listener of this.listeners) {\n await listener(event);\n }\n }\n\n /**\n * Subscribe to store events\n */\n subscribe(listener: SecretStoreEventListener): Unsubscribe {\n this.listeners.push(listener);\n return () => {\n const index = this.listeners.indexOf(listener);\n if (index !== -1) {\n this.listeners.splice(index, 1);\n }\n };\n }\n\n async encrypt(\n tenantId: string,\n secretName: string,\n plaintext: string,\n options?: EncryptOptions,\n ): Promise<EncryptedEnvelope> {\n this.ensureInitialized();\n\n // Input validation\n if (typeof tenantId !== 'string' || tenantId.trim().length === 0) {\n throw new EncryptionError(\n 'Invalid tenantId provided for encryption',\n 'database',\n );\n }\n if (typeof secretName !== 'string' || secretName.trim().length === 0) {\n throw new EncryptionError(\n 'Invalid secretName provided for encryption',\n 'database',\n );\n }\n\n let dataKey: Buffer | null = null;\n try {\n // Get or create tenant's data encryption key\n let tenantKey = await this.getTenantKey(tenantId);\n if (!tenantKey) {\n tenantKey = await this.createTenantKey(tenantId);\n }\n\n // Get AMK and unwrap the TDEK\n const amk = this.getAMK();\n const {\n wrappedKey,\n iv: keyIv,\n authTag: keyAuthTag,\n } = EnvelopeEncryption.parseWrappedKey(tenantKey.wrappedKey);\n dataKey = EnvelopeEncryption.unwrapKey(\n wrappedKey,\n keyIv,\n keyAuthTag,\n amk,\n );\n\n // Create the envelope using the unwrapped TDEK\n const envelope = EnvelopeEncryption.createEnvelope(\n plaintext,\n dataKey,\n { wrappedKey, iv: keyIv, authTag: keyAuthTag },\n tenantKey.amkKeyId,\n options?.metadata,\n );\n\n // Emit event\n await this.emitEvent({\n type: 'secret.encrypted',\n tenantId,\n keyId: tenantKey.keyId,\n secretName,\n timestamp: new Date(),\n });\n\n return envelope;\n } catch (error) {\n if (\n error instanceof AMKUnavailableError ||\n error instanceof TenantKeyMissingError\n ) {\n throw error;\n }\n throw new EncryptionError(\n `Failed to encrypt secret '${secretName}' for tenant ${tenantId}: ${(error as Error).message}`,\n 'database',\n error instanceof Error ? error : undefined,\n );\n } finally {\n // Zero out the data key buffer to prevent key material from remaining in memory\n if (dataKey && Buffer.isBuffer(dataKey)) {\n dataKey.fill(0);\n }\n }\n }\n\n async decrypt(\n tenantId: string,\n envelope: EncryptedEnvelope,\n ): Promise<DecryptedSecret> {\n this.ensureInitialized();\n\n // Input validation\n if (typeof tenantId !== 'string' || tenantId.trim().length === 0) {\n throw new DecryptionError(\n 'Invalid tenantId provided for decryption',\n 'database',\n );\n }\n\n // Get all key versions for this tenant (including retired keys for decryption)\n const tenantKeys = await this.listKeyVersions(tenantId);\n if (tenantKeys.length === 0) {\n throw new TenantKeyMissingError(tenantId);\n }\n\n // Verify that the envelope's wrapped key belongs to this tenant\n // Extract the wrapped key portion from the envelope (format: wrappedKey:iv:authTag)\n const envelopeWrappedKeyParts = envelope.wrappedKey.split(':');\n const envelopeWrappedKey = envelopeWrappedKeyParts[0];\n\n const matchingKey = tenantKeys.find((key) => {\n const keyParts = key.wrappedKey.split(':');\n return keyParts[0] === envelopeWrappedKey;\n });\n\n if (!matchingKey) {\n throw new DecryptionError(\n `Envelope was not encrypted with a key belonging to tenant ${tenantId}`,\n 'database',\n );\n }\n\n try {\n // Get AMK and decrypt the envelope\n const amk = this.getAMK();\n const plaintext = EnvelopeEncryption.decryptEnvelope(envelope, amk);\n\n // Emit event\n await this.emitEvent({\n type: 'secret.decrypted',\n tenantId,\n keyId: matchingKey.keyId,\n timestamp: new Date(),\n });\n\n return {\n value: plaintext,\n keyId: matchingKey.keyId,\n createdAt: new Date(envelope.createdAt),\n metadata: envelope.metadata,\n };\n } catch (error) {\n if (error instanceof AMKUnavailableError) {\n throw error;\n }\n throw new DecryptionError(\n `Failed to decrypt secret for tenant ${tenantId}: ${(error as Error).message}`,\n 'database',\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n async getTenantKey(\n tenantId: string,\n ): Promise<TenantDataEncryptionKey | null> {\n this.ensureInitialized();\n\n const row = await this.db.get(this.keysTable, {\n tenant_id: tenantId,\n status: 'active',\n });\n\n if (!row) return null;\n\n return this.parseKeyRow(row as Record<string, unknown>);\n }\n\n async createTenantKey(tenantId: string): Promise<TenantDataEncryptionKey> {\n this.ensureInitialized();\n\n // Generate and wrap a new data key\n const amk = this.getAMK();\n const dataKey = EnvelopeEncryption.generateDataKey();\n const wrapped = EnvelopeEncryption.wrapKey(dataKey, amk);\n\n const keyId = createId();\n const wrappedKeyStr = `${wrapped.wrappedKey}:${wrapped.iv}:${wrapped.authTag}`;\n const now = new Date();\n const rotateAfter = new Date(now.getTime() + DEFAULT_ROTATION_PERIOD_MS);\n\n await this.db.insert(this.keysTable, {\n id: keyId,\n tenant_id: tenantId,\n wrapped_key: wrappedKeyStr,\n amk_key_id: this.amkConfig.keyId,\n status: 'active',\n version: 1,\n rotate_after: rotateAfter.toISOString(),\n created_at: now.toISOString(),\n updated_at: now.toISOString(),\n });\n\n // Emit event\n await this.emitEvent({\n type: 'key.created',\n tenantId,\n keyId,\n timestamp: now,\n });\n\n return {\n keyId,\n tenantId,\n wrappedKey: wrappedKeyStr,\n amkKeyId: this.amkConfig.keyId,\n status: 'active',\n version: 1,\n createdAt: now,\n rotateAfter,\n };\n }\n\n async rotateTenantKey(tenantId: string): Promise<TenantDataEncryptionKey> {\n this.ensureInitialized();\n\n const currentKey = await this.getTenantKey(tenantId);\n if (!currentKey) {\n throw new TenantKeyMissingError(tenantId);\n }\n\n const now = new Date();\n\n try {\n // Mark current key as rotating\n await this.db.update(\n this.keysTable,\n { id: currentKey.keyId },\n { status: 'rotating', updated_at: now.toISOString() },\n );\n\n // Create new key with incremented version\n const amk = this.getAMK();\n const dataKey = EnvelopeEncryption.generateDataKey();\n const wrapped = EnvelopeEncryption.wrapKey(dataKey, amk);\n\n const newKeyId = createId();\n const wrappedKeyStr = `${wrapped.wrappedKey}:${wrapped.iv}:${wrapped.authTag}`;\n const rotateAfter = new Date(now.getTime() + DEFAULT_ROTATION_PERIOD_MS);\n\n await this.db.insert(this.keysTable, {\n id: newKeyId,\n tenant_id: tenantId,\n wrapped_key: wrappedKeyStr,\n amk_key_id: this.amkConfig.keyId,\n status: 'active',\n version: currentKey.version + 1,\n rotate_after: rotateAfter.toISOString(),\n created_at: now.toISOString(),\n updated_at: now.toISOString(),\n });\n\n // Mark old key as retired\n await this.db.update(\n this.keysTable,\n { id: currentKey.keyId },\n {\n status: 'retired',\n retired_at: now.toISOString(),\n updated_at: now.toISOString(),\n },\n );\n\n // Emit event\n await this.emitEvent({\n type: 'key.rotated',\n tenantId,\n keyId: newKeyId,\n timestamp: now,\n });\n\n return {\n keyId: newKeyId,\n tenantId,\n wrappedKey: wrappedKeyStr,\n amkKeyId: this.amkConfig.keyId,\n status: 'active',\n version: currentKey.version + 1,\n createdAt: now,\n rotateAfter,\n };\n } catch (error) {\n throw new KeyRotationError(\n `Failed to rotate key for tenant ${tenantId}: ${(error as Error).message}`,\n 'database',\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n async retireTenantKey(tenantId: string, keyId: string): Promise<void> {\n this.ensureInitialized();\n\n const now = new Date();\n\n await this.db.update(\n this.keysTable,\n { id: keyId, tenant_id: tenantId },\n {\n status: 'retired',\n retired_at: now.toISOString(),\n updated_at: now.toISOString(),\n },\n );\n\n // Emit event\n await this.emitEvent({\n type: 'key.retired',\n tenantId,\n keyId,\n timestamp: now,\n });\n }\n\n async getActiveAMK(): Promise<ApplicationMasterKey> {\n // Validate that the AMK is accessible\n this.getAMK();\n\n return {\n keyId: this.amkConfig.keyId,\n description: `AMK from env var ${this.amkConfig.keyEnvVar}`,\n status: 'active',\n provider: 'env',\n keyReference: this.amkConfig.keyEnvVar,\n createdAt: new Date(),\n };\n }\n\n async rewrapTenantKey(\n _tenantId: string,\n _newAmkKeyId: string,\n ): Promise<TenantDataEncryptionKey> {\n // For env-based AMK, this would require:\n // 1. Having both old and new AMK available\n // 2. Unwrapping with old AMK\n // 3. Rewrapping with new AMK\n // This is complex and risky for env-based keys\n throw new Error(\n 'AMK rotation not supported for env-based keys. ' +\n 'Use AWS KMS, Vault, or Azure Key Vault for AMK rotation support.',\n );\n }\n\n async listKeyVersions(tenantId: string): Promise<TenantDataEncryptionKey[]> {\n this.ensureInitialized();\n\n const { rows } = await this.db.query(\n `SELECT * FROM \"${this.keysTable}\" WHERE tenant_id = ? ORDER BY version DESC`,\n tenantId,\n );\n\n return rows.map((row: Record<string, unknown>) => this.parseKeyRow(row));\n }\n\n /**\n * Parse a database row into a TenantDataEncryptionKey\n */\n private parseKeyRow(row: Record<string, unknown>): TenantDataEncryptionKey {\n return {\n keyId: row.id as string,\n tenantId: row.tenant_id as string,\n wrappedKey: row.wrapped_key as string,\n amkKeyId: row.amk_key_id as string,\n status: row.status as TenantDataEncryptionKey['status'],\n version: row.version as number,\n createdAt: new Date(row.created_at as string),\n rotateAfter: row.rotate_after\n ? new Date(row.rotate_after as string)\n : undefined,\n retiredAt: row.retired_at\n ? new Date(row.retired_at as string)\n : undefined,\n };\n }\n}\n\nexport default DatabaseSecretStore;\n","import type {\n AWSKMSSecretStoreOptions,\n AzureKeyVaultSecretStoreOptions,\n DatabaseSecretStoreOptions,\n GetSecretStoreOptions,\n SecretStore,\n VaultSecretStoreOptions,\n} from './types.js';\n\n/**\n * Type guard for database options\n */\nexport function isDatabaseOptions(\n opts: GetSecretStoreOptions,\n): opts is DatabaseSecretStoreOptions {\n return opts.type === 'database';\n}\n\n/**\n * Type guard for AWS KMS options\n */\nexport function isAWSKMSOptions(\n opts: GetSecretStoreOptions,\n): opts is AWSKMSSecretStoreOptions {\n return opts.type === 'aws-kms';\n}\n\n/**\n * Type guard for Vault options\n */\nexport function isVaultOptions(\n opts: GetSecretStoreOptions,\n): opts is VaultSecretStoreOptions {\n return opts.type === 'vault';\n}\n\n/**\n * Type guard for Azure Key Vault options\n */\nexport function isAzureKeyVaultOptions(\n opts: GetSecretStoreOptions,\n): opts is AzureKeyVaultSecretStoreOptions {\n return opts.type === 'azure-keyvault';\n}\n\n/**\n * Factory function to create secret store instances\n *\n * Uses lazy imports to avoid loading unnecessary adapters and their dependencies.\n *\n * @example\n * ```typescript\n * // Database-backed store with env-based AMK\n * const store = await getSecretStore({\n * type: 'database',\n * db,\n * amk: {\n * provider: 'env',\n * keyEnvVar: 'SECRET_MASTER_KEY',\n * keyId: 'amk-v1'\n * }\n * });\n *\n * // Encrypt a secret\n * const envelope = await store.encrypt('tenant-123', 'api-key', 'sk_live_xxx');\n *\n * // Decrypt\n * const decrypted = await store.decrypt('tenant-123', envelope);\n * ```\n */\nexport async function getSecretStore(\n options: GetSecretStoreOptions,\n): Promise<SecretStore> {\n if (isDatabaseOptions(options)) {\n const { DatabaseSecretStore } = await import('../adapters/database.js');\n const store = new DatabaseSecretStore(options);\n await store.initialize();\n return store;\n }\n\n if (isAWSKMSOptions(options)) {\n // Phase 2: AWS KMS adapter\n throw new Error(\n 'AWS KMS adapter not yet implemented. Coming in a future release.',\n );\n }\n\n if (isVaultOptions(options)) {\n // Phase 2: HashiCorp Vault adapter\n throw new Error(\n 'HashiCorp Vault adapter not yet implemented. Coming in a future release.',\n );\n }\n\n if (isAzureKeyVaultOptions(options)) {\n // Phase 2: Azure Key Vault adapter\n throw new Error(\n 'Azure Key Vault adapter not yet implemented. Coming in a future release.',\n );\n }\n\n throw new Error(\n `Unsupported secret store type: ${(options as { type: string }).type}`,\n );\n}\n","/**\n * @happyvertical/secrets\n *\n * Envelope encryption for per-tenant secret management with pluggable backends.\n *\n * @example\n * ```typescript\n * import { getSecretStore } from '@happyvertical/secrets';\n * import { getDatabase } from '@happyvertical/sql';\n *\n * // Get a database connection\n * const db = await getDatabase({ type: 'sqlite', url: ':memory:' });\n *\n * // Create a secret store\n * const store = await getSecretStore({\n * type: 'database',\n * db,\n * amk: {\n * provider: 'env',\n * keyEnvVar: 'SECRET_MASTER_KEY', // 64 hex chars (32 bytes)\n * keyId: 'amk-v1'\n * }\n * });\n *\n * // Encrypt a secret for a tenant\n * const envelope = await store.encrypt('tenant-123', 'api-key', 'sk_live_xxx');\n *\n * // Decrypt the secret\n * const { value } = await store.decrypt('tenant-123', envelope);\n * console.log(value); // 'sk_live_xxx'\n *\n * // Rotate tenant's encryption key\n * await store.rotateTenantKey('tenant-123');\n * ```\n *\n * @packageDocumentation\n */\n\n// Direct adapter exports (for advanced usage)\nexport { DatabaseSecretStore } from './adapters/database.js';\n// Envelope encryption primitives\nexport { EnvelopeEncryption } from './shared/envelope.js';\n// Error classes\nexport {\n AMKUnavailableError,\n DecryptionError,\n EncryptionError,\n InvalidKeyFormatError,\n KeyNotFoundError,\n KeyRotationError,\n SecretError,\n StoreNotInitializedError,\n TenantKeyMissingError,\n} from './shared/errors.js';\n\n// Factory function\nexport {\n getSecretStore,\n isAWSKMSOptions,\n isAzureKeyVaultOptions,\n isDatabaseOptions,\n isVaultOptions,\n} from './shared/factory.js';\n// Core types\nexport type {\n // Adapter options\n AMKConfig,\n ApplicationMasterKey,\n AWSKMSSecretStoreOptions,\n AzureKeyVaultSecretStoreOptions,\n DatabaseSecretStoreOptions,\n DecryptedSecret,\n EncryptedEnvelope,\n EncryptOptions,\n GetSecretStoreOptions,\n SecretAdapterType,\n SecretStore,\n SecretStoreEvent,\n SecretStoreEventListener,\n SecretStoreEventType,\n TenantDataEncryptionKey,\n Unsubscribe,\n VaultSecretStoreOptions,\n} from './shared/types.js';\n\n/** @internal */\nexport const PACKAGE_VERSION_INITIALIZED = true;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["#bytes","#destroyed","#privateKey","#receipts","#events","#issuer","#verifier","#ledger","#attestor","#finalizer","#sink","#ephemeralTtlMs","#ephemeralRevokeRetryMs","#orphanGraceMs","#finalizationTakeoverMs","#now","#onBackgroundError","#leaseInvalidators","#pendingTimers","#findReceipt","#event","#createLease","#revokeReceipt","#scheduleRetirement","#abortAndRevoke","#rollback","#scheduleRollback","#scheduleFinalizationTakeover","#scheduleReceiptExpiry","#completeRollback","#assertLeaseAvailable","#recoverChildProcessCleanupFailure","#appendEventEventually","#persistRollbackIntent"],"sources":["../src/shared/envelope.ts","../src/shared/errors.ts","../src/adapters/database.ts","../src/shared/custody.ts","../src/shared/factory.ts","../src/index.ts"],"sourcesContent":["import * as crypto from 'node:crypto';\nimport type { EncryptedEnvelope } from './types.js';\n\n/**\n * Core envelope encryption primitives using AES-256-GCM\n *\n * This class provides the cryptographic operations for envelope encryption:\n * - Generate data encryption keys (DEKs)\n * - Wrap/unwrap DEKs with a master key\n * - Encrypt/decrypt data with DEKs\n *\n * @example\n * ```typescript\n * // Generate a new data key\n * const dataKey = EnvelopeEncryption.generateDataKey();\n *\n * // Wrap the data key with an AMK\n * const wrapped = EnvelopeEncryption.wrapKey(dataKey, amk);\n *\n * // Encrypt data with the data key\n * const encrypted = EnvelopeEncryption.encryptData('secret', dataKey);\n *\n * // Later: unwrap and decrypt\n * const unwrappedKey = EnvelopeEncryption.unwrapKey(wrapped.wrappedKey, wrapped.iv, wrapped.authTag, amk);\n * const plaintext = EnvelopeEncryption.decryptData(encrypted.ciphertext, encrypted.iv, encrypted.authTag, unwrappedKey);\n * ```\n */\nexport class EnvelopeEncryption {\n private static readonly ALGORITHM = 'aes-256-gcm' as const;\n private static readonly KEY_LENGTH = 32; // 256 bits\n private static readonly IV_LENGTH = 12; // 96 bits for GCM (recommended)\n private static readonly AUTH_TAG_LENGTH = 16; // 128 bits\n\n /**\n * Generate a new random data encryption key\n *\n * @returns A 32-byte (256-bit) random key\n */\n static generateDataKey(): Buffer {\n return crypto.randomBytes(EnvelopeEncryption.KEY_LENGTH);\n }\n\n /**\n * Generate a random IV for encryption\n *\n * @returns A 12-byte (96-bit) random IV\n */\n static generateIV(): Buffer {\n return crypto.randomBytes(EnvelopeEncryption.IV_LENGTH);\n }\n\n /**\n * Wrap (encrypt) a data encryption key with a master key\n *\n * @param dataKey - The data key to wrap (32 bytes)\n * @param masterKey - The master key to wrap with (32 bytes)\n * @returns Object containing wrapped key, IV, and auth tag (all base64-encoded)\n */\n static wrapKey(\n dataKey: Buffer,\n masterKey: Buffer,\n ): { wrappedKey: string; iv: string; authTag: string } {\n EnvelopeEncryption.validateKeyLength(dataKey);\n EnvelopeEncryption.validateKeyLength(masterKey);\n const iv = EnvelopeEncryption.generateIV();\n const cipher = crypto.createCipheriv(\n EnvelopeEncryption.ALGORITHM,\n masterKey,\n iv,\n );\n\n const encrypted = Buffer.concat([cipher.update(dataKey), cipher.final()]);\n const authTag = cipher.getAuthTag();\n\n return {\n wrappedKey: encrypted.toString('base64'),\n iv: iv.toString('base64'),\n authTag: authTag.toString('base64'),\n };\n }\n\n /**\n * Unwrap (decrypt) a data encryption key with a master key\n *\n * @param wrappedKey - Base64-encoded wrapped key\n * @param iv - Base64-encoded IV\n * @param authTag - Base64-encoded authentication tag\n * @param masterKey - The master key to unwrap with (32 bytes)\n * @returns The unwrapped data key (32 bytes)\n * @throws Error if decryption fails (invalid key, tampered data, etc.)\n */\n static unwrapKey(\n wrappedKey: string,\n iv: string,\n authTag: string,\n masterKey: Buffer,\n ): Buffer {\n EnvelopeEncryption.validateKeyLength(masterKey);\n const decipher = crypto.createDecipheriv(\n EnvelopeEncryption.ALGORITHM,\n masterKey,\n Buffer.from(iv, 'base64'),\n );\n decipher.setAuthTag(Buffer.from(authTag, 'base64'));\n\n return Buffer.concat([\n decipher.update(Buffer.from(wrappedKey, 'base64')),\n decipher.final(),\n ]);\n }\n\n /**\n * Encrypt data with a data encryption key\n *\n * @param plaintext - The plaintext string to encrypt\n * @param dataKey - The data encryption key (32 bytes)\n * @returns Object containing ciphertext, IV, and auth tag (all base64-encoded)\n */\n static encryptData(\n plaintext: string,\n dataKey: Buffer,\n ): { ciphertext: string; iv: string; authTag: string } {\n EnvelopeEncryption.validateKeyLength(dataKey);\n const iv = EnvelopeEncryption.generateIV();\n const cipher = crypto.createCipheriv(\n EnvelopeEncryption.ALGORITHM,\n dataKey,\n iv,\n );\n\n const encrypted = Buffer.concat([\n cipher.update(plaintext, 'utf8'),\n cipher.final(),\n ]);\n const authTag = cipher.getAuthTag();\n\n return {\n ciphertext: encrypted.toString('base64'),\n iv: iv.toString('base64'),\n authTag: authTag.toString('base64'),\n };\n }\n\n /**\n * Decrypt data with a data encryption key\n *\n * @param ciphertext - Base64-encoded ciphertext\n * @param iv - Base64-encoded IV\n * @param authTag - Base64-encoded authentication tag\n * @param dataKey - The data encryption key (32 bytes)\n * @returns The decrypted plaintext string\n * @throws Error if decryption fails (invalid key, tampered data, etc.)\n */\n static decryptData(\n ciphertext: string,\n iv: string,\n authTag: string,\n dataKey: Buffer,\n ): string {\n EnvelopeEncryption.validateKeyLength(dataKey);\n const decipher = crypto.createDecipheriv(\n EnvelopeEncryption.ALGORITHM,\n dataKey,\n Buffer.from(iv, 'base64'),\n );\n decipher.setAuthTag(Buffer.from(authTag, 'base64'));\n\n return Buffer.concat([\n decipher.update(Buffer.from(ciphertext, 'base64')),\n decipher.final(),\n ]).toString('utf8');\n }\n\n /**\n * Create a full encrypted envelope\n *\n * This combines key wrapping and data encryption into a single envelope\n * that can be stored and later decrypted.\n *\n * @param plaintext - The plaintext string to encrypt\n * @param dataKey - The data encryption key (32 bytes)\n * @param wrappedKeyInfo - The wrapped key info (from wrapKey)\n * @param amkKeyId - The ID of the AMK used to wrap the key\n * @param metadata - Optional metadata to include in the envelope\n * @returns A complete encrypted envelope\n */\n static createEnvelope(\n plaintext: string,\n dataKey: Buffer,\n wrappedKeyInfo: { wrappedKey: string; iv: string; authTag: string },\n amkKeyId: string,\n metadata?: Record<string, string>,\n ): EncryptedEnvelope {\n const encrypted = EnvelopeEncryption.encryptData(plaintext, dataKey);\n\n // Combine wrapped key info into a single string: wrappedKey:iv:authTag\n const combinedWrappedKey = `${wrappedKeyInfo.wrappedKey}:${wrappedKeyInfo.iv}:${wrappedKeyInfo.authTag}`;\n\n return {\n version: 1,\n algorithm: 'aes-256-gcm',\n wrappedKey: combinedWrappedKey,\n amkKeyId,\n iv: encrypted.iv,\n authTag: encrypted.authTag,\n ciphertext: encrypted.ciphertext,\n createdAt: new Date().toISOString(),\n metadata,\n };\n }\n\n /**\n * Parse a combined wrapped key string\n *\n * @param combinedWrappedKey - The combined wrapped key string (wrappedKey:iv:authTag)\n * @returns Object with wrappedKey, iv, and authTag\n */\n static parseWrappedKey(combinedWrappedKey: string): {\n wrappedKey: string;\n iv: string;\n authTag: string;\n } {\n const parts = combinedWrappedKey.split(':');\n if (parts.length !== 3) {\n throw new Error(\n 'Invalid wrapped key format: expected wrappedKey:iv:authTag',\n );\n }\n const [wrappedKey, iv, authTag] = parts;\n // Validate that all parts are non-empty\n if (!wrappedKey || !iv || !authTag) {\n throw new Error(\n 'Invalid wrapped key format: wrappedKey, iv, and authTag must all be non-empty',\n );\n }\n return { wrappedKey, iv, authTag };\n }\n\n /**\n * Decrypt a full envelope\n *\n * @param envelope - The encrypted envelope\n * @param masterKey - The master key to unwrap the data key\n * @returns The decrypted plaintext string\n */\n static decryptEnvelope(\n envelope: EncryptedEnvelope,\n masterKey: Buffer,\n ): string {\n // Parse the wrapped key\n const {\n wrappedKey,\n iv: keyIv,\n authTag: keyAuthTag,\n } = EnvelopeEncryption.parseWrappedKey(envelope.wrappedKey);\n\n // Unwrap the data key\n const dataKey = EnvelopeEncryption.unwrapKey(\n wrappedKey,\n keyIv,\n keyAuthTag,\n masterKey,\n );\n\n // Decrypt the data\n return EnvelopeEncryption.decryptData(\n envelope.ciphertext,\n envelope.iv,\n envelope.authTag,\n dataKey,\n );\n }\n\n /**\n * Validate that a key is the correct length\n *\n * @param key - The key to validate\n * @param expectedLength - Expected length in bytes (default: 32)\n * @throws Error if key length is invalid\n */\n static validateKeyLength(\n key: Buffer,\n expectedLength: number = EnvelopeEncryption.KEY_LENGTH,\n ): void {\n if (key.length !== expectedLength) {\n throw new Error(\n `Invalid key length: expected ${expectedLength} bytes, got ${key.length} bytes`,\n );\n }\n }\n\n /**\n * Parse a hex-encoded key from string\n *\n * @param hexKey - Hex-encoded key string (64 chars for 32 bytes)\n * @returns The key as a Buffer\n * @throws Error if the hex string is invalid\n */\n static parseHexKey(hexKey: string): Buffer {\n if (!/^[0-9a-fA-F]+$/.test(hexKey)) {\n throw new Error('Invalid hex key: contains non-hex characters');\n }\n\n const key = Buffer.from(hexKey, 'hex');\n EnvelopeEncryption.validateKeyLength(key);\n\n return key;\n }\n}\n","/**\n * Base error class for secret operations\n */\nexport class SecretError extends Error {\n readonly code: string;\n readonly adapterType?: string;\n readonly cause?: Error;\n\n constructor(\n message: string,\n code: string,\n options?: { adapterType?: string; cause?: Error },\n ) {\n super(message);\n this.name = 'SecretError';\n this.code = code;\n this.adapterType = options?.adapterType;\n this.cause = options?.cause;\n }\n}\n\n/**\n * Error thrown when a key is not found\n */\nexport class KeyNotFoundError extends SecretError {\n constructor(keyId: string, adapterType?: string) {\n super(`Key not found: ${keyId}`, 'KEY_NOT_FOUND', { adapterType });\n this.name = 'KeyNotFoundError';\n }\n}\n\n/**\n * Error thrown when decryption fails\n */\nexport class DecryptionError extends SecretError {\n constructor(message: string, adapterType?: string, cause?: Error) {\n super(message, 'DECRYPTION_FAILED', { adapterType, cause });\n this.name = 'DecryptionError';\n }\n}\n\n/**\n * Error thrown when encryption fails\n */\nexport class EncryptionError extends SecretError {\n constructor(message: string, adapterType?: string, cause?: Error) {\n super(message, 'ENCRYPTION_FAILED', { adapterType, cause });\n this.name = 'EncryptionError';\n }\n}\n\n/**\n * Error thrown when key rotation fails\n */\nexport class KeyRotationError extends SecretError {\n constructor(message: string, adapterType?: string, cause?: Error) {\n super(message, 'KEY_ROTATION_FAILED', { adapterType, cause });\n this.name = 'KeyRotationError';\n }\n}\n\n/**\n * Error thrown when a tenant's encryption key is missing\n */\nexport class TenantKeyMissingError extends SecretError {\n constructor(tenantId: string) {\n super(\n `No active encryption key for tenant: ${tenantId}`,\n 'TENANT_KEY_MISSING',\n );\n this.name = 'TenantKeyMissingError';\n }\n}\n\n/**\n * Error thrown when the Application Master Key is unavailable\n */\nexport class AMKUnavailableError extends SecretError {\n constructor(message: string) {\n super(message, 'AMK_UNAVAILABLE');\n this.name = 'AMKUnavailableError';\n }\n}\n\n/**\n * Error thrown when the store is not initialized\n */\nexport class StoreNotInitializedError extends SecretError {\n constructor(adapterType?: string) {\n super('Secret store not initialized', 'STORE_NOT_INITIALIZED', {\n adapterType,\n });\n this.name = 'StoreNotInitializedError';\n }\n}\n\n/**\n * Error thrown when an invalid key format is encountered\n */\nexport class InvalidKeyFormatError extends SecretError {\n constructor(message: string, adapterType?: string) {\n super(message, 'INVALID_KEY_FORMAT', { adapterType });\n this.name = 'InvalidKeyFormatError';\n }\n}\n","import { type DatabaseInterface, syncSchema } from '@happyvertical/sql';\nimport { createId } from '@happyvertical/utils';\nimport { EnvelopeEncryption } from '../shared/envelope.js';\nimport {\n AMKUnavailableError,\n DecryptionError,\n EncryptionError,\n KeyRotationError,\n StoreNotInitializedError,\n TenantKeyMissingError,\n} from '../shared/errors.js';\nimport type {\n ApplicationMasterKey,\n DatabaseSecretStoreOptions,\n DecryptedSecret,\n EncryptedEnvelope,\n EncryptOptions,\n SecretStore,\n SecretStoreEvent,\n SecretStoreEventListener,\n TenantDataEncryptionKey,\n Unsubscribe,\n} from '../shared/types.js';\n\n/**\n * Default rotation period in milliseconds (90 days)\n */\nconst DEFAULT_ROTATION_PERIOD_MS = 90 * 24 * 60 * 60 * 1000;\n\n/**\n * Database-backed secret store\n *\n * Uses envelope encryption with AES-256-GCM:\n * - Application Master Key (AMK) from environment variable\n * - Per-tenant Data Encryption Keys (TDEKs) wrapped by AMK\n * - Secrets encrypted by unwrapped TDEKs\n *\n * @example\n * ```typescript\n * const store = new DatabaseSecretStore({\n * type: 'database',\n * db,\n * amk: {\n * provider: 'env',\n * keyEnvVar: 'SECRET_MASTER_KEY',\n * keyId: 'amk-v1'\n * }\n * });\n *\n * await store.initialize();\n *\n * // Encrypt a secret for a tenant\n * const envelope = await store.encrypt('tenant-123', 'api-key', 'synthetic-secret');\n *\n * // Decrypt\n * const { value } = await store.decrypt('tenant-123', envelope);\n * ```\n */\nexport class DatabaseSecretStore implements SecretStore {\n private db: DatabaseInterface;\n private keysTable: string;\n private amkConfig: DatabaseSecretStoreOptions['amk'];\n private cachedAmk: Buffer | null = null;\n private initialized = false;\n private listeners: SecretStoreEventListener[] = [];\n\n constructor(options: DatabaseSecretStoreOptions) {\n this.db = options.db;\n\n // Validate table name to prevent SQL injection via identifier interpolation\n const tableName = options.keysTable ?? 'tenant_encryption_keys';\n if (!/^[A-Za-z0-9_]+$/.test(tableName)) {\n throw new Error(\n `Invalid keysTable name \"${tableName}\". Table name must contain only alphanumeric characters and underscores.`,\n );\n }\n this.keysTable = tableName;\n\n this.amkConfig = options.amk;\n }\n\n /**\n * Initialize the store (create schema if needed)\n */\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n // Create the tenant keys table\n const schema = `\n CREATE TABLE IF NOT EXISTS \"${this.keysTable}\" (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n wrapped_key TEXT NOT NULL,\n amk_key_id TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'active',\n version INTEGER NOT NULL DEFAULT 1,\n rotate_after TEXT,\n retired_at TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS \"idx_${this.keysTable}_tenant_status\"\n ON \"${this.keysTable}\" (tenant_id, status);\n\n CREATE INDEX IF NOT EXISTS \"idx_${this.keysTable}_rotate_after\"\n ON \"${this.keysTable}\" (rotate_after)\n WHERE status = 'active';\n `;\n\n await syncSchema({ db: this.db, schema });\n\n this.initialized = true;\n }\n\n /**\n * Get the Application Master Key from environment\n */\n private getAMK(): Buffer {\n if (this.cachedAmk) {\n return this.cachedAmk;\n }\n\n const keyHex = process.env[this.amkConfig.keyEnvVar];\n if (!keyHex) {\n throw new AMKUnavailableError(\n `Application Master Key not found in environment variable: ${this.amkConfig.keyEnvVar}`,\n );\n }\n\n try {\n const key = EnvelopeEncryption.parseHexKey(keyHex);\n this.cachedAmk = key;\n return key;\n } catch (error) {\n throw new AMKUnavailableError(\n `Invalid AMK in ${this.amkConfig.keyEnvVar}: ${(error as Error).message}`,\n );\n }\n }\n\n /**\n * Ensure the store is initialized\n */\n private ensureInitialized(): void {\n if (!this.initialized) {\n throw new StoreNotInitializedError('database');\n }\n }\n\n /**\n * Emit an event to all listeners\n */\n private async emitEvent(event: SecretStoreEvent): Promise<void> {\n for (const listener of this.listeners) {\n await listener(event);\n }\n }\n\n /**\n * Subscribe to store events\n */\n subscribe(listener: SecretStoreEventListener): Unsubscribe {\n this.listeners.push(listener);\n return () => {\n const index = this.listeners.indexOf(listener);\n if (index !== -1) {\n this.listeners.splice(index, 1);\n }\n };\n }\n\n async encrypt(\n tenantId: string,\n secretName: string,\n plaintext: string,\n options?: EncryptOptions,\n ): Promise<EncryptedEnvelope> {\n this.ensureInitialized();\n\n // Input validation\n if (typeof tenantId !== 'string' || tenantId.trim().length === 0) {\n throw new EncryptionError(\n 'Invalid tenantId provided for encryption',\n 'database',\n );\n }\n if (typeof secretName !== 'string' || secretName.trim().length === 0) {\n throw new EncryptionError(\n 'Invalid secretName provided for encryption',\n 'database',\n );\n }\n\n let dataKey: Buffer | null = null;\n try {\n // Get or create tenant's data encryption key\n let tenantKey = await this.getTenantKey(tenantId);\n if (!tenantKey) {\n tenantKey = await this.createTenantKey(tenantId);\n }\n\n // Get AMK and unwrap the TDEK\n const amk = this.getAMK();\n const {\n wrappedKey,\n iv: keyIv,\n authTag: keyAuthTag,\n } = EnvelopeEncryption.parseWrappedKey(tenantKey.wrappedKey);\n dataKey = EnvelopeEncryption.unwrapKey(\n wrappedKey,\n keyIv,\n keyAuthTag,\n amk,\n );\n\n // Create the envelope using the unwrapped TDEK\n const envelope = EnvelopeEncryption.createEnvelope(\n plaintext,\n dataKey,\n { wrappedKey, iv: keyIv, authTag: keyAuthTag },\n tenantKey.amkKeyId,\n options?.metadata,\n );\n\n // Emit event\n await this.emitEvent({\n type: 'secret.encrypted',\n tenantId,\n keyId: tenantKey.keyId,\n secretName,\n timestamp: new Date(),\n });\n\n return envelope;\n } catch (error) {\n if (\n error instanceof AMKUnavailableError ||\n error instanceof TenantKeyMissingError\n ) {\n throw error;\n }\n throw new EncryptionError(\n `Failed to encrypt secret '${secretName}' for tenant ${tenantId}: ${(error as Error).message}`,\n 'database',\n error instanceof Error ? error : undefined,\n );\n } finally {\n // Zero out the data key buffer to prevent key material from remaining in memory\n if (dataKey && Buffer.isBuffer(dataKey)) {\n dataKey.fill(0);\n }\n }\n }\n\n async decrypt(\n tenantId: string,\n envelope: EncryptedEnvelope,\n ): Promise<DecryptedSecret> {\n this.ensureInitialized();\n\n // Input validation\n if (typeof tenantId !== 'string' || tenantId.trim().length === 0) {\n throw new DecryptionError(\n 'Invalid tenantId provided for decryption',\n 'database',\n );\n }\n\n // Get all key versions for this tenant (including retired keys for decryption)\n const tenantKeys = await this.listKeyVersions(tenantId);\n if (tenantKeys.length === 0) {\n throw new TenantKeyMissingError(tenantId);\n }\n\n // Verify that the envelope's wrapped key belongs to this tenant\n // Extract the wrapped key portion from the envelope (format: wrappedKey:iv:authTag)\n const envelopeWrappedKeyParts = envelope.wrappedKey.split(':');\n const envelopeWrappedKey = envelopeWrappedKeyParts[0];\n\n const matchingKey = tenantKeys.find((key) => {\n const keyParts = key.wrappedKey.split(':');\n return keyParts[0] === envelopeWrappedKey;\n });\n\n if (!matchingKey) {\n throw new DecryptionError(\n `Envelope was not encrypted with a key belonging to tenant ${tenantId}`,\n 'database',\n );\n }\n\n try {\n // Get AMK and decrypt the envelope\n const amk = this.getAMK();\n const plaintext = EnvelopeEncryption.decryptEnvelope(envelope, amk);\n\n // Emit event\n await this.emitEvent({\n type: 'secret.decrypted',\n tenantId,\n keyId: matchingKey.keyId,\n timestamp: new Date(),\n });\n\n return {\n value: plaintext,\n keyId: matchingKey.keyId,\n createdAt: new Date(envelope.createdAt),\n metadata: envelope.metadata,\n };\n } catch (error) {\n if (error instanceof AMKUnavailableError) {\n throw error;\n }\n throw new DecryptionError(\n `Failed to decrypt secret for tenant ${tenantId}: ${(error as Error).message}`,\n 'database',\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n async getTenantKey(\n tenantId: string,\n ): Promise<TenantDataEncryptionKey | null> {\n this.ensureInitialized();\n\n const row = await this.db.get(this.keysTable, {\n tenant_id: tenantId,\n status: 'active',\n });\n\n if (!row) return null;\n\n return this.parseKeyRow(row as Record<string, unknown>);\n }\n\n async createTenantKey(tenantId: string): Promise<TenantDataEncryptionKey> {\n this.ensureInitialized();\n\n // Generate and wrap a new data key\n const amk = this.getAMK();\n const dataKey = EnvelopeEncryption.generateDataKey();\n const wrapped = EnvelopeEncryption.wrapKey(dataKey, amk);\n\n const keyId = createId();\n const wrappedKeyStr = `${wrapped.wrappedKey}:${wrapped.iv}:${wrapped.authTag}`;\n const now = new Date();\n const rotateAfter = new Date(now.getTime() + DEFAULT_ROTATION_PERIOD_MS);\n\n await this.db.insert(this.keysTable, {\n id: keyId,\n tenant_id: tenantId,\n wrapped_key: wrappedKeyStr,\n amk_key_id: this.amkConfig.keyId,\n status: 'active',\n version: 1,\n rotate_after: rotateAfter.toISOString(),\n created_at: now.toISOString(),\n updated_at: now.toISOString(),\n });\n\n // Emit event\n await this.emitEvent({\n type: 'key.created',\n tenantId,\n keyId,\n timestamp: now,\n });\n\n return {\n keyId,\n tenantId,\n wrappedKey: wrappedKeyStr,\n amkKeyId: this.amkConfig.keyId,\n status: 'active',\n version: 1,\n createdAt: now,\n rotateAfter,\n };\n }\n\n async rotateTenantKey(tenantId: string): Promise<TenantDataEncryptionKey> {\n this.ensureInitialized();\n\n const currentKey = await this.getTenantKey(tenantId);\n if (!currentKey) {\n throw new TenantKeyMissingError(tenantId);\n }\n\n const now = new Date();\n\n try {\n // Mark current key as rotating\n await this.db.update(\n this.keysTable,\n { id: currentKey.keyId },\n { status: 'rotating', updated_at: now.toISOString() },\n );\n\n // Create new key with incremented version\n const amk = this.getAMK();\n const dataKey = EnvelopeEncryption.generateDataKey();\n const wrapped = EnvelopeEncryption.wrapKey(dataKey, amk);\n\n const newKeyId = createId();\n const wrappedKeyStr = `${wrapped.wrappedKey}:${wrapped.iv}:${wrapped.authTag}`;\n const rotateAfter = new Date(now.getTime() + DEFAULT_ROTATION_PERIOD_MS);\n\n await this.db.insert(this.keysTable, {\n id: newKeyId,\n tenant_id: tenantId,\n wrapped_key: wrappedKeyStr,\n amk_key_id: this.amkConfig.keyId,\n status: 'active',\n version: currentKey.version + 1,\n rotate_after: rotateAfter.toISOString(),\n created_at: now.toISOString(),\n updated_at: now.toISOString(),\n });\n\n // Mark old key as retired\n await this.db.update(\n this.keysTable,\n { id: currentKey.keyId },\n {\n status: 'retired',\n retired_at: now.toISOString(),\n updated_at: now.toISOString(),\n },\n );\n\n // Emit event\n await this.emitEvent({\n type: 'key.rotated',\n tenantId,\n keyId: newKeyId,\n timestamp: now,\n });\n\n return {\n keyId: newKeyId,\n tenantId,\n wrappedKey: wrappedKeyStr,\n amkKeyId: this.amkConfig.keyId,\n status: 'active',\n version: currentKey.version + 1,\n createdAt: now,\n rotateAfter,\n };\n } catch (error) {\n throw new KeyRotationError(\n `Failed to rotate key for tenant ${tenantId}: ${(error as Error).message}`,\n 'database',\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n async retireTenantKey(tenantId: string, keyId: string): Promise<void> {\n this.ensureInitialized();\n\n const now = new Date();\n\n await this.db.update(\n this.keysTable,\n { id: keyId, tenant_id: tenantId },\n {\n status: 'retired',\n retired_at: now.toISOString(),\n updated_at: now.toISOString(),\n },\n );\n\n // Emit event\n await this.emitEvent({\n type: 'key.retired',\n tenantId,\n keyId,\n timestamp: now,\n });\n }\n\n async getActiveAMK(): Promise<ApplicationMasterKey> {\n // Validate that the AMK is accessible\n this.getAMK();\n\n return {\n keyId: this.amkConfig.keyId,\n description: `AMK from env var ${this.amkConfig.keyEnvVar}`,\n status: 'active',\n provider: 'env',\n keyReference: this.amkConfig.keyEnvVar,\n createdAt: new Date(),\n };\n }\n\n async rewrapTenantKey(\n _tenantId: string,\n _newAmkKeyId: string,\n ): Promise<TenantDataEncryptionKey> {\n // For env-based AMK, this would require:\n // 1. Having both old and new AMK available\n // 2. Unwrapping with old AMK\n // 3. Rewrapping with new AMK\n // This is complex and risky for env-based keys\n throw new Error(\n 'AMK rotation not supported for env-based keys. ' +\n 'Use AWS KMS, Vault, or Azure Key Vault for AMK rotation support.',\n );\n }\n\n async listKeyVersions(tenantId: string): Promise<TenantDataEncryptionKey[]> {\n this.ensureInitialized();\n\n const { rows } = await this.db.query(\n `SELECT * FROM \"${this.keysTable}\" WHERE tenant_id = ? ORDER BY version DESC`,\n tenantId,\n );\n\n return rows.map((row: Record<string, unknown>) => this.parseKeyRow(row));\n }\n\n /**\n * Parse a database row into a TenantDataEncryptionKey\n */\n private parseKeyRow(row: Record<string, unknown>): TenantDataEncryptionKey {\n return {\n keyId: row.id as string,\n tenantId: row.tenant_id as string,\n wrappedKey: row.wrapped_key as string,\n amkKeyId: row.amk_key_id as string,\n status: row.status as TenantDataEncryptionKey['status'],\n version: row.version as number,\n createdAt: new Date(row.created_at as string),\n rotateAfter: row.rotate_after\n ? new Date(row.rotate_after as string)\n : undefined,\n retiredAt: row.retired_at\n ? new Date(row.retired_at as string)\n : undefined,\n };\n }\n}\n\nexport default DatabaseSecretStore;\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport { spawn } from 'node:child_process';\nimport {\n createHash,\n type KeyObject,\n randomUUID,\n sign as signPayload,\n verify as verifyPayload,\n} from 'node:crypto';\n\nconst REDACTED = '[REDACTED]';\nconst MAX_TIMER_DELAY_MS = 2_147_483_647;\nconst SAFE_IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$/;\nconst ENVIRONMENT_VARIABLE = /^[A-Za-z_][A-Za-z0-9_]*$/;\nconst CREDENTIAL_PATTERNS: Array<{\n pattern: RegExp;\n replacement: string;\n}> = [\n {\n pattern:\n /\\b(?:gh[pousr]|github_pat|hvwk|sk|xox[baprs])_[A-Za-z0-9_-]{16,}\\b/gi,\n replacement: REDACTED,\n },\n {\n pattern: /\\b[A-Za-z0-9_-]{12,}\\.[A-Za-z0-9_-]{12,}\\.[A-Za-z0-9_-]{12,}\\b/g,\n replacement: REDACTED,\n },\n {\n pattern:\n /((?:authorization|credential|password|secret|token)\\s*[:=]\\s*(?:bearer\\s+)?)[^\\s,;]+/gi,\n replacement: `$1${REDACTED}`,\n },\n];\nlet environmentLock: Promise<void> = Promise.resolve();\nconst environmentLockContext = new AsyncLocalStorage<boolean>();\n\nasync function withEnvironmentLock<T>(\n operation: () => T | Promise<T>,\n): Promise<T> {\n if (environmentLockContext.getStore()) {\n throw new CustodyError(\n 'REENTRANT_ENVIRONMENT_INJECTION',\n 'inject',\n 'Nested environment credential injection is not allowed',\n );\n }\n const previous = environmentLock;\n let release: () => void = () => undefined;\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n const queued = previous.then(() => current);\n environmentLock = queued;\n await previous;\n try {\n return await environmentLockContext.run(true, operation);\n } finally {\n release();\n if (environmentLock === queued) environmentLock = Promise.resolve();\n }\n}\n\nexport type CredentialIssuanceMode = 'ephemeral' | 'durable';\nexport type CustodyStage =\n | 'issue'\n | 'store'\n | 'retrieve'\n | 'verify'\n | 'activate'\n | 'record'\n | 'inject'\n | 'revoke'\n | 'reconcile';\n\nexport class CustodyError extends Error {\n readonly code: string;\n readonly stage: CustodyStage;\n readonly details?: Readonly<Record<string, unknown>>;\n\n constructor(\n code: string,\n stage: CustodyStage,\n message: string,\n options?: { cause?: unknown; details?: Record<string, unknown> },\n ) {\n super(redactCredentialText(message));\n this.name = 'CustodyError';\n this.code =\n SAFE_IDENTIFIER.test(code) && redactCredentialText(code) === code\n ? code\n : 'INVALID_CUSTODY_ERROR_CODE';\n this.stage = stage;\n this.details = options?.details\n ? (redactCredentialValues(options.details) as Record<string, unknown>)\n : undefined;\n // Raw provider causes are deliberately not retained: Node's Error\n // inspection prints non-enumerable causes and can expose credentials.\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n code: this.code,\n stage: this.stage,\n message: this.message,\n ...(this.details ? { details: this.details } : {}),\n };\n }\n}\n\nexport class SecretMaterial {\n #bytes: Buffer;\n #destroyed = false;\n\n private constructor(value: string) {\n this.#bytes = Buffer.from(value, 'utf8');\n }\n\n static fromString(value: string): SecretMaterial {\n if (!value) {\n throw new CustodyError(\n 'EMPTY_SECRET_MATERIAL',\n 'issue',\n 'Secret material must not be empty',\n );\n }\n return new SecretMaterial(value);\n }\n\n get destroyed(): boolean {\n return this.#destroyed;\n }\n\n async use(operation: (value: string) => void | Promise<void>): Promise<void> {\n if (this.#destroyed) {\n throw new CustodyError(\n 'SECRET_MATERIAL_DESTROYED',\n 'inject',\n 'Secret material is no longer available',\n );\n }\n const plaintext = this.#bytes.toString('utf8');\n try {\n await operation(plaintext);\n } catch (cause) {\n if (cause instanceof CustodyError) {\n throw new CustodyError(\n redactCredentialText(cause.code, [plaintext]) === cause.code\n ? cause.code\n : 'INVALID_CUSTODY_ERROR_CODE',\n cause.stage,\n redactCredentialText(cause.message, [plaintext]),\n {\n details: redactCredentialValues(cause.details, [plaintext]) as\n | Record<string, unknown>\n | undefined,\n },\n );\n }\n throw new CustodyError(\n 'SECRET_MATERIAL_OPERATION_FAILED',\n 'inject',\n 'Secret material operation failed',\n { cause },\n );\n }\n }\n\n destroy(): void {\n this.#bytes.fill(0);\n this.#destroyed = true;\n }\n\n toJSON(): string {\n return REDACTED;\n }\n\n toString(): string {\n return REDACTED;\n }\n\n [Symbol.for('nodejs.util.inspect.custom')](): string {\n return REDACTED;\n }\n}\n\nexport interface CredentialIssueRequest {\n mode: CredentialIssuanceMode;\n subject: string;\n expiresAt?: string;\n metadata?: Readonly<Record<string, string>>;\n}\n\nexport interface IssuedCredential {\n credentialId: string;\n secret: SecretMaterial;\n issuedAt?: string;\n expiresAt?: string;\n}\n\nexport interface CredentialIssuer {\n readonly name: string;\n issue(request: CredentialIssueRequest): Promise<IssuedCredential>;\n /** Must be idempotent so failed custody transitions can be retried. */\n revoke(credentialId: string, reason: string): Promise<void>;\n}\n\nexport interface CredentialVerifier {\n readonly name: string;\n verify(input: {\n credentialId: string;\n secret: SecretMaterial;\n }): Promise<{ verified: boolean; verificationId?: string }>;\n}\n\nexport interface SecretSinkRecord {\n sinkName: string;\n reference: string;\n version: string;\n storedAt: string;\n}\n\nexport interface SecretSinkInventoryEntry extends SecretSinkRecord {\n credentialId: string;\n}\n\nexport interface CredentialSecretSink {\n readonly name: string;\n store(input: {\n credentialId: string;\n secret: SecretMaterial;\n metadata: Readonly<Record<string, string>>;\n }): Promise<SecretSinkRecord>;\n retrieve(record: SecretSinkRecord): Promise<SecretMaterial>;\n /**\n * Removes any write for this credential, including a store that persisted\n * data but failed before returning its record. Must be idempotent.\n */\n removeByCredentialId(credentialId: string, reason: string): Promise<void>;\n /** Must conditionally remove this exact version and be idempotent. */\n remove(record: SecretSinkRecord, reason: string): Promise<void>;\n inventory(): Promise<SecretSinkInventoryEntry[]>;\n}\n\nexport interface CustodyAttribution {\n actor: string;\n runtime: string;\n session: string;\n}\n\nexport interface CustodyReceipt {\n receiptId: string;\n mode: CredentialIssuanceMode;\n credentialId: string;\n subject: string;\n issuer: string;\n verifier: string;\n verificationId?: string;\n verificationState: 'verified';\n issuedAt: string;\n verifiedAt: string;\n expiresAt?: string;\n sink?: SecretSinkRecord;\n replacesReceiptId?: string;\n rotationRootReceiptId: string;\n attribution: CustodyAttribution;\n metadata: Readonly<Record<string, string>>;\n finalizer: string;\n finalizationId: string;\n attestation: CustodyReceiptAttestation;\n}\n\nexport interface CustodyReceiptAttestation {\n algorithm: 'Ed25519';\n attestor: string;\n keyId: string;\n signature: string;\n}\n\nexport interface CredentialReceiptAttestor {\n readonly name: string;\n readonly keyId: string;\n attest(payload: string): Promise<string>;\n}\n\nexport interface CredentialCustodyFinalizer {\n readonly name: string;\n prepare(input: {\n receipt: CustodyReceipt;\n secret: SecretMaterial;\n }): Promise<{ prepared: boolean }>;\n commit(input: { receipt: CustodyReceipt }): Promise<void>;\n abort(input: { receipt: CustodyReceipt; reason: string }): Promise<void>;\n status(input: {\n receipt: CustodyReceipt;\n }): Promise<'missing' | 'prepared' | 'committed' | 'aborted'>;\n}\n\nexport class Ed25519CustodyReceiptAttestor\n implements CredentialReceiptAttestor\n{\n readonly name: string;\n readonly keyId: string;\n readonly #privateKey: KeyObject;\n\n constructor(options: {\n privateKey: KeyObject;\n keyId: string;\n name?: string;\n }) {\n if (\n options.privateKey.type !== 'private' ||\n options.privateKey.asymmetricKeyType !== 'ed25519'\n ) {\n throw new CustodyError(\n 'INVALID_ATTESTATION_KEY',\n 'record',\n 'Custody attestation requires a private signing key',\n );\n }\n this.#privateKey = options.privateKey;\n this.name = options.name ?? 'ed25519';\n this.keyId = options.keyId;\n assertSafeIdentifier(this.name, 'attestor.name', 'record');\n assertSafeIdentifier(this.keyId, 'attestor.keyId', 'record');\n }\n\n async attest(payload: string): Promise<string> {\n return signPayload(null, Buffer.from(payload), this.#privateKey).toString(\n 'base64url',\n );\n }\n}\n\nexport type CustodyEventType =\n | 'issued'\n | 'revoked'\n | 'expired'\n | 'replaced'\n | 'rollback-pending'\n | 'rollback-complete'\n | 'finalization-pending'\n | 'retirement-pending'\n | 'retirement-complete'\n | 'orphan-detected'\n | 'orphan-removed';\n\nexport interface CustodyEvent {\n eventId: string;\n type: CustodyEventType;\n occurredAt: string;\n receiptId?: string;\n recoveryId?: string;\n credentialId?: string;\n requiresSink?: boolean;\n finalizationReceipt?: CustodyReceipt;\n recoveryReceipt?: CustodyReceipt;\n replacementReceiptId?: string;\n sinkReference?: string;\n reason?: string;\n}\n\nexport interface CustodyLedger {\n /**\n * Atomically records a receipt and its finalization-pending event.\n * Implementations must reject duplicate receipt IDs, terminal predecessors,\n * a second child for the same replacesReceiptId, and any other event type.\n */\n recordIssuance(receipt: CustodyReceipt, event: CustodyEvent): Promise<void>;\n /**\n * Idempotently appends the issued event and optional retirement-pending\n * followup in one transaction. No other event types are accepted.\n */\n commitIssuance(\n receiptId: string,\n event: CustodyEvent,\n followup?: CustodyEvent,\n ): Promise<void>;\n /**\n * Idempotently appends an event by eventId. Replaying the same event must\n * succeed; reusing an eventId for different content must fail closed.\n */\n appendEvent(event: CustodyEvent): Promise<void>;\n listReceipts(): Promise<CustodyReceipt[]>;\n listEvents(): Promise<CustodyEvent[]>;\n}\n\nexport class InMemoryCustodyLedger implements CustodyLedger {\n readonly #receipts: CustodyReceipt[] = [];\n readonly #events: CustodyEvent[] = [];\n\n async recordIssuance(\n receipt: CustodyReceipt,\n event: CustodyEvent,\n ): Promise<void> {\n if (event.type !== 'finalization-pending') {\n throw new CustodyError(\n 'INVALID_CUSTODY_EVENT',\n 'record',\n 'Prepared receipt requires a finalization-pending event',\n );\n }\n if (this.#receipts.some((item) => item.receiptId === receipt.receiptId)) {\n throw new CustodyError(\n 'DUPLICATE_CUSTODY_RECEIPT',\n 'record',\n 'Custody receipt already exists',\n );\n }\n if (receipt.replacesReceiptId) {\n const predecessor = this.#receipts.find(\n (item) => item.receiptId === receipt.replacesReceiptId,\n );\n const predecessorTerminal = this.#events.some(\n (item) =>\n item.receiptId === receipt.replacesReceiptId &&\n ['revoked', 'expired', 'replaced'].includes(item.type),\n );\n const predecessorIssued = this.#events.some(\n (item) =>\n item.receiptId === receipt.replacesReceiptId &&\n item.type === 'issued',\n );\n const existingChild = this.#receipts.some(\n (item) =>\n item.replacesReceiptId === receipt.replacesReceiptId &&\n !this.#events.some(\n (event) =>\n event.receiptId === item.receiptId &&\n ['revoked', 'expired', 'replaced'].includes(event.type),\n ),\n );\n if (\n !predecessor ||\n !predecessorIssued ||\n predecessorTerminal ||\n existingChild\n ) {\n throw new CustodyError(\n 'CUSTODY_ROTATION_CONFLICT',\n 'record',\n 'Custody predecessor is not active and replaceable',\n );\n }\n }\n this.#receipts.push(structuredClone(receipt));\n this.#events.push(structuredClone(event));\n }\n\n async appendEvent(event: CustodyEvent): Promise<void> {\n const existing = this.#events.find(\n (item) => item.eventId === event.eventId,\n );\n if (existing) {\n if (sameCustodyEvent(existing, event)) return;\n throw new CustodyError(\n 'CUSTODY_EVENT_ID_CONFLICT',\n 'record',\n 'Custody event ID is already bound to different content',\n );\n }\n this.#events.push(structuredClone(event));\n }\n\n async commitIssuance(\n receiptId: string,\n event: CustodyEvent,\n followup?: CustodyEvent,\n ): Promise<void> {\n if (\n event.type !== 'issued' ||\n (followup !== undefined && followup.type !== 'retirement-pending')\n ) {\n throw new CustodyError(\n 'INVALID_CUSTODY_EVENT',\n 'record',\n 'Issuance commit requires issued and optional retirement-pending events',\n );\n }\n if (!this.#receipts.some((receipt) => receipt.receiptId === receiptId)) {\n throw new CustodyError(\n 'CUSTODY_RECEIPT_NOT_FOUND',\n 'record',\n 'Prepared custody receipt was not found',\n );\n }\n if (\n !this.#events.some(\n (item) => item.receiptId === receiptId && item.type === 'issued',\n )\n ) {\n this.#events.push(structuredClone(event));\n if (followup) this.#events.push(structuredClone(followup));\n }\n }\n\n async listReceipts(): Promise<CustodyReceipt[]> {\n return structuredClone(this.#receipts);\n }\n\n async listEvents(): Promise<CustodyEvent[]> {\n return structuredClone(this.#events);\n }\n}\n\nexport interface CredentialLease {\n readonly receipt: CustodyReceipt;\n withEnvironment(\n variableName: string,\n operation: () => void | Promise<void>,\n ): Promise<void>;\n withChildProcess(\n options: CredentialChildProcessOptions,\n ): Promise<CredentialChildProcessResult>;\n revoke(reason?: string): Promise<void>;\n}\n\nexport interface CredentialChildProcessOptions {\n /**\n * Acknowledges that the command and every credential-bearing descendant are\n * trusted to remain in the SDK-owned POSIX process group.\n */\n trust: 'cooperative-process-group';\n command: string;\n args?: readonly string[];\n environmentVariable: string;\n environment?: Readonly<Record<string, string>>;\n cwd?: string;\n timeoutMs?: number;\n maxOutputBytes?: number;\n}\n\nexport interface CredentialChildProcessResult {\n exitCode: number | null;\n signal: NodeJS.Signals | null;\n stdout: string;\n stderr: string;\n timedOut: boolean;\n}\n\nexport interface CustodyReconciliation {\n checkedAt: string;\n orphaned: SecretSinkInventoryEntry[];\n missing: CustodyReceipt[];\n}\n\nexport interface CredentialCustodyOptions {\n issuer: CredentialIssuer;\n verifier: CredentialVerifier;\n ledger: CustodyLedger;\n attestor: CredentialReceiptAttestor;\n finalizer: CredentialCustodyFinalizer;\n sink?: CredentialSecretSink;\n ephemeralTtlMs?: number;\n ephemeralRevokeRetryMs?: number;\n orphanGraceMs?: number;\n finalizationTakeoverMs?: number;\n now?: () => Date;\n onBackgroundError?: (error: CustodyError) => void;\n}\n\nexport interface CustodyIssuanceRequest {\n mode: CredentialIssuanceMode;\n subject: string;\n attribution: CustodyAttribution;\n expiresAt?: string;\n metadata?: Readonly<Record<string, string>>;\n replacesReceiptId?: string;\n}\n\nexport class CredentialCustody {\n readonly #issuer: CredentialIssuer;\n readonly #verifier: CredentialVerifier;\n readonly #ledger: CustodyLedger;\n readonly #attestor: CredentialReceiptAttestor;\n readonly #finalizer: CredentialCustodyFinalizer;\n readonly #sink?: CredentialSecretSink;\n readonly #ephemeralTtlMs: number;\n readonly #ephemeralRevokeRetryMs: number;\n readonly #orphanGraceMs: number;\n readonly #finalizationTakeoverMs: number;\n readonly #now: () => Date;\n readonly #onBackgroundError?: (error: CustodyError) => void;\n readonly #leaseInvalidators = new Map<string, () => void>();\n readonly #pendingTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\n constructor(options: CredentialCustodyOptions) {\n assertSafeIdentifier(options.issuer.name, 'issuer.name', 'issue');\n assertSafeIdentifier(options.verifier.name, 'verifier.name', 'verify');\n assertSafeIdentifier(options.attestor.name, 'attestor.name', 'record');\n assertSafeIdentifier(options.attestor.keyId, 'attestor.keyId', 'record');\n assertSafeIdentifier(options.finalizer.name, 'finalizer.name', 'activate');\n if (options.sink) {\n assertSafeIdentifier(options.sink.name, 'sink.name', 'store');\n }\n this.#issuer = options.issuer;\n this.#verifier = options.verifier;\n this.#ledger = options.ledger;\n this.#attestor = options.attestor;\n this.#finalizer = options.finalizer;\n this.#sink = options.sink;\n this.#ephemeralTtlMs = options.ephemeralTtlMs ?? 5 * 60_000;\n this.#ephemeralRevokeRetryMs = options.ephemeralRevokeRetryMs ?? 1_000;\n this.#orphanGraceMs = options.orphanGraceMs ?? 5 * 60_000;\n this.#finalizationTakeoverMs = options.finalizationTakeoverMs ?? 30_000;\n if (\n !Number.isFinite(this.#ephemeralTtlMs) ||\n !Number.isFinite(this.#ephemeralRevokeRetryMs) ||\n !Number.isFinite(this.#orphanGraceMs) ||\n !Number.isFinite(this.#finalizationTakeoverMs) ||\n this.#ephemeralTtlMs <= 0 ||\n this.#ephemeralRevokeRetryMs <= 0 ||\n this.#orphanGraceMs < 0 ||\n this.#finalizationTakeoverMs < 0\n ) {\n throw new CustodyError(\n 'INVALID_CUSTODY_DURATION',\n 'issue',\n 'Custody durations must be positive',\n );\n }\n this.#now = options.now ?? (() => new Date());\n this.#onBackgroundError = options.onBackgroundError;\n }\n\n async issue(request: CustodyIssuanceRequest): Promise<CredentialLease> {\n if (request.mode === 'durable' && !this.#sink) {\n throw new CustodyError(\n 'DURABLE_SINK_REQUIRED',\n 'store',\n 'Durable credential issuance requires a secret sink',\n );\n }\n if (request.replacesReceiptId && request.mode !== 'durable') {\n throw new CustodyError(\n 'DURABLE_ROTATION_REQUIRED',\n 'issue',\n 'Credential replacement requires durable mode',\n );\n }\n assertSafeIdentifier(request.subject, 'subject', 'issue');\n assertAttribution(request.attribution);\n const replacedReceipt = request.replacesReceiptId\n ? await this.#findReceipt(request.replacesReceiptId)\n : undefined;\n if (\n replacedReceipt?.mode !== undefined &&\n replacedReceipt.mode !== 'durable'\n ) {\n throw new CustodyError(\n 'DURABLE_ROTATION_REQUIRED',\n 'issue',\n 'Only durable credentials can be replaced',\n );\n }\n\n const now = this.#now();\n const expiresAt =\n request.expiresAt ??\n (request.mode === 'ephemeral'\n ? new Date(now.getTime() + this.#ephemeralTtlMs).toISOString()\n : undefined);\n const requestedExpiryMs = expiresAt\n ? parseTimestamp(expiresAt, 'expiresAt', 'issue')\n : undefined;\n if (requestedExpiryMs !== undefined && requestedExpiryMs <= now.getTime())\n throw new CustodyError(\n 'INVALID_EXPIRY',\n 'issue',\n 'Credential expiry must be in the future',\n );\n\n const metadata = sanitizeMetadata(request.metadata);\n let issued: IssuedCredential | undefined;\n let sinkRecord: SecretSinkRecord | undefined;\n let retrieved: SecretMaterial | undefined;\n let preparedReceipt: CustodyReceipt | undefined;\n let receiptRecorded = false;\n let stage: CustodyStage = 'issue';\n\n try {\n issued = await this.#issuer.issue({\n mode: request.mode,\n subject: request.subject,\n expiresAt,\n metadata,\n });\n assertSafeIdentifier(issued.credentialId, 'credentialId', 'issue');\n if (issued.issuedAt) parseTimestamp(issued.issuedAt, 'issuedAt', 'issue');\n if (issued.expiresAt) {\n const providerExpiryMs = parseTimestamp(\n issued.expiresAt,\n 'provider expiresAt',\n 'issue',\n );\n if (providerExpiryMs <= now.getTime()) {\n throw new CustodyError(\n 'INVALID_EXPIRY',\n 'issue',\n 'Credential provider returned an expired credential',\n );\n }\n if (\n requestedExpiryMs !== undefined &&\n providerExpiryMs > requestedExpiryMs\n ) {\n throw new CustodyError(\n 'EXPIRY_BOUND_EXCEEDED',\n 'issue',\n 'Credential provider exceeded the requested expiry bound',\n );\n }\n }\n\n let material = issued.secret;\n if (request.mode === 'durable') {\n const sink = this.#sink;\n if (!sink) {\n throw new CustodyError(\n 'DURABLE_SINK_REQUIRED',\n 'store',\n 'Durable credential issuance requires a secret sink',\n );\n }\n stage = 'store';\n sinkRecord = await sink.store({\n credentialId: issued.credentialId,\n secret: issued.secret,\n metadata,\n });\n assertSinkRecord(sinkRecord, sink.name);\n\n stage = 'retrieve';\n retrieved = await sink.retrieve(sinkRecord);\n material = retrieved;\n }\n\n stage = 'verify';\n const verification = await this.#verifier.verify({\n credentialId: issued.credentialId,\n secret: material,\n });\n if (!verification.verified) {\n throw new CustodyError(\n 'CREDENTIAL_VERIFICATION_FAILED',\n 'verify',\n 'Credential verification failed',\n );\n }\n if (verification.verificationId) {\n assertSafeIdentifier(\n verification.verificationId,\n 'verificationId',\n 'verify',\n );\n }\n const receiptId = randomUUID();\n const finalizationId = randomUUID();\n const unsignedReceipt: Omit<CustodyReceipt, 'attestation'> = {\n receiptId,\n mode: request.mode,\n credentialId: issued.credentialId,\n subject: request.subject,\n issuer: this.#issuer.name,\n verifier: this.#verifier.name,\n ...(verification.verificationId\n ? { verificationId: verification.verificationId }\n : {}),\n verificationState: 'verified',\n issuedAt: issued.issuedAt ?? now.toISOString(),\n verifiedAt: this.#now().toISOString(),\n ...((issued.expiresAt ?? expiresAt)\n ? { expiresAt: issued.expiresAt ?? expiresAt }\n : {}),\n ...(sinkRecord ? { sink: sinkRecord } : {}),\n ...(request.replacesReceiptId\n ? { replacesReceiptId: request.replacesReceiptId }\n : {}),\n rotationRootReceiptId:\n replacedReceipt?.rotationRootReceiptId ?? receiptId,\n attribution: { ...request.attribution },\n metadata,\n finalizer: this.#finalizer.name,\n finalizationId,\n };\n\n stage = 'record';\n let signature = '';\n await material.use(async (plaintext) => {\n signature = await this.#attestor.attest(\n custodyAttestationPayload(\n unsignedReceipt,\n credentialCommitment(plaintext),\n {\n algorithm: 'Ed25519',\n attestor: this.#attestor.name,\n keyId: this.#attestor.keyId,\n },\n ),\n );\n });\n if (\n !/^[A-Za-z0-9_-]{86}$/.test(signature) ||\n Buffer.from(signature, 'base64url').byteLength !== 64 ||\n Buffer.from(signature, 'base64url').toString('base64url') !==\n signature ||\n redactCredentialText(signature) !== signature\n ) {\n throw new CustodyError(\n 'INVALID_CUSTODY_ATTESTATION',\n 'record',\n 'Custody attestor returned an invalid signature',\n );\n }\n const receipt: CustodyReceipt = {\n ...unsignedReceipt,\n attestation: {\n algorithm: 'Ed25519',\n attestor: this.#attestor.name,\n keyId: this.#attestor.keyId,\n signature,\n },\n };\n stage = 'record';\n await this.#ledger.recordIssuance(\n receipt,\n this.#event('finalization-pending', receiptId),\n );\n receiptRecorded = true;\n stage = 'activate';\n preparedReceipt = receipt;\n const activation = await this.#finalizer.prepare({\n receipt: structuredClone(receipt),\n secret: material,\n });\n if (!activation.prepared) {\n throw new CustodyError(\n 'CREDENTIAL_ACTIVATION_FAILED',\n 'activate',\n 'Credential post-attestation activation failed',\n );\n }\n retrieved?.destroy();\n retrieved = undefined;\n if (request.mode === 'durable') issued.secret.destroy();\n stage = 'activate';\n await this.#finalizer.commit({ receipt: structuredClone(receipt) });\n stage = 'record';\n const retirementRecoveryId = replacedReceipt ? randomUUID() : undefined;\n const retirementPending =\n replacedReceipt && retirementRecoveryId\n ? {\n ...this.#event(\n 'retirement-pending' as const,\n replacedReceipt.receiptId,\n ),\n eventId: retirementRecoveryId,\n recoveryId: retirementRecoveryId,\n credentialId: replacedReceipt.credentialId,\n recoveryReceipt: structuredClone(replacedReceipt),\n replacementReceiptId: receipt.receiptId,\n }\n : undefined;\n await this.#ledger.commitIssuance(\n receipt.receiptId,\n this.#event('issued', receipt.receiptId),\n retirementPending,\n );\n const lease = this.#createLease(\n receipt,\n request.mode === 'ephemeral' ? issued.secret : undefined,\n );\n if (replacedReceipt) {\n try {\n this.#leaseInvalidators.get(replacedReceipt.receiptId)?.();\n await this.#revokeReceipt(replacedReceipt, 'replaced', 'replaced');\n this.#leaseInvalidators.delete(replacedReceipt.receiptId);\n await this.#ledger.appendEvent({\n ...this.#event('retirement-complete', replacedReceipt.receiptId),\n recoveryId: retirementRecoveryId,\n });\n } catch (cause) {\n if (retirementRecoveryId) {\n this.#scheduleRetirement(\n retirementRecoveryId,\n replacedReceipt,\n receipt.receiptId,\n );\n }\n try {\n this.#onBackgroundError?.(\n new CustodyError(\n 'PREDECESSOR_RETIREMENT_PENDING',\n 'revoke',\n 'Replacement is active while predecessor retirement retries',\n { cause, details: { recoveryId: retirementRecoveryId } },\n ),\n );\n } catch {\n // Observability cannot interrupt the active replacement.\n }\n }\n }\n preparedReceipt = undefined;\n return lease;\n } catch (cause) {\n const safeCause = await sanitizeCustodyCause(cause, [\n retrieved,\n issued?.secret,\n ]);\n retrieved?.destroy();\n issued?.secret.destroy();\n if (issued) {\n const requiresSink = request.mode === 'durable';\n try {\n if (receiptRecorded && preparedReceipt) {\n await this.#abortAndRevoke(\n preparedReceipt,\n `rollback after ${stage}`,\n );\n } else {\n await this.#rollback(\n issued.credentialId,\n sinkRecord,\n stage,\n requiresSink,\n );\n }\n } catch (rollbackCause) {\n const recoveryId = randomUUID();\n const pendingEvent: CustodyEvent = {\n ...this.#event(\n 'rollback-pending',\n receiptRecorded ? preparedReceipt?.receiptId : undefined,\n sinkRecord?.reference,\n `rollback after ${stage}`,\n ),\n eventId: recoveryId,\n recoveryId,\n credentialId: issued.credentialId,\n requiresSink,\n finalizationReceipt: preparedReceipt\n ? structuredClone(preparedReceipt)\n : undefined,\n };\n try {\n await this.#ledger.appendEvent(pendingEvent);\n } catch {\n // Automatic in-process retry still proceeds if persistence fails.\n }\n this.#scheduleRollback(\n recoveryId,\n issued.credentialId,\n sinkRecord,\n stage,\n requiresSink,\n preparedReceipt,\n receiptRecorded,\n );\n throw new CustodyError(\n 'CUSTODY_ROLLBACK_FAILED',\n 'revoke',\n 'Credential rollback is pending automatic retry',\n {\n cause: rollbackCause,\n details: {\n recoveryId,\n credentialId: issued.credentialId,\n requiresSink,\n failedStage: stage,\n },\n },\n );\n }\n }\n if (safeCause instanceof CustodyError) throw safeCause;\n throw new CustodyError(\n `CUSTODY_${stage.toUpperCase()}_FAILED`,\n stage,\n `Credential custody failed during ${stage}`,\n { cause: safeCause },\n );\n }\n }\n\n async rotate(\n receiptId: string,\n request: Omit<CustodyIssuanceRequest, 'mode' | 'replacesReceiptId'>,\n ): Promise<CredentialLease> {\n const previous = await this.#findReceipt(receiptId);\n if (previous.mode !== 'durable') {\n throw new CustodyError(\n 'DURABLE_ROTATION_REQUIRED',\n 'issue',\n 'Only durable credentials can be rotated',\n );\n }\n return this.issue({\n ...request,\n mode: 'durable',\n replacesReceiptId: receiptId,\n });\n }\n\n async revoke(receiptId: string, reason = 'recovery'): Promise<void> {\n const receipt = await this.#findReceipt(receiptId);\n this.#leaseInvalidators.get(receiptId)?.();\n await this.#revokeReceipt(receipt, reason);\n this.#leaseInvalidators.delete(receiptId);\n }\n\n async recoverPendingRollbacks(): Promise<void> {\n const events = await this.#ledger.listEvents();\n const receipts = await this.#ledger.listReceipts();\n const activeIds = new Set(\n events\n .filter((event) => event.type === 'issued')\n .map((event) => event.receiptId),\n );\n const terminalIds = new Set(\n events\n .filter((event) =>\n ['revoked', 'expired', 'replaced'].includes(event.type),\n )\n .map((event) => event.receiptId),\n );\n const completedRollbackIds = new Set(\n events\n .filter((event) => event.type === 'rollback-complete')\n .map((event) => event.recoveryId),\n );\n const rollbackReceiptIds = new Set(\n events\n .filter(\n (event) =>\n event.type === 'rollback-pending' &&\n !completedRollbackIds.has(event.recoveryId),\n )\n .map((event) => event.receiptId),\n );\n const pendingByReceipt = new Map(\n events\n .filter(\n (event) => event.type === 'finalization-pending' && event.receiptId,\n )\n .map((event) => [event.receiptId, event]),\n );\n const takeoverCutoff = this.#now().getTime() - this.#finalizationTakeoverMs;\n const failures: string[] = [];\n for (const receipt of receipts.filter(\n (item) =>\n !activeIds.has(item.receiptId) &&\n !terminalIds.has(item.receiptId) &&\n !rollbackReceiptIds.has(item.receiptId) &&\n (() => {\n const pending = pendingByReceipt.get(item.receiptId);\n if (pending) {\n const pendingAt = parseTimestamp(\n pending.occurredAt,\n 'occurredAt',\n 'reconcile',\n );\n if (pendingAt > takeoverCutoff) {\n this.#scheduleFinalizationTakeover(\n item.finalizationId,\n pendingAt + this.#finalizationTakeoverMs,\n );\n return false;\n }\n }\n return (\n pending !== undefined &&\n parseTimestamp(pending.occurredAt, 'occurredAt', 'reconcile') <=\n takeoverCutoff\n );\n })(),\n )) {\n try {\n const status = await this.#finalizer.status({\n receipt: structuredClone(receipt),\n });\n if (status === 'committed') {\n const predecessor = receipt.replacesReceiptId\n ? receipts.find(\n (item) => item.receiptId === receipt.replacesReceiptId,\n )\n : undefined;\n const recoveryId = predecessor ? randomUUID() : undefined;\n await this.#ledger.commitIssuance(\n receipt.receiptId,\n this.#event('issued', receipt.receiptId),\n predecessor && recoveryId\n ? {\n ...this.#event('retirement-pending', predecessor.receiptId),\n eventId: recoveryId,\n recoveryId,\n credentialId: predecessor.credentialId,\n recoveryReceipt: structuredClone(predecessor),\n replacementReceiptId: receipt.receiptId,\n }\n : undefined,\n );\n activeIds.add(receipt.receiptId);\n if (predecessor && recoveryId) {\n this.#scheduleRetirement(\n recoveryId,\n predecessor,\n receipt.receiptId,\n );\n }\n } else {\n await this.#abortAndRevoke(\n receipt,\n 'incomplete finalization recovery',\n );\n }\n } catch {\n failures.push(receipt.finalizationId);\n this.#scheduleFinalizationTakeover(\n receipt.finalizationId,\n this.#now().getTime() + this.#ephemeralRevokeRetryMs,\n );\n }\n }\n for (const receipt of receipts.filter(\n (item) =>\n item.mode === 'ephemeral' &&\n activeIds.has(item.receiptId) &&\n !terminalIds.has(item.receiptId) &&\n item.expiresAt !== undefined,\n )) {\n try {\n const deadline = parseTimestamp(\n receipt.expiresAt ?? '',\n 'expiresAt',\n 'revoke',\n );\n if (deadline <= this.#now().getTime()) {\n await this.#revokeReceipt(\n receipt,\n 'restart expiry recovery',\n 'expired',\n );\n } else {\n this.#scheduleReceiptExpiry(receipt, deadline);\n }\n } catch {\n failures.push(receipt.receiptId);\n }\n }\n const completed = new Set(\n events\n .filter((event) => event.type === 'rollback-complete')\n .map((event) => event.recoveryId),\n );\n const pendingRollbacks = new Map(\n events\n .filter(\n (event) =>\n event.type === 'rollback-pending' &&\n event.recoveryId &&\n !completed.has(event.recoveryId),\n )\n .map((event) => [event.recoveryId, event]),\n );\n for (const pending of pendingRollbacks.values()) {\n if (!pending.credentialId || !pending.recoveryId) continue;\n const timer = this.#pendingTimers.get(pending.recoveryId);\n if (timer) clearTimeout(timer);\n this.#pendingTimers.delete(pending.recoveryId);\n try {\n assertSafeIdentifier(pending.credentialId, 'credentialId', 'revoke');\n if (pending.receiptId && pending.finalizationReceipt) {\n await this.#abortAndRevoke(\n pending.finalizationReceipt,\n 'pending rollback recovery',\n );\n } else {\n await this.#rollback(\n pending.credentialId,\n undefined,\n 'revoke',\n pending.requiresSink === true,\n );\n }\n await this.#completeRollback(\n pending.recoveryId,\n pending.credentialId,\n pending.sinkReference,\n );\n if (pending.receiptId) {\n terminalIds.add(pending.receiptId);\n this.#leaseInvalidators.delete(pending.receiptId);\n }\n } catch {\n failures.push(pending.recoveryId);\n this.#scheduleRollback(\n pending.recoveryId,\n pending.credentialId,\n undefined,\n 'revoke',\n pending.requiresSink === true,\n pending.finalizationReceipt,\n Boolean(pending.receiptId),\n );\n }\n }\n const retired = new Set(\n events\n .filter((event) => event.type === 'retirement-complete')\n .map((event) => event.recoveryId),\n );\n for (const pending of events.filter(\n (event) =>\n event.type === 'retirement-pending' &&\n event.recoveryId &&\n !retired.has(event.recoveryId),\n )) {\n if (!pending.recoveryId || !pending.recoveryReceipt) continue;\n if (\n pending.replacementReceiptId &&\n (!activeIds.has(pending.replacementReceiptId) ||\n terminalIds.has(pending.replacementReceiptId))\n ) {\n await this.#ledger.appendEvent({\n ...this.#event(\n 'retirement-complete',\n pending.recoveryReceipt.receiptId,\n ),\n recoveryId: pending.recoveryId,\n reason: 'replacement is not active',\n });\n continue;\n }\n try {\n await this.#revokeReceipt(\n pending.recoveryReceipt,\n 'replacement retirement recovery',\n 'replaced',\n );\n await this.#ledger.appendEvent({\n ...this.#event(\n 'retirement-complete',\n pending.recoveryReceipt.receiptId,\n ),\n recoveryId: pending.recoveryId,\n });\n this.#leaseInvalidators.delete(pending.recoveryReceipt.receiptId);\n } catch {\n failures.push(pending.recoveryId);\n this.#scheduleRetirement(\n pending.recoveryId,\n pending.recoveryReceipt,\n pending.replacementReceiptId,\n );\n }\n }\n if (failures.length) {\n throw new CustodyError(\n 'PENDING_ROLLBACK_RECOVERY_FAILED',\n 'revoke',\n 'One or more pending credential rollbacks could not be recovered',\n { details: { recoveryIds: failures } },\n );\n }\n }\n\n async recoverCredential(\n credentialId: string,\n options: { requiresSink?: boolean } = {},\n ): Promise<void> {\n assertSafeIdentifier(credentialId, 'credentialId', 'revoke');\n await this.#rollback(\n credentialId,\n undefined,\n 'revoke',\n options.requiresSink === true,\n );\n }\n\n async reconcile(): Promise<CustodyReconciliation> {\n if (!this.#sink) {\n throw new CustodyError(\n 'DURABLE_SINK_REQUIRED',\n 'reconcile',\n 'Reconciliation requires a secret sink',\n );\n }\n const [receipts, events, inventory] = await Promise.all([\n this.#ledger.listReceipts(),\n this.#ledger.listEvents(),\n this.#sink.inventory(),\n ]);\n for (const entry of inventory) {\n assertSinkRecord(entry, this.#sink.name);\n assertSafeIdentifier(entry.credentialId, 'credentialId', 'reconcile');\n }\n const terminal = new Set(\n events\n .filter((event) =>\n ['revoked', 'expired', 'replaced'].includes(event.type),\n )\n .map((event) => event.receiptId),\n );\n const issued = new Set(\n events\n .filter((event) => event.type === 'issued')\n .map((event) => event.receiptId),\n );\n for (const replacement of receipts) {\n if (\n replacement.replacesReceiptId &&\n issued.has(replacement.receiptId) &&\n !terminal.has(replacement.receiptId)\n ) {\n terminal.add(replacement.replacesReceiptId);\n }\n }\n const active = receipts.filter(\n (receipt): receipt is CustodyReceipt & { sink: SecretSinkRecord } =>\n receipt.mode === 'durable' &&\n receipt.sink !== undefined &&\n issued.has(receipt.receiptId) &&\n !terminal.has(receipt.receiptId),\n );\n const orphanCutoff = this.#now().getTime() - this.#orphanGraceMs;\n return {\n checkedAt: this.#now().toISOString(),\n orphaned: inventory.filter(\n (entry) =>\n parseTimestamp(entry.storedAt, 'storedAt', 'reconcile') <=\n orphanCutoff &&\n !active.some(\n (receipt) =>\n sameSinkRecord(receipt.sink, entry) &&\n entry.credentialId === receipt.credentialId,\n ),\n ),\n missing: active.filter(\n (receipt) =>\n !inventory.some(\n (entry) =>\n sameSinkRecord(receipt.sink, entry) &&\n entry.credentialId === receipt.credentialId,\n ),\n ),\n };\n }\n\n async recoverOrphans(report: CustodyReconciliation): Promise<void> {\n if (!this.#sink) {\n throw new CustodyError(\n 'DURABLE_SINK_REQUIRED',\n 'reconcile',\n 'Orphan recovery requires a secret sink',\n );\n }\n const current = await this.reconcile();\n const requested = new Set(\n report.orphaned.map((entry) => sinkIdentity(entry, entry.credentialId)),\n );\n const confirmed = current.orphaned.filter((entry) =>\n requested.has(sinkIdentity(entry, entry.credentialId)),\n );\n for (const orphan of confirmed) {\n assertSinkRecord(orphan, this.#sink.name);\n await this.#ledger.appendEvent(\n this.#event('orphan-detected', undefined, orphan.reference),\n );\n assertSafeIdentifier(orphan.credentialId, 'credentialId', 'reconcile');\n try {\n await this.#issuer.revoke(orphan.credentialId, 'orphan recovery');\n } catch {\n throw new CustodyError(\n 'ORPHAN_REVOCATION_FAILED',\n 'reconcile',\n 'Orphan credential revocation failed',\n );\n }\n await this.#sink.remove(orphan, 'orphan recovery');\n await this.#ledger.appendEvent(\n this.#event('orphan-removed', undefined, orphan.reference),\n );\n }\n }\n\n #createLease(\n receipt: CustodyReceipt,\n ephemeralMaterial?: SecretMaterial,\n ): CredentialLease {\n let unavailable = false;\n let revocationComplete = false;\n let revocationInFlight: Promise<void> | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const revoke = async (\n reason = 'requested',\n terminalType: 'revoked' | 'expired' = 'revoked',\n ): Promise<void> => {\n unavailable = true;\n if (timer) clearTimeout(timer);\n ephemeralMaterial?.destroy();\n if (revocationComplete) return;\n if (revocationInFlight) return revocationInFlight;\n revocationInFlight = this.#revokeReceipt(receipt, reason, terminalType)\n .then(() => {\n revocationComplete = true;\n this.#leaseInvalidators.delete(receipt.receiptId);\n })\n .finally(() => {\n revocationInFlight = undefined;\n });\n return revocationInFlight;\n };\n const invalidate = (): void => {\n unavailable = true;\n if (timer) clearTimeout(timer);\n ephemeralMaterial?.destroy();\n };\n this.#leaseInvalidators.set(receipt.receiptId, invalidate);\n\n if (receipt.mode === 'ephemeral' && receipt.expiresAt) {\n const deadline = parseTimestamp(receipt.expiresAt, 'expiresAt', 'issue');\n const schedule = (delay: number): void => {\n timer = setTimeout(\n () => {\n const remaining = deadline - this.#now().getTime();\n if (remaining > 0) {\n schedule(Math.min(remaining, MAX_TIMER_DELAY_MS));\n return;\n }\n revoke('expired', 'expired').catch((cause) => {\n try {\n this.#onBackgroundError?.(\n cause instanceof CustodyError\n ? cause\n : new CustodyError(\n 'EPHEMERAL_CLEANUP_FAILED',\n 'revoke',\n 'Ephemeral credential cleanup failed',\n { cause },\n ),\n );\n } catch {\n // Observability hooks must never interrupt revocation retry.\n } finally {\n schedule(this.#ephemeralRevokeRetryMs);\n }\n });\n },\n Math.min(Math.max(0, delay), MAX_TIMER_DELAY_MS),\n );\n timer.unref?.();\n };\n schedule(deadline - this.#now().getTime());\n }\n\n return {\n receipt: structuredClone(receipt),\n withEnvironment: async (\n variableName: string,\n operation: () => void | Promise<void>,\n ): Promise<void> => {\n this.#assertLeaseAvailable(receipt, unavailable);\n if (!ENVIRONMENT_VARIABLE.test(variableName)) {\n throw new CustodyError(\n 'INVALID_ENVIRONMENT_VARIABLE',\n 'inject',\n 'Environment variable name is invalid',\n );\n }\n let material = ephemeralMaterial;\n if (!material) {\n if (!this.#sink || !receipt.sink) {\n throw new CustodyError(\n 'DURABLE_SINK_REQUIRED',\n 'retrieve',\n 'Durable credential retrieval requires a secret sink record',\n );\n }\n material = await this.#sink.retrieve(receipt.sink);\n }\n try {\n this.#assertLeaseAvailable(receipt, unavailable);\n await withEnvironmentSecret(material, variableName, operation, {\n expiresAt: receipt.expiresAt,\n });\n } finally {\n if (!ephemeralMaterial) material.destroy();\n }\n },\n withChildProcess: async (\n options: CredentialChildProcessOptions,\n ): Promise<CredentialChildProcessResult> => {\n this.#assertLeaseAvailable(receipt, unavailable);\n let material = ephemeralMaterial;\n if (!material) {\n if (!this.#sink || !receipt.sink) {\n throw new CustodyError(\n 'DURABLE_SINK_REQUIRED',\n 'retrieve',\n 'Durable credential retrieval requires a secret sink record',\n );\n }\n material = await this.#sink.retrieve(receipt.sink);\n }\n try {\n this.#assertLeaseAvailable(receipt, unavailable);\n return await runCredentialChildProcess(material, options, {\n expiresAt: receipt.expiresAt,\n });\n } catch (cause) {\n if (\n cause instanceof CustodyError &&\n cause.code === 'CREDENTIAL_CHILD_PROCESS_CLEANUP_FAILED'\n ) {\n invalidate();\n await this.#recoverChildProcessCleanupFailure(receipt);\n }\n throw cause;\n } finally {\n if (!ephemeralMaterial) material.destroy();\n }\n },\n revoke: (reason?: string) => revoke(reason),\n };\n }\n\n #assertLeaseAvailable(receipt: CustodyReceipt, unavailable: boolean): void {\n const expired =\n receipt.expiresAt !== undefined &&\n parseTimestamp(receipt.expiresAt, 'expiresAt', 'inject') <=\n this.#now().getTime();\n if (unavailable || expired) {\n throw new CustodyError(\n 'CREDENTIAL_REVOKED',\n 'inject',\n 'Credential is no longer available',\n );\n }\n }\n\n async #revokeReceipt(\n receipt: CustodyReceipt,\n reason: string,\n terminalType: 'revoked' | 'expired' | 'replaced' = 'revoked',\n ): Promise<void> {\n const failures: string[] = [];\n try {\n await this.#issuer.revoke(receipt.credentialId, reason);\n } catch {\n failures.push('issuer');\n }\n if (!failures.length && receipt.sink && this.#sink) {\n try {\n await this.#sink.remove(receipt.sink, reason);\n } catch {\n failures.push('sink');\n }\n }\n if (failures.length) {\n throw new CustodyError(\n 'CREDENTIAL_REVOCATION_FAILED',\n 'revoke',\n 'Credential revocation did not complete',\n { details: { failedOperations: failures } },\n );\n }\n await this.#appendEventEventually(\n this.#event(terminalType, receipt.receiptId, undefined, reason),\n );\n }\n\n async #rollback(\n credentialId: string,\n sinkRecord: SecretSinkRecord | undefined,\n failedStage: CustodyStage,\n requiresSink = false,\n ): Promise<void> {\n const failures: string[] = [];\n try {\n await this.#issuer.revoke(credentialId, `rollback after ${failedStage}`);\n } catch {\n failures.push('issuer');\n }\n if (!failures.length && requiresSink && !this.#sink) {\n failures.push('sink');\n }\n if (!failures.length && requiresSink && this.#sink) {\n try {\n if (sinkRecord) {\n await this.#sink.remove(sinkRecord, `rollback after ${failedStage}`);\n } else {\n await this.#sink.removeByCredentialId(\n credentialId,\n `rollback after ${failedStage}`,\n );\n }\n } catch {\n failures.push('sink');\n }\n }\n if (failures.length) {\n throw new CustodyError(\n 'CUSTODY_ROLLBACK_FAILED',\n 'revoke',\n 'Credential rollback did not complete',\n { details: { failedOperations: failures, failedStage } },\n );\n }\n }\n\n async #abortAndRevoke(\n receipt: CustodyReceipt,\n reason: string,\n ): Promise<void> {\n const failures: string[] = [];\n try {\n await this.#finalizer.abort({\n receipt: structuredClone(receipt),\n reason,\n });\n } catch {\n failures.push('finalizer');\n }\n try {\n await this.#revokeReceipt(receipt, reason);\n } catch {\n failures.push('credential');\n }\n if (failures.length) {\n throw new CustodyError(\n 'CUSTODY_ROLLBACK_FAILED',\n 'revoke',\n 'Finalization rollback did not complete',\n { details: { failedOperations: failures } },\n );\n }\n }\n\n async #recoverChildProcessCleanupFailure(\n receipt: CustodyReceipt,\n ): Promise<void> {\n const reason = 'child process cleanup could not be verified';\n try {\n await this.#abortAndRevoke(receipt, reason);\n this.#leaseInvalidators.delete(receipt.receiptId);\n return;\n } catch (cause) {\n const recoveryId = randomUUID();\n const pendingEvent: CustodyEvent = {\n ...this.#event(\n 'rollback-pending',\n receipt.receiptId,\n receipt.sink?.reference,\n reason,\n ),\n eventId: recoveryId,\n recoveryId,\n credentialId: receipt.credentialId,\n requiresSink: receipt.mode === 'durable',\n finalizationReceipt: structuredClone(receipt),\n };\n await this.#persistRollbackIntent(pendingEvent);\n this.#scheduleRollback(\n recoveryId,\n receipt.credentialId,\n receipt.sink,\n 'inject',\n receipt.mode === 'durable',\n receipt,\n true,\n );\n try {\n this.#onBackgroundError?.(\n new CustodyError(\n 'CREDENTIAL_CHILD_PROCESS_CLEANUP_PENDING',\n 'revoke',\n 'Credential revocation is pending after child process cleanup failure',\n { cause, details: { recoveryId } },\n ),\n );\n } catch {\n // Observability cannot interrupt cleanup retry.\n }\n }\n }\n\n async #persistRollbackIntent(event: CustodyEvent): Promise<void> {\n for (;;) {\n try {\n await this.#ledger.appendEvent(event);\n return;\n } catch (cause) {\n try {\n if (\n (await this.#ledger.listEvents()).some(\n (existing) =>\n existing.eventId === event.eventId &&\n sameCustodyEvent(existing, event),\n )\n ) {\n return;\n }\n } catch {\n // Retry persistence when acknowledgement state is unavailable.\n }\n try {\n this.#onBackgroundError?.(\n new CustodyError(\n 'ROLLBACK_INTENT_PERSISTENCE_FAILED',\n 'record',\n 'Credential rollback intent persistence is retrying',\n { cause, details: { recoveryId: event.recoveryId } },\n ),\n );\n } catch {\n // Observability cannot interrupt durable recovery persistence.\n }\n await new Promise<void>((resolve) => {\n // This timer deliberately remains referenced: returning or allowing\n // normal process exit before the recovery identity is durable would\n // strand a credential that may have escaped child-process custody.\n setTimeout(resolve, this.#ephemeralRevokeRetryMs);\n });\n }\n }\n }\n\n #scheduleRollback(\n recoveryId: string,\n credentialId: string,\n sinkRecord: SecretSinkRecord | undefined,\n failedStage: CustodyStage,\n requiresSink: boolean,\n finalizationReceipt?: CustodyReceipt,\n receiptRecorded = false,\n ): void {\n if (this.#pendingTimers.has(recoveryId)) return;\n const timer = setTimeout(() => {\n this.#pendingTimers.delete(recoveryId);\n Promise.resolve()\n .then(async () => {\n if (receiptRecorded && finalizationReceipt) {\n await this.#abortAndRevoke(\n finalizationReceipt,\n `rollback after ${failedStage}`,\n );\n } else {\n await this.#rollback(\n credentialId,\n sinkRecord,\n failedStage,\n requiresSink,\n );\n }\n })\n .then(() =>\n this.#completeRollback(\n recoveryId,\n credentialId,\n sinkRecord?.reference,\n ),\n )\n .then(() => {\n if (receiptRecorded && finalizationReceipt) {\n this.#leaseInvalidators.delete(finalizationReceipt.receiptId);\n }\n })\n .catch((cause) => {\n try {\n this.#onBackgroundError?.(\n cause instanceof CustodyError\n ? cause\n : new CustodyError(\n 'CUSTODY_ROLLBACK_FAILED',\n 'revoke',\n 'Credential rollback retry failed',\n { cause },\n ),\n );\n } catch {\n // Observability hooks must never interrupt cleanup retry.\n } finally {\n this.#scheduleRollback(\n recoveryId,\n credentialId,\n sinkRecord,\n failedStage,\n requiresSink,\n finalizationReceipt,\n receiptRecorded,\n );\n }\n });\n }, this.#ephemeralRevokeRetryMs);\n this.#pendingTimers.set(recoveryId, timer);\n timer.unref?.();\n }\n\n #scheduleRetirement(\n recoveryId: string,\n receipt: CustodyReceipt,\n replacementReceiptId?: string,\n ): void {\n if (this.#pendingTimers.has(recoveryId)) return;\n const timer = setTimeout(() => {\n this.#pendingTimers.delete(recoveryId);\n this.#ledger\n .listEvents()\n .then(async (events) => {\n if (\n replacementReceiptId &&\n (!events.some(\n (event) =>\n event.receiptId === replacementReceiptId &&\n event.type === 'issued',\n ) ||\n events.some(\n (event) =>\n event.receiptId === replacementReceiptId &&\n ['revoked', 'expired', 'replaced'].includes(event.type),\n ))\n ) {\n return;\n }\n await this.#revokeReceipt(\n receipt,\n 'replacement retirement retry',\n 'replaced',\n );\n this.#leaseInvalidators.delete(receipt.receiptId);\n })\n .then(() =>\n this.#ledger.appendEvent({\n ...this.#event('retirement-complete', receipt.receiptId),\n recoveryId,\n }),\n )\n .catch(() =>\n this.#scheduleRetirement(recoveryId, receipt, replacementReceiptId),\n );\n }, this.#ephemeralRevokeRetryMs);\n this.#pendingTimers.set(recoveryId, timer);\n timer.unref?.();\n }\n\n #scheduleReceiptExpiry(receipt: CustodyReceipt, deadline: number): void {\n if (this.#pendingTimers.has(receipt.receiptId)) return;\n const delay = Math.min(\n Math.max(0, deadline - this.#now().getTime()),\n MAX_TIMER_DELAY_MS,\n );\n const timer = setTimeout(() => {\n this.#pendingTimers.delete(receipt.receiptId);\n if (deadline > this.#now().getTime()) {\n this.#scheduleReceiptExpiry(receipt, deadline);\n return;\n }\n this.#revokeReceipt(receipt, 'restart expiry recovery', 'expired').catch(\n () => this.#scheduleReceiptExpiry(receipt, deadline),\n );\n }, delay);\n this.#pendingTimers.set(receipt.receiptId, timer);\n timer.unref?.();\n }\n\n #scheduleFinalizationTakeover(recoveryId: string, deadline: number): void {\n if (this.#pendingTimers.has(recoveryId)) return;\n const timer = setTimeout(\n () => {\n this.#pendingTimers.delete(recoveryId);\n this.recoverPendingRollbacks().catch((cause) => {\n try {\n this.#onBackgroundError?.(\n cause instanceof CustodyError\n ? cause\n : new CustodyError(\n 'PENDING_ROLLBACK_RECOVERY_FAILED',\n 'revoke',\n 'Finalization takeover recovery failed',\n { cause },\n ),\n );\n } catch {\n // Observability cannot interrupt takeover recovery.\n }\n });\n },\n Math.min(\n Math.max(0, deadline - this.#now().getTime()),\n MAX_TIMER_DELAY_MS,\n ),\n );\n this.#pendingTimers.set(recoveryId, timer);\n timer.unref?.();\n }\n\n async #completeRollback(\n recoveryId: string,\n credentialId: string,\n sinkReference?: string,\n ): Promise<void> {\n await this.#ledger.appendEvent({\n ...this.#event(\n 'rollback-complete',\n undefined,\n sinkReference,\n 'credential rollback completed',\n ),\n recoveryId,\n credentialId,\n });\n }\n\n async #appendEventEventually(event: CustodyEvent): Promise<void> {\n try {\n await this.#ledger.appendEvent(event);\n } catch (cause) {\n const recoveryId = `event-${event.eventId}`;\n if (this.#pendingTimers.has(recoveryId)) return;\n const timer = setTimeout(() => {\n this.#pendingTimers.delete(recoveryId);\n this.#appendEventEventually(event).catch(() => undefined);\n }, this.#ephemeralRevokeRetryMs);\n this.#pendingTimers.set(recoveryId, timer);\n try {\n this.#onBackgroundError?.(\n new CustodyError(\n 'CUSTODY_EVENT_RECORD_FAILED',\n 'record',\n 'Custody terminal event recording is pending retry',\n { cause },\n ),\n );\n } catch {\n // Observability hooks must never interrupt event retry.\n }\n }\n }\n\n async #findReceipt(receiptId: string): Promise<CustodyReceipt> {\n assertSafeIdentifier(receiptId, 'receiptId', 'record');\n const receipt = (await this.#ledger.listReceipts()).find(\n (candidate) => candidate.receiptId === receiptId,\n );\n if (!receipt) {\n throw new CustodyError(\n 'CUSTODY_RECEIPT_NOT_FOUND',\n 'record',\n 'Custody receipt was not found',\n );\n }\n return receipt;\n }\n\n #event(\n type: CustodyEventType,\n receiptId?: string,\n sinkReference?: string,\n reason?: string,\n ): CustodyEvent {\n return {\n eventId: randomUUID(),\n type,\n occurredAt: this.#now().toISOString(),\n ...(receiptId ? { receiptId } : {}),\n ...(sinkReference ? { sinkReference } : {}),\n ...(reason ? { reason: redactCredentialText(reason) } : {}),\n };\n }\n}\n\nexport async function withEnvironmentSecret(\n material: SecretMaterial,\n variableName: string,\n operation: () => void | Promise<void>,\n bounds?: { expiresAt?: string },\n): Promise<void> {\n if (!ENVIRONMENT_VARIABLE.test(variableName)) {\n throw new CustodyError(\n 'INVALID_ENVIRONMENT_VARIABLE',\n 'inject',\n 'Environment variable name is invalid',\n );\n }\n if (bounds?.expiresAt) {\n parseTimestamp(bounds.expiresAt, 'expiresAt', 'inject');\n throw new CustodyError(\n 'EXPIRING_ENVIRONMENT_CALLBACK_UNSAFE',\n 'inject',\n 'Expiring credentials require bounded child-process injection',\n );\n }\n await withEnvironmentLock(async () => {\n const existed = Object.hasOwn(process.env, variableName);\n const previous = process.env[variableName];\n try {\n await material.use(async (value) => {\n process.env[variableName] = value;\n await operation();\n });\n } catch (cause) {\n if (cause instanceof CustodyError) throw cause;\n throw new CustodyError(\n 'SECRET_OPERATION_FAILED',\n 'inject',\n 'Bounded secret operation failed',\n { cause },\n );\n } finally {\n if (existed) process.env[variableName] = previous;\n else delete process.env[variableName];\n }\n });\n}\n\nexport async function runCredentialChildProcess(\n material: SecretMaterial,\n options: CredentialChildProcessOptions,\n bounds?: { expiresAt?: string },\n): Promise<CredentialChildProcessResult> {\n if (\n options.trust !== 'cooperative-process-group' ||\n !options.command ||\n !ENVIRONMENT_VARIABLE.test(options.environmentVariable)\n ) {\n throw new CustodyError(\n 'INVALID_CHILD_PROCESS_OPTIONS',\n 'inject',\n 'Credential child-process options are invalid',\n );\n }\n if (process.platform === 'win32') {\n throw new CustodyError(\n 'CREDENTIAL_CHILD_PROCESS_UNSUPPORTED',\n 'inject',\n 'Credential child-process group custody is unavailable on this platform',\n );\n }\n const timeoutMs = options.timeoutMs ?? 60_000;\n const maxOutputBytes = options.maxOutputBytes ?? 1024 * 1024;\n if (\n !Number.isFinite(timeoutMs) ||\n !Number.isFinite(maxOutputBytes) ||\n timeoutMs <= 0 ||\n timeoutMs > MAX_TIMER_DELAY_MS ||\n maxOutputBytes <= 0\n ) {\n throw new CustodyError(\n 'INVALID_CHILD_PROCESS_BOUNDS',\n 'inject',\n 'Credential child-process bounds must be positive',\n );\n }\n\n let result: CredentialChildProcessResult | undefined;\n await material.use(async (secret) => {\n const expiryLimit = bounds?.expiresAt\n ? parseTimestamp(bounds.expiresAt, 'expiresAt', 'inject') - Date.now()\n : Number.POSITIVE_INFINITY;\n const effectiveTimeout = Math.min(timeoutMs, expiryLimit);\n if (effectiveTimeout <= 0) {\n throw new CustodyError(\n 'CREDENTIAL_EXPIRED',\n 'inject',\n 'Credential expired before child-process launch',\n );\n }\n\n const childEnvironment = await withEnvironmentLock(() => ({\n ...process.env,\n ...options.environment,\n [options.environmentVariable]: secret,\n }));\n result = await new Promise<CredentialChildProcessResult>(\n (resolve, reject) => {\n const child = spawn(options.command, [...(options.args ?? [])], {\n cwd: options.cwd,\n detached: true,\n env: childEnvironment,\n shell: false,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n const stdout: Buffer[] = [];\n const stderr: Buffer[] = [];\n let outputBytes = 0;\n let outputExceeded = false;\n let timedOut = false;\n let settled = false;\n let escalationTimer: ReturnType<typeof setTimeout> | undefined;\n\n const terminateGroup = (signal: NodeJS.Signals): void => {\n if (child.pid) {\n try {\n process.kill(-child.pid, signal);\n return;\n } catch {\n // The group may already have exited; use the child handle.\n }\n }\n child.kill(signal);\n };\n const waitForGroupExit = async (): Promise<boolean> => {\n if (!child.pid) return true;\n const deadline = Date.now() + 2_000;\n while (Date.now() < deadline) {\n try {\n process.kill(-child.pid, 0);\n } catch (cause) {\n if ((cause as NodeJS.ErrnoException).code === 'ESRCH')\n return true;\n }\n await new Promise((next) => setTimeout(next, 10));\n }\n return false;\n };\n const beginTermination = (): void => {\n terminateGroup('SIGTERM');\n if (!escalationTimer) {\n escalationTimer = setTimeout(() => {\n escalationTimer = undefined;\n terminateGroup('SIGKILL');\n }, 1_000);\n escalationTimer.unref?.();\n }\n };\n const timer = setTimeout(() => {\n timedOut = true;\n beginTermination();\n }, effectiveTimeout);\n timer.unref?.();\n\n const capture =\n (target: Buffer[]) =>\n (chunk: Buffer): void => {\n outputBytes += chunk.length;\n if (outputBytes > maxOutputBytes) {\n timedOut = true;\n outputExceeded = true;\n target.length = 0;\n beginTermination();\n return;\n }\n if (outputExceeded) return;\n target.push(Buffer.from(chunk));\n };\n child.stdout.on('data', capture(stdout));\n child.stderr.on('data', capture(stderr));\n child.once('error', () => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (escalationTimer) clearTimeout(escalationTimer);\n terminateGroup('SIGKILL');\n reject(\n new CustodyError(\n 'CREDENTIAL_CHILD_PROCESS_FAILED',\n 'inject',\n 'Credential child process could not start',\n ),\n );\n });\n child.once('close', async (exitCode, signal) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (escalationTimer) clearTimeout(escalationTimer);\n // The leader may have exited while descendants retained the secret.\n // Kill the original process group before returning any output.\n terminateGroup('SIGTERM');\n terminateGroup('SIGKILL');\n if (!(await waitForGroupExit())) {\n reject(\n new CustodyError(\n 'CREDENTIAL_CHILD_PROCESS_CLEANUP_FAILED',\n 'inject',\n 'Credential child process group cleanup could not be verified',\n ),\n );\n return;\n }\n resolve({\n exitCode,\n signal,\n stdout: outputExceeded\n ? REDACTED\n : redactCredentialText(Buffer.concat(stdout).toString('utf8'), [\n secret,\n ]),\n stderr: outputExceeded\n ? REDACTED\n : redactCredentialText(Buffer.concat(stderr).toString('utf8'), [\n secret,\n ]),\n timedOut,\n });\n });\n },\n );\n });\n\n if (!result) {\n throw new CustodyError(\n 'CREDENTIAL_CHILD_PROCESS_FAILED',\n 'inject',\n 'Credential child process did not return a result',\n );\n }\n return result;\n}\n\nexport async function verifyCustodyReceiptAttestation(\n receipt: CustodyReceipt,\n material: SecretMaterial,\n publicKey: KeyObject,\n): Promise<boolean> {\n if (\n receipt.attestation.algorithm !== 'Ed25519' ||\n publicKey.type !== 'public' ||\n publicKey.asymmetricKeyType !== 'ed25519' ||\n !/^[A-Za-z0-9_-]{86}$/.test(receipt.attestation.signature) ||\n Buffer.from(receipt.attestation.signature, 'base64url').byteLength !== 64 ||\n Buffer.from(receipt.attestation.signature, 'base64url').toString(\n 'base64url',\n ) !== receipt.attestation.signature\n ) {\n return false;\n }\n let verified = false;\n await material.use((plaintext) => {\n const payload = custodyAttestationPayload(\n receipt,\n credentialCommitment(plaintext),\n );\n try {\n verified = verifyPayload(\n null,\n Buffer.from(payload),\n publicKey,\n Buffer.from(receipt.attestation.signature, 'base64url'),\n );\n } catch {\n verified = false;\n }\n });\n return verified;\n}\n\nexport function redactCredentialText(\n input: string,\n knownSecrets: readonly string[] = [],\n): string {\n let output = input;\n for (const secret of knownSecrets.filter(Boolean)) {\n output = output.replaceAll(secret, REDACTED);\n }\n for (const { pattern, replacement } of CREDENTIAL_PATTERNS) {\n output = output.replace(pattern, replacement);\n }\n return output;\n}\n\nexport function redactCredentialValues(\n value: unknown,\n knownSecrets: readonly string[] = [],\n): unknown {\n if (value instanceof SecretMaterial) return REDACTED;\n if (typeof value === 'string') {\n return redactCredentialText(value, knownSecrets);\n }\n if (Array.isArray(value)) {\n return value.map((entry) => redactCredentialValues(entry, knownSecrets));\n }\n if (value && typeof value === 'object') {\n return Object.fromEntries(\n Object.entries(value).map(([key, entry]) => [\n redactCredentialText(key, knownSecrets),\n redactCredentialValues(entry, knownSecrets),\n ]),\n );\n }\n return value;\n}\n\nasync function sanitizeCustodyCause(\n cause: unknown,\n materials: ReadonlyArray<SecretMaterial | undefined>,\n): Promise<unknown> {\n if (!(cause instanceof CustodyError)) return cause;\n const knownSecrets: string[] = [];\n for (const material of materials) {\n if (!material || material.destroyed) continue;\n await material.use((value) => {\n knownSecrets.push(value);\n });\n }\n const safeCode =\n redactCredentialText(cause.code, knownSecrets) === cause.code\n ? cause.code\n : 'INVALID_CUSTODY_ERROR_CODE';\n return new CustodyError(\n safeCode,\n cause.stage,\n redactCredentialText(cause.message, knownSecrets),\n cause.details\n ? {\n details: redactCredentialValues(\n cause.details,\n knownSecrets,\n ) as Record<string, unknown>,\n }\n : undefined,\n );\n}\n\nfunction credentialCommitment(plaintext: string): string {\n return createHash('sha256').update(plaintext).digest('base64url');\n}\n\nfunction custodyAttestationPayload(\n receipt: Omit<CustodyReceipt, 'attestation'> | CustodyReceipt,\n commitment: string,\n identity?: Omit<CustodyReceiptAttestation, 'signature'>,\n): string {\n const { attestation, ...unsigned } = receipt as CustodyReceipt;\n return JSON.stringify(\n canonicalize({\n schema: 'happyvertical.credential-custody-attestation.v1',\n receipt: unsigned,\n attestation: identity ?? {\n algorithm: attestation.algorithm,\n attestor: attestation.attestor,\n keyId: attestation.keyId,\n },\n credentialCommitment: commitment,\n }),\n );\n}\n\nfunction canonicalize(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(canonicalize);\n if (value && typeof value === 'object') {\n return Object.fromEntries(\n Object.entries(value)\n .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))\n .map(([key, entry]) => [key, canonicalize(entry)]),\n );\n }\n return value;\n}\n\nfunction sameCustodyEvent(left: CustodyEvent, right: CustodyEvent): boolean {\n return (\n JSON.stringify(canonicalize(left)) === JSON.stringify(canonicalize(right))\n );\n}\n\nfunction assertAttribution(attribution: CustodyAttribution): void {\n assertSafeIdentifier(attribution.actor, 'attribution.actor', 'issue');\n assertSafeIdentifier(attribution.runtime, 'attribution.runtime', 'issue');\n assertSafeIdentifier(attribution.session, 'attribution.session', 'issue');\n}\n\nfunction assertSinkRecord(record: SecretSinkRecord, sinkName: string): void {\n if (record.sinkName !== sinkName) {\n throw new CustodyError(\n 'SINK_IDENTITY_MISMATCH',\n 'store',\n 'Secret sink returned a mismatched identity',\n );\n }\n assertSafeIdentifier(record.sinkName, 'sinkName', 'store');\n assertSafeIdentifier(record.reference, 'sinkReference', 'store');\n assertSafeIdentifier(record.version, 'sinkVersion', 'store');\n if (!Number.isFinite(new Date(record.storedAt).getTime())) {\n throw new CustodyError(\n 'INVALID_SINK_TIMESTAMP',\n 'store',\n 'Secret sink returned an invalid timestamp',\n );\n }\n}\n\nfunction assertSafeIdentifier(\n value: string,\n field: string,\n stage: CustodyStage,\n): void {\n if (!SAFE_IDENTIFIER.test(value) || redactCredentialText(value) !== value) {\n throw new CustodyError(\n 'UNSAFE_CUSTODY_IDENTIFIER',\n stage,\n `Custody ${field} must be a non-secret identifier`,\n );\n }\n}\n\nfunction sanitizeMetadata(\n metadata: Readonly<Record<string, string>> | undefined,\n): Readonly<Record<string, string>> {\n return Object.freeze(\n Object.fromEntries(\n Object.entries(metadata ?? {}).map(([key, value]) => {\n if (redactCredentialText(key) !== key) {\n throw new CustodyError(\n 'UNSAFE_CUSTODY_METADATA_KEY',\n 'issue',\n 'Custody metadata key must not contain credential material',\n );\n }\n return [key, redactCredentialText(value)];\n }),\n ),\n );\n}\n\nfunction parseTimestamp(\n value: string,\n field: string,\n stage: CustodyStage,\n): number {\n const timestamp = new Date(value).getTime();\n if (!Number.isFinite(timestamp)) {\n throw new CustodyError(\n 'INVALID_CUSTODY_TIMESTAMP',\n stage,\n `Custody ${field} timestamp is invalid`,\n );\n }\n return timestamp;\n}\n\nfunction sameSinkRecord(\n left: SecretSinkRecord,\n right: SecretSinkRecord,\n): boolean {\n return (\n left.sinkName === right.sinkName &&\n left.reference === right.reference &&\n left.version === right.version &&\n left.storedAt === right.storedAt\n );\n}\n\nfunction sinkIdentity(record: SecretSinkRecord, credentialId?: string): string {\n return [\n record.sinkName,\n record.reference,\n record.version,\n record.storedAt,\n credentialId ?? '',\n ].join('\\u0000');\n}\n","import type {\n AWSKMSSecretStoreOptions,\n AzureKeyVaultSecretStoreOptions,\n DatabaseSecretStoreOptions,\n GetSecretStoreOptions,\n SecretStore,\n VaultSecretStoreOptions,\n} from './types.js';\n\n/**\n * Type guard for database options\n */\nexport function isDatabaseOptions(\n opts: GetSecretStoreOptions,\n): opts is DatabaseSecretStoreOptions {\n return opts.type === 'database';\n}\n\n/**\n * Type guard for AWS KMS options\n */\nexport function isAWSKMSOptions(\n opts: GetSecretStoreOptions,\n): opts is AWSKMSSecretStoreOptions {\n return opts.type === 'aws-kms';\n}\n\n/**\n * Type guard for Vault options\n */\nexport function isVaultOptions(\n opts: GetSecretStoreOptions,\n): opts is VaultSecretStoreOptions {\n return opts.type === 'vault';\n}\n\n/**\n * Type guard for Azure Key Vault options\n */\nexport function isAzureKeyVaultOptions(\n opts: GetSecretStoreOptions,\n): opts is AzureKeyVaultSecretStoreOptions {\n return opts.type === 'azure-keyvault';\n}\n\n/**\n * Factory function to create secret store instances\n *\n * Uses lazy imports to avoid loading unnecessary adapters and their dependencies.\n *\n * @example\n * ```typescript\n * // Database-backed store with env-based AMK\n * const store = await getSecretStore({\n * type: 'database',\n * db,\n * amk: {\n * provider: 'env',\n * keyEnvVar: 'SECRET_MASTER_KEY',\n * keyId: 'amk-v1'\n * }\n * });\n *\n * // Encrypt a secret\n * const envelope = await store.encrypt('tenant-123', 'api-key', 'synthetic-secret');\n *\n * // Decrypt\n * const decrypted = await store.decrypt('tenant-123', envelope);\n * ```\n */\nexport async function getSecretStore(\n options: GetSecretStoreOptions,\n): Promise<SecretStore> {\n if (isDatabaseOptions(options)) {\n const { DatabaseSecretStore } = await import('../adapters/database.js');\n const store = new DatabaseSecretStore(options);\n await store.initialize();\n return store;\n }\n\n if (isAWSKMSOptions(options)) {\n // Phase 2: AWS KMS adapter\n throw new Error(\n 'AWS KMS adapter not yet implemented. Coming in a future release.',\n );\n }\n\n if (isVaultOptions(options)) {\n // Phase 2: HashiCorp Vault adapter\n throw new Error(\n 'HashiCorp Vault adapter not yet implemented. Coming in a future release.',\n );\n }\n\n if (isAzureKeyVaultOptions(options)) {\n // Phase 2: Azure Key Vault adapter\n throw new Error(\n 'Azure Key Vault adapter not yet implemented. Coming in a future release.',\n );\n }\n\n throw new Error(\n `Unsupported secret store type: ${(options as { type: string }).type}`,\n );\n}\n","/**\n * @happyvertical/secrets\n *\n * Envelope encryption for per-tenant secret management with pluggable backends.\n *\n * @example\n * ```typescript\n * import { getSecretStore } from '@happyvertical/secrets';\n * import { getDatabase } from '@happyvertical/sql';\n *\n * // Get a database connection\n * const db = await getDatabase({ type: 'sqlite', url: ':memory:' });\n *\n * // Create a secret store\n * const store = await getSecretStore({\n * type: 'database',\n * db,\n * amk: {\n * provider: 'env',\n * keyEnvVar: 'SECRET_MASTER_KEY', // 64 hex chars (32 bytes)\n * keyId: 'amk-v1'\n * }\n * });\n *\n * // Encrypt a secret for a tenant\n * const envelope = await store.encrypt('tenant-123', 'api-key', 'synthetic-secret');\n *\n * // Decrypt the secret\n * const { value } = await store.decrypt('tenant-123', envelope);\n * // Use value without logging or retaining plaintext.\n *\n * // Rotate tenant's encryption key\n * await store.rotateTenantKey('tenant-123');\n * ```\n *\n * @packageDocumentation\n */\n\n// Direct adapter exports (for advanced usage)\nexport { DatabaseSecretStore } from './adapters/database.js';\nexport type {\n CredentialChildProcessOptions,\n CredentialChildProcessResult,\n CredentialCustodyFinalizer,\n CredentialCustodyOptions,\n CredentialIssuanceMode,\n CredentialIssueRequest,\n CredentialIssuer,\n CredentialLease,\n CredentialReceiptAttestor,\n CredentialSecretSink,\n CredentialVerifier,\n CustodyAttribution,\n CustodyEvent,\n CustodyEventType,\n CustodyIssuanceRequest,\n CustodyLedger,\n CustodyReceipt,\n CustodyReceiptAttestation,\n CustodyReconciliation,\n CustodyStage,\n IssuedCredential,\n SecretSinkInventoryEntry,\n SecretSinkRecord,\n} from './shared/custody.js';\nexport {\n CredentialCustody,\n CustodyError,\n Ed25519CustodyReceiptAttestor,\n InMemoryCustodyLedger,\n redactCredentialText,\n redactCredentialValues,\n runCredentialChildProcess,\n SecretMaterial,\n verifyCustodyReceiptAttestation,\n withEnvironmentSecret,\n} from './shared/custody.js';\n// Envelope encryption primitives\nexport { EnvelopeEncryption } from './shared/envelope.js';\n// Error classes\nexport {\n AMKUnavailableError,\n DecryptionError,\n EncryptionError,\n InvalidKeyFormatError,\n KeyNotFoundError,\n KeyRotationError,\n SecretError,\n StoreNotInitializedError,\n TenantKeyMissingError,\n} from './shared/errors.js';\n// Factory function\nexport {\n getSecretStore,\n isAWSKMSOptions,\n isAzureKeyVaultOptions,\n isDatabaseOptions,\n isVaultOptions,\n} from './shared/factory.js';\n// Core types\nexport type {\n // Adapter options\n AMKConfig,\n ApplicationMasterKey,\n AWSKMSSecretStoreOptions,\n AzureKeyVaultSecretStoreOptions,\n DatabaseSecretStoreOptions,\n DecryptedSecret,\n EncryptedEnvelope,\n EncryptOptions,\n GetSecretStoreOptions,\n SecretAdapterType,\n SecretStore,\n SecretStoreEvent,\n SecretStoreEventListener,\n SecretStoreEventType,\n TenantDataEncryptionKey,\n Unsubscribe,\n VaultSecretStoreOptions,\n} from './shared/types.js';\n\n/** @internal */\nexport const PACKAGE_VERSION_INITIALIZED = true;\n"],"mappings":"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"}