Getting started
What mera does:
- Create a passkey and get 32 secret bytes from it through the PRF extension.
- Derive accounts from those bytes.
- Sign with a signing session.
Prerequisites:
- A secure context: HTTPS, or
localhostduring development. - An authenticator that supports the PRF extension. Authenticator support lists the combinations known to work. 1Password and iCloud Keychain are good first choices.
Install
Section titled “Install”npm install @category-labs/meraThis walkthrough derives accounts with @scure/bip32 and @scure/bip39. Any derivation scheme works.
npm install @scure/bip32 @scure/bip39Create a passkey
Section titled “Create a passkey”createPasskeyWithPrfOutput creates a passkey and evaluates its PRF in one call.
import { createPasskeyWithPrfOutput } from "@category-labs/mera";
// The relying party ID, the domain the passkey is bound to.const rpId = "account.example.com";
const { prfOutput } = await createPasskeyWithPrfOutput({ rp: { id: rpId, name: "Example" }, user: { name: "account@example.com", displayName: "Example account" },});mera uses a stable PRF salt for this call, so a later sign-in reproduces the same 32 bytes.
Derive an account
Section titled “Derive an account”The PRF output is the root of every account. This walkthrough maps it through BIP-39 to a phrase and a seed, then derives keys with BIP-32.
import { HDKey } from "@scure/bip32";import { entropyToMnemonic, mnemonicToSeedSync } from "@scure/bip39";import { wordlist } from "@scure/bip39/wordlists/english.js";
const firstEthereumAccountPath = "m/44'/60'/0'/0/0";
const mnemonic = entropyToMnemonic(prfOutput, wordlist);const seed = mnemonicToSeedSync(mnemonic);const node = HDKey.fromMasterSeed(seed).derive(firstEthereumAccountPath);if (node.privateKey === null) throw new Error("derivation produced no key");import { createSecp256k1SigningSession, getEvmAddress,} from "@category-labs/mera";
const session = createSecp256k1SigningSession({ privateKey: node.privateKey,});
const address = getEvmAddress(session.publicKey);
const digest = new Uint8Array(32);const signature = await session.signDigest(digest);
session.end();Sign back in
Section titled “Sign back in”A later visit reproduces the same account.
import { getPasskeyPrfOutput } from "@category-labs/mera";
const { prfOutput } = await getPasskeyPrfOutput({ rpId,});Without credential, the browser offers any discoverable passkey it holds for the relying party. The Create passkey accounts recipe shows the app storing the credential ID at create time and passing it back to pin later sign-ins.
Where next
Section titled “Where next”- Send a transaction with viem: from sign-in to a sent transaction.
- Passkey accounts: how the same passkey reproduces the same accounts.
- Secret vaults: use passkey PRF output to encrypt a secret, useful for cases where the secret must come from elsewhere.
- Security model: what mera protects, and the risks left to the app.