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 three main parts:
- the service (
npm start) that handles NWC traffic - the management console (
nutbits) with CLI and TUI modes - the web GUI in
gui/, served bynpm run gui
The service usually runs in one terminal or in the background. The console and GUI connect to the same local API, so you can manage the same NUTbits instance from terminal or browser.
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.
Run these commands from the repository root, where package.json lives.
npm start # backend only, in your terminal
npm run nutbits # backend + GUI, in the background
npm run service:mac # macOS 24/7 backend service
npm run service:linux # Linux 24/7 backend serviceThat is the simplest mental model:
- use
npm startwhen you want the normal terminal experience - use
npm run nutbitswhen you want the web GUI too - use
npm run service:macornpm run service:linuxwhen you want the backend to keep running 24/7
If you want the backend to run 24/7 under your operating system's service manager, see SERVICE.md.
npm run nutbits # backend + GUI in the background
npm run nutbits:interactive # GUI in background, backend in your terminal
npm run nutbits:stop # stop backend + GUI
npm run nutbits:restart # restart NUTbits mode
npm run nutbits:update # update, rebuild GUI, restart background NUTbitsThese helper scripts live in scripts/. In background mode, logs go to logs/nutbits.log and logs/gui.log.
All settings in .env (see .env.example):
| Variable | Default | Description |
|---|---|---|
NUTBITS_MINT_URL |
https://mint.minibits.cash/Bitcoin |
Cashu mint URL (find mints) |
NUTBITS_STATE_PASSPHRASE |
(required) | Passphrase to encrypt state at rest |
| Variable | Default | Description |
|---|---|---|
NUTBITS_SEED |
(auto-generated) | Deterministic wallet seed for proof recovery. Back this up. |
NUTBITS_MINT_URLS |
(optional) | Comma-separated mint URLs for multi-mint failover (first = primary). When set, overrides NUTBITS_MINT_URL. |
NUTBITS_RELAYS |
wss://nostrue.com |
Comma-separated Nostr relays for NWC |
NUTBITS_STATE_BACKEND |
file |
Storage backend: file, sqlite, or mysql |
NUTBITS_STATE_FILE |
./nutbits_state.enc |
Path to encrypted state file (file backend) |
NUTBITS_SQLITE_PATH |
./nutbits_state.db |
SQLite database path (sqlite backend) |
NUTBITS_MYSQL_URL |
(optional) | MySQL connection URL (mysql backend) |
| Variable | Default | Description |
|---|---|---|
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_FEE_RESERVE_PCT |
1 |
Percentage reserved for Lightning routing fees |
| Variable | Default | Description |
|---|---|---|
NUTBITS_SERVICE_FEE_PPM |
0 |
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) |
| Variable | Default | Description |
|---|---|---|
NUTBITS_HEALTH_CHECK_INTERVAL_MS |
60000 |
Mint health check interval |
NUTBITS_FAILOVER_COOLDOWN_MS |
10000 |
Cooldown before retrying a failed mint |
NUTBITS_INVOICE_CHECK_MAX_RETRIES |
60 |
Max retries for pending invoice checks |
NUTBITS_INVOICE_CHECK_INTERVAL_SECS |
20 |
Seconds between pending invoice checks |
NUTBITS_FETCH_TIMEOUT_MS |
15000 |
Timeout for mint API requests |
| Variable | Default | Description |
|---|---|---|
NUTBITS_API_ENABLED |
true |
Set to false to disable the management API, CLI, and GUI |
NUTBITS_API_PORT |
(optional) | HTTP port for the API (leave empty for Unix socket only) |
NUTBITS_API_SOCKET |
(auto) | Unix socket path (default: ~/.nutbits/nutbits.sock) |
NUTBITS_API_TOKEN |
(auto) | Bearer token for API access (auto-generated on start) |
NUTBITS_LOG_LEVEL |
info |
error, warn, info, or debug |
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 |
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 status by payment hashlist_transactions- transaction history with filtering
Notifications (push):
payment_received- sent when an incoming invoice settlespayment_sent- sent when an outgoing payment completes
Lightning Address support:
Each NWC connection can optionally carry a Lightning Address (lud16). Apps that understand this parameter can use it for receiving payments. The address is included in the NWC connection string as &lud16=user@domain.com and is validated via LUD-16 resolution on create and edit.
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.
For a plain-language deep dive, see HOW-IT-WORKS.md.
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 connect --lud16 user@example.com # attach a Lightning Address
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 status
nutbits logs # recent log output
nutbits watch # live activity monitor
nutbits config # view runtime configurationCreate 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.
NUTbits ships with a browser-based GUI in gui/. It talks to the same local management API as the CLI and TUI, so all three interfaces stay in sync.
- Default local URL:
http://127.0.0.1:8080 - Backend API default:
http://127.0.0.1:3338 - The GUI can bootstrap the local API token automatically when the backend is running on loopback
| Page | What it does |
|---|---|
| Dashboard | Live overview: balance, connections, relays, uptime, mint health, recent transactions |
| Connections | Create, view, export, and revoke NWC connections. Card and list views. QR codes for NWC strings. |
| History | Transaction history with volume chart, type/connection filters, and CSV/JSON export |
| Pay | Pay a Lightning invoice or LNURL/Lightning Address from the GUI |
| Receive | Create a Lightning invoice with QR code |
| Mints | Active mint details, multi-mint management, failover priority, NUT capability matrix |
| Relays | Nostr relay status, add/remove relays |
| NUTs | Detailed NUT protocol support for each mint |
| Fees | Service fee earnings dashboard with 7-day chart and per-connection breakdown |
| Settings | All configuration in one place: wallet, network, limits, fees, API, advanced |
| Logs | Live log viewer with level filtering, search, and auto-refresh |
- Encrypted state persistence (AES-256-GCM + scrypt, N=65536)
- 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
- Seed-based proof recovery (NUT-09, NUT-13)
- DLEQ proof verification (NUT-12)
All wallet data (ecash proofs, NWC keys, transaction history) is stored in an encrypted state file. Read BACKUP.md for backup, recovery, and encryption details - 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, CLI, and GUI day-to-day |
| CLI.md | Full command reference - flags, scripting, connections |
| INSTALL.md | Setup guide - bare metal, Docker, LNbits |
| SERVICE.md | 24/7 backend service setup with launchd and systemd |
| DATABASE.md | Storage backends - file, SQLite, MySQL |
| BACKUP.md | Backup, recovery, and encryption details |
| STATE.md | Deep dive into the encrypted state file |
| AGENTS.md | Agent/developer reference for building on NUTbits |
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.