Files
obsidian-mcp-server/docs/plans/2025-10-26-crypto-compatibility-design.md

6.3 KiB

Cross-Environment Crypto Compatibility Design

Date: 2025-10-26 Status: Approved Author: Design session with user

Problem Statement

The generateApiKey() function in src/utils/auth-utils.ts uses crypto.getRandomValues() which works in Electron/browser environments but fails in Node.js test environment with "ReferenceError: crypto is not defined". This causes test failures during CI/CD builds.

Goals

  1. Make code work cleanly in both browser/Electron and Node.js environments
  2. Use only built-in APIs (no additional npm dependencies)
  3. Maintain cryptographic security guarantees
  4. Keep production runtime behavior unchanged
  5. Enable tests to pass without mocking

Constraints

  • Must use only built-in APIs (no third-party packages)
  • Must maintain existing API surface of generateApiKey()
  • Must preserve cryptographic security in both environments
  • Must work with current Node.js version in project

Architecture

Component Overview

The solution uses an abstraction layer pattern with environment detection:

  1. crypto-adapter.ts - New utility that provides unified crypto access
  2. auth-utils.ts - Modified to use the adapter
  3. crypto-adapter.test.ts - New test file for adapter verification

Design Decisions

Why abstraction layer over other approaches:

  • vs Runtime detection in auth-utils: Separates concerns, makes crypto access reusable
  • vs Jest polyfill: Makes production code environment-aware instead of test-specific workarounds
  • vs Dynamic require(): Cleaner than inline environment detection, easier to test

Why Web Crypto API standard:

  • Node.js 15+ includes crypto.webcrypto implementing the same Web Crypto API as browsers
  • Allows using identical API (getRandomValues()) in both environments
  • Standards-based approach, future-proof

Implementation

File: src/utils/crypto-adapter.ts (new)

/**
 * Cross-environment crypto adapter
 * Provides unified access to cryptographically secure random number generation
 * Works in both browser/Electron (window.crypto) and Node.js (crypto.webcrypto)
 */

/**
 * Gets the appropriate Crypto interface for the current environment
 * @returns Crypto interface with getRandomValues method
 * @throws Error if no crypto API is available
 */
function getCrypto(): Crypto {
	// Browser/Electron environment
	if (typeof window !== 'undefined' && window.crypto) {
		return window.crypto;
	}

	// Node.js environment (15+) - uses Web Crypto API standard
	if (typeof global !== 'undefined') {
		const nodeCrypto = require('crypto');
		if (nodeCrypto.webcrypto) {
			return nodeCrypto.webcrypto;
		}
	}

	throw new Error('No Web Crypto API available in this environment');
}

/**
 * Fills a typed array with cryptographically secure random values
 * @param array TypedArray to fill with random values
 */
export function getCryptoRandomValues<T extends ArrayBufferView>(array: T): T {
	return getCrypto().getRandomValues(array);
}

File: src/utils/auth-utils.ts (modified)

import { getCryptoRandomValues } from './crypto-adapter';

/**
 * Generates a cryptographically secure random API key
 * @param length Length of the API key (default: 32 characters)
 * @returns A random API key string
 */
export function generateApiKey(length: number = 32): string {
	const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
	const values = new Uint8Array(length);

	// Use cross-environment crypto adapter
	getCryptoRandomValues(values);

	let result = '';
	for (let i = 0; i < length; i++) {
		result += charset[values[i] % charset.length];
	}

	return result;
}

// validateApiKey() remains unchanged

File: tests/crypto-adapter.test.ts (new)

Test coverage for the adapter:

  • Verify getCryptoRandomValues() returns filled array with correct length
  • Verify randomness (different calls produce different results)
  • Verify it works in Node.js test environment
  • Verify type preservation (Uint8Array in = Uint8Array out)

Error Handling

Scenarios Covered

  1. Missing crypto API - Throws descriptive error if neither environment has crypto
  2. Node.js version incompatibility - Error message guides developers to upgrade
  3. Type safety - TypeScript ensures correct typed array usage

Error Messages

  • "No Web Crypto API available in this environment" - Clear indication of what's missing

Testing Strategy

Existing Tests

  • tests/main-migration.test.ts - Will now pass without modification
  • Uses real Node.js crypto.webcrypto instead of mocks
  • No change to test assertions needed

New Tests

  • tests/crypto-adapter.test.ts - Verifies adapter functionality
  • Tests environment detection logic
  • Tests randomness properties
  • Tests type preservation

Coverage Impact

  • New file adds to overall coverage
  • No reduction in existing coverage
  • All code paths in adapter are testable

Production Behavior

Obsidian/Electron Environment

  • Always uses window.crypto (first check in getCrypto)
  • Zero change to existing runtime behavior
  • Same cryptographic guarantees as before

Node.js Test Environment

  • Uses crypto.webcrypto (Node.js 15+)
  • Provides identical Web Crypto API
  • Real cryptographic functions (not mocked)

Migration Path

Changes Required

  1. Create src/utils/crypto-adapter.ts
  2. Modify src/utils/auth-utils.ts to import and use adapter
  3. Create tests/crypto-adapter.test.ts
  4. Run tests to verify fix

Backward Compatibility

  • No breaking changes to public API
  • generateApiKey() signature unchanged
  • No settings or configuration changes needed

Rollback Plan

  • Single commit contains all changes
  • Can revert commit if issues found
  • Original implementation preserved in git history

Benefits

  1. Clean separation of concerns - Crypto access logic isolated
  2. Standards-based - Uses Web Crypto API in both environments
  3. Reusable - Other code can use crypto-adapter for crypto needs
  4. Type-safe - TypeScript ensures correct usage
  5. Testable - Each component can be tested independently
  6. No mocking needed - Tests use real crypto functions

Future Considerations

  • If other utilities need crypto, they can import crypto-adapter
  • Could extend adapter with other crypto operations (hashing, etc.)
  • Could add feature detection for specific crypto capabilities