Files
routstr-core/.env.example
T
Jeroen UbbinkandClaude Opus 4.8 a2cedd6769 feat: make the DB connection pool env-configurable
Add DATABASE_POOL_SIZE / DATABASE_MAX_OVERFLOW / DATABASE_POOL_TIMEOUT,
consumed like every other typed env var through the pydantic Settings
(constrained Fields, defaults 5/10/30 matching SQLAlchemy's own baseline
so leaving them unset is behaviour-neutral). An out-of-range or
non-integer value fails validation and refuses to boot — the same
fail-loud behaviour a malformed DATABASE_URL already has — rather than
silently starting up misconfigured. create_db_engine sizes the pool from
these and logs the effective values at startup so they can be confirmed
from the boot output during an incident. In-memory SQLite (StaticPool,
which rejects the pool kwargs) is detected and built without them.

pool_pre_ping is deliberately not exposed: the default backend is a local
SQLite file with no network peer to drop idle connections, so it would add
a SELECT 1 per checkout for no benefit — and it detects dead connections,
not the live-but-wedged ones behind the exhaustion this series addresses.

These knobs are infrastructure the node needs before it can open a DB
session, so they can never be sourced from the DB (chicken-and-egg). A new
ENV_ONLY_FIELDS set keeps them out of the persisted settings blob and
stops a DB value from shadowing env in both SettingsService.initialize and
.update, so env stays authoritative.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 22:26:57 +02:00

92 lines
4.0 KiB
Bash

# Core Configuration
UPSTREAM_BASE_URL=https://api.openai.com/v1
UPSTREAM_API_KEY=your-upstream-api-key
# Tinfoil (confidential inference enclaves, EHBP)
# TINFOIL_API_KEY=your-tinfoil-api-key
# Secret key used to encrypt node secrets at rest (optional). If unset, the node
# generates one on first start, writes it to routstr_secret.key (override the path
# with ROUTSTR_SECRET_KEY_FILE), and prints it once — back that file up, because
# losing the key makes previously encrypted secrets unreadable. Set it explicitly
# to manage the key yourself (recommended in production). Generate one with:
# uv run python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
ROUTSTR_SECRET_KEY=
# The admin password and the Nostr identity (nsec) are NOT set here. The admin
# password is generated and logged once on first start (read it from the logs to
# sign in); both are managed afterwards from the admin UI and stored encrypted in
# the database. ADMIN_PASSWORD / NSEC are still read once as a legacy seed for
# existing deployments, but new nodes should set them in the UI — a value left in
# .env is ignored once the node has been configured.
# Database
# DATABASE_URL=sqlite+aiosqlite:///keys.db
# Database tuning (advanced — you almost certainly do not need these)
# ---------------------------------------------------------------------------
# These size the SQLAlchemy connection pool. The defaults below match
# SQLAlchemy's own baseline, so leaving them unset changes nothing. Touch them
# ONLY if the logs show the pool running out of connections:
#
# QueuePool limit of size 5 overflow 10 reached, connection timed out, timeout 30.00
#
# The effective values are printed at startup ("Database pool configured: ...")
# so you can confirm what is actually in effect while diagnosing an incident.
# An invalid value (non-integer, or a pool size below 1) stops the node at boot
# with a clear error rather than starting up misconfigured.
#
# DATABASE_POOL_SIZE Connections kept open in the pool. (default 5)
# DATABASE_MAX_OVERFLOW Extra connections opened under load, (default 10)
# beyond pool_size, then discarded.
# DATABASE_POOL_TIMEOUT Seconds a request waits for a free (default 30)
# connection before failing with the error above.
#
# Note for the default SQLite backend: SQLite serialises writes behind a single
# database-level lock, so a bigger pool does NOT buy more write throughput — it
# only lets more readers run at once (WAL mode) and trades pool-timeout errors
# for "database is locked" errors. If you exhaust the pool on SQLite, the cause
# is almost always connections being held too long, not the pool being too
# small. These knobs come into their own if you point DATABASE_URL at a
# networked database (e.g. Postgres), where connections genuinely run in
# parallel; there, size the pool against the server's max_connections.
#
# DATABASE_POOL_SIZE=5
# DATABASE_MAX_OVERFLOW=10
# DATABASE_POOL_TIMEOUT=30
# Node Information
# NAME=My Routstr Node
# DESCRIPTION=Fast AI API access with Bitcoin payments
# HTTP_URL=https://api.mynode.com
# ONION_URL=http://mynode.onion (auto fetched from compose)
# RELAYS="wss://relay.damus.io,wss://relay.nostr.band,wss://eden.nostr.land,wss://relay.routstr.com"
# ENABLE_ANALYTICS_SHARING=true
# CASHU_MINTS="https://mint.minibits.cash/Bitcoin,https://mint.cubabitcoin.org,https://ecashmint.otrta.me"
# RECEIVE_LN_ADDRESS=
# Custom Pricing Configuration
# MODEL_BASED_PRICING=true
# COST_PER_REQUEST=1
# COST_PER_1K_INPUT_TOKENS=0
# COST_PER_1K_OUTPUT_TOKENS=0
# EXCHANGE_FEE=1.005
# UPSTREAM_PROVIDER_FEE=1.05
# Network Configuration
# CORS_ORIGINS=*
# TOR_PROXY_URL=socks5://127.0.0.1:9050
# Logging
# LOG_LEVEL=INFO
# ENABLE_CONSOLE_LOGGING=true
# Custom Model Management
# BASE_URL=https://openrouter.ai/api/v1
# MODELS_PATH=models.json
# SOURCE=
# UI Configuration (for Next.js frontend)
# These variables are prefixed with NEXT_PUBLIC_ to be accessible in the browser
# NEXT_PUBLIC_API_URL=http://127.0.0.1:8000