Align README and running-the-server.md with the Docker-only policy (B-44)

CLAUDE.md declares the server always runs via Docker (dev and prod
alike) with no supported bare-uvicorn workflow, but README's Quick
start and docs/running-the-server.md's "Locale / venv" section still
documented running uvicorn directly — a leftover from before that
policy was adopted. Rewrite both to a single Docker-only path and
update CLAUDE.md's own note about it.

Verified docker compose run --rm app python scripts/generate_master_key.py
against a real build/run to confirm the Quick start's Docker commands
actually work as documented.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-27 16:04:24 +02:00
co-authored by Claude Sonnet 5
parent d60da11603
commit a574db0d93
5 changed files with 50 additions and 55 deletions
+6 -13
View File
@@ -1,11 +1,11 @@
# Known bugs
A second full-codebase audit on 2026-07-27 found **25 further issues** (4 critical, 6 high,
7 medium, 8 low), listed below as B-44 … B-49. B-25 through B-43 are fixed (see "Previously
fixed" below) — no Critical-, High- or Medium-severity finding remains open; the remaining 6 are
7 medium, 8 low), listed below as B-45 … B-49. B-25 through B-44 are fixed (see "Previously
fixed" below) — no Critical-, High- or Medium-severity finding remains open; the remaining 5 are
Low/hygiene. The 139-test suite was green at the time of the audit, so none of these were caught
by existing coverage — every fix lands with a regression test (the nineteen fixes so far brought
the suite from 139 to 230).
by existing coverage — every fix lands with a regression test (the twenty fixes so far brought
the suite from 139 to 232).
The recurring pattern across the open findings is worth stating once: the code is rigorous
about the failure modes that have actually been hit, and silent about the ones that have not.
@@ -18,14 +18,6 @@ admin auth, single-process assumptions, no user-facing history, etc.) are docume
## Low / hygiene
### B-44 — README and CLAUDE.md contradict each other
The README says to run `uvicorn --reload` directly and
`docker compose run --rm app python scripts/generate_master_key.py`; CLAUDE.md says explicitly
that neither is supported. Whoever opens the repo reads the README first.
**Fix:** align the README's Quick start with the Docker-only workflow documented in
CLAUDE.md and `docs/setup.md`.
### B-45 — Unvalidated and unpaginated admin list endpoints
`limit: int = 50` on `/admin/rounds` and `/admin/audit-log` has no bounds (`-1` means
@@ -75,6 +67,7 @@ already does.
- **B-31** — resubscribing on reconnect ran serially before anything else started, freezing the chain tip (and so an in-flight draw) for the whole sweep
- **B-42** — Swagger/ReDoc/the raw OpenAPI JSON enumerated the entire API surface, admin endpoints included, to anyone who requested them; now off by default and gated behind `ENABLE_API_DOCS`
- **B-43** — the Caddyfile sent no CSP, no `X-Frame-Options`/`frame-ancestors`, and no HSTS, on a page whose JWT lives in `localStorage`
- **B-44** — README's Quick start documented a bare `uvicorn --reload` workflow, and `docs/running-the-server.md` still had a matching "Locale / venv" section, both contradicting CLAUDE.md's Docker-only policy
- **B-32** — an RBF bump could retry forever below BIP125's relay-mandated minimum fee delta, with no ceiling on the fee rate either
- **B-33** — `POST /auth/login` had no rate limiting, so a password could be brute-forced against an enumerable username list
- **B-34** — password change/reset didn't invalidate already-issued JWTs, so a stolen token survived a change meant to lock it out
@@ -87,7 +80,7 @@ already does.
- **B-41** — confirmation/reconciliation depended on a verbose `blockchain.transaction.get` reply many Electrum servers reject, and abandonment relied on fragile substring-matching of an error message
See git history for the fix-by-fix breakdown (commits `f13f685`, `50a43ae`, `933760e`, and the
B-28/B-29/B-30/B-31/B-32/B-33/B-34/B-35/B-36/B-37/B-38/B-39/B-40/B-41/B-42/B-43 fixes). Suite grew from 139 to 230 tests over the nineteen.
B-28/B-29/B-30/B-31/B-32/B-33/B-34/B-35/B-36/B-37/B-38/B-39/B-40/B-41/B-42/B-43/B-44 fixes). Suite grew from 139 to 232 tests over the twenty.
A full-codebase audit on 2026-07-26 (commit `d4e0974`) found 24 bugs across every Python
module under `app/`, both static frontends, and the Docker/Caddy deployment — 5 critical,
+3 -3
View File
@@ -12,11 +12,11 @@ All 10 stages of the original build order are code-complete and unit-tested —
Verified on mainnet with real money: registration + address derivation, deposit crediting (1-conf), a real 10 PLM bet (broadcast → confirmed → change credited back), and one full round cycle (close → draw on a real block hash → 70/30 payout with sat math checked against the broadcast tx → confirmation → close → next round auto-opened). **Withdrawal and the RBF bump path have never been exercised against a live broadcast** — unit-tested only.
**Read [BUGS.md](BUGS.md) before trusting any behaviour here.** Two audits: 2026-07-26 found 24 bugs (5 critical), all fixed; 2026-07-27 found 25 more (B-25 … B-49), of which **6 are still open** — no Critical, High or Medium remains, only Low/hygiene: an admin list endpoint with no pagination bound (B-45), unbounded `String` columns for large text (B-47), among others. BUGS.md is the live open list with a proposed fix per finding; "Known gaps" at the end of this file is for limitations accepted **by design** instead. Don't fix a BUGS.md item silently as a side effect of other work — each fix lands with its own regression test.
**Read [BUGS.md](BUGS.md) before trusting any behaviour here.** Two audits: 2026-07-26 found 24 bugs (5 critical), all fixed; 2026-07-27 found 25 more (B-25 … B-49), of which **5 are still open** — no Critical, High or Medium remains, only Low/hygiene: an admin list endpoint with no pagination bound (B-45), unbounded `String` columns for large text (B-47), among others. BUGS.md is the live open list with a proposed fix per finding; "Known gaps" at the end of this file is for limitations accepted **by design** instead. Don't fix a BUGS.md item silently as a side effect of other work — each fix lands with its own regression test.
Before writing code, read the "Architecture" section below in full plus the diagrams in [flowchart/](flowchart/): [platform-overview.mmd](flowchart/platform-overview.mmd) (the 5-phase flow) and [round-lifecycle.mmd](flowchart/round-lifecycle.mmd) (the round/draw lifecycle). Every node **and edge label** (conditions, retries, loops) is a behaviour that must be implemented as described. Regenerate the companion PDFs with `flowchart/render-pdf.sh <file>.mmd` after editing either.
Human-facing guides are in [docs/](docs/), in Italian by explicit request (an exception to the English-only rule): [setup.md](docs/setup.md), [running-the-server.md](docs/running-the-server.md), [guida-utente.md](docs/guida-utente.md), [guida-admin.md](docs/guida-admin.md). **[README.md](README.md)'s Quick start is stale** (bare `uvicorn --reload`, `docker compose run … generate_master_key.py` — neither is supported; B-44); this file and `docs/setup.md` are authoritative.
Human-facing guides are in [docs/](docs/), in Italian by explicit request (an exception to the English-only rule): [setup.md](docs/setup.md), [running-the-server.md](docs/running-the-server.md), [guida-utente.md](docs/guida-utente.md), [guida-admin.md](docs/guida-admin.md). README's Quick start and `docs/running-the-server.md` are Docker-only, matching this file — a bare `uvicorn --reload` workflow was removed from both (B-44).
## Commands
@@ -233,7 +233,7 @@ Explicit design choices, not derivable from any single file — respect them:
## Known gaps / TODO
Accepted **by design**. For actual bugs see [BUGS.md](BUGS.md) (6 open) — not duplicated here.
Accepted **by design**. For actual bugs see [BUGS.md](BUGS.md) (5 open) — not duplicated here.
- **`drawing` doesn't resume after a restart.** `_tick()` handles `open`, `closing` and `paying_out` (the last via `_retry_payout_if_due`); nothing re-enters `_wait_for_next_block` after a crash. That wait is unbounded by design (the draw's entropy genuinely depends on a future block) but no longer silent — past `_DRAW_STALL_THRESHOLD_SECONDS` it logs progress and writes a `draw_stalled` audit entry, and `GET /rounds/current`'s `draw_waiting_since` surfaces it live (B-36). Restart-resumption itself remains the last prerequisite for running unattended.
- **RBF handles one shape only**: a single change output, back to the tx's own sender, big enough to absorb the increase. No extra-input fallback — an exact-amount tx or too-small change raises `RbfError`. Not permanent, though: an unbumpable tx that never confirms is eventually abandoned and its UTXOs released.
+16 -19
View File
@@ -13,33 +13,30 @@ production-ready.
## Quick start
The server always runs via Docker (app + Caddy reverse proxy with automatic
TLS) — in dev and production alike, with only `SITE_ADDRESS` differing
between the two. There's no supported way to run `uvicorn` directly; the
venv is only for local tooling (tests, Alembic migrations, the one-time key
scripts) — see [CLAUDE.md](CLAUDE.md#commands).
```bash
cp .env.example .env # then fill in the generated secrets, see docs/setup.md
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
PYTHONPATH=. python scripts/generate_master_key.py
alembic upgrade head
uvicorn app.main:app --reload --port 8123
```
Open `http://127.0.0.1:8123/` for the test UI, `http://127.0.0.1:8123/admin`
for the admin dashboard. The interactive API docs at `/docs` are disabled by
default (they'd otherwise expose the whole API surface, admin endpoints
included) — set `ENABLE_API_DOCS=true` in `.env` for local development to
enable them.
Or run the whole stack (app + Caddy reverse proxy with automatic TLS) via
Docker:
```bash
mkdir -p data/db data/keys data/logs
docker compose run --rm app python scripts/generate_master_key.py
docker compose up -d --build
```
Open `https://localhost/` for the test UI, `https://localhost/admin` for the
admin dashboard (a self-signed-certificate warning on first visit is
expected in dev — accept it, or use `curl -k`). The interactive API docs at
`/docs` are disabled by default (they'd otherwise expose the whole API
surface, admin endpoints included) — set `ENABLE_API_DOCS=true` in `.env` to
enable them.
See [docs/setup.md](docs/setup.md) and
[docs/running-the-server.md](docs/running-the-server.md) for the full
walkthrough (both workflows, dev vs. production TLS).
walkthrough (secrets, master key generation, production TLS with a real
domain).
## Documentation
@@ -64,7 +61,7 @@ python -m pytest # all tests
python -m pytest tests/unit/test_hd.py # one file
```
76 unit tests cover HD derivation, PSBT building, the Electrum client, bets,
232 unit tests cover HD derivation, PSBT building, the Electrum client, bets,
deposits, withdrawals, the round/draw engine, RBF fee-bumping, admin config,
the pending-inclusive balance calculation, and the SSE push channel. No
automated integration tests against a live Electrum connection — mainnet
+7 -20
View File
@@ -3,27 +3,14 @@
Presuppone che [setup.md](setup.md) sia già stato completato (`.env` pronto,
master key generata, migrazioni applicate).
## Locale / venv (sviluppo rapido)
Il server gira sempre via Docker, in sviluppo e in produzione allo stesso
modo — non esiste un modo supportato per lanciare `uvicorn` direttamente.
Il venv locale (`.venv/`) serve solo per i test, per scrivere le migrazioni
Alembic e per gli script una tantum di generazione chiavi (vedi
[setup.md](setup.md) e la sezione "Commands" di
[CLAUDE.md](../CLAUDE.md#commands)).
```bash
source .venv/bin/activate
uvicorn app.main:app --reload --port 8123
```
- App su `http://127.0.0.1:8123/`
- Pannello admin su `http://127.0.0.1:8123/admin`
- Docs API interattive su `http://127.0.0.1:8123/docs` solo se `ENABLE_API_DOCS=true`
in `.env` — disattivate di default perché espongono l'intera API, endpoint
admin inclusi (vedi [setup.md](setup.md))
- Log applicativi in `logs/app.log` (rotante, 10MB × 5 backup)
- Nessun TLS, nessun reverse proxy — solo per test locali sulla tua macchina.
Per fermarlo: `Ctrl+C`, oppure se lanciato in background con `nohup`:
```bash
pkill -f "uvicorn app.main:app"
```
## Docker + Caddy (consigliato, anche per i test con dominio/TLS)
## Docker + Caddy (unico workflow supportato)
```bash
mkdir -p data/db data/keys data/logs # una tantum, se non già presenti
+18
View File
@@ -0,0 +1,18 @@
"""B-44: README and docs/running-the-server.md must not document a bare
`uvicorn --reload` workflow — the server always runs via Docker, in dev and
production alike (CLAUDE.md's "Commands" section), and the two files had
drifted back to contradicting that policy."""
from pathlib import Path
REPO_ROOT = Path(__file__).parent.parent.parent
def test_readme_has_no_bare_uvicorn_command():
readme = (REPO_ROOT / "README.md").read_text()
assert "uvicorn app.main:app --reload" not in readme
def test_running_the_server_doc_has_no_bare_uvicorn_command():
doc = (REPO_ROOT / "docs" / "running-the-server.md").read_text()
assert "uvicorn app.main:app --reload" not in doc