PassSeeds is a technical experiment that explores how WebAuthn passkeys can be hijacked to unlock broader cryptographic use cases. Instead of restricting passkeys to login scenarios, PassSeeds derives cryptographic seed material from passkeys in a way that lets apps generated a wide array of cryptographic keys (beyond the standard WebAuthn-supported curves), while retaining the strongly authenticaed biometric UX of passkeys.
npm install passseeds
import { PassSeed } from 'passseeds';
const seedString = await PassSeed.create({
user: "Alice B. Carol",
seedName: "My Seed"
});
console.log(seedString);
const mnemonic = await PassSeed.toMnemonic(seedString, 12);
console.log(mnemonic); // 12-word phrase
// Prompts user to authenticate with passkey
const seedString = await PassSeed.get();
// Or target a specific credential
const seedString = await PassSeed.get({ credentialId });
// Or pause between prompts to show custom UI
const seedString = await PassSeed.get({
credentialId,
onBeforeSecondSignature: async () => {
showSecondPromptUI();
await waitForUserConfirmation();
}
});
// Convert hex seed string to bytes
const bytes = PassSeed.hexToBytes(seedString);
// Convert bytes back to hex
const hex = PassSeed.bytesToHex(bytes);
PassSeed.create(options?: { user?: string; seedName?: string }): Promise<string>Creates a new P-256 passkey through the WebAuthn API and derives a deterministic 32-byte seed string (hex) from the public key. You'll be prompted to create a new passkey using your platform's authenticator.
Parameters:
- options (optional) - Labels shown in passkey UI
- options.user (optional) - Display label (maps to WebAuthn user.displayName)
- options.seedName (optional) - Seed name (maps to WebAuthn user.name)
Returns: A 32-byte seed string derived from the passkey (hex)
PassSeed.toMnemonic(passSeed: Uint8Array | string, wordCount?: 12 | 24): Promise<string>Converts PassSeed bytes or a seed string into a human-readable BIP39 mnemonic phrase for easy backup and recovery.
Parameters:
- passSeed - Exactly 32 bytes of PassSeed data or a 32-byte hex seed string
- wordCount (optional) - 12 or 24 word mnemonic length (default: 24)
Returns: A 12- or 24-word BIP39 mnemonic phrase
PassSeed.get(options?: { credentialId?: string; onBeforeSecondSignature?: () => void | Promise<void> }): Promise<string>Retrieves an existing passkey and performs dual WebAuthn signatures to recover the P-256 public key. Requires that you've already created a passkey using PassSeed.create().
Parameters:
- options (optional) - Passkey lookup options
- options.credentialId (optional) - Specific credential ID (base64url) to target
- options.onBeforeSecondSignature (optional) - Async hook before the second signature request
Returns: A 32-byte seed string derived from the passkey (hex)
PassSeed.bytesToHex(bytes: Uint8Array): stringConverts a Uint8Array to a hex string for easy display and storage.
PassSeed.hexToBytes(hex: string): Uint8ArrayConverts a hex string back to a Uint8Array.
PassSeed.create() to create a new passkeyPassSeed.get()PassSeed.toMnemonic() to get a 12- or 24-word phrasenpm run build # Compile TypeScript + browser bundle
npm run dev # Watch mode compilation
npm run dev:demo # Watch mode + live demo reload
npm test # Run tests
npm run demo # Start interactive demo
Build outputs to dist/index.js and dist/index.d.ts. The demo server runs at
http://localhost:8080 and reloads on changes.
npm version patch # or minor, major
npm run build
npm publish --access public
The package ships from dist/ via the exports map in package.json.
passseeds/
├── src/
│ ├── index.ts # Core PassSeed implementation
│ └── tests/
│ └── passseeds.test.ts
├── dist/ # Compiled output
├── demo/
│ └── index.html # Interactive web demo
├── scripts/
│ ├── serve-demo.js # Demo server with live reload
│ └── dev.js # Dev runner (tsc watch + optional demo)
├── package.json
├── tsconfig.json
└── [documentation files]
MIT
$ claude mcp add passseeds \
-- python -m otcore.mcp_server <graph>