Files
plm-lottery/docs/setup.md
T
davideandClaude Sonnet 5.5 c1b192f6d1 Harden the container: non-root, pinned images and dependencies, the key in a file (B-91, SEC-51, SEC-52, SEC-53; closes B-71, B-72)
- app runs as uid 1000; a one-shot data-permissions service chowns ./data to it
  and closes it to others (works on an older root-owned install with no host sudo);
  files are created 0600 (umask 077)
- python:3.12-slim and caddy:2-alpine pinned by digest; dependencies installed from
  requirements.lock with --require-hashes
- the Fernet key is XPRV_ENCRYPTION_KEY_FILE (./secrets/xprv_encryption.key, a compose
  secret), out of every environment variable and of a ./data backup
- setup.md no longer presents a second way to run the server (B-72)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 00:54:23 +02:00

7.0 KiB

Setup

Passaggi da eseguire una tantum per preparare un'istanza di PLM Lottery, prima di poterla avviare. Il server gira sempre in Docker; il venv serve solo a test, migrazioni da generare e script una tantum. Per come avviarla poi ogni volta, vedi running-the-server.md.

1. Prerequisiti

  • Docker + Docker Compose (il server gira solo così)
  • Python 3.12+ (solo per gli strumenti locali: test, alembic revision e gli script delle chiavi)
  • Un server Electrum raggiungibile per la rete PLM. Il server di bootstrap per lo sviluppo è santantonio.sytes.net:50002 (SSL) — va bene per i test, ma in produzione conviene usarne uno di cui ci si fida o gestirne uno proprio.
  • Consigliato in produzione: più di un server. Tutto passa da questa singola connessione (accredito depositi, invio transazioni, conferme, altezza della catena su cui si basa l'estrazione), quindi un solo server è il principale punto di rottura della piattaforma. Elencane altri in ELECTRUM_FALLBACK_SERVERS (vedi sotto): l'app li prova a rotazione, così un server irraggiungibile costa un solo tentativo di riconnessione invece di un disservizio.

2. Creare il file .env

Copia .env.example in .env e compila i segreti. Ogni valore sotto viene generato una volta e non cambia più (ruotarlo invalida sessioni/dati cifrati esistenti):

cp .env.example .env
Variabile Scopo Come generarla
XPRV_ENCRYPTION_KEY_FILE Percorso del file che contiene la chiave simmetrica che cifra a riposo la master xprv del server (./secrets/xprv_encryption.key, cartella 0700, file 0600; nel container è montato come secret). Perdere questa chiave significa perdere per sempre l'accesso ai fondi di tutti gli utenti. mkdir -p secrets && chmod 700 secrets && python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" > secrets/xprv_encryption.key && chmod 600 secrets/xprv_encryption.key
JWT_SECRET Firma i token di sessione degli utenti. python -c "import secrets; print(secrets.token_urlsafe(32))"
ADMIN_TOKEN Token bearer richiesto sugli endpoint admin (header X-Admin-Token). python -c "import secrets; print(secrets.token_urlsafe(32))"

Le chiavi Electrum (ELECTRUM_HOST/PORT/USE_SSL) hanno default sensati in .env.example. DATABASE_URL e MASTER_KEY_PATH non sono elencate lì: hanno un default in app/config.py, e dentro il container vengono comunque sovrascritte da docker-compose.yml per puntare a ./data/ (elenco completo delle variabili in architettura.md).

ENABLE_API_DOCS (default false) controlla Swagger/ReDoc/l'OpenAPI JSON grezzo su /docs, /redoc e /openapi.json: espongono l'intera superficie dell'API, endpoint admin inclusi, quindi restano disattivati a meno di non impostarlo esplicitamente a true — utile in locale, da evitare in produzione.

ELECTRUM_FALLBACK_SERVERS elenca i server di riserva, separati da virgola, nel formato host:porta (TLS, il caso normale), host:porta:notls oppure host:porta:pin=<sha256> (TLS con il certificato del server fissato). Esempio:

ELECTRUM_FALLBACK_SERVERS=nodo2.example.net:50002,nodo3.example.net:50002,nodo4.example.net:50002:pin=<sha256>

Vengono provati a rotazione dopo il primario e servono anche da quorum: ne servono almeno 2, altrimenti l'app non parte (ALLOW_NO_QUORUM=true lo permette, ma solo in sviluppo). ELECTRUM_CERT_PIN fissa allo stesso modo il certificato del primario. Attenzione: un valore scritto male blocca l'avvio dell'app — è voluto, meglio accorgersene subito che durante il disservizio in cui il fallback serve davvero.

JWT_SECRET e la chiave in XPRV_ENCRYPTION_KEY_FILE vengono verificati all'avvio: se sono vuoti (o JWT_SECRET è più corto di 32 caratteri) il container si rifiuta di partire con un errore esplicito, invece di avviarsi e rompersi al primo login. Nota: .env contiene solo segreti e configurazione di infrastruttura — i parametri di business (bet amount, durata round, fee, ecc.) si configurano dal pannello admin dopo l'avvio, non qui — vedi guida-admin.md.

Non committare mai .env. È già escluso da .gitignore.

3. Generare la master key

Il server deriva l'indirizzo di deposito di ogni utente (e l'indirizzo pool) da un'unica master xprv, generata una volta e cifrata a riposo con la chiave in XPRV_ENCRYPTION_KEY_FILE. Questo passaggio va eseguito esattamente una volta per ogni deployment, dopo aver creato quel file e impostato XPRV_ENCRYPTION_KEY_FILE in .env:

Dal venv: PYTHONPATH=. python scripts/generate_master_key.py, con MASTER_KEY_PATH=./data/keys/master.xprv.enc in .env, così lo script scrive esattamente il file che legge il container.

Questo scrive un file cifrato (MASTER_KEY_PATH, in Docker ./data/keys/master.xprv.enc). Fai il backup di questo file insieme a secrets/xprv_encryption.key — uno dei due da solo è inutile, ma perderli entrambi insieme significa perdere i fondi di tutti gli utenti senza possibilità di recupero.

Recuperare o portare una xprv esistente

Due script, entrambi manuali/una tantum, per lo scenario di disaster recovery o per usare una xprv generata altrove (es. offline/air-gapped) invece di farla generare al server:

  • scripts/decrypt_master_key.py: decifra e stampa a schermo la xprv già presente in MASTER_KEY_PATH (con fallback automatico su ./data/keys/master.xprv.enc se il path di .env non esiste in locale). Chiede conferma esplicita prima di stampare.
  • scripts/encrypt_master_key.py: cifra una xprv esterna e la scrive in MASTER_KEY_PATH con lo stesso identico schema (Fernet + XPRV_ENCRYPTION_KEY) usato da generate_master_key.py. La xprv va incollata con input nascosto (non appare a schermo). Si rifiuta di sovrascrivere un file esistente a meno di passare --overwrite.

Entrambi vanno eseguiti localmente (o dentro il container via docker compose run --rm app ...), mai esposti da un endpoint API o dal pannello admin: chi ottiene questa xprv ottiene il controllo dei fondi di tutti gli utenti e del pool.

4. Installare gli strumenti locali (venv)

Servono per i test e per gli script delle chiavi; il server non ne ha bisogno, perché l'immagine installa le proprie dipendenze (da requirements.lock).

python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

5. Applicare le migrazioni del database

Le migrazioni vengono eseguite automaticamente all'avvio del container (vedi il CMD del Dockerfile) — nessun passaggio manuale, e alembic upgrade head non si lancia mai a mano.

6. Impostare l'indirizzo delle fee

Prima che si possa aprire il primo round, un admin deve impostare fee_address tramite il pannello admin o l'API — vedi guida-admin.md. Finché non è impostato nessun round si apre, perché il pagamento non si potrebbe costruire.

A questo punto l'istanza è pronta per essere avviata — continua con running-the-server.md.