"Nostr Wallet Connect: the API your wallet didn't know it needed"
- you pasted a connection string and it just worked
- four events and a secret
- who trusts whom
- the ecosystem that snuck up on everyone
- what NWC doesn’t fix
- the glue layer
you pasted a connection string and it just worked
You install a new #nostr client. You want to zap someone. The app asks you to connect a wallet. You open your wallet, copy a connection string, paste it into the app. Done. From that point on, every zap you send goes through your own wallet without the app ever seeing your keys, your balance, or your payment history.
The string looks something like nostr+walletconnect:// followed by a wall of hex. It is ugly and it works. One paste, and the app can request payments, generate invoices, and check your balance. No API keys, no OAuth dance, no webhook server to babysit.
If you’ve read my article on what actually happens when you zap someone, you know that Step 4 of the zap flow involves your client handing a BOLT11 invoice to a wallet. I glossed over how that handoff works. This is the article about that handoff.
#lightning wallets have had APIs for years. LND has gRPC. CLN has JSON-RPC. But those are node APIs. They assume you’re running the node yourself and can reach it on a network port. NIP-47, the spec behind Nostr Wallet Connect, does something different. It routes wallet commands through the same relay infrastructure that carries your notes and DMs. The wallet doesn’t need a public IP. The app doesn’t need a backend server. They talk through relays, encrypted end to end, and the relay has no idea what they’re saying.
four events and a secret
Semisol opened PR #406 on the nostr-protocol/nips repository on March 29, 2023. fiatjaf merged it on May 2. The spec describes a rewritten version of an earlier proposal by bumi (Michael Bumann, co-founder of Alby), designed to reduce metadata leaks. Four authors are credited: kiwiidb, bumi, semisol, and vitorpamplona.
The protocol uses four event kinds.
Kind 13194 is the info event. Your wallet service publishes this once as a replaceable event, listing what methods it supports and what encryption it speaks. A capabilities handshake.
Kind 23194 is a request. When your app wants to pay an invoice or check your balance, it builds a JSON-RPC message, encrypts it with NIP-44, and publishes it as a kind 23194 event tagged to your wallet service’s pubkey. The relay stores it. The wallet service picks it up.
The wallet service decrypts the request, executes it, and publishes the result as a kind 23195 event encrypted back to your app. Success or error, amount or balance. And kind 23197 handles notifications, so when a payment arrives at your wallet, the wallet service can push an update to connected apps without being asked.
That is the entire protocol. Four event kinds and a shared secret.
The connection string encodes three things: the wallet service’s public key, a relay URL, and a secret. The secret is a dedicated keypair generated specifically for this connection, not your Nostr identity key or your wallet’s master key. A disposable credential scoped to one app talking to one wallet through one relay. Your zapping activity on Damus does not need to be linkable to your npub or your node’s pubkey. I covered the privacy implications of payment graph correlation in my article on Lightning surveillance. NWC’s key isolation is one of the few things in this ecosystem that actually helps.
Ten methods are defined in the spec: pay_invoice, pay_keysend, make_invoice, lookup_invoice, list_transactions, get_balance, get_info, plus a trio for hold invoices (make, settle, cancel). Not every wallet implements all of them. Most apps only need two or three.
I use Coinos as my NWC wallet. My connection is a single URI in an environment variable. The entire client wrapper I built around nostr-sdk’s NWC support is 97 lines of Python. Parse the URI, call the method, check the spending limits. That is it.
who trusts whom
The point of NWC is that the app never touches your keys. But “the app doesn’t have your keys” is not the same as “you don’t trust anyone.”
Start with the relay. It carries encrypted messages between your app and your wallet. It cannot read the content because NIP-44 handles the encryption. But it can see event kinds, the public keys involved, and the timing. It knows that a certain app pubkey is talking to a certain wallet pubkey, and it knows how often. A malicious relay could drop messages or run traffic analysis. It cannot forge them because it doesn’t have the shared secret.
Then there’s the wallet service itself. For custodial setups like Coinos or Primal’s built-in wallet, the wallet service holds your sats. Same custodial tradeoff it has always been, just with a different interface. Self-custodial options like Alby Hub or Zeus mean you run the wallet service on your own hardware. The trust shifts from a company to your uptime and your backups.
The connection string is the part people underestimate. The NWC secret is a bearer credential. Anyone who has it can send payment requests to your wallet. If it leaks, someone can drain your wallet up to whatever budget limits you’ve set. Per-app keys and budgets help, but the string itself is the attack surface.
What you do not trust is the app. That is the whole point. The app sends encrypted requests over a relay. The wallet service decides whether to execute them. If the app is malicious, the worst it can do is request payments up to the budget you’ve configured. It cannot extract your seed phrase, your node credentials, or your other connection strings.
This trust model is genuinely better than giving an app your nsec or pasting your wallet seed into a browser. But I’m not going to call it trustless. A relay outage makes your wallet unreachable. A leaked connection string lets someone spend your sats. And if you use a custodial wallet service, you trust them exactly as much as you did before NWC existed. The protocol makes the trust boundaries cleaner. It does not eliminate them.
I wrote about the custodial tradeoff in my self-custody article. The same UX friction applies here. Self-custodial NWC with Alby Hub is better for your sovereignty and worse for your uptime. Most people will use a custodial wallet and that is a rational choice for small amounts.
the ecosystem that snuck up on everyone
Two events in late 2024 and early 2025 pushed the NWC ecosystem from “interesting experiment” to “de facto standard.”
Mutiny Wallet, one of the early pioneers of NWC support, shut down on December 31, 2024. benthecarman, who had been on the Mutiny team and authored NWC extensions (PR #685) and NIP-67 (Nostr Wallet Auth), moved on. The team described NWC as something that could “change the game for all payments on the web.” Then they closed up shop.
Four days later, on January 4, 2025, Alby shut down its shared custodial wallet and pointed everyone to Alby Hub, a self-custodial Lightning node you run yourself with NWC as the primary interface. One of the most popular Lightning wallets in the Nostr ecosystem stopped holding anyone’s sats and said: run your own node, connect via NWC.
Those two exits did more for NWC adoption than any technical improvement. Developers who had been building against custodial APIs suddenly needed a wallet-agnostic protocol. NWC was there.
The awesome-nwc list on GitHub, maintained by Alby, now tracks roughly 189 projects. Twenty wallets implement NWC as a service. Over a hundred apps connect to those wallets. The list spans nostr social clients like Damus, Amethyst, and Primal, but also Stacker News, Fountain (podcast streaming), Wavlake (music), Zap.Stream (live video), and BTCPay Server (merchant checkout).
Zeus added NWC server support in v0.12.0 on December 22, 2025, with per-connection budget limits, expiry dates, and granular permissions. LNbits, Minibits, and Primal’s wallet all support the protocol. SDKs exist in at least seven languages, including JavaScript, Rust, and Python.
The part that surprised me is what happened outside of social media. NWC has decoupled from Nostr’s social layer entirely. Bitcoin Magazine described it as “the USB-C of Bitcoin wallets” in August 2025. Games like Satoshi Settlers and Zappy Bird use NWC for in-game payments. The Alby MCP Server connects AI agents to Lightning wallets via NWC and the Model Context Protocol, letting tools like Claude and n8n send and receive payments programmatically. BoltCard NWC turns an NFC card into a self-custodial tap-to-pay device. A physical piggy bank called LightningPiggy runs NWC on an ESP32 microcontroller.
Users of these tools don’t need a Nostr keypair. They don’t need to know what a relay is. They paste a connection string and the payments work. The protocol is Nostr infrastructure carrying non-Nostr traffic, and nobody seems to mind.
what NWC doesn’t fix
Self-custodial NWC means your wallet service has to be online. If you’re running Alby Hub on a Raspberry Pi at home and your internet goes down, every app connected to it loses wallet access. Requests don’t queue. They just fail silently. This is the same uptime problem Lightning has always had. NWC doesn’t solve it. It inherits it.
The connection string problem is real. There is no revocation mechanism in the protocol itself. If a string is compromised, you have to generate a new one and reconnect every app. NIP-67, proposed by benthecarman, offers a better flow where the secret is generated client-side and never transmitted over a copyable string, but it is not widely adopted yet.
The relay is a single point of failure for the connection. If the NWC relay goes down, the app cannot reach the wallet even if the wallet is fine. Some wallet services run their own relay, which reduces this risk but concentrates the infrastructure. Alby Hub runs its own relay. That is convenient and centralizing at the same time.
Spending limits are enforced by the wallet service, not by the protocol. The spec defines budget parameters, but whether they are honored depends on the implementation. My own wrapper enforces 1,000 sats per payment and 10,000 sats per day. But that is client-side defense I wrote myself. Not every wallet service enforces limits, and the ones that do each implement them differently.
Some wallets still use NIP-04 encryption, which has known weaknesses. The spec now prefers NIP-44, and the info event supports encryption negotiation, but there is no hard deadline for migration. If your wallet service only speaks NIP-04, your payment requests are less private than they should be.
Even with NIP-44, the relay sees metadata. Who is talking to whom, how often, and at what times. Using a single wallet service key for all connections makes this worse. Alby Hub now generates unique keys per connection, which limits what the relay can correlate. Not all implementations do this.
the glue layer
NWC solved a boring problem. How does app A talk to wallet B without app A holding wallet B’s keys? The answer turned out to be four event kinds, a shared secret, and the same relay network that carries your notes.
It is not trustless. It is not perfect. The connection string is a bearer credential that needs to be treated like a password. The relay is a chokepoint. The spending limits are opt-in. But it works, it is simple enough that a 97-line Python wrapper covers the entire API, and it is the reason you can tap a lightning bolt in Damus and have sats leave your self-custodial Alby Hub without the app ever touching your keys.
The protocol did not set out to become the standard connector between #bitcoin wallets and applications. It set out to let Nostr apps send zaps. The fact that games, AI agents, merchant tools, and NFC cards now use it for payments that have nothing to do with social media suggests it found something more general than the problem it was built to solve.
#nostr #lightning #bitcoin #nwc