Three changes that harden the mint-fallback branch against upstream failures and enable proper provider failover: 1. Mint health cache (src/daemon/wallet/index.ts) Track mints that are temporarily unreachable (connection refused, DNS failure, TLS error). Unhealthy mints are skipped by getActiveMintUrl() and sendToken() so we don't waste a round-trip on every request. Mints are automatically retried after a 60s cooldown. The active mint switches to a healthy one when the current one goes unhealthy. This is critical for the xcashu 402 failover fix: when the primary provider (localhost:8011) is down, the wallet needs to successfully create a token via a healthy mint BEFORE the SDK's failover logic can route the request to the next provider (routstr.otrta.me). Without the health cache, every request wasted 2-3 seconds on a failed mint.minibits.cash fetch before falling back to cubabitcoin. 2. xcashu token sweep (src/daemon/index.ts) The scheduled refund loop now also sweeps pending xcashu tokens that failed inline refund (e.g. 'proofs already spent' — the token stays in storage and needs retry). This prevents orphaned IOUs from accumulating and ensures the background refundXcashuTokens path gets a chance to reclaim sats from tokens where the inline receive raced with the sweep. This is the race condition that caused the double-refund negative satsSpent bug (xcashu-double-refund-negative-sats, prio 998): the inline receive and the background sweep both received the same token, producing a negative satsSpent written to usage_tracking. 3. Network error patterns (src/daemon/wallet/mint-fallback.ts) Extend inspectForMintUnreachable to recognize Bun-specific network failures: 'Failed to fetch mint <url>', 'NetworkError when attempting to fetch resource', and 'Load failed'. These are treated as mint-unreachable so the fallback to the next configured mint kicks in immediately instead of surfacing the raw error. This pairs with the SDK fix (routstr-sdk PR #32) that adds 'Unable to connect' and 'ECONNREFUSED' to isNetworkErrorMessage so the SDK's failover path is triggered when the upstream provider is completely unreachable (not just returning an HTTP error). Related issues: - Nostr: xcashu-402-body-refund-no-failover (prio 990, event a4d7e1cd...) - Nostr: xcashu-double-refund-negative-sats (prio 998, event 14982f8d...) - SDK PR: https://github.com/Routstr/routstr-sdk/pull/32 - SDK PR: https://github.com/Routstr/routstr-sdk/pull/31 (clamp fix)
routstrd
Routstr daemon - A CLI tool for managing routstr processes, similar to cocod (a Cashu wallet daemon).
Overview
routstrd is a Bun-based CLI tool that provides a background daemon for the Routstr protocol. It integrates with cocod for wallet management and uses the Routstr SDK to handle provider routing and model discovery.
Routstr for Teams
For team-based routing, see routstrd-auth.
Features
- Daemon Mode: Run routstrd as a background HTTP server
- Wallet Integration: Works with cocod for Cashu token management
- Provider Routing: Automatically discovers and routes requests to available providers
- Config Management: Stores configuration in
~/.routstrd/
Requirements
- Bun runtime
curl -fsSL https://bun.com/install | bash
Installation
Step 1: Install
Global with bun:
bun i -g routstrd
OR - From source:
git clone https://github.com/routstr/routstrd.git
cd routstrd
bun install
bun link
Step 2: Setup & Fund
routstrd onboard
routstrd receive <cashu> # receive a Cashu token
routstrd receive 2100 # to top up 2100 sats with lightning
Step 3: Integrate with Claude Code
routstrd clients add --claude-code # or --pi-agent / --opencode
Use Routstrd Skill
Tip: You can also install the routstrd skill so the agent can manage routstrd for you.
More Commands
Start Daemon
Start the background daemon:
routstrd start
With custom port:
routstrd start --port 9000
With specific provider:
routstrd start --provider https://your-provider.com
CLI Commands
Check daemon status:
routstrd status
Get wallet balance:
routstrd balance
Test connection:
routstrd ping
Stop the daemon:
routstrd stop
Daemon API
The daemon exposes an HTTP server (default port 8008) with the following endpoints:
Health Check
GET /health
Route Request
POST /
Request body:
{
"model": "model-id",
"messages": [...],
"stream": false
}
Response:
{
"choices": [...],
"usage": {...}
}
Configuration
Configuration is stored in ~/.routstrd/config.json:
{
"port": 8008,
"provider": null,
"cocodPath": null
}
Environment Variables
ROUTSTRD_DIR- Config directory (default:~/.routstrd)ROUTSTRD_SOCKET- Socket path (default:~/.routstrd/routstrd.sock)ROUTSTRD_PID- PID file path (default:~/.routstrd/routstrd.pid)
Development
Install dependencies:
bun install
Run CLI:
bun run start
Run daemon:
bun run start
Typecheck:
bun run lint
Project Structure
routstrd/
├── src/
│ ├── index.ts # Entry point with shebang
│ ├── cli.ts # Commander CLI commands
│ ├── cli-shared.ts # IPC utilities
│ ├── daemon.ts # HTTP server daemon
│ └── utils/
│ └── config.ts # Path configuration
├── package.json
└── tsconfig.json
License
MIT