NUTbits is an NWC wallet service that translates between Cashu Mint (NUTs) and Nostr Wallet Connect (NIP-47). It connects to a Cashu mint, manages ecash tokens automatically, and exposes a full NWC server, so any NWC-compatible app can send and receive Lightning payments through the mint.
Use case: plug the NWC string into LNbits as a funding source, and your ecash mint powers the entire LNbits instance with 60+ Extensions.
Inspired by supertestnet/bankify. Built on @cashu/cashu-ts and nostr-core.
See INSTALL.md for full setup instructions (bare metal, Docker, LNbits integration).
git clone https://github.com/DoktorShift/nutbits.git && cd nutbits
npm install && cp .env.example .env
# Edit .env - set NUTBITS_MINT_URL and NUTBITS_STATE_PASSPHRASE
npm startNUTbits has two parts: the service (npm start) and the management console (nutbits). The service runs in one terminal and handles all NWC traffic. The console runs in a second terminal and lets you create connections, check balances, and manage everything without restarting the service.
The default NWC string printed on startup works, but you'll want to use the console to create dedicated connections with scoped permissions and spending limits - one for LNbits, another for a POS, each with its own rules. See Management Console below.
All settings in .env (see .env.example):
| Variable | Default | Description |
|---|---|---|
NUTBITS_MINT_URL |
https://mint.minibits.cash/Bitcoin |
Cashu mint URL (find mints) |
NUTBITS_MINT_URLS |
(optional) | Comma-separated mint URLs for failover (first = primary) |
NUTBITS_RELAYS |
wss://nostrue.com |
Comma-separated Nostr relays |
NUTBITS_STATE_PASSPHRASE |
(required) | Passphrase to encrypt state at rest |
NUTBITS_STATE_FILE |
./nutbits_state.enc |
Path to encrypted state file |
NUTBITS_LOG_LEVEL |
info |
error, warn, info, or debug |
NUTBITS_FEE_RESERVE_PCT |
1 |
Percentage reserved for routing fees |
NUTBITS_MAX_PAYMENT_SATS |
0 |
Max sats per payment (0 = no limit) |
NUTBITS_DAILY_LIMIT_SATS |
0 |
Max sats per day (0 = no limit) |
NUTBITS_HEALTH_CHECK_INTERVAL_MS |
60000 |
Mint health check interval in ms |
NUTBITS_FAILOVER_COOLDOWN_MS |
10000 |
Cooldown before retrying a failed mint |
NUTBITS_STATE_BACKEND |
file |
Storage backend: file, sqlite, or mysql |
NUTBITS_SQLITE_PATH |
./nutbits_state.db |
SQLite database path |
NUTBITS_MYSQL_URL |
(optional) | MySQL connection URL |
NUTBITS_SERVICE_FEE_PPM |
0 |
Service fee in parts per million on outgoing payments (0 = disabled) |
NUTBITS_SERVICE_FEE_BASE |
0 |
Flat base fee in sats per outgoing payment (0 = disabled) |
NUTBITS_API_ENABLED |
true |
Set to false to disable management API/CLI |
Storage: See DATABASE.md for backend comparison, setup, and migration. Backups: See BACKUP.md for backup and recovery procedures. Service fees: See CLI.md for fee configuration, per-connection overrides, and revenue tracking.
Encryption: NIP-44 (preferred) with NIP-04 fallback, auto-detected per client.
| NUT | Name | Status | Used For |
|---|---|---|---|
| NUT-00 | Cryptography & Models | Proof structure, keyset IDs | |
| NUT-01 | Mint Public Keys | Key fetching via loadMint() |
|
| NUT-02 | Keysets | Keyset management, fee calculation | |
| NUT-03 | Swap | Proof selection via wallet.send() |
|
| NUT-04 | Mint (BOLT11) | Receiving: make_invoice |
|
| NUT-05 | Melt (BOLT11) | Sending: pay_invoice |
|
| NUT-06 | Mint Info | Feature detection, capability gating | |
| NUT-07 | Proof State Check | Verify proofs on mint recovery | |
| NUT-08 | Lightning Fee Return | Change proofs from overpaid fees | |
| NUT-09 | Signature Restore | Recover proofs from seed after data loss | |
| NUT-12 | DLEQ Proofs | Verify mint signatures, anti-counterfeit | |
| NUT-13 | Deterministic Secrets | Seed-based proof generation for recovery | |
| NUT-17 | WebSocket Subscriptions | Instant invoice settlement (replaces polling) |
| NIP | Name | Used For |
|---|---|---|
| NIP-04 | Encrypted DMs | Legacy encryption fallback |
| NIP-40 | Expiration Tag | Ignoring expired NWC requests |
| NIP-44 | Versioned Encryption | Preferred encryption (auto-detected) |
| NIP-47 | Nostr Wallet Connect | Core protocol for all wallet operations |
get_info- wallet metadata, capabilities, encryption supportget_balance- current balance in millisatsmake_invoice- create a Lightning invoice (NUT-4 mint)pay_invoice- pay a Lightning invoice (NUT-5 melt)lookup_invoice- check invoice statuslist_transactions- transaction history with filtering
Notifications (push):
payment_received- sent when an incoming invoice settlespayment_sent- sent when an outgoing payment completes
Optional NIP-47 extensions (non-breaking, clients can ignore or honor):
get_inforesponse includesservice_feeobject when fees are enabled (ppm, base, applies_to)pay_invoiceresponse includesservice_feefield (msats) separate fromfees_paid(routing)
- NUTbits generates a keypair and creates an NWC connection string
- It subscribes to NWC request events (kind 23194) on configured Nostr relays
- When a command arrives, it translates to Cashu operations via
@cashu/cashu-ts:pay_invoice-> wallet melts ecash to pay the Lightning invoice (NUT-5). Optional service fee deducted if configured.make_invoice-> wallet requests a mint quote; upon payment, mints new ecash (NUT-4). No fees on incoming.
- Responses are sent back as NWC events (kind 23195)
State (keys, ecash proofs, transaction history) is encrypted with AES-256-GCM and persisted to disk.
NUTbits includes a CLI and interactive TUI to manage the daemon while it's running. Open a second terminal and use nutbits to control connections, check balances, pay invoices, and monitor activity, all without restarting the service.
nutbits # interactive TUI dashboard
nutbits balance # check balance across mints
nutbits connections # list NWC connections
nutbits connect # create new connection (guided wizard)
nutbits revoke <label> # revoke a connection
nutbits pay <invoice> # pay a Lightning invoice
nutbits receive <amount> # create an invoice
nutbits history # transaction history
nutbits fees # view/manage service fees
nutbits mints # mint status and health
nutbits relays # relay connection statusCreate multiple NWC connections with scoped permissions and spending limits; one for LNbits with full access, another for a POS with pay-only and a daily cap. Revoke any connection without affecting the others.
See CLI.md for the full command reference and CONSOLE.md for TUI usage.
Set
NUTBITS_API_ENABLED=falsein.envto disable the management API entirely.
- Encrypted state persistence (AES-256-GCM + scrypt)
- Event deduplication across relays (prevents double-payments)
- Per-payment and daily spend limits (global + per-connection)
- Per-connection service fee scoping
- NWC string masked in logs
- State file permissions restricted to owner (0600)
- Atomic state writes (crash-safe)
- Graceful shutdown with state save
All wallet data (ecash proofs, NWC keys, transaction history) is stored in an encrypted state file. Read STATE.md for backup, recovery, and decryption instructions - this is critical if you're running NUTbits with real funds.
NUTbits supports optional multi-mint failover for higher reliability. Configure multiple mints and NUTbits will automatically switch to the next one if the active mint goes down. Your NWC connection string stays the same.
# In .env - first mint is primary, rest are fallbacks
NUTBITS_MINT_URLS=https://your-primary-mint.com,https://your-backup-mint.com- On startup, NUTbits tries mints in order until one responds
- If the active mint goes down, it automatically fails over to the next healthy mint
- A background health check runs every 60s. When the primary mint recovers, NUTbits switches back
- In-flight invoices are checked against the mint that created them, even during failover
- Your NWC connection string stays the same throughout
Ecash proofs are cryptographically bound to the mint that issued them. Proofs from Mint A cannot be spent through Mint B. This means:
- On failover, your spendable balance is whatever was pre-funded on the new active mint. Proofs on the old mint are not lost; they become spendable again when that mint recovers.
- In-flight invoices (created but not yet paid) are tied to their originating mint. They will still resolve when that mint comes back online.
- When a mint recovers, NUTbits automatically switches back and the full balance on that mint is available again.
These trade-offs are minimal if you run your own mints. Pre-fund both mints, and failover is seamless. Recovery is automatic.
Be aware that switching mints can temporarily affect users trying to pay out, since the spendable balance depends on which mint is active. If you run NUTbits as a funding source for others, consider whether the failover behavior fits your use case before enabling it in production.
| Document | Description |
|---|---|
| HOW-IT-WORKS.md | Plain-language guide; what NUTbits does and why |
| CONSOLE.md | How to use the TUI dashboard and CLI day-to-day |
| CLI.md | Full command reference - flags, scripting, connections |
| INSTALL.md | Setup guide - bare metal, Docker, LNbits |
| DATABASE.md | Storage backends - file, SQLite, MySQL |
| BACKUP.md | Backup, recovery, and encryption details |
| STATE.md | Deep dive into the encrypted state file |
Ecash is custodial. The mint holds the funds. Standard risks apply: the mint can steal, get shut down, or get hacked. Only use mints you trust, and only with amounts you can afford to lose.
NUTbits operators can optionally enable a service fee on outgoing payments. This fee is transparent - advertised in the NWC get_info response and reported separately in every pay_invoice response. Receiving payments is always free. By default, no fees are charged.
- Cashu - Ecash protocol for Bitcoin
- LNbits - Lightning accounts system
- Nostr Wallet Connect (NIP-47) - Wallet protocol over Nostr
- nostr-core - Nostr + LNURL library used by NUTbits
- @cashu/cashu-ts - Cashu TypeScript library
- supertestnet/bankify - Original inspiration for this project
- bitcoinmints.com - Directory of Cashu mints
AGPL-3.0 - Free to use, modify, and distribute. If you run a modified version as a network service, you must share your source code.