diff --git a/.env.example b/.env.example index e0dbae73..8ff04b35 100644 --- a/.env.example +++ b/.env.example @@ -5,9 +5,12 @@ UPSTREAM_API_KEY=your-upstream-api-key # Tinfoil (confidential inference enclaves, EHBP) # TINFOIL_API_KEY=your-tinfoil-api-key -# Secret key used to encrypt secrets at rest (REQUIRED). The node refuses to -# start without it. Generate one with: -# python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" +# 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 diff --git a/README.md b/README.md index 767bacae..023ccd18 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,10 @@ If you are a node runner, start a Routstr Core instance using Docker Compose: 1. **Prepare your `.env`**: ```bash - # Required: encrypts secrets at rest. The node won't start without it. + # Optional: encrypts node secrets at rest. If unset, the node generates a key + # on first start, writes it to routstr_secret.key, and prints it once — back + # up that file. Set it explicitly to manage the key yourself (recommended in + # production). ROUTSTR_SECRET_KEY= NAME="My AI Node" DESCRIPTION="Fast access to models" @@ -63,10 +66,11 @@ If you are a node runner, start a Routstr Core instance using Docker Compose: RECEIVE_LN_ADDRESS=yourname@wallet.com ``` - Generate `ROUTSTR_SECRET_KEY` once and keep it stable — changing it makes - previously encrypted secrets unreadable: + If you don't set one, a key is generated and printed on first start — save it + somewhere safe (losing it makes previously encrypted secrets unreadable). To + supply your own, generate it once and keep it stable: ```bash - python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" + uv run python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" ``` 2. **Start the services**: @@ -80,7 +84,7 @@ If you are a node runner, start a Routstr Core instance using Docker Compose: ```bash docker compose logs routstr | grep -i admin ``` - (Lost it? Reset with `python scripts/reset_admin_password.py --regenerate`.) + (Lost it? Reset with `docker compose exec routstr /.venv/bin/python scripts/reset_admin_password.py --regenerate`.) 4. **Configure**: Open [http://localhost:8000/admin/](http://localhost:8000/admin/) to connect your AI providers and set pricing. diff --git a/docs/provider/configuration.md b/docs/provider/configuration.md index 40930676..f548c990 100644 --- a/docs/provider/configuration.md +++ b/docs/provider/configuration.md @@ -15,6 +15,11 @@ Before running your node, you should create a `.env` file in the project root. T ```bash ADMIN_PASSWORD=your-secure-password +# Encrypts node secrets at rest. Optional — if unset, the node generates a key on +# first start and prints it once (back it up). Set it to manage the key yourself +# (recommended in production). See "Secrets at Rest" below. +ROUTSTR_SECRET_KEY= + # Node Identity NAME="My AI Node" DESCRIPTION="Fast access to models" @@ -124,6 +129,8 @@ Use environment variables for: | `UPSTREAM_BASE_URL` | Upstream API endpoint | — | | `UPSTREAM_API_KEY` | Upstream API key | — | | `ADMIN_PASSWORD` | Dashboard password | (none) | +| `ROUTSTR_SECRET_KEY` | Master key encrypting node secrets at rest. Auto-generated to a key file if unset | (auto-generated) | +| `ROUTSTR_SECRET_KEY_FILE` | Path to the generated key file (used when `ROUTSTR_SECRET_KEY` is unset) | `routstr_secret.key` beside the database | | `DATABASE_URL` | Database connection string | `sqlite+aiosqlite:///keys.db` | | `NAME` | Node display name | `ARoutstrNode` | | `DESCRIPTION` | Node description | `A Routstr Node` | @@ -142,6 +149,20 @@ Use environment variables for: Environment variables are read on startup. Dashboard settings override them and persist in the database. Once you change a setting in the dashboard, the env var is ignored for that setting. +### Secrets at Rest + +The node's Nostr private key (`nsec`) is encrypted in the database using +`ROUTSTR_SECRET_KEY`. You don't have to set it: if it's unset, the node generates a +key on first start, writes it **beside the database** (the file named by +`ROUTSTR_SECRET_KEY_FILE`, default `routstr_secret.key`) so it persists on the same +volume as your data, and prints it once. + +**Back up that key** — it lives on the same volume as your database, so include it +in your backups. If it is lost or changed, previously encrypted secrets can't be +decrypted and must be re-entered — there is no rotation. To keep the key off the +data volume, set `ROUTSTR_SECRET_KEY` explicitly (an env value always takes +precedence over the file). See also [Deployment](deployment.md). + --- ## Models diff --git a/docs/provider/deployment.md b/docs/provider/deployment.md index 5a3a2899..24c0591a 100644 --- a/docs/provider/deployment.md +++ b/docs/provider/deployment.md @@ -156,10 +156,21 @@ Example `.env`: UPSTREAM_BASE_URL=https://api.openai.com/v1 UPSTREAM_API_KEY=sk-proj-... ADMIN_PASSWORD=change-me +# Encrypts node secrets at rest. Optional — if unset, a key is generated next to +# your database (on the same volume) and printed once. Set it explicitly to +# manage the key yourself. +ROUTSTR_SECRET_KEY= NAME=My Provider Node RECEIVE_LN_ADDRESS=me@walletofsatoshi.com ``` +!!! note "Secret key persistence" + If you leave `ROUTSTR_SECRET_KEY` unset, the node generates one and stores it + as `routstr_secret.key` **next to your database**, so it persists on the same + volume as your data — just include that volume in your backups. For stronger + isolation (keeping the key off the data volume), set `ROUTSTR_SECRET_KEY` from + a secrets manager instead. + See [Configuration](configuration.md) for all available options. --- diff --git a/docs/provider/quickstart.md b/docs/provider/quickstart.md index 5b33c359..ef959f40 100644 --- a/docs/provider/quickstart.md +++ b/docs/provider/quickstart.md @@ -32,6 +32,10 @@ Create a `.env` file in the root of the project to store your secrets: # Initial Admin Password ADMIN_PASSWORD=mysecretpassword +# Encrypts node secrets at rest. Optional — if unset, the node generates a key on +# first start and prints it once (back it up). +ROUTSTR_SECRET_KEY= + # Node Identity NAME="My AI Node" DESCRIPTION="Fast access to models"