mirror of
https://github.com/Routstr/routstrd.git
synced 2026-10-05 12:28:23 +00:00
Add a way to manually clear the router's cooldowns instead of waiting out the 210s window: - POST /cooldowns/reset clears every active cooldown via the SDK ProviderManager and also drops the failure strikes (lastFailed) and failed-provider set, so a provider is retried from a clean slate. It reports how many entries were cleared and which providers. - routstrd cooldowns --reset drives the new endpoint; --json prints the raw response. A daemon too old to have the route is detected and reported instead of silently proxying the request. - Add formatCooldownsReset plus unit/route tests, and update README and SKILL docs.
447 lines
13 KiB
Markdown
447 lines
13 KiB
Markdown
# routstrd
|
|
|
|
Routstr daemon - A CLI tool for managing routstr processes, with a built-in Cashu wallet.
|
|
|
|
## Overview
|
|
|
|
routstrd is a Bun-based CLI tool that provides a background daemon for the Routstr protocol. It carries an in-process Cashu wallet (coco) for payments and uses the Routstr SDK to handle provider routing and model discovery.
|
|
|
|
## Routstr for Teams
|
|
|
|
For team-based routing, see [routstrd-auth](https://github.com/Routstr/routstrd-auth).
|
|
|
|
## Features
|
|
|
|
- **Daemon Mode**: Run routstrd as a background HTTP server
|
|
- **Wallet Integration**: In-process Cashu wallet, with Lightning, NWC, and NPC support
|
|
- **Provider Routing**: Automatically discovers and routes requests to available providers
|
|
- **Config Management**: Stores configuration in `~/.routstrd/`
|
|
|
|
## Requirements
|
|
|
|
The standalone release does not require Bun, Node.js, or npm. Installing from
|
|
npm or running from source requires the [Bun](https://bun.sh) runtime.
|
|
|
|
## Installation
|
|
|
|
### Step 1: Install
|
|
|
|
**Standalone binary (recommended):**
|
|
|
|
Installs the standalone executable for Linux or macOS (x64 or arm64) into
|
|
`$HOME/.local/bin`. No Bun, Node.js, or npm required.
|
|
|
|
```sh
|
|
curl -fsSL https://github.com/Routstr/routstrd/releases/latest/download/install.sh | sh
|
|
```
|
|
|
|
Pin a version, change the install directory, or print the resolved asset without
|
|
installing anything:
|
|
|
|
```sh
|
|
curl -fsSL https://github.com/Routstr/routstrd/releases/latest/download/install.sh \
|
|
| sh -s -- --version 0.4.9 --dir /usr/local/bin
|
|
```
|
|
|
|
The installer downloads the release archive, verifies it against the release
|
|
`SHA256SUMS`, and only replaces an existing `routstrd` once the checksum matches
|
|
and the extracted binary reports the expected version.
|
|
|
|
<details>
|
|
<summary>Manual install</summary>
|
|
|
|
Download the archive for your operating system and architecture from the
|
|
[latest GitHub Release](https://github.com/Routstr/routstrd/releases/latest).
|
|
Release archives are available for Linux and macOS on x64 and arm64.
|
|
|
|
```sh
|
|
grep "routstrd-v0.4.9-linux-x64.tar.gz" SHA256SUMS | shasum -a 256 -c -
|
|
tar -xzf routstrd-v0.4.9-linux-x64.tar.gz
|
|
mkdir -p "$HOME/.local/bin"
|
|
install -m 755 routstrd "$HOME/.local/bin/routstrd"
|
|
```
|
|
|
|
Substitute the version, platform, and architecture for the archive you
|
|
downloaded, and ensure `$HOME/.local/bin` is on `PATH`.
|
|
|
|
</details>
|
|
|
|
Installing the standalone binary is preferred over the npm package: the npm
|
|
package runs through the Bun runtime, while the standalone executable has no
|
|
runtime dependency.
|
|
|
|
**Global with bun:**
|
|
```sh
|
|
bun i -g routstrd
|
|
```
|
|
|
|
**OR - From source:**
|
|
```sh
|
|
git clone https://github.com/routstr/routstrd.git
|
|
cd routstrd
|
|
bun install
|
|
bun link
|
|
```
|
|
|
|
### Step 2: Setup & Fund
|
|
|
|
```sh
|
|
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
|
|
|
|
```sh
|
|
routstrd clients add --claude-code # or --pi-agent / --opencode
|
|
```
|
|
|
|
## Use Routstrd Skill
|
|
|
|
> **Tip:** You can also install the [routstrd skill](https://github.com/Routstr/routstrd/blob/main/SKILL.md) so the agent can manage routstrd for you.
|
|
|
|
## More Commands
|
|
|
|
[`SKILL.md`](SKILL.md) is the complete per-command reference — every command,
|
|
subcommand, and flag. The sections below cover the common ones.
|
|
### Start Daemon
|
|
|
|
Start the background daemon:
|
|
|
|
```sh
|
|
routstrd start
|
|
```
|
|
|
|
With custom port:
|
|
```sh
|
|
routstrd start --port 9000
|
|
```
|
|
|
|
The daemon binds to `127.0.0.1` by default. To expose it on another interface:
|
|
```sh
|
|
routstrd start --host 0.0.0.0
|
|
```
|
|
|
|
Only expose the daemon behind appropriate network controls.
|
|
|
|
Pin all requests to one provider (no cross-provider failover):
|
|
```sh
|
|
routstrd start --provider https://your-provider.com
|
|
```
|
|
See [Provider pinning](#provider-pinning) for the request-level header/query.
|
|
|
|
### CLI Commands
|
|
|
|
Check daemon status:
|
|
```sh
|
|
routstrd status
|
|
```
|
|
|
|
Get wallet balance:
|
|
```sh
|
|
routstrd balance
|
|
```
|
|
|
|
Test connection:
|
|
```sh
|
|
routstrd ping
|
|
```
|
|
|
|
Refresh models and client integrations on demand:
|
|
```sh
|
|
routstrd clients --manual-refresh # same as `routstrd refresh`
|
|
```
|
|
|
|
Turn the daemon's scheduled refresh on or off (no restart needed):
|
|
```sh
|
|
routstrd clients --disable-automatic-refresh
|
|
routstrd clients --enable-automatic-refresh
|
|
```
|
|
|
|
Stop the daemon:
|
|
```sh
|
|
routstrd stop
|
|
```
|
|
|
|
See which providers/models the router is currently skipping (cooldowns):
|
|
```sh
|
|
routstrd cooldowns
|
|
routstrd cooldowns --json
|
|
```
|
|
|
|
Clear all active cooldowns and failure strikes so every provider is retried now:
|
|
```sh
|
|
routstrd cooldowns --reset
|
|
```
|
|
|
|
### NPC (Lightning Address)
|
|
|
|
The in-process wallet registers the NPC (npubx.cash) plugin, which gives the
|
|
daemon a persistent Lightning address backed by the wallet's Cashu mints.
|
|
Payments to the address are imported into the wallet automatically (websocket
|
|
push, plus manual sync on demand).
|
|
|
|
```sh
|
|
# Show your NPC Lightning address (username@npubx.cash, or npub fallback)
|
|
routstrd wallet npc address
|
|
|
|
# Claim a username (quote first, then confirm to pay the claim fee from the wallet)
|
|
routstrd wallet npc username myname
|
|
routstrd wallet npc username myname --confirm
|
|
|
|
# Manually sync paid NPC quotes into the wallet
|
|
routstrd wallet npc sync
|
|
```
|
|
|
|
Equivalent daemon endpoints: `GET /wallet/npc/address`,
|
|
`POST /wallet/npc/username`, `POST /wallet/npc/sync`.
|
|
|
|
### Daemon API
|
|
|
|
The daemon exposes an HTTP server (default port 8008) with the following endpoints:
|
|
|
|
#### Health Check
|
|
```
|
|
GET /health
|
|
```
|
|
|
|
#### Cooldowns
|
|
```
|
|
GET /cooldowns
|
|
POST /cooldowns/reset
|
|
```
|
|
|
|
`GET /cooldowns` lists providers and models the router is currently skipping.
|
|
Each entry reports its scope (`provider` blocks every model on that provider,
|
|
`model` blocks one), when the cooldown started, and when it lifts. Expired
|
|
entries are filtered out, and the cooldown window comes from the SDK
|
|
(`cooldownDurationMs`).
|
|
|
|
`POST /cooldowns/reset` clears every active cooldown (and the failure strikes
|
|
that turn the next failure into an instant cooldown) so the router retries all
|
|
providers immediately. It returns the number of entries cleared and the
|
|
affected providers.
|
|
|
|
#### Automatic Refresh Settings
|
|
```
|
|
POST /settings/auto-refresh
|
|
```
|
|
|
|
Request body:
|
|
```json
|
|
{ "enabled": false }
|
|
```
|
|
|
|
Enables or disables the scheduled refresh job. Persisted to the daemon's
|
|
`config.json` as `autoRefresh.enabled` and picked up on the next tick, so no
|
|
daemon restart is required.
|
|
|
|
#### Route Request
|
|
```
|
|
POST /v1/chat/completions
|
|
```
|
|
|
|
Any unmatched `POST` path is proxied to the selected provider with the incoming
|
|
path preserved, so `POST /v1/messages` (Anthropic Messages API) and
|
|
`POST /v1/responses` (OpenAI Responses API) work in their own formats too.
|
|
|
|
#### Provider pinning
|
|
|
|
Pin a request to a single provider with either the `x-routstr-provider` header
|
|
or the `?provider=` query parameter (the query takes precedence):
|
|
|
|
```sh
|
|
curl -H 'x-routstr-provider: https://your-provider.com' ... \
|
|
http://127.0.0.1:8008/v1/chat/completions
|
|
```
|
|
|
|
A pin is strict: the request is sent only to that provider. On an upstream
|
|
error — including a 400/422 the node itself rejects — the request is **not**
|
|
failed over to another node; the error is returned to the client. Retries
|
|
against the same provider (top-up, mint fallback) still happen. The config
|
|
`provider` and `routstrd start --provider` set the same strict pin as a
|
|
default for every request.
|
|
|
|
Request body:
|
|
```json
|
|
{
|
|
"model": "model-id",
|
|
"messages": [...],
|
|
"stream": false
|
|
}
|
|
```
|
|
|
|
Response:
|
|
```json
|
|
{
|
|
"choices": [...],
|
|
"usage": {...}
|
|
}
|
|
```
|
|
|
|
## Wallet storage
|
|
|
|
The in-process Cashu wallet stores its mnemonic and proof database in
|
|
`~/.routstrd/wallet/`. On first startup, an existing wallet in `~/.cocod/` is
|
|
migrated automatically after routstrd verifies that the legacy cocod daemon is
|
|
not running. Back up your mnemonic before upgrading.
|
|
|
|
Set `ROUTSTRD_WALLET_DIR` to override the canonical wallet directory. The
|
|
`COCOD_DIR`, `COCOD_SOCKET`, and `COCOD_PID` variables are retained only for
|
|
locating and excluding a legacy external cocod process.
|
|
|
|
If both `~/.routstrd/wallet` and `~/.cocod` contain different wallets, startup
|
|
refuses to migrate rather than picking a mnemonic for you. Run
|
|
`routstrd wallet doctor` to compare the two wallets (mnemonic fingerprints,
|
|
timestamps, and balances) and see which one to keep.
|
|
|
|
## Configuration
|
|
|
|
Configuration is stored in `~/.routstrd/config.json`:
|
|
|
|
```json
|
|
{
|
|
"port": 8008,
|
|
"host": "127.0.0.1",
|
|
"provider": null,
|
|
"autoModelPath": false,
|
|
"autoRefresh": { "enabled": true }
|
|
}
|
|
```
|
|
|
|
`autoRefresh.enabled` (default `true`) controls the daemon's scheduled refresh
|
|
job, which re-fetches Nostr events, routstr21 models, and client integrations
|
|
every 21 minutes. Set it to `false` (or run
|
|
`routstrd clients --disable-automatic-refresh`) to turn the schedule off and
|
|
refresh manually with `routstrd clients --manual-refresh`. `autoRefresh.intervalMs`
|
|
overrides the 21-minute interval.
|
|
|
|
`autoModelPath` defaults to `false`. Set it to `true` to let the SDK automatically
|
|
choose and pin an advertised model path for `deepseek-v4.1-flash` requests.
|
|
Explicit `x-routstr-model-path` request headers work independently of this
|
|
setting and take precedence. Restart the daemon after changing `autoModelPath`.
|
|
|
|
`provider` pins every request to one node (same strict behavior as the
|
|
`x-routstr-provider` header / `?provider=` query). Leave it `null` to let the
|
|
router pick the cheapest eligible provider and fail over between nodes.
|
|
|
|
### 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`)
|
|
- `ROUTSTRD_WALLET_DIR` - Wallet data directory (default: `~/.routstrd/wallet`)
|
|
- `COCOD_DIR` - Legacy external cocod directory, used only for migration and exclusion (default: `~/.cocod`)
|
|
|
|
## Development
|
|
|
|
Install dependencies:
|
|
```sh
|
|
bun install
|
|
```
|
|
|
|
Run the CLI from source:
|
|
```sh
|
|
bun src/index.ts <command>
|
|
```
|
|
|
|
Run the daemon:
|
|
```sh
|
|
bun run start
|
|
```
|
|
|
|
Run the tests:
|
|
```sh
|
|
bun test
|
|
```
|
|
|
|
Build a standalone executable for the current platform:
|
|
|
|
```sh
|
|
bun run build:binary
|
|
./dist/routstrd --version
|
|
```
|
|
|
|
Standalone installations update directly from GitHub Releases with
|
|
`routstrd update`. npm installations continue to update through Bun. PM2 is an
|
|
optional external dependency used only by `routstrd service`; normal daemon
|
|
operation does not require it.
|
|
|
|
When an update finds a process on the configured daemon port, it only stops that
|
|
process if the wallet PID file confirms a live daemon owned by the same routstrd
|
|
configuration. Otherwise the update remains installed, but automatic restart is
|
|
refused so an unrelated daemon is not interrupted.
|
|
|
|
Existing PM2 registrations created by routstrd 0.4.x continue to work through a
|
|
compatibility daemon entrypoint. Recreate the registration to use the unified
|
|
CLI entrypoint and remove its legacy path dependency:
|
|
|
|
```sh
|
|
routstrd service uninstall
|
|
routstrd service install
|
|
pm2 save
|
|
```
|
|
|
|
Typecheck:
|
|
```sh
|
|
bun run lint
|
|
```
|
|
|
|
### Manual chat-completions smoke test
|
|
|
|
With a funded daemon running, create or reuse a client API key and pass one or
|
|
more current model IDs to the smoke script:
|
|
|
|
```sh
|
|
routstrd clients add --name smoke-test
|
|
ROUTSTRD_API_KEY=<api-key> bun run smoke <model> [model ...]
|
|
```
|
|
|
|
Set `ROUTSTRD_BASE_URL` to test a daemon at a different address. The script
|
|
makes live provider requests that may spend wallet funds, so it is intentionally
|
|
not part of `bun test`.
|
|
|
|
### Publishing a standalone release
|
|
|
|
1. Set a new `package.json` version and commit it. The release tag must be the
|
|
same version prefixed with `v`, and the tag must not already exist.
|
|
2. Push the tag. The release workflow runs lint and tests, builds Linux and
|
|
macOS executables for x64 and arm64, smoke-tests them, verifies the archives
|
|
through `install.sh` itself, and publishes the archives with `SHA256SUMS` and
|
|
`install.sh`. It also publishes `routstrd` to npm via trusted publishing
|
|
(OIDC, no token), skipping gracefully if that version already exists.
|
|
3. Verify all four archives and `install.sh` appear in the GitHub Release and
|
|
validate each checksum before announcing it.
|
|
4. In disposable environments for each platform, test `--version`, `--help`,
|
|
foreground startup failure, and background `start`, `status`, and `stop`
|
|
without Bun on `PATH`.
|
|
5. Test `routstrd service install` and restart behavior with PM2 in a disposable
|
|
environment. Never run release/update lifecycle tests against a production
|
|
daemon. When isolation is needed, use both a separate `ROUTSTRD_DIR` and a
|
|
non-production port in that configuration.
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
routstrd/
|
|
├── src/
|
|
│ ├── index.ts # CLI entry point with shebang
|
|
│ ├── cli.ts # Commander CLI commands
|
|
│ ├── daemon.ts # Compatibility daemon entrypoint (legacy PM2 registrations)
|
|
│ ├── start-daemon.ts # Daemon process launcher
|
|
│ ├── daemon/ # HTTP server, wallet, provider routing
|
|
│ ├── integrations/ # Client integrations (Claude Code, pi, OpenCode, ...)
|
|
│ ├── tui/ # Interactive usage monitor (`routstrd monitor`)
|
|
│ └── utils/ # Config, paths, daemon client, update checker
|
|
├── tests/ # Integration tests (unit tests sit beside their source)
|
|
├── scripts/smoke/ # Manual end-to-end smoke test
|
|
├── docs/plans/ # Design/migration plans not yet executed
|
|
├── package.json
|
|
└── tsconfig.json
|
|
```
|
|
|
|
## License
|
|
|
|
MIT
|