Quick start with Docker
mainThe fastest way to run a local instance of Cashu.me is using Docker Compose. This will set up the wallet environment locally.
git clone https://github.com/cashubtc/cashu.me.git
cd cashu.me
docker compose up -drepository·main·Indexed 42 days ago
https://github.com/cashubtc/cashu.meA free and open-source progressive web app (PWA) wallet for the Cashu ecash protocol. It enables private sending and receiving of funds using Chaumian ecash, featuring support for Lightning, multi-mint payments, and Nostr integration. The project is built with Vue, Quasar, and Capacitor for mobile (Android/iOS) and Electron for desktop deployment.
The fastest way to run a local instance of Cashu.me is using Docker Compose. This will set up the wallet environment locally.
git clone https://github.com/cashubtc/cashu.me.git
cd cashu.me
docker compose up -dBecause Cashu.me is a Single Page Application (SPA) using Vue Router in history mode, your web server must:
index.html for all non-file paths to allow page refreshes.Example Caddyfile configuration:
(cors) {
@cors_preflight method OPTIONS
@cors header Origin {args.0}
handle @cors_preflight {
header Access-Control-Allow-Origin "{args.0}"
header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE"
header Access-Control-Allow-Headers "Content-Type"
header Access-Control-Max-Age "3600"
respond "" 204
}
handle @cors {
header Access-Control-Allow-Origin "{args.0}"
header Access-Control-Expose-Headers "Link"
}
}
host.com {
import cors *
encode gzip
header /service-worker.js {
Service-Worker-Allowed "/"
Cache-Control "no-cache"
}
# SPA root
root * /usr/share/caddy/cashu.me/
# quasar vue router fallback history mode
try_files {path} /index.html
file_server
}Cashu.me uses Capacitor to ship native Android and iOS apps. To build the web assets and sync them into the native projects, follow these steps:
To regenerate app icons and splash screens after modifying icon.png:
npx capacitor-assets generatenpm run build:pwa
npx cap copy
npx cap sync
npx cap open android
npx cap open iosFor desktop development and production:
make electron to start an Electron development build.npm run build:electron to produce a production build.To develop on Cashu.me, ensure you have Node.js >= 22.4 and npm installed. Use the following commands to install dependencies and start the development server with hot reloading.
npm install
npm run devUnit tests use Vitest. To run a specific test file:
npx vitest src/path/to/test.tsE2E tests use Playwright against a local test stack (two CDK mints with a Lightning backend) started via Docker Compose.
Full automated run:
npm run test:e2eManual stack management: If you want to manage the Docker stack yourself and run Playwright directly:
npm run test:e2e:stack:up
npm run test:e2e:playwright
npm run test:e2e:stack:downThe MintClass is a helper class that wraps a StoredMint object to provide high-level access to its API and related data. It is used to bridge the gap between stored state and live API interactions.
Key Properties:
api: Returns a new instance of the @cashu/cashu-ts Mint class initialized with the mint's URL.proofs: Returns all proofs in the proofsStore that belong to this mint's keysets.allBalances: Returns a record of balances for all supported units in the mint.keysets: Returns only the currently active keysets.units: Returns a unique list of currency units supported by the mint's keysets.Methods:
unitBalance(unit: string): Returns the sum of all non-reserved proofs for a specific unit.unitProofs(unit: string): Returns all non-reserved WalletProof objects for a specific unit.const mintClass = new MintClass(storedMint);
const balance = mintClass.unitBalance('sat');
const api = mintClass.api; // Access @cashu/cashu-ts Mint instanceThe walletMelt.ts module provides functions for managing the lifecycle of 'melt' operations, where ecash (Cashu proofs) is exchanged for Bitcoin. This includes normalizing quotes from different payment methods (Bolt11, Bolt12, Onchain), executing the melt, checking the status of outgoing invoices, and recovering change proofs.
normalizeMeltQuote is used to convert them into a consistent AppMeltQuote format used by the application.OutputData to allow the user to reconstruct those proofs later.You can deploy the cashu.me service using Docker Compose. The service builds from the local directory and maps the internal port 80 to port 3000 on your local loopback address (127.0.0.1).
To run the service, use the following command in the directory containing the docker-compose.yaml file:
docker-compose upservices:
cashu.me:
image: cashu.me
build: .
container_name: cashu.me
restart: always
ports:
- "127.0.0.1:3000:80"The project uses Playwright for end-to-end (E2E) testing. The configuration is driven by several environment variables that allow switching between local development and CI environments.
E2E_APP_URL: The base URL of the application under test. Defaults to http://127.0.0.1:4173.CI: If set, enables CI-specific behaviors such as forbidding .only tests, setting retries to 1, and changing the reporter to line and html.E2E_VIDEO: Set to on to enable video recording for all tests; otherwise, it defaults to retain-on-failure.E2E: Automatically set to true when running via the webServer command.test/e2e/specstest-resultsfullyParallel: false) with 1 worker.90,000ms20,000ms15,000ms120,000msen-USclipboard-read, clipboard-writeon-first-retryonly-on-failurereduce enabled// Example of setting the app URL via environment variable before running tests
E2E_APP_URL=http://localhost:3000 npx playwright testThe pwa object configures the application as a PWA. Key settings include:
workboxMode: Set to 'generateSW' or 'injectManifest'.injectPwaMetaTags: Boolean to inject meta tags.swFilename: The service worker filename (defaults to 'sw.js').manifestFilename: The web manifest filename (defaults to 'manifest.json').workboxOptions: Configuration for Workbox, such as skipWaiting: true and clientsClaim: true.pwa: {
workboxMode: "generateSW",
injectPwaMetaTags: true,
swFilename: "sw.js",
manifestFilename: "manifest.json",
useCredentialsForManifestTag: false,
workboxOptions: {
skipWaiting: true,
clientsClaim: true,
},
}The capacitor.config.ts file defines the configuration for the Capacitor native bridge. This configuration determines the application ID, the display name, and the directory containing the web assets that will be bundled into the native mobile application.
Key configuration properties:
appId: The unique identifier for the application (e.g., me.cashu.wallet).appName: The user-facing name of the application (Cashu.me).webDir: The path to the compiled web assets. For this project, it points to dist/pwa/.import type { CapacitorConfig } from "@capacitor/cli";
const config: CapacitorConfig = {
appId: "me.cashu.wallet",
appName: "Cashu.me",
webDir: "dist/pwa/",
};
export default config;