Routstr/Cashu safety
Routstr/Cashu code is money-path code. Treat changes here differently from ordinary UI work: a small bug can strand a user’s node balance, overwrite a recovery token, or make a browser wallet look empty after an upgrade. Use this page before changing any of:js/cashu-wallet.jsjs/provider-wallet-runtime.jsjs/provider-wallet-panels.jsjs/provider-wallet-funding-recovery.jsjs/nostr-discovery.js- Routstr provider key, model, or balance handling in
js/api.js - wallet IndexedDB schema or proof storage
cashu-tsupgrades or migration code- Cashu token receive, send, export, or restore
- Lightning funding or withdrawal
- Routstr node deposit or refund logic
Safety invariants
Do not leak bearer material
Never print, persist to test logs, or include in screenshots:- wallet seed phrases;
- Cashu tokens;
- Routstr
sk-...session keys.
Preserve pending recovery state
pendingDepositmust be saved before sending a node deposit token.pendingDepositmust survive node rejection or network failure until the token is recovered or explicitly cleared.pendingWithdrawmust be saved before importing a node refund token.pendingWithdrawmust be cleared only after successful receive.- A new pending withdraw token must not overwrite an existing pending withdraw token.
Do not replace existing Routstr sessions
Deposit routing must be:| State | Endpoint |
|---|---|
no existing sk-... key | /v1/balance/create |
existing sk-... key | POST /v1/balance/topup |
Keep the wallet facade stable
js/cashu-wallet.js is the app’s compatibility facade. Library upgrades should happen behind this facade without forcing a wallet reset.
Upgrades must preserve:
- existing wallet seed;
- existing mint URL;
- existing proof rows;
- pending funding quotes;
- pending deposit token;
- pending withdraw token;
- Routstr node key and selected node;
- JSON-safe proof rows, even if the Cashu library uses richer Amount objects internally.
Required test layers
Run tests in this order. Do not run real-funds tests until the no-money layers pass.1. Runtime wallet tests
2. Browser Routstr wallet test
Run against the active dev server. If getbased is running on port 8180:- seed gate and restore UI;
- mint switching for node requirements;
- Lightning invoice funding UI;
- pending Lightning funding recovery UI;
- node deposit failure recovery UI;
- node refund recovery path;
- backup/export token action;
- send-as-token action;
- Lightning invoice withdrawal quote and execute;
- Lightning address withdrawal with amount.
PORT to the actual getbased dev-server port. Verify the app identity if another service owns the default port.
3. Guarded tiny-sats canary
Only run this after the first two layers pass and after the developer intentionally chooses a tiny real amount. The app repo script is:PAY_THIS_LIGHTNING_INVOICE=... invoice, then run:
/tmp. Do not use a user’s normal browser profile for test funds.
Expected canary checks:
- paid Lightning invoice recovers into the app wallet;
- first node deposit uses
/v1/balance/create; - second deposit with existing key uses
/v1/balance/topup; - a tiny model call works;
- node refund returns a Cashu token;
- refund token is saved before receive;
- refund token receives into the wallet;
- pending withdraw clears only after receive;
- Routstr key is cleared after refund;
- Cashu token export/import works in a second profile;
- token send-back recovers funds to the main profile;
- seed restore works in a third throwaway profile;
- final state has no pending deposit, no pending withdraw, and no Routstr key.
Mint selection
Do not assume the default mint is reachable or accepted by the node. Before a real-funds canary, use a mint that is both:- reachable from the test environment, and
- listed by the selected Routstr node’s
/v1/infoas accepted.
mint.cubabitcoin.org reachable and accepted by api.routstr.com, while mint.minibits.cash was unreachable or refused from the VM path. Re-check live state before relying on either.
What blocks shipping
A Routstr/Cashu change should not ship if any of these are true:- existing wallet balance disappears after reload or upgrade;
- pending funding quotes are lost;
- pending deposit token is lost after a failed node deposit;
- pending withdraw token can be overwritten;
- existing Routstr key causes
/createinstead of/topup; - refund token is imported before being saved as pending;
- seed restore no longer finds issued proofs;
- real-funds canary leaves a node key with unrecovered balance;
- real-funds canary leaves unexplained pending state;
- test logs contain seed phrases, Cashu tokens, or
sk-...keys.
Current baseline before Cashu TS upgrade
The current baseline was tested with a 1000 sat canary before the Cashu TS migration work:- wallet funding recovered: 1000 sats;
- first node deposit:
/v1/balance/create; - second node deposit:
/v1/balance/topup; - model call: 200 OK;
- node refund: received back into wallet;
- token export/import: passed;
- seed restore: passed;
- final wallet: 993 sats;
- pending deposit: false;
- pending withdraw: false;
- Routstr key present: false.