npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

stacks-passkey-wallet

v0.2.0

Published

Passkey-derived wallet reference implementation for Stacks and Bitcoin — WebAuthn PRF → deterministic HD wallet

Readme

stacks-passkey-wallet

⚠️ Pre-release — v0.1.0. Production hardening is in progress and the cross-device PRF support matrix is incomplete (see docs/prf-support-matrix.md). Adopters inherit these caveats: pin an exact version, and verify on your target passkey providers before relying on it for funds.

Passkey-derived wallet reference implementation for Stacks and Bitcoin — WebAuthn PRF → deterministic HD wallet. MIT licensed. Clean-room from the WebAuthn PRF, BIP-39, BIP-32/44/84 and RFC 5869 specifications.

One passkey, one biometric tap, and a user has a Stacks + Bitcoin wallet — no seed phrase to write down at signup, no browser extension. This repository is the open reference library; deorganized.com consumes it as a dependency.

How it works

passkey PRF output (32 bytes)
   │  HKDF-SHA256 (RFC 5869)
BIP-39 entropy (32 bytes) → 24-word mnemonic → BIP-32 seed
   ├─▶ m/44'/5757'/0'/0/0  → Stacks address   (SP…)
   └─▶ m/84'/0'/0'/0/0     → Bitcoin address  (bc1q…, native SegWit)

The same passkey + the same salt always derives the same wallet. The derivation paths are byte-identical to Leather's and Xverse's account-0 defaults (verified against their current sources). The salt, HKDF parameters, and locked test vectors are frozen together — see docs/wallet-identity.md.

⚠ Trust model — read this first

This is a hot wallet — the same security class as a browser-extension wallet. At signing time the private key is briefly in clear-text page memory; device malware at that moment could steal it. The passkey improves the experience (no seed at signup, phishing-resistant login) — it does not change the underlying trust model.

Appropriate for onboarding and casual/moderate balances. Not for treasuries.

The library never persists anything key-related: the seed is re-derived on demand, lives only in page memory, and is zeroized after use. Nothing touches localStorage, IndexedDB, cookies, or any server.

⚠ The #1 pitfall: a new passkey is a new, empty wallet

Two actions look similar and must never be conflated:

  • "Add a passkey" = login convenience. Another way to sign in to the same wallet, via a passkey synced by the same provider (iCloud Keychain / Google Password Manager).
  • "Restore my wallet" = funds recovery. Re-deriving your wallet on a new device from your exported seed phrase.

Creating a genuinely new passkey (a different provider, or an unsynced device) derives a different, empty wallet — not access to your original. Always back up the seed phrase for recovery. The library exposes WALLET_ACTIONS so host apps keep this language straight.

Supported platforms

Reliable, byte-identical PRF across a user's synced devices exists on two providers today:

| Provider | Status | |---|---| | iCloud Keychain (iOS/iPadOS 18.4+, macOS 15.4+) | ✅ supported | | Google Password Manager (Chrome/Edge 116+, desktop + Android) | ✅ supported | | Windows | ✅ via Google Password Manager — Windows Hello is not supported for derivation (device-bound, would be single-device) | | Firefox Android, Android WebView | ❌ not supported |

The iOS 18.4 floor is enforced as a hard runtime check (earlier versions can derive inconsistent bytes across devices). Full detail: docs/prf-support-matrix.md.

Install

npm install stacks-passkey-wallet

Usage

import {
  createPasskey,
  deriveAddressesFromPasskey,
  signStacksMessage,
  evaluatePrf,
  exportSeedPhrase,
} from "stacks-passkey-wallet";

// 1. Create a passkey with the PRF extension (throws PrfUnsupportedError if unavailable).
await createPasskey({
  rpName: "Your App",
  rpId: "yourapp.com",
  userId: crypto.getRandomValues(new Uint8Array(16)),
  userName: "alice",
  challenge: crypto.getRandomValues(new Uint8Array(32)),
});

// 2. Derive the wallet (fresh get() → PRF bytes → addresses).
const { stacks, bitcoin } = await deriveAddressesFromPasskey({
  challenge: crypto.getRandomValues(new Uint8Array(32)),
  rpId: "yourapp.com",
});
// stacks.address → "SP…", bitcoin.address → "bc1q…"

// 3. Sign — the library derives, signs, and discards the key internally.
const bytes = await evaluatePrf({ challenge: crypto.getRandomValues(new Uint8Array(32)), rpId: "yourapp.com" });
const { signature, publicKey } = signStacksMessage(bytes, "hello");
bytes.fill(0);

// 4. Export the seed for backup (gated + shown once).
const seed = await exportSeedPhrase({
  challenge: crypto.getRandomValues(new Uint8Array(32)),
  rpId: "yourapp.com",
  acknowledgedBackupWarning: true, // set only after the multi-step warning UI
});
console.log(seed.reveal()); // the 24-word phrase, once

See the integration guide for the full host-app flow and the demo for a runnable standalone example (npm run demo).

Development

npm install
npm run typecheck   # tsc --noEmit
npm test            # vitest — derivation vectors, signing, PRF, export
npm run build       # tsup → dist/ (ESM + types)
npm run gen:vectors # regenerate the frozen vectors (cross-validated)
npm run demo        # runnable browser demo (vite)

Test vectors are cross-validated against @stacks/wallet-sdk (Stacks) and bitcoinjs-lib (Bitcoin), plus a BIP-84 published golden anchor.

License & credits

MIT © DeOrganized. Built as the open reference deliverable for a DeGrants Milestone 1. Contributions welcome under MIT.