Files
routstr-core/docs/provider/quickstart.md
9qeklajcandGitHub 9882a4682d Merge pull request #819 from Routstr/docs/remove-admin-proxy-restriction
docs: drop mandatory /admin reverse-proxy restriction from provider docs
2026-10-09 13:51:06 +02:00

12 KiB

Run a provider node

A Routstr node connects to an upstream AI provider and accepts Cashu payments for inference.

The default onboarding assumes you already have a subdomain with HTTPS and a reverse proxy (nginx, Caddy, …) forwarding to 127.0.0.1:8000. With that in place the node publishes its Nostr listing as soon as it starts — one command and it is live. If you do not have a public URL yet, start in private mode and publish later.

A node that is running is not necessarily selling: public mode gets you discoverable, but you still need an upstream with enabled models and a rotated admin password. The phases below cover the whole path.


Before you start

  • A host with Docker and the Compose plugin, Python 3, and enough space to build the dashboard. Keep the checkout on persistent storage — it holds your database, key and wallet.
  • An upstream API key (OpenAI, Anthropic, OpenRouter, …) to serve models. The node boots without one, but reports zero models until an upstream is configured.
  • Public mode only: a subdomain with a valid TLS certificate and a reverse proxy forwarding it to 127.0.0.1:8000. The dashboard is protected by the node's own admin password, so read the one-time bootstrap password and rotate it. Restricting /admin at the proxy (IP allowlist or basic auth) is optional defense-in-depth, not a setup requirement.

Clone the repository and, for production, check out a reviewed release tag:

git clone https://github.com/Routstr/routstr-core.git
cd routstr-core
# git checkout <latest release tag>

!!! warning "Keep secrets out of chats" Don't paste admin passwords, API keys, wallet tokens or startup logs into an agent chat. If an agent is helping, point it at llms.txt; it cannot complete login, funding or public exposure without you.


The five phases

Phase What you do
0 Prepare the host: DNS, TLS, reverse proxy → 127.0.0.1:8000
1 Start the node (node_setup.py start) — it generates secrets and publishes
2 Back up the master key, database and wallet before configuring
3 Configure via the dashboard: rotate password, add upstream, pricing
4 Verify and accept: external check, then a small paid request

Phase 0 — Prepare the host

Public mode (default). Before starting the node:

  1. Point a subdomain at the host (e.g. node.example).
  2. Get a TLS certificate for it.
  3. Run a reverse proxy on the same host forwarding https://node.example → http://127.0.0.1:8000. Only 443 should be publicly routed.
  4. Optional: restrict /admin at the proxy (IP allowlist or basic auth) for an extra layer. This is not required — the node already requires its admin password. Reading and rotating the bootstrap password is the required step.

Private mode (opt-out). Nothing to prepare; administer the node over an SSH tunnel (see Phase 1).


Phase 1 — Start the node

Public mode (default):

python3 scripts/node_setup.py start \
  --public-url https://node.example \
  --ln-address you@wallet.com

start:

  • pre-flights the public origin over HTTPS — DNS, TLS and the proxy must answer before anything is published (a proxy 502 while the node boots is fine). Use --private if this fails;
  • writes .env from .env.example with owner-only (0600) permissions, setting HTTP_URL and RECEIVE_LN_ADDRESS and leaving the template's upstream pair commented, so the node boots with no upstream until you add one in the dashboard. An existing .env is never blindly overwritten: a conflicting HTTP_URL is an error, and NSEC / ONION_URL are refused (manage those elsewhere);
  • builds and starts compose.node.yml: the API binds 127.0.0.1 only, there is no Tor service, and analytics sharing starts off (change it later in Settings);
  • auto-generates the node's secrets — the master key routstr_secret.key and the Nostr nsec (stored encrypted, never printed; only its npub is logged);
  • waits up to three minutes for the local API, then publishes the kind 38421 listing on its first announce pass and prints the remaining phases;
  • mints a long-lived CLI token and writes it to ~/.routstr/config.json (mode 0600; left untouched, with no token minted, if it already points at another node) so the Routstr CLI works immediately. The token is full node admin — see Connect the Routstr CLI. Opt out with --no-cli-token.

Private mode (opt-out):

python3 scripts/node_setup.py start --private --ln-address you@wallet.com

Same node, but HTTP_URL stays unset: it is loopback-only and publishes nothing. Set a public URL in the dashboard later to go live.

Options

Flag Meaning
--port N loopback port (default 8000)
--ln-address ADDR payout Lightning address (user@host, lnurl1…, or an https:// LNURL-pay URL)
--min-payout-sat N minimum payout balance in sats (default 210)
--payout-interval N seconds between payout checks (default 900)
--cli-config PATH where to write the CLI config (default ~/.routstr/config.json)
--cli-token-name NAME label for the generated token (default node_setup)
--cli-token-expires-in-days N expire the CLI token after N days (default: never)
--no-cli-token skip creating the CLI token / CLI config

The payout address is validated at setup (a user@host address is resolved to /.well-known/lnurlp/…), but saved is not tested — verify an actual payout in Phase 4.

The payout flags take effect on the node's first boot. The node saves its settings then, and saved values win over .env afterwards, so re-running start with different payout flags only updates .env. Change payout settings on a running node under Settings in the dashboard.

!!! tip "Payouts are optional at boot" A node can accept payments with no payout address; the payout loop simply idles. Set one whenever you are ready to withdraw.

!!! warning "Port already in use? Don't stop the other service" Pick a free port instead and point the proxy at it:

```bash
python3 scripts/node_setup.py start --public-url https://node.example --port 18080
```

!!! note "Every later Compose command uses the private stack" Use docker compose -f compose.node.yml … (e.g. logs routstr). Plain docker compose up starts the different public/Tor stack.

Remote host

Forward the loopback port from your own computer and open the dashboard there:

ssh -L 8000:127.0.0.1:8000 user@your-server
# then browse http://127.0.0.1:8000/admin

Phase 2 — Back up before configuring

The master key and identity exist the moment the node boots, so capture them before configuring anything. Stop nothing else, but ensure writers are quiesced for a consistent copy, and copy:

  • routstr_secret.key — encrypts node secrets at rest, including the nsec in keys.db. Lose it and the stored nsec cannot be decrypted;
  • keys.db (plus any keys.db-wal / keys.db-shm sidecars);
  • .wallet/ — your Bitcoin. It is created the first time the node handles ecash, so a fresh node has none yet; include it in every backup once it exists;
  • .env — node configuration.

Reveal the nsec to store beside the key:

docker compose -f compose.node.yml exec routstr \
  /.venv/bin/python scripts/reveal_nsec.py

Keep the database and its encryption key together in an encrypted off-host backup, and verify an isolated restore. See Deployment → Backup and Restore.


Phase 3 — Configure via the dashboard

Open the dashboard at https://node.example/admin (public) or http://127.0.0.1:8000/admin (private/tunnel), and read the one-time bootstrap password in a private terminal:

docker compose -f compose.node.yml logs routstr | grep -i admin

Don't capture those logs in automated output.

  1. Rotate the password — Settings → Admin Settings → Change Admin Password.
  2. Add an upstream — Providers → Add Provider: choose the type, enter the base URL and API key, review the Fee, and save. Confirm the provider is enabled and has enabled models. With public mode you are already listed; a zero-model catalog self-heals as soon as models are discovered.
  3. Pricing — per-provider Fee (multiplier on upstream cost), or per-model prices, or a flat FIXED_PRICING rate. See Pricing.
  4. Review node name, payout address, mints and relays in Settings → Admin Settings.

Lost the password? Clear it in a private terminal, then restart the node; the restart generates and logs a new one-time password (the API is briefly unavailable). Read it as above and rotate it again:

docker compose -f compose.node.yml exec routstr \
  /.venv/bin/python scripts/reset_admin_password.py --regenerate
docker compose -f compose.node.yml restart routstr

Never delete volumes or wallet state to fix a login problem.

Connect the Routstr CLI

start already minted a long-lived token and wrote it to ~/.routstr/config.json, so the Routstr CLI works with no further setup.

If ~/.routstr/config.json already pointed at another node, start left it alone and minted no token. The CLI only reads $HOME/.routstr/config.json, so to keep both, re-run start with --cli-config /some/dir/.routstr/config.json and run the CLI as HOME=/some/dir routstr ….

git clone https://github.com/routstr/routstr-cli.git
cd routstr-cli
bun install

routstr instruct        # canonical agent guide for this node
routstr status
routstr providers list
routstr providers add openrouter --api-key sk-or-... --base-url https://openrouter.ai/api/v1

If the CLI runs on a different machine than the node, copy the token and configure it there instead:

routstr init --node-url https://node.example --token <token>

The token is full node admin. Revoke it in Settings → CLI Tokens (or DELETE /admin/api/cli-tokens/{id}) when you are done; give agents their own named, expiring token rather than sharing yours. To mint another one directly:

docker compose -f compose.node.yml exec routstr \
  /.venv/bin/python scripts/create_cli_token.py --name agent

Phase 4 — Verify and accept

From another machine with a checkout (public mode):

python3 scripts/node_setup.py check --public-url https://node.example

It reports the node name and public model count and exits 0 when reachable with ≥1 model, or 2 when the API is up but not configured (zero models). A passing check proves discovery only — it does not test payments, streaming, upstream requests, refunds or payout.

Before calling the node production-ready, make a small, budget-approved Cashu-paid request — non-streaming and streaming — reconcile usage/upstream charges/refunds, and test a payout separately. See Production Acceptance.


Monitoring and withdrawals

The dashboard shows:

  • Total Wallet: all Bitcoin held by the node
  • User Balance: funds belonging to active client sessions
  • Your Balance: your profit (Total - User Balances)

Withdraw from Balances → Withdraw: select the mint/currency and amount, generate a Cashu token, and redeem it to your Lightning wallet.


Next Steps

  • Deployment: public/private stacks, publishing, backups and updates
  • Dashboard Guide: full reference for all dashboard features
  • Pricing: configure pricing strategies and per-model overrides
  • Discovery: how the node announces itself on Nostr
  • llms.txt: instructions for an agent helping you set up a node