Skip to content

Instantly share code, notes, and snippets.

@pretyflaco
Last active March 21, 2026 14:58
Show Gist options
  • Select an option

  • Save pretyflaco/bc4eaf35b4a05d5d52906b2d2d6ed585 to your computer and use it in GitHub Desktop.

Select an option

Save pretyflaco/bc4eaf35b4a05d5d52906b2d2d6ed585 to your computer and use it in GitHub Desktop.
Passkeys (Breez SDK WebAuthn PRF) vs NIP-46 Nostr Remote Signing — Technical Comparison

Passkeys (Breez SDK WebAuthn PRF) vs NIP-46 Nostr Remote Signing

A comparison for both general audiences and developers working across Bitcoin wallets, Nostr clients, and self-custody UX.

Sources:


Plain English Summary

Private keys are the load-bearing wall of self-sovereign Bitcoin and decentralised applications. Everything rests on them — your wallet balance, your Nostr identity, your ability to send, receive, sign, and prove ownership. And yet, for most people, the experience of managing private keys is either terrifying (a 12-word seed phrase handwritten on paper and stored… somewhere) or completely invisible (an exchange holds them and the user never thinks about it at all). Both outcomes are bad. The first scares people away from self-custody entirely; the second defeats the point of owning Bitcoin. Breez Passkey Login and NIP-46 Nostr Remote Signing are two different attempts to close that gap — to make key management feel like something ordinary people can actually do, without compromising the underlying security guarantees that make self-custody worth doing in the first place. They come from different corners of the problem space, but they are not rivals. Understanding both, and how they relate, is increasingly useful for anyone building in this ecosystem.

The seed phrase problem — and what Passkey Login does about it. The 12-word mnemonic (or 24-word, for the especially cautious) is the Bitcoin industry's answer to key backup. It is mathematically elegant: a single sequence of common words encodes the entropy from which your entire wallet — every address, every key, every account — can be deterministically reconstructed on any device. The problem is human. People lose the paper. They store it digitally, which defeats the purpose. They skip writing it down at all because the onboarding screen interrupts what they were trying to do. They type it into a phishing site. The seed phrase asks ordinary users to behave like security engineers, and most of them don't — not because they are careless, but because nothing in their prior experience with technology has trained them to treat a piece of paper as the single point of failure for their savings. Breez Passkey Login attacks this problem at the root. Instead of generating a random seed phrase and asking you to back it up, it derives your wallet keys from your passkey — the same hardware-backed biometric credential you already use to unlock your phone and log into your banking app. Your Bitcoin wallet keys are derived, silently and deterministically, every time you authenticate with Face ID or a fingerprint. Nothing is stored anywhere that needs backing up. The 12-word phrase becomes an optional emergency exit you can export if you want it, not a day-one requirement that most users will fumble.

How passkeys actually work, and why this matters. A passkey is not a password stored somewhere. It is a cryptographic key pair — a private key and a public key — generated inside a dedicated security chip on your device: Apple's Secure Enclave, Android's Titan chip, a Windows TPM, or a hardware key like a YubiKey. The private key is generated inside the chip and, by design, never leaves it. When a website or app wants to verify your identity, it sends a challenge; your chip signs it with the private key and returns the signature; the app verifies the signature against your public key. Your private key never travels over the network, never touches the app's code, and cannot be extracted even by someone with physical access to your device, because the chip is specifically engineered to refuse extraction requests. Breez extends this model with a feature called PRF — Pseudo-Random Function — part of the WebAuthn Level 3 specification. PRF lets your passkey do something beyond just signing challenges: it can produce a deterministic cryptographic output for any input you give it. Same passkey, same input, same output. Always. Breez uses this to derive your wallet keys: it feeds a salt string into the PRF, gets a deterministic blob of entropy out, and runs that through the standard Bitcoin key derivation process (BIP39 → BIP32 → wallet keys). The result is a wallet that rebuilds itself from your biometric every time, with no stored secret anywhere.

