Files
routstr-core/compose.yml
redshift b93fca9f73 feat: auto-generate the node's Nostr identity on first boot
A fresh provider node currently has no Nostr identity and stays invisible
until an operator pastes an nsec in the dashboard. This adds an opt-in
boot-time provisioning path so a deployment can announce itself with no
manual setup.

- Add AUTO_GENERATE_NSEC (default false). When enabled and no legacy
  nsec/NSEC is configured, bootstrap_secrets generates an identity,
  stores it Fernet-encrypted, and sets the live nsec/npub.
- The generated nsec is printed once at startup (like the admin
  password) so the operator can back it up; it never hits the log files.
- The empty legacy slot is claimed with an atomic UPDATE so concurrent
  workers don't mint competing identities.
- A provided NSEC always wins, an existing stored identity is reused,
  and an intentionally cleared identity is never resurrected.
- Enable AUTO_GENERATE_NSEC=true in compose.yml so the bundled stack
  self-provisions; document the flag across README/docs/.env.example.
- Add integration coverage for generation, idempotency, opt-in default,
  provided-nsec precedence, and cleared-identity behavior.

Tests: 2329 unit passed; test_secret_bootstrap 21 passed; ruff + mypy clean.
2026-10-07 01:10:00 +08:00

53 lines
1.4 KiB
YAML

services:
ui:
env_file:
- .env
build:
context: ./ui
dockerfile: Dockerfile.build
args:
# NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://127.0.0.1:8000}
NEXT_PUBLIC_ADMIN_API_KEY: ${NEXT_PUBLIC_ADMIN_API_KEY:-}
user: root
volumes:
- ./ui_out:/output:z
command:
["sh", "-c", "mkdir -p /output && cp -r /app/built/. /output/ && echo 'UI build copied to mounted volume' && ls -la /output/ && echo 'UI built and ready' && tail -f /dev/null"]
restart: unless-stopped
routstr:
build: .
depends_on:
- ui
volumes:
- .:/app:z
- ./logs:/app/logs:z
- tor-data:/var/lib/tor:ro
- ./ui_out:/app/ui_out:ro,z
env_file:
- .env
environment:
- TOR_PROXY_URL=socks5://tor:9050
# Self-provision a Nostr identity on first boot so the node announces
# itself without an operator pasting an nsec. A provided NSEC still wins.
- AUTO_GENERATE_NSEC=true
ports:
- 8000:8000
extra_hosts: # Needed to access locally running models
- "host.docker.internal:host-gateway"
restart: unless-stopped
tor:
image: ghcr.io/hundehausen/tor-hidden-service:latest
volumes:
- tor-data:/var/lib/tor
environment:
# Format: HS_<NAME>=<TARGET_HOST>:<TARGET_PORT>:<VIRTUAL_PORT>
- HS_ROUTER=routstr:8000:80
depends_on:
- routstr
restart: unless-stopped
volumes:
tor-data: