A comparison for both general audiences and developers working across Bitcoin wallets, Nostr clients, and self-custody UX.
Sources:
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.
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.
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:
- Authenticate with passkey → derive
account_master→ derivenostr_account - Query Nostr relays for kind-1 events from
nostr_account→ retrieve salt list - For each salt, re-run
PRF(passkey, salt)→ re-derive all keys - 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)
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
nseclives 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
| 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 |
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.
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_urlphishing (maliciousauth_urlpointing to attacker page)- Relay replay / duplicate message attacks (mitigated by
sincefilters, but implementation-dependent) - The documented security hole around showing connection requests without a
secretin 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.
Passkey Login optimizes for mainstream users. The entire onboarding is:
- Create passkey (OS biometric prompt — same UX as "sign in with Face ID")
- Wallet keys are derived silently
- 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:
- Knowing what a bunker is and choosing one
- Entering a
bunker://URL or NIP-05 address - Handling async relay connections and loading states
- Clicking through
auth_urlconfirmation popups - Managing the ephemeral
client-keypairin 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.
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.
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.
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.
| 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 |
- 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.
create_accountis rough: Library support for account creation via NIP-46 is incomplete or buggy in bothnostr-toolsand NDK as of early 2026.- Relay ephemeral event handling is inconsistent: Many public relays do not properly delete ephemeral events;
sincefilters 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
secretrequirement) andauth_urlhandling. - 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.
| 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