The Nostr identity problem — and what NIP-46 does about it. Nostr is a decentralised protocol for social networks, messaging, marketplaces, and more. Your identity on Nostr is a single cryptographic key pair — a private key (nsec) and a public key (npub). Everything you do on Nostr — every post, every message, every reaction — is signed with your private key. This is what makes Nostr censorship-resistant and self-sovereign: there is no account on a server to delete, no platform to ban you, only your key. The problem is that Nostr users typically interact with many different clients: a desktop app for reading, a mobile app for posting, a web client for discovery, a specialised app for a particular community. Each of those clients, if it is going to sign events on your behalf, needs access to your nsec. The naive solution — pasting your master key into every client — is the equivalent of giving a copy of your house key to every tradesperson who ever works in your home. Technically functional, but a compounding risk: each additional copy is another attack surface.

How a NIP-46 bunker works. NIP-46 solves the key-spreading problem by introducing a layer of indirection. Your nsec stays in one place — a "bunker" — and every other client asks the bunker to sign things on its behalf, over an encrypted channel. The client never sees your key; it only sees signed responses. Think of the bunker as a personal notary locked in a vault. You set it up once, hand it your nsec, and then forget about the key management problem. When a Nostr client wants to post something in your name, it sends a signing request to the bunker, encrypted so only the bunker can read it. The bunker decrypts the request, checks it against whatever permissions you have configured, and either signs it automatically (if it is a pre-approved type of request) or sends you an alert asking you to approve it manually. The signed event goes back to the client. At no point did the client hold your key, see your key, or need to know anything about it. The bunker itself can take several forms: an app on your own phone (Amber on Android is the most widely used example), a self-hosted service running on a server you control, or a third-party hosted service like nsec.app — in which case you are trusting that provider with your identity, much as you trust a password manager with your passwords. The connection between client and bunker happens over Nostr relays using end-to-end encryption, so the relay itself cannot read the requests.

The essential distinction. Passkey Login is about generating and protecting key material at the hardware level. It creates sovereignty from scratch: your keys did not exist before, they are derived on demand from your biometric, and they never exist as a storable secret anywhere — except optionally as a BIP39 mnemonic that you choose to export. NIP-46 is about delegating access to key material you already have. Your nsec exists somewhere; NIP-46 ensures it exists in only one place while still being usable across many clients and applications. One protocol solves the problem of creating a key you can actually live with. The other solves the problem of using a key you already have without spreading it everywhere. They are not solving the same problem, which is why a sophisticated user or developer might reasonably want both.

The surprising way they are already connected. Breez's passkey system does not operate in isolation from Nostr — it uses Nostr as part of its own infrastructure. When you set up a wallet with Passkey Login, a small piece of metadata — a short string called a salt, which might be a word like "savings" or "work" — is published to Nostr's relay network as a plain text event, signed by a Nostr keypair that is itself derived from your passkey. This event looks exactly like an ordinary Nostr post, which is intentional: it provides plausible deniability. When you restore your wallet on a new device, the app authenticates your passkey, derives the same Nostr keypair, queries the relay network for events from that keypair, finds the salt, and uses it to re-derive your wallet keys. Nostr here is not acting as a social network. It is acting as a censorship-resistant, serverless, publicly auditable lookup table — a role it is well-suited to and for which no modification to the protocol is required. The two technologies are not competing; one is quietly using the other as infrastructure.

The technical sections below cover protocol internals, security trade-offs, cryptographic primitives, and implementation details for developers building on either stack.


1. What Problem Each Solves

Both protocols answer the same underlying question:

How does a user's key material authorize actions inside an app — without ever exposing the raw private key to that app?

They arrive at very different answers.

Breez Passkey Login starts from the Bitcoin self-custody problem: seed phrases are the primary barrier to non-custodial adoption. It uses the FIDO2/WebAuthn PRF extension to derive a deterministic BIP39/BIP32 key hierarchy directly from a passkey, inside the device's secure enclave. No seed phrase is required during normal use; no server ever touches the entropy.

NIP-46 (Nostr Remote Signing / "bunker") starts from the Nostr multi-client problem: users don't want to paste their nsec into every client they use. It separates the key holder (a bunker — local daemon, mobile app, or hosted service) from the key consumer (any Nostr client), connecting them over encrypted Nostr relay messages. The bunker holds the actual private key; the client delegates signing to it via RPC.


2. How Each Protocol Works

2.1 Breez Passkey Login (WebAuthn PRF)

The core primitive is the PRF extension of WebAuthn Level 3 — a deterministic pseudo-random function keyed by the passkey's private key, callable only when the user authenticates (biometric, PIN).

Derivation flow:

PRF(passkey, MAGIC_BYTES)          → account_master
BIP39/BIP32(account_master, m/44'/1237'/55'/0/0) → nostr_account   (salt registry identity)
BIP39/BIP32(account_master, m/44'/1237'/0'/0/0)  → nostr_identity  (general Nostr use — standard NIP-06 path)

For each wallet/account:
  PRF(passkey, salt_string)        → root_key
  BIP39(root_key)                  → mnemonic
  BIP32/BIP44(mnemonic)            → wallet/app keys

MAGIC_BYTES = 0x4e594f415354525453414f594e (hex of "NYOASTRTSAOYN") — a fixed value specifically chosen to prevent collision with any salt values.

Salt registry: Each salt_string is published as a plain Nostr kind-1 event signed by nostr_account. This means salts look like regular social posts (plausible deniability), are stored on censorship-resistant public infrastructure, and can be enumerated on restore without any app-side state.

Restore flow:

  1. Authenticate with passkey → derive account_master → derive nostr_account
  2. Query Nostr relays for kind-1 events from nostr_account → retrieve salt list
  3. For each salt, re-run PRF(passkey, salt) → re-derive all keys
  4. No cloud backup, no server state — only the passkey and the Nostr event log

Key properties:

  • The passkey's private key never leaves the secure enclave — the PRF output (root entropy) is derived by the hardware and returned to the app
  • The passkey's private key is non-exportable by design (FIDO2 requirement)
  • Every call to PRF requires user verification (biometric/PIN)
  • Domain-bound: PRF outputs are scoped to the relying party ID (phishing-resistant)

2.2 NIP-46 Nostr Remote Signing

The model is delegated signing via encrypted relay RPC. The bunker holds the user's actual nsec; clients hold only an ephemeral client-keypair used to encrypt the channel.

Connection setup (bunker-initiated):

bunker://<remote-signer-pubkey>?relay=wss://...&secret=<token>

Connection setup (client-initiated):

nostrconnect://<client-pubkey>?relay=wss://...&secret=<token>&perms=sign_event:1,nip44_encrypt&name=MyApp

Request/response wire format (both directions use kind 24133, NIP-44 encrypted):

// Request (client → bunker)
{
  "kind": 24133,
  "pubkey": "<client-pubkey>",
  "content": nip44_encrypt({
    "id": "<random>",
    "method": "sign_event",
    "params": ["<json_stringified_event>"]
  }),
  "tags": [["p", "<remote-signer-pubkey>"]]
}

// Response (bunker → client)
{
  "kind": 24133,
  "pubkey": "<remote-signer-pubkey>",
  "content": nip44_encrypt({
    "id": "<same_random>",
    "result": "<json_stringified_signed_event>",
    "error": null
  }),
  "tags": [["p", "<client-pubkey>"]]
}

Available RPC methods: connect, sign_event, get_public_key, ping, nip04_encrypt, nip04_decrypt, nip44_encrypt, nip44_decrypt, switch_relays

Auth challenges: If the bunker requires explicit user confirmation (e.g. first use of a permission, or a sensitive event kind), it returns result: "auth_url" with a URL. The client opens it in a popup; the user approves; the bunker re-sends the signed response.

Key properties:

  • The nsec lives in the bunker — not in the client application
  • Communication is end-to-end encrypted (NIP-44 / ChaCha20 + HMAC-SHA256)
  • The bunker can be a local app (Amber (Android), Keystache (desktop)), self-hosted service, or third-party hosted (nsec.app, nsecbunker.com)
  • No domain binding — relay-based, origin-agnostic

3. Side-by-Side Comparison

Dimension Breez Passkey Login NIP-46 Remote Signing
Primary purpose Bitcoin wallet key derivation (seedless UX) Nostr event signing (remote delegation)
Key location Device secure enclave (non-exportable) Bunker process (server or local app)
Key custody Full self-custody — hardware-enforced Custodial-to-bunker; trust model depends on deployment
Cryptographic core WebAuthn PRF (ECDSA P-256 internally) + BIP39/BIP32 on output secp256k1 schnorr (Nostr native); NIP-44 for channel encryption
Transport OS/browser WebAuthn API — no network for key ops Nostr WebSocket relays — key ops require relay connectivity
Authentication UX OS-native biometric/PIN prompt Bunker UI popup (auth_url) or pre-approved permissions
Key derivation Deterministic — same passkey + salt → same keys, always N/A — key is pre-existing; signing is delegated, not derived
Phishing resistance Strong — FIDO2 domain-bound by relying party ID None built-in — optional NIP-05/NIP-89 signer discovery provides partial DNS-based trust anchor
Recovery Platform sync (iCloud/Google) + optional BIP39 export Entirely bunker-dependent; bunker must have its own backup
Platform dependency Apple / Google / Microsoft ecosystem for passkey sync Relay availability + bunker uptime
Works offline Yes — PRF calls are local to device No — requires relay connection
Decentralization High — no server needed for key ops Partial — relay + bunker are live dependencies
Bitcoin wallet compat. Native — outputs BIP39 mnemonic + BIP32 hierarchy Not applicable
Nostr identity compat. Yes — derives Nostr keys via NIP-06 path structure (account 55 is Breez-specific) Native — this is its primary purpose
Cross-platform interop Limited — passkey sync is ecosystem-scoped (Apple ↔ Apple, etc.) Strong — any relay, any NIP-46 client
Permission model Binary: user must biometrically approve each PRF call Granular: per-method, per-kind permissions negotiated at connect
Implementation complexity Moderate — WebAuthn + BIP39/BIP32 pipeline High — relay lifecycle, encrypted RPC, popup flows, reconnect logic
Spec maturity Very new — v0.9.1, March 2026 Established — implemented in NDK, nostr-tools, Amber, nsec.app
Underlying open standard FIDO2 / W3C WebAuthn Level 3 (PRF = Level 3 extension) Nostr NIP-46 (community spec)
Hardware key support YubiKey (with PRF), Secure Enclave, Titan, TPM Irrelevant — bunker is software
Auditability FIDO2 is a mature, formally specified standard NIP-46 has no formal security audit; relay handling has known edge cases

4. Deep Dives

4.1 Key Custody Model

Passkey Login achieves the strongest custody model: the private key is generated inside and never leaves the secure element (Apple Secure Enclave, Android Titan, TPM, YubiKey). The PRF output — the wallet entropy — is derived inside the hardware and handed to the app as opaque bytes. No key material is ever serialized to disk by the application. The only way to compromise the entropy is to compromise the passkey itself (physical device) or the platform sync account (iCloud/Google).

NIP-46 is a delegation model, not a custody model. The nsec lives in the bunker as a software secret. A self-hosted bunker on your own device is approximately as strong as local key storage — the threat model collapses to physical device security plus OS process isolation. A hosted bunker (nsec.app, nsecbunker.com) introduces an explicit custodian: you trust the operator not to steal your key. The channel encryption (NIP-44) protects signing requests in transit but not the key at rest in the bunker.

Verdict: For raw key safety, Passkey Login is strictly stronger. NIP-46 self-hosted is comparable to existing wallet approaches; hosted NIP-46 is effectively custodial.

4.2 Threat Model

Passkey Login attack surface:

  • Physical theft of device (mitigated by biometric/PIN for PRF)
  • Compromise of platform sync account (iCloud/Google credential theft)
  • Malicious app on same device that can invoke WebAuthn APIs (mitigated by OS-level app sandboxing and UV requirement)
  • Supply chain attack on the secure element (nation-state level)

NIP-46 attack surface:

  • Compromise of bunker host (process or server)
  • Compromise of relay (relay sees metadata: who is signing for whom, at what rate)
  • Connection secret theft (allows impersonating a client to the bunker)
  • auth_url phishing (malicious auth_url pointing to attacker page)
  • Relay replay / duplicate message attacks (mitigated by since filters, but implementation-dependent)
  • The documented security hole around showing connection requests without a secret in bunker UIs

A critical structural difference: Passkey Login has no phishing vector for the derived entropy because WebAuthn binds the PRF operation to the relying party domain. A malicious site at evil.com cannot trigger a PRF call scoped to breezwallet.com. NIP-46 has no equivalent binding — any app that knows the bunker:// URL and secret can request signing.

4.3 UX and Onboarding Friction

Passkey Login optimizes for mainstream users. The entire onboarding is:

  1. Create passkey (OS biometric prompt — same UX as "sign in with Face ID")
  2. Wallet keys are derived silently
  3. Done — no "write down these 12 words"

This is the same UX flow as logging into a banking app. For a user who has never held self-custodial Bitcoin, this removes the single biggest psychological barrier.

NIP-46 optimizes for power users who already understand Nostr identity. The onboarding requires:

  1. Knowing what a bunker is and choosing one
  2. Entering a bunker:// URL or NIP-05 address
  3. Handling async relay connections and loading states
  4. Clicking through auth_url confirmation popups
  5. Managing the ephemeral client-keypair in persistent storage

The nostrconnect.org guide itself warns: "Wow, we made it! Well hopefully we didn't miss anything and it actually worked." — an honest acknowledgment of the implementation complexity.

Verdict: Passkey Login is consumer-grade. NIP-46 is developer/power-user-grade.

4.4 Recovery Story

Passkey Login:

  • Happy path: New device, same Apple/Google account → passkeys sync automatically → re-authenticate → re-derive all keys from Nostr salt registry
  • Lost platform account: If you can't recover iCloud/Google, you're locked out unless you exported the BIP39 mnemonic
  • Lost passkey, no sync: Mnemonic backup is the fallback; this collapses to traditional seed phrase recovery
  • The spec explicitly acknowledges passkeys are "not fully interoperable across platforms yet" — Apple → Android migration requires the BIP39 export path

NIP-46:

  • Recovery = accessing your bunker
  • Self-hosted: depends on whatever backup you have of the bunker's key storage
  • Hosted: the provider's recovery flow (account recovery, support tickets, etc.)
  • There is no protocol-level recovery mechanism in NIP-46; it's entirely an implementation concern
  • A hosted bunker failure (shutdown, hack) can mean permanent key loss

Verdict: Passkey Login has a structured, deterministic recovery path with a graceful BIP39 fallback. NIP-46's recovery story is entirely outside the protocol.

4.5 Cross-Platform and Interoperability

Passkey Login produces standard BIP39 mnemonics and BIP32/BIP44 hierarchies. Any Bitcoin wallet that accepts a seed phrase can import the derived keys. It also derives Nostr keypairs following the NIP-06 BIP32 path structure (m/44'/1237'/account'/0/0) — though account 0 is the standard NIP-06 path, while account 55 is a Breez-specific choice for the salt registry identity. The output bridges into the entire Bitcoin and Nostr ecosystem through industry-standard primitives.

The limitation is on the input side: the passkey that generates the entropy is ecosystem-scoped. Your Apple passkey won't work on Android and vice versa without the BIP39 export. Cross-ecosystem passkey portability (CXF/CXP formats) is in progress at the FIDO Alliance but not yet standardized.

NIP-46 interoperates broadly on the consumer side: any NIP-46 compliant client can connect to any compliant bunker. The permissioning model (sign_event:1, nip44_encrypt, etc.) is standardized enough that different implementations generally work together — though edge cases around relay handling, duplicate messages, and create_account remain rough.

NIP-46 is Nostr-native and has no direct applicability to Bitcoin key management.

4.6 The Interesting Intersection

Breez's spec doesn't compete with Nostr — it uses Nostr as infrastructure.

The salt registry (which Nostr keypairs were used and what salts they derive) is published as standard kind-1 text events on public relays, signed by a Nostr keypair derived from the same passkey (m/44'/1237'/55'/0/0). These events are indistinguishable from ordinary social posts — a deliberate plausible deniability design choice.

This means:

  • A developer building a combined Bitcoin + Nostr app can use Passkey Login for both: one passkey flow derives both the BTC wallet keys and the Nostr identity
  • The salt registry lives on the same relays as the user's social graph
  • Nostr's censorship-resistant, serverless event storage is used as a replacement for a lookup server — without any modification to the Nostr protocol

Where the two protocols might genuinely interact: a NIP-46 bunker could, in principle, store its nsec as a passkey-derived key — getting hardware-enforced key security for the bunker's own storage while still exposing the NIP-46 signing interface to clients. This is not currently specified but is a natural extension.


5. When to Use Which

Use case Recommendation Rationale
Consumer Bitcoin self-custody wallet Passkey Login Zero seed phrase UX; hardware-enforced security; BIP39 fallback
Nostr client needing remote signing NIP-46 Purpose-built; broad client/bunker ecosystem; granular permissions
App needing both BTC wallet and Nostr identity Passkey Login Single passkey derives both via BIP32 path separation
Power user with multiple Nostr clients NIP-46 Connect once to bunker; all clients delegate without key re-exposure
High-risk phishing environment Passkey Login FIDO2 domain-binding prevents credential phishing entirely
Self-hosted key control with full RPC NIP-46 (self-hosted bunker) Full control over signing policy, permissions, audit logs
Multi-device across Apple + Android NIP-46 or BIP39 fallback Passkey cross-platform sync not yet fully standardized
Offline / airgapped signing Passkey Login PRF calls are local; no relay dependency
Programmatic / bot signing NIP-46 No biometric prompt requirement; pre-approved permission scopes

6. Known Limitations and Open Problems

Passkey Login

  • PRF extension availability: WebAuthn Level 3 PRF is not universally supported. Some hardware security keys lack PRF support. Older OS versions may not expose the API.
  • Cross-ecosystem migration: Moving from iCloud Keychain to Google Password Manager (or vice versa) requires going through the BIP39 mnemonic export path — the "seamless" UX breaks at ecosystem boundaries.
  • CXF/CXP not yet standard: The Cross-Platform/Cloud Export Format for passkey migration is still being defined at the FIDO Alliance. Until it lands, platform lock-in is real.
  • Spec is v0.9.1: Very new. One production reference app (Glow). Library ecosystem is immature.
  • Nostr relay dependency for restore: While key ops are offline, recovery requires Nostr relay access to retrieve the salt list.

NIP-46

  • create_account is rough: Library support for account creation via NIP-46 is incomplete or buggy in both nostr-tools and NDK as of early 2026.
  • Relay ephemeral event handling is inconsistent: Many public relays do not properly delete ephemeral events; since filters are a workaround, not a fix.
  • No formal security audit: The protocol as a whole has not been formally analyzed. Known issues exist around connection spoofing (the secret requirement) and auth_url handling.
  • Rate limiting: High-throughput signing (e.g. an app making many RPC calls rapidly) can hit relay rate limits.
  • Bunker availability: If you use a hosted bunker and it goes offline, your Nostr identity is non-functional until it comes back. There is no protocol-level failover.
  • Relay metadata leakage: Relays can observe the frequency and timing of signing requests even if they cannot read the encrypted content.

7. Summary

Breez Passkey Login NIP-46 Remote Signing
Best for Bitcoin wallets, consumer onboarding Nostr clients, power-user key delegation
Key security ceiling Hardware (secure enclave) Software (bunker process)
Decentralization High Moderate
UX floor Low friction (biometric) High friction (bunker setup)
Ecosystem Bitcoin + Nostr (via BIP32 paths) Nostr-native
Maturity Very early Production-ready
Recovery robustness Structured (platform sync + BIP39) Implementation-defined
Phishing resistance Strong (FIDO2 domain binding) None built-in (optional NIP-05/NIP-89 DNS trust anchor)

They are complementary, not competing. Passkey Login solves key generation and derivation behind a hardware-enforced biometric. NIP-46 solves key delegation and remote access across multiple clients. A fully-featured sovereign Bitcoin/Nostr stack could reasonably use both: Passkey Login to derive and protect the root key material, and a NIP-46 interface on top for programmatic or multi-client Nostr signing — ideally backed by a passkey-derived nsec rather than a server-stored one.


Comparison written March 2026. Specs referenced: Breez passkey-login spec v0.9.1 · NIP-46 · nostrconnect.org

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment