Build the guided first run into a public-by-default workflow: - compose.node.yml enables AUTO_GENERATE_NSEC. The identity is created on first boot and stored encrypted; nothing is published until a reachable HTTP_URL/onion endpoint exists, so a private node stays invisible. - node_setup.py gains explicit modes: --public-url (default) pre-flights DNS/TLS/reverse proxy, writes HTTP_URL and lets the node publish on first boot; --private stays loopback-only and publishes nothing. - Add --ln-address (validated against the LNURL-pay endpoint), --min-payout-sat and --payout-interval. Managed keys are upserted into .env without clobbering operator edits; NSEC/ONION_URL and conflicting HTTP_URL are refused. - Rewrite quickstart, llms.txt, deployment, README and .env.example around the five phases: prepare host, start, back up, dashboard, verify. Backup moves ahead of configuration because the master key and nsec exist at first boot.
4.0 KiB
Discovery
Routstr uses Nostr as a decentralized directory for service discovery. Your node announces its presence, models, and pricing on Nostr relays, allowing clients to find you without a central server.
How It Works
- Provider Advertisement (Kind 38421): Your node periodically publishes an event with its URL, models, and pricing
- Client Discovery: Clients query relays for these events to find suitable providers
When announcements are published
Your node publishes an advertisement as soon as it has both a Nsec and at least one
reachable endpoint (a public HTTP_URL, or an .onion address). Saving the Nsec in the
dashboard is enough — the announcement follows within a minute, and no restart is
required. After the first publish it re-announces every 24 hours, and immediately
whenever the Nsec, endpoints, mints or relays change.
A node that has no Nsec yet simply waits, and starts announcing the moment one is
configured. Note that HTTP_URL defaults to http://localhost:8000, which is not a
reachable endpoint: a node with the default value and no onion address has nothing to
advertise and will not publish until one is set.
Automatic identity (no manual nsec)
Setting AUTO_GENERATE_NSEC=true makes the node create its own Nostr identity on
first boot when none is configured — no dashboard step required. Both bundled
stacks enable this by default (compose.yml and compose.node.yml), so a fresh
node starts announcing as soon as it has a reachable endpoint: the Tor service
supplies one for compose.yml, and a public HTTP_URL (behind your reverse
proxy) supplies one for compose.node.yml. With neither, the generated identity
simply waits — nothing is published.
Only the generated npub is logged at startup; the nsec is stored encrypted at
rest and never printed. Retrieve it when you need to back it up with
python scripts/reveal_nsec.py (requires ROUTSTR_SECRET_KEY). It is the node's
identity: back up the database and the master key together. A node that already has
an identity — a provided NSEC seed or an nsec saved in the dashboard — is never
regenerated, and an identity you intentionally clear in the dashboard stays cleared.
Configuration
Configure discovery in Dashboard → Settings → Nostr.
Required Settings
| Field | Description |
|---|---|
| Npub | Your node's public identity (clients use this to verify your node) |
| Nsec | Your node's private key (used to sign advertisements) |
| Relays | Where to publish your announcements |
These can be set by hand, or skipped entirely by letting the node generate its own
identity with AUTO_GENERATE_NSEC=true (see above).
Default Relays
If not configured, Routstr publishes to:
wss://relay.damus.iowss://relay.nostr.bandwss://nos.lol
Advertisement Format
Your node publishes events like:
{
"kind": 38421,
"content": {
"name": "My Routstr Node",
"description": "Fast GPT-4 access via Lightning",
"endpoints": {
"http": "https://api.mynode.com",
"onion": "http://xyz...onion"
},
"models": ["gpt-4", "claude-3-opus"],
"pricing": { ... }
},
"tags": [
["d", "routstr-provider"],
["g", "US"]
]
}
Tor Integration
If you're running with Tor (see Tor Support), your .onion address is automatically included in announcements. This allows clients to connect anonymously.
Verify Your Announcements
Check if your node is broadcasting:
- Copy your
Npub - Search on Nostr.band or Primal
- Look for Kind 38421 events
Generating Keys
If you don't have a Nostr identity:
- Use any Nostr client (e.g., Primal, Damus)
- Create an account
- Export your keys (npub and nsec)
- Enter them in the dashboard
Or generate keys programmatically:
from nostr_sdk import Keys
keys = Keys.generate()
print(f"npub: {keys.public_key().to_bech32()}")
print(f"nsec: {keys.secret_key().to_bech32()}")