Cashu eCash
The module supports Cashu eCash tokens as an alternative payment method to Lightning invoices.
Standard Mode vs P2PK Mode
| Standard Mode | P2PK Mode | |
|---|---|---|
| How it works | Calls wallet.receive() → contacts mint to swap tokens | Verifies token locked to proxy’s public key locally |
| Speed | Slower (blocks on mint API call per request) | Fast (milliseconds — no mint call!) |
| Best for | Low-traffic or simple setups | High-traffic production deployments |
| Extra requirement | None | CASHU_WHITELISTED_MINTS is required |
Standard Mode Setup
Environment=CASHU_ECASH_SUPPORT=true
Environment=CASHU_DB_PATH=/var/lib/nginx/cashu_tokens.db
Environment=CASHU_WALLET_SECRET=<your-secret-random-string>
# Optional: Whitelist specific mints (comma-separated)
Environment=CASHU_WHITELISTED_MINTS=https://mint1.example.com,https://mint2.example.com
# Optional: Auto-redeem to Lightning
Environment=CASHU_REDEEM_ON_LIGHTNING=true
Environment=CASHU_REDEMPTION_INTERVAL_SECS=3600
⚠️ Security:
CASHU_WALLET_SECRETis used to generate the wallet seed. Anyone with this secret can steal your tokens! Generate withopenssl rand -hex 32and never commit it to Git.
P2PK Mode Setup (High Performance)
Environment=CASHU_P2PK_MODE=true
Environment=CASHU_P2PK_PRIVATE_KEY=<your-private-key-hex>
# CASHU_WHITELISTED_MINTS is REQUIRED in P2PK mode
Environment=CASHU_WHITELISTED_MINTS=https://mint1.example.com
⚠️ Security:
CASHU_P2PK_PRIVATE_KEYis equally critical. Anyone with this key can spend tokens locked to your public key. Generate withopenssl rand -hex 32.
How P2PK mode works per request:
- Proxy derives a public key from
CASHU_P2PK_PRIVATE_KEYand sends it to clients via theX-Cashuheader (NUT-24) - Client creates P2PK-locked tokens to that public key
- Proxy verifies tokens are locked to its public key (NUT-11) — local check, no network call
- Proxy unlocks proofs with private key (local cryptographic operation)
- Unlocked proofs are stored directly in CDK database via
wallet.receive_proofs() - Background redemption task finds proofs via
wallet.get_unspent_proofs()and redeems to Lightning viawallet.melt()
Redemption Fee Configuration
# Minimum balance to attempt melting (default: 10 sats)
Environment=CASHU_MELT_MIN_BALANCE_SATS=10
# Percentage to reserve for fees (default: 1%)
Environment=CASHU_MELT_FEE_RESERVE_PERCENT=1
# Minimum fee reserve when percentage is small (default: 4 sats)
Environment=CASHU_MELT_MIN_FEE_RESERVE_SATS=4
# Maximum proofs per melt operation (default: 0 = unlimited)
# Use this if your mint has a per-melt proof limit (e.g. mint.coinos.io = 1000)
Environment=CASHU_MAX_PROOFS_PER_MELT=1000
Fee calculation: fee_reserve = max(total_amount × percent/100, min_fee_sats)
Example 1 — Large balance (500 sats) with 1% fee reserve:
- Percentage fee:
500 × 1% = 5 sats - Minimum fee:
4 sats - Used reserve:
max(5, 4) = 5 sats - Redeemable:
500 - 5 = 495 sats
Example 2 — Small balance (50 sats) with 1% fee reserve:
- Percentage fee:
50 × 1% = 0.5 sats - Minimum fee:
4 sats - Used reserve:
max(0.5, 4) = 4 sats← Minimum kicks in! - Redeemable:
50 - 4 = 46 sats
Example 3 — Proof count limiting when exceeding mint limit (CASHU_MAX_PROOFS_PER_MELT=1000):
- Scenario: 1282 proofs worth 13,588 sats total
- Check:
1282 proofs > 1000 limit→ Limiting triggered - Action: Select first 1000 proofs worth ~10,600 sats
- Invoice: Generate invoice for 10,600 sats
- Remaining: 282 proofs (~2,988 sats) stay for next cycle
- Next cycle:
282 proofs < 1000 limit→ all remaining proofs melted
Actual melt quote fees are verified against the reserve; warnings appear if the reserve was insufficient.
Note on
CASHU_WHITELISTED_MINTS: If not configured, all mints are accepted in standard mode. In P2PK mode, whitelisted mints are REQUIRED for security and the payment request (NUT-24).
SQLite Database Setup
# One-time setup — persists across restarts
sudo mkdir -p /var/lib/nginx
sudo chown nginx:nginx /var/lib/nginx
sudo chmod 755 /var/lib/nginx
The cdk-sqlite crate automatically creates the database file and tables. Database location: /var/lib/nginx/cashu_tokens.db