"NIP-60 wallets: carrying your ecash across every Nostr app"

NIP-60 stores your Cashu wallet state on relays so any client can read it. The idea is simple. The trust model is not.
"NIP-60 wallets: carrying your ecash across every Nostr app"

Your wallet doesn’t follow you

You open Amethyst and zap someone 100 sats. You close it, open Olas to scroll through photos, and want to tip a photographer. But Olas doesn’t know about the #cashu tokens sitting in your other app. You have ecash in three different wallets on three different clients and none of them can see each other.

This is the problem with ecash on #nostr right now. Each app manages its own wallet. Your tokens live in local storage on whatever device you were using when you minted them. Move to a new phone, try a new client, clear your browser cache at the wrong moment, and your ecash is gone. Not because anything went wrong with the mint or the protocol, but because nobody told your new app where your tokens were.

If you’ve read my article on Cashu ecash and the privacy layer Lightning doesn’t have, you know how the tokens work. Blind signatures, bearer instruments, privacy through cryptography instead of policy. What that article didn’t cover is where those tokens actually live and what happens when you want to use them somewhere else.

NIP-60 is the answer, or at least an attempt at one. It stores your wallet state on relays. The same relays that hold your notes, your follow list, your profile. Log into any app that supports it, and your ecash balance is there. Like your contact list, but for money.

How wallet state becomes relay events

pablof7z wrote the spec, opened PR #1369 on the nostr-protocol/nips repository in July 2024, and fiatjaf merged it that October. The design is built on four event kinds.

Kind 17375 is the wallet event. It’s a replaceable event that stores your wallet configuration: which mints you use and a private key dedicated to wallet operations. This private key is separate from your Nostr identity key. It exists only for Cashu P2PK operations, which means a compromised wallet key doesn’t compromise your Nostr identity, and vice versa. The entire content is encrypted with NIP-44, so relays store it but can’t read it.

Kind 7375 events hold your actual tokens. The unspent Cashu proofs. Each event contains the mint URL, the denomination unit, and an array of proofs in standard Cashu format. There can be multiple 7375 events per mint. All of it is NIP-44 encrypted. The relay sees opaque blobs. Your client decrypts them with your Nostr key and knows your balance.

Kind 7376 tracks spending history. When you redeem tokens, your client can publish a 7376 event recording what happened. Other clients can use these to reconstruct your transaction history. The events are optional; the wallet works without them.

Kind 7374 handles quote states for pending #lightning deposits or withdrawals. These use NIP-40 expiration tags so they clean themselves up when they’re no longer relevant.

The whole system piggybacks on infrastructure that already exists. Nostr relays already store encrypted events. NIP-44 encryption already works. NIP-09 deletion already works. NIP-60 just says: use all of that for money.

The token lifecycle

Minting is straightforward. You send sats to a Cashu mint via a Lightning invoice. The mint issues ecash tokens. Your client takes those tokens and publishes them as a kind 7375 event, encrypted, to your relays. Now any NIP-60-compatible client that loads your events can see that balance.

Spending is where it gets interesting. When you spend some proofs from a token event, your client has to do three things in sequence. First, delete the original kind 7375 event using NIP-09. Second, create a new kind 7375 event containing whatever proofs are left unspent, plus any change outputs from the mint. Third, include a del array in the new event that references the destroyed event’s ID, so other clients can follow the state transition.

This is the part that matters for portability. When you open a different client, it fetches your kind 7375 events from relays, decrypts them, and knows your current balance. If events were deleted, the NIP-09 deletion markers and the del references in newer events tell the client what happened. The token events are the wallet. The relays are the storage layer.

NIP-61 nutzaps fit into this directly. When someone nutzaps you, they publish a kind 9321 event containing Cashu tokens locked to the P2PK public key you advertised in your kind 10019 event. Your client picks up the nutzap, unlocks the tokens using the private key stored in your kind 17375 wallet event, redeems them at the mint, and publishes the new proofs as a kind 7375 event. The whole cycle happens through relay events. No LNURL server, no HTTP callbacks.

What’s using it

callebtc, Cashu’s creator, posted in late 2024 that NIP-60 had gained traction with at least six different wallets supporting it. The number has grown since.

Olas, the Instagram-style Nostr client built by pablof7z using NDK Mobile, has NIP-60 baked in. Shopstr uses it for its marketplace payment flow. AFK, a community app, supports it. nostr.blue, built with CDK compiled to WebAssembly, ships with a NIP-60 wallet. Cashu Cache is a dedicated tool for creating NIP-60-compliant wallets and advertising your NIP-61 nutzap configuration.

On the developer side, pablof7z’s NDK wallet package (@nostr-dev-kit/ndk-wallet) provides a reference implementation covering NIP-60, NIP-57 zaps, and NIP-47 NWC in a single library. It includes a nutzap monitor that automatically detects and redeems incoming nutzaps. fiatjaf added nak wallet commands to his CLI tool in February 2025, implementing NIP-60 token receiving, sending, and swapping from the command line.

The ecosystem is real, though still small. Most Nostr users have never heard of NIP-60. The clients that support it are not the mainstream ones yet. Primal and Damus, the two apps most new users encounter first, don’t implement it.

The trust problem nobody wants to talk about

Here’s where I have to push back on the appeal of this design.

NIP-60 stores your money on relays. Not your notes. Not your follow list. Your money. And relays are not banks. They have no obligation to keep your data, no regulatory framework requiring them to do so, and no recourse if they delete your events.

A user called floppy put it plainly on X: “With NIP-60, nostr relays will become a trusted third party involved in custody of ecash.” That’s an overstatement in one sense. The relay can’t spend your tokens because they’re encrypted and the proofs are still redeemable at the mint using keys the relay doesn’t have. But it’s accurate in another sense. If the relay deletes your kind 7375 events and you don’t have a local backup, you’ve lost the proofs. The mint still holds the #bitcoin backing those tokens, but you no longer have the blinded secrets needed to redeem them.

This is different from losing a note. If a relay drops your kind 1 event, you can repost it from another relay or from your client’s cache. It’s annoying but not catastrophic. If a relay drops your kind 7375 event and that was your only copy, you’ve lost actual money.

The mitigation is the same one that applies to all Nostr data: use multiple relays. Publish your wallet events to several, so if one goes down, the others still have your proofs. Clients should keep a local copy too. Don’t rely solely on relay availability.

But “use multiple relays” is the same answer we give for every Nostr reliability problem, and it works exactly as well here as it does everywhere else, which is imperfectly.

There’s a subtler issue too. Suppose you have two clients open. You spend 50 sats from Client A, which deletes a kind 7375 event and creates a new one with the remaining balance. Meanwhile, Client B still has the old event cached. Client B tries to spend 30 sats from the same proofs. The mint rejects the second spend, because Cashu’s double-spend protection handles that part. But now your relay state is a mess. Client B published a deletion and a new event based on proofs that no longer exist.

NIP-60 doesn’t define a locking or reservation mechanism. There’s no way for Client A to tell Client B “I’m about to spend these proofs, don’t touch them.” This was debated in PR #1369. Some reviewers wanted an event-sourcing approach instead of deletion-based state management. pablof7z argued that the deletion model was simpler and that edge cases could be handled at the client level. For now, the practical advice is: don’t use two NIP-60 wallets simultaneously on two devices. That’s not great advice for a feature whose entire selling point is cross-app portability.

What the spec doesn’t solve

NIP-60 is a storage format. It tells clients where to put wallet data and how to encrypt it. The harder problems are someone else’s job.

How does a new user know which mints to trust? NIP-60 lets your wallet list its mints, but choosing those mints in the first place is still a manual decision with real financial consequences. Pick a mint that rugs and your tokens are worthless regardless of how well they’re stored on relays. The spec has no opinion on this.

Performance is a real concern. A wallet with activity across several mints might accumulate dozens or hundreds of kind 7375 events. Each one needs to be fetched and decrypted individually. If you’re using NIP-46 remote signing, every decryption requires a separate WebSocket round-trip to the signer. GitHub issue #2160 on the nips repository proposes key wrapping to fix this. Generate a local wallet key, encrypt it once with your identity key, then decrypt everything locally. It’s not merged.

The recovery story worries me more. Lose your nsec, lose your NIP-60 wallet. The proofs are on relays, encrypted to your key. No key, no decryption, no tokens. Deterministic Cashu wallets using NUT-13 seed phrases could help. With a deterministic wallet, you regenerate proofs from a seed without needing the relay events at all. A proposal extends NIP-60 with a kind 17376 event for seed and counter state. It’s still being discussed.

Pocket change, not savings

I set up NIP-61 nutzaps on my own account months ago. The experience is genuinely better than the NIP-57 zap flow for small amounts. No LNURL server in the middle, no invoice round-trip, no wondering whether the receipt will show up. But I keep the amounts small. A few hundred sats at most. I treat NIP-60 the way I treat the cash in my pocket. Enough for a coffee, not enough to ruin my week if I lose it.

The idea that your wallet follows you like your follow list is compelling. The fact that your follow list occasionally gets clobbered by conflicting client writes should tell you something about how well this will work for financial data.

Use it. Set up nutzaps. Carry some sats across apps. Just don’t carry more than you’d be okay losing to a relay that decided to clean house.

#nostr #cashu #bitcoin #privacy


No comments yet.