Restructure docs: docs-first, SSOT, one ADR per decision

- Add docs/README.md as the index of all documentation.
- Split the 52 DEC entries of decisioni-e-limiti.md into docs/adr/ADR-NN
  files (same numbers), add ADR-53 (audit log), TEMPLATE.md and an ADR index
  with a feature -> ADR table.
- Rename decisioni-e-limiti.md to limiti-noti.md (LIM only) and rewrite all
  DEC-nn references as links to the ADRs.
- Document the docs-first / SSOT / ADR rules in processo-di-modifica.md and
  map the docs in CLAUDE.md and README.md.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 5.5 committed 2026-10-14 12:05:57 +02:00
1 parent 7ba8e22bb4
commit b424f670c5
76 files changed
+2147 -1530

No files matched your search

+27 -27
View File
@@ -14,13 +14,13 @@ per l'estrazione [ciclo-di-vita-del-round.md](ciclo-di-vita-del-round.md#3--estr
per gli endpoint e i codici di errore [api.md](api.md); per l'elenco degli
eventi di audit [audit-log.md](audit-log.md); per backup e procedure
operative [operativita.md](operativita.md); per le decisioni di progetto e i
limiti accettati [decisioni-e-limiti.md](decisioni-e-limiti.md).
limiti accettati [limiti-noti.md](limiti-noti.md).
## Modello di fiducia
La piattaforma è **custodiale**: tutte le chiavi private sono generate e
conservate dal server. L'utente non controlla nulla finché non preleva
([DEC-01](decisioni-e-limiti.md), [LIM-26](decisioni-e-limiti.md)).
([ADR-01](adr/ADR-01-piattaforma-custodiale-tutte-chiavi-generate-tenute.md), [LIM-26](limiti-noti.md)).
| Soggetto | Fiducia | Perché |
|---|---|---|
@@ -51,7 +51,7 @@ ricompensa di quel blocco.
vincere. Chi trasmette i blocchi ha una leva analoga: il server Electrum
attivo può non annunciare un blocco e far estrarre dal successivo; la
corroborazione verificava che il blocco esistesse, non che fosse il primo
dopo la chiusura ([LIM-13](decisioni-e-limiti.md)).
dopo la chiusura ([LIM-13](limiti-noti.md)).
- **Come il commit-reveal toglie il movente al minatore.** All'apertura il
server si impegna su un segreto pubblicandone solo l'impronta
([RND-34](ciclo-di-vita-del-round.md#0--apertura)); all'ingresso in
@@ -76,22 +76,22 @@ ricompensa di quel blocco.
costa in media 2³² hash, pochi millisecondi a un ASIC: il controllo toglie
la contraffazione gratuita, non quella economica. A proteggere l'estrazione
sono il quorum sull'header di estrazione e il segreto del commit-reveal
([LIM-14](decisioni-e-limiti.md)).
([LIM-14](limiti-noti.md)).
Restano **accettati**, e descritti in
[decisioni-e-limiti.md](decisioni-e-limiti.md):
[limiti-noti.md](limiti-noti.md):
- **Operatore d'accordo con un minatore** ([LIM-25](decisioni-e-limiti.md)).
- **Operatore d'accordo con un minatore** ([LIM-25](limiti-noti.md)).
L'operatore conosce il segreto prima della rivelazione e può calcolare
l'esito di ogni blocco candidato; un minatore complice può scartare quelli
sfavorevoli. È accettato perché l'operatore è già fidato come custode di
tutti i fondi ([LIM-26](decisioni-e-limiti.md)): chi può leggere il segreto
tutti i fondi ([LIM-26](limiti-noti.md)): chi può leggere il segreto
dal database può già spendere ogni moneta.
- **Un attacco al 51% su PLM** ([LIM-24](decisioni-e-limiti.md)) è
- **Un attacco al 51% su PLM** ([LIM-24](limiti-noti.md)) è
contenuto, non impedito: 6 conferme sui depositi
([DEC-06](decisioni-e-limiti.md), B-82), rilevamento delle
([ADR-06](adr/ADR-06-depositi-richiedono-deposit-confirmations-conferme-6.md), B-82), rilevamento delle
riorganizzazioni con pausa sopra la profondità 1 e verifica delle monete
del round prima del pagamento ([DEC-52](decisioni-e-limiti.md), B-82,
del round prima del pagamento ([ADR-52](adr/ADR-52-riorganizzazioni-catena-contengono-invece-ignorarle.md), B-82,
B-85). Il denaro che un attaccante riesce a sottrarre è perso per la
piattaforma. *Oggi:* depositi a 1 conferma, nessuna riorganizzazione
rilevata, nessuna verifica prima del pagamento.
@@ -104,7 +104,7 @@ Restano **accettati**, e descritti in
| Chiave di cifratura della master key (Fernet) | File `./secrets/xprv_encryption.key` (permessi `0600`, fuori da `./data/`), montato in sola lettura come secret di compose in `/run/secrets/xprv_encryption_key` e indicato da `XPRV_ENCRYPTION_KEY_FILE` ([SEC-53]) | `Fernet.generate_key()` (comando in `.env.example`) | Insieme al file cifrato: come sopra | Il file cifrato non è più leggibile: fondi persi senza backup della xprv | Manuale, con la procedura [OPS-22](operativita.md#segreto-compromesso). Non esiste uno script dedicato |
| `JWT_SECRET` | `.env` (permessi `0600`) | `secrets.token_urlsafe(32)` | Chiunque può firmare un token per qualunque utente (con il `tv` corretto, che è un piccolo intero) e agire come lui, prelievi compresi | Nessuna perdita di dati: se ne genera uno nuovo | Cambiarlo e riavviare invalida **tutte** le sessioni |
| `ADMIN_TOKEN` | `.env` (permessi `0600`); nel browser dell'operatore in `sessionStorage` (`plm_admin_token`) | `secrets.token_urlsafe(32)` | Accesso completo a `/admin/*`, compreso l'export delle chiavi private | Nessuna: se ne imposta uno nuovo | Cambiarlo e riavviare; l'operatore reinserisce il token |
| Segreto del round (`server_seed`) | Tabella `rounds`, **cifrato** con la chiave della master xprv (`server_seed_enc`) fino alla rivelazione, poi in chiaro ([RND-34](ciclo-di-vita-del-round.md#0--apertura), [SEC-56]) | 32 byte da un generatore crittograficamente sicuro, all'apertura del round | Prima della rivelazione permette di calcolare il vincitore di ogni blocco candidato ([LIM-25](decisioni-e-limiti.md)) | L'estrazione di quel round non può concludersi | Uno nuovo per ogni round; pubblico dopo la rivelazione |
| Segreto del round (`server_seed`) | Tabella `rounds`, **cifrato** con la chiave della master xprv (`server_seed_enc`) fino alla rivelazione, poi in chiaro ([RND-34](ciclo-di-vita-del-round.md#0--apertura), [SEC-56]) | 32 byte da un generatore crittograficamente sicuro, all'apertura del round | Prima della rivelazione permette di calcolare il vincitore di ogni blocco candidato ([LIM-25](limiti-noti.md)) | L'estrazione di quel round non può concludersi | Uno nuovo per ogni round; pubblico dopo la rivelazione |
| Hash delle password | Tabella `users` (SQLite, `./data/db/`) | Argon2id | Attacco offline a dizionario, rallentato dai parametri Argon2 | — | Reset dall'operatore |
| JWT dell'utente | Browser, `localStorage` (`plm_token`) | Emesso al login/registrazione | Sessione dell'utente fino alla scadenza (24h) o al cambio password | — | Cambio password o reset dall'operatore |
@@ -361,7 +361,7 @@ che ne è il proprietario; qui ci sono le regole di sicurezza.
- ~~[SEC-18]~~ Assorbito da [REG-01](registrazione-e-depositi.md#registrazione)
e [REG-02](registrazione-e-depositi.md#registrazione): formato dello
username, confronto senza maiuscole e indice unico su `lower(username)` sono
regole del dominio, con la motivazione in [DEC-38](decisioni-e-limiti.md);
regole del dominio, con la motivazione in [ADR-38](adr/ADR-38-username-unico-spazio-nomi-senza-distinzione.md);
qui non resta nulla di specifico della sicurezza.
- **[SEC-19]** Login con username inesistente e con password errata danno la
@@ -377,7 +377,7 @@ che ne è il proprietario; qui ci sono le regole di sicurezza.
- ~~[SEC-20]~~ Assorbito da [REG-12](registrazione-e-depositi.md#sessione-e-password)
e [REG-13](registrazione-e-depositi.md#sessione-e-password): l'assenza di
recupero, il cambio con la password attuale e il reset dall'operatore sono
il flusso del dominio (motivazione in [DEC-37](decisioni-e-limiti.md)); la
il flusso del dominio (motivazione in [ADR-37](adr/ADR-37-nessun-recupero-password-reset.md)); la
parte di sicurezza, cioè il freno sui tentativi, è [SEC-54], e il reset è
[SEC-27].
@@ -563,7 +563,7 @@ ogni riavvio e non funzionerebbero con più worker.
gli altri endpoint di lettura. Scommesse e prelievi sono serializzati per
utente dal blocco per-utente (vedi
[scommesse-e-prelievi.md](scommesse-e-prelievi.md#scommessa-e-prelievo-insieme)),
che non è un limite di frequenza ([LIM-07](decisioni-e-limiti.md)).
che non è un limite di frequenza ([LIM-07](limiti-noti.md)).
*Codice:* `app/api/routes/bets.py`, `app/api/routes/withdrawals.py`,
`app/api/routes/admin.py:require_admin`
*Stato:* da implementare (B-94) · *Oggi:* anche il cambio password e
@@ -609,7 +609,7 @@ loro significato per la sicurezza.
riserva raggiungibile — sceglierebbe da solo il blocco di estrazione, gli
accrediti, le monete date per spese e le conferme. Una rete in cui
rispondono meno di 2 server ritarda le decisioni, non le prende
([DEC-07](decisioni-e-limiti.md)).
([ADR-07](adr/ADR-07-ogni-decisione-sposta-denaro-base-risposta.md)).
- **`ALLOW_NO_QUORUM=true`** riporta la regola ridotta (nessun altro server:
la verifica passa; altrimenti basta una risposta). Rende fidato il server
attivo ed è **solo per lo sviluppo**; ogni avvio lo scrive nel log come
@@ -628,7 +628,7 @@ loro significato per la sicurezza.
nel quorum solo come server indipendente.
- **Resta accettato** che i server del quorum siano scelti dall'operatore:
più server controllati dalla stessa parte contano come voci indipendenti
([LIM-11](decisioni-e-limiti.md)).
([LIM-11](limiti-noti.md)).
*Oggi* (B-84): un `ELECTRUM_FALLBACK_SERVERS` vuoto (il valore di
`.env.example`) è accettato in silenzio e fa passare ogni corroborazione; con
@@ -840,7 +840,7 @@ La struttura completa delle tabelle è in [modello-dati.md](modello-dati.md).
database o di un backup, senza la chiave, non può calcolare in anticipo il
vincitore di un round aperto, e quindi non può pilotarlo insieme a un
minatore; per farlo servono database **e** chiave, cioè lo stesso livello
di accesso che già consente di spendere i fondi ([LIM-25](decisioni-e-limiti.md)).
di accesso che già consente di spendere i fondi ([LIM-25](limiti-noti.md)).
Per questo la chiave va conservata separata dai backup del database
([operativita.md](operativita.md) OPS-06). All'avvio la verifica di
[SEC-49] decifra anche il segreto dei round non ancora rivelati: se non ci
@@ -868,35 +868,35 @@ La struttura completa delle tabelle è in [modello-dati.md](modello-dati.md).
## Rischi accettati
Limiti noti e voluti, descritti in dettaglio in
[decisioni-e-limiti.md](decisioni-e-limiti.md):
[limiti-noti.md](limiti-noti.md):
- **Custodia totale** ([LIM-26](decisioni-e-limiti.md)). Chi controlla l'host
- **Custodia totale** ([LIM-26](limiti-noti.md)). Chi controlla l'host
o `ADMIN_TOKEN` controlla tutti i fondi; la cifratura a riposo e i permessi
stretti non proteggono da chi ha accesso all'host.
- **Operatore d'accordo con un minatore** ([LIM-25](decisioni-e-limiti.md)):
- **Operatore d'accordo con un minatore** ([LIM-25](limiti-noti.md)):
possono scegliere il vincitore, perché l'operatore conosce il segreto del
round prima della rivelazione.
- **Attacco al 51% su PLM** ([LIM-24](decisioni-e-limiti.md)): contenuto
- **Attacco al 51% su PLM** ([LIM-24](limiti-noti.md)): contenuto
(6 conferme sui depositi, riorganizzazioni rilevate e pausa, pagamento di
ciò che è in catena), non impedito.
- **Header a difficoltà minima** ([LIM-14](decisioni-e-limiti.md)): senza il
- **Header a difficoltà minima** ([LIM-14](limiti-noti.md)): senza il
ricalcolo della difficoltà, un server ostile può ancora produrre una breve
catena falsa al powLimit; la neutralizzano il quorum e il commit-reveal.
- **Server del quorum scelti dall'operatore** ([LIM-11](decisioni-e-limiti.md)):
- **Server del quorum scelti dall'operatore** ([LIM-11](limiti-noti.md)):
più server della stessa parte contano come voci indipendenti; con
`ALLOW_NO_QUORUM=true` (solo sviluppo) il server attivo è fidato.
- **Token admin unico e condiviso** ([LIM-06](decisioni-e-limiti.md)), senza
- **Token admin unico e condiviso** ([LIM-06](limiti-noti.md)), senza
identità per operatore né limite di tentativi: una fuga ha il raggio
d'azione massimo.
- **JWT in `localStorage`** ([LIM-09](decisioni-e-limiti.md)), quindi
- **JWT in `localStorage`** ([LIM-09](limiti-noti.md)), quindi
leggibile da un eventuale XSS; la CSP lo mitiga ma ammette script inline.
- **Limiti di frequenza parziali** ([LIM-07](decisioni-e-limiti.md)): login,
- **Limiti di frequenza parziali** ([LIM-07](limiti-noti.md)): login,
registrazione, cambio password, segnalazioni di bug e SSE sì; scommesse,
prelievi ed endpoint admin no. Tutti in memoria e per singolo processo.
- **`JWT_SECRET` e `ADMIN_TOKEN` restano variabili d'ambiente**, visibili con
`docker inspect` a chi ha accesso a Docker, che è già fidato come l'host.
- **Una conferma per le transazioni della piattaforma**
([DEC-06](decisioni-e-limiti.md)): scommesse, pagamenti e prelievi le
([ADR-06](adr/ADR-06-depositi-richiedono-deposit-confirmations-conferme-6.md)): scommesse, pagamenti e prelievi le
firma solo il server, quindi un utente non può spenderne due volte gli
ingressi; solo i depositi ne richiedono 6.
- **La master key non è ruotabile**: gli indirizzi degli utenti sono