A pay-per-message AI chat frontend. Billing happens directly in Bitcoin sats via Cashu ecash, routed through the Routstr protocol — no accounts, no subscriptions, no stored payment method.
Live demo: https://moutstr.vercel.app
Features
- Chat UI with conversation history, starring, and search
- Cashu wallet — paste an existing token or fund via Lightning invoice
- Live model catalog fetched from Routstr, with real per-token sats pricing
- Pre-flight balance checks before sending, so unaffordable requests are blocked rather than charged
- Automatic change handling — unspent sats returned by the API (
X-Cashuheader) are folded back into the wallet balance
Tech Stack
- React 19, Vite
- Zustand for wallet, chat, and model state
- Dexie (IndexedDB) for local persistence
- TanStack Query for polling Lightning invoice settlement
- Cashu TS for ecash token handling
Getting Started
npm install npm run dev
The app talks directly to the Routstr API and to your Cashu mint — no backend of your own is required.
Scripts
npm run dev # Start dev server npm run build # Build production assets to dist/ npm run preview # Preview the production build locally npm run lint # Lint
Project Structure
src/pages/: top-level views (chat list, chat view)src/components/: UI components (header, model picker, wallet modal, toasts)src/hooks/: wallet, chat, and Lightning invoice logicsrc/utils/: Routstr API client, Cashu helpers, Zustand stores
Balance Safety
Three layers prevent sats from being spent without value received:
- A pre-flight estimate (
estimateMinSatsinsrc/utils/modelStore.js) checks the selected model is affordable before anything is sent. - Each request is capped with
max_tokens, computed from the user's real balance (computeSafeMaxTokensinsrc/utils/routstr.js), bounding worst-case cost. - If the provider still rejects a request (HTTP 402), the token is not spent — handled in
src/hooks/useChat.js.