The auto top-up field has always been labelled "When credits are below (Sats)", but the backend compared it as `balance >= threshold * 1000` against a balance get_balance() had already converted to sats. Its sibling topup_amount_limit is plain sats - it goes straight to send_token(amount, "sat", ...). So one settings blob carried two units and the UI promised the one it did not use: an operator asking to top up below 1000 sats got one below 1,000,000. topup_threshold_sats says what it means and is compared as written. A legacy topup_threshold keeps the thousandfold it has always been compared with, since reinterpreting stored values as sats would drop the trigger point by a factor of 1000 on upgrade and leave a peer to run dry. A one-off warning per provider names the sats value to migrate to. The settings form now edits topup_threshold_sats, seeding it from the legacy value times 1000 so the number shown is the number in force. Editing therefore starts from the truth rather than silently moving the trigger on the next save, and saving drops the legacy key so the stored blob carries one unit. Validation accepts either key and rejects a blob carrying neither. The PPQ path keeps its own topup_threshold, which is USD and unaffected.
Routstr node admin UI
A Next.js app (App Router, static export) that provides the
admin dashboard for a routstr-core node: login, settings, providers, balances,
transactions, usage, and logs.
There is no separate web server in production. next build produces a fully static
export (next.config.ts sets output: 'export'), and the FastAPI backend serves it
directly from ../ui_out/ (see routstr/core/main.py). So the UI and the API are
served from the same origin in production.
Developing the UI (hot reload)
The everyday loop runs two processes side by side — you do not rebuild the static export while developing:
- Start the backend on
:8000— from the repo root:make docker-up(oruvicorn routstr.core.main:app --reload). - Start the Next.js dev server on
:3000— from the repo root:make ui-dev(orcd ui && pnpm dev). Edits hot-reload instantly.
Open http://localhost:3000. With no NEXT_PUBLIC_API_URL set, the UI falls back to
http://127.0.0.1:8000 in development (see lib/api/services/configuration.ts), so it
talks to the local backend out of the box.
Because dev is cross-origin (:3000 → :8000), it relies on the backend's CORS
allowing the UI origin. The default cors_origins is ["*"]; if you tighten CORS,
keep http://localhost:3000 allowed for development.
Building the integrated/static UI (what production serves)
To produce the bundle that FastAPI serves from ../ui_out/:
make ui-build— builds with local Node/pnpm (scripts/build-ui.sh), then movesui/out/*to../ui_out/.make ui-build-docker— same, but inside Docker (no local Node needed).
NEXT_PUBLIC_* variables are read from the repo-root .env at build time and baked in.
For a same-origin deployment leave NEXT_PUBLIC_API_URL empty (relative paths); the UI
uses window.location.origin at runtime. After building, start the backend and open
http://localhost:8000 — the dashboard is served at / and /admin.
If ../ui_out/ does not exist, the backend logs a warning at startup and serves the API
only (hitting a UI route returns a small JSON fallback instead of the dashboard).