The OCAP Kernel supports BIP39 mnemonic phrases for backing up and recovering kernel identity. This enables users to restore their kernel's peer ID on a new device or after data loss.
Each kernel has a unique identity derived from a cryptographic seed. This identity determines the kernel's peer ID, which is used for peer-to-peer communication. By default, the kernel generates a random seed on first initialization. With BIP39 support, you can:
- Create a recoverable identity by generating a mnemonic and using it during initialization
- Recover an existing identity by providing the same mnemonic phrase during initialization
A BIP39 mnemonic is a human-readable sequence of words (typically 12 or 24 words) that represents cryptographic entropy. The same mnemonic will always produce the same seed when using the standard PBKDF2 derivation, making it ideal for backup and recovery.
Example 12-word mnemonic:
abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
Security Note: Never share your mnemonic phrase. Anyone with access to it can impersonate your kernel identity.
The following functions are exported from @metamask/ocap-kernel:
Generates a new random BIP39 mnemonic phrase.
import { generateMnemonic } from '@metamask/ocap-kernel';
// Generate 12-word mnemonic (default, 128 bits of entropy)
const mnemonic12 = generateMnemonic();
// Generate 24-word mnemonic (256 bits of entropy)
const mnemonic24 = generateMnemonic(256);Validates a BIP39 mnemonic phrase.
import { isValidMnemonic } from '@metamask/ocap-kernel';
const mnemonic =
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
if (isValidMnemonic(mnemonic)) {
console.log('Valid mnemonic');
} else {
console.log('Invalid mnemonic');
}Converts a BIP39 mnemonic phrase to a 32-byte hex-encoded seed using standard PBKDF2 derivation.
This is a one-way operation - you cannot reverse a seed back to its mnemonic. To enable backup/recovery, store the original mnemonic.
import { mnemonicToSeed } from '@metamask/ocap-kernel';
const mnemonic =
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
const seed = mnemonicToSeed(mnemonic);
// seed is a 64-character hex string (32 bytes)The mnemonic parameter can be passed either to Kernel.make (recommended) or to initRemoteComms:
// Option 1: Pass mnemonic to Kernel.make (recommended)
const kernel = await Kernel.make(platformServices, kernelDatabase, {
mnemonic: 'your twelve word mnemonic phrase here ...',
});
await kernel.initRemoteComms({
relays: ['/ip4/127.0.0.1/tcp/9001/ws/p2p/12D3KooW...'],
});
// Option 2: Pass mnemonic to initRemoteComms
const kernel = await Kernel.make(platformServices, kernelDatabase);
await kernel.initRemoteComms({
relays: ['/ip4/127.0.0.1/tcp/9001/ws/p2p/12D3KooW...'],
mnemonic: 'your twelve word mnemonic phrase here ...',
});If mnemonic is provided in both places, the one in initRemoteComms takes precedence.
For new installations where you want backup capability, generate a mnemonic first:
import { generateMnemonic } from '@metamask/ocap-kernel';
// Generate and display mnemonic for user to backup
const mnemonic = generateMnemonic();
console.log('Please write down your recovery phrase:');
console.log(mnemonic);
// User confirms they've backed up the mnemonic...
// Initialize kernel with the mnemonic
const kernel = await Kernel.make(platformServices, kernelDatabase, {
mnemonic,
});
await kernel.initRemoteComms({ relays });
// The peer ID is now derived from the mnemonic and can be recovered
const status = await kernel.getStatus();
console.log('Peer ID:', status.remoteComms?.peerId);If you don't provide a mnemonic, the kernel generates a random identity that cannot be recovered:
// Initialize kernel normally
const kernel = await Kernel.make(platformServices, kernelDatabase, options);
// Initialize remote comms without mnemonic
await kernel.initRemoteComms({ relays });
// Get the peer ID - this identity cannot be backed up as a mnemonic
const status = await kernel.getStatus();
console.log('Peer ID:', status.remoteComms?.peerId);Note: Random seeds cannot be converted to mnemonics. If you need backup capability, use Scenario 1 and generate a mnemonic first.
To restore a kernel's identity using a previously backed-up mnemonic:
import { isValidMnemonic } from '@metamask/ocap-kernel';
const mnemonic = 'user provided mnemonic phrase from backup';
// Validate the mnemonic first
if (!isValidMnemonic(mnemonic)) {
throw new Error('Invalid recovery phrase');
}
// Initialize kernel with fresh storage and the recovery mnemonic
const kernel = await Kernel.make(platformServices, kernelDatabase, {
resetStorage: true,
mnemonic,
});
// Initialize remote comms
await kernel.initRemoteComms({ relays });
// The kernel now has the same peer ID as before
const status = await kernel.getStatus();
console.log('Recovered Peer ID:', status.remoteComms?.peerId);To verify a mnemonic will produce the expected peer ID without actually initializing:
import { mnemonicToSeed, isValidMnemonic } from '@metamask/ocap-kernel';
import { generateKeyPairFromSeed } from '@libp2p/crypto/keys';
import { peerIdFromPrivateKey } from '@libp2p/peer-id';
import { fromHex } from '@metamask/kernel-utils';
async function getPeerIdFromMnemonic(mnemonic: string): Promise<string> {
if (!isValidMnemonic(mnemonic)) {
throw new Error('Invalid mnemonic');
}
const seed = mnemonicToSeed(mnemonic);
const keyPair = await generateKeyPairFromSeed('Ed25519', fromHex(seed));
return peerIdFromPrivateKey(keyPair).toString();
}
// Verify before recovery
const expectedPeerId = '12D3KooW...'; // Your known peer ID
const recoveryMnemonic = 'your recovery phrase';
const recoveredPeerId = await getPeerIdFromMnemonic(recoveryMnemonic);
if (recoveredPeerId === expectedPeerId) {
console.log('Mnemonic verified! Safe to proceed with recovery.');
} else {
console.log('Warning: This mnemonic produces a different peer ID.');
}If the kernel already has a stored identity and you provide a mnemonic, an error is thrown to prevent accidentally using the wrong identity. To use a mnemonic for recovery:
- Use a fresh database, OR
- Initialize the kernel with
resetStorage: true
// This ensures the mnemonic is used by clearing existing identity
const kernel = await Kernel.make(platformServices, kernelDatabase, {
resetStorage: true, // Clears existing identity first
mnemonic: recoveryMnemonic,
});
await kernel.initRemoteComms({ relays });If you attempt to provide a mnemonic when an identity already exists without resetStorage: true, you'll get:
Error: Cannot use mnemonic: kernel identity already exists. Use resetStorage to clear existing identity first.
Always validate mnemonics before use:
import { isValidMnemonic } from '@metamask/ocap-kernel';
if (!isValidMnemonic(userInput)) {
// Handle invalid mnemonic (wrong words, bad checksum, etc.)
throw new Error('Please enter a valid 12 or 24-word recovery phrase');
}All standard BIP39 mnemonic lengths are supported:
- 12 words (128 bits of entropy)
- 15 words (160 bits of entropy)
- 18 words (192 bits of entropy)
- 21 words (224 bits of entropy)
- 24 words (256 bits of entropy)
When generating mnemonics with generateMnemonic(), you can choose between 12 words (default) or 24 words.
This implementation uses standard BIP39 PBKDF2-HMAC-SHA512 derivation (2048 iterations) with an empty passphrase. This ensures compatibility with standard BIP39 test vectors and other implementations.
- Generate mnemonic first - If you need backup capability, always generate a mnemonic before initialization
- Never log or transmit mnemonics - Display only to the user for manual backup
- Clear mnemonic from memory - Don't store in application state longer than necessary
- Use secure input methods - Avoid clipboard operations if possible
- Verify before recovery - Confirm the mnemonic produces the expected peer ID
- Store backups securely - Recommend users write down the phrase offline
import { isValidMnemonic } from '@metamask/ocap-kernel';
try {
if (!isValidMnemonic(mnemonic)) {
throw new Error('Invalid mnemonic phrase');
}
const kernel = await Kernel.make(platformServices, kernelDatabase, {
mnemonic,
});
await kernel.initRemoteComms({ relays });
} catch (error) {
if (error.message === 'Invalid BIP39 mnemonic') {
// Handle invalid mnemonic
console.error('The recovery phrase is invalid');
} else {
// Handle other errors
throw error;
}
}