Files
plm-lottery/BUGS.md
T
davideandClaude Sonnet 5.5 24e5fc3d8b Bound credentials, brake password changes, cap bug reports, page the user list, structure every error (B-94, SEC-54, SEC-55, API-09, API-15, API-16, API-19, API-21, API-28, API-33, REG-12, AUD-42)
- login and password-change fields: at most 256 characters
- wrong current password: per-user brake (5 free, 2s doubling to 300s), 429 even for the
  right password while locked; password_changed audited in the same transaction
- POST /bug-reports: 10 per IP per hour, consumed only by a stored report
- GET /admin/users: limit/after_id paging, the panel walks the pages
- 401/403/404/405/422/503 all answer {code, message, params}; the 422 omits the input
- static files revalidated on every use (Cache-Control: no-cache)
- error.* keys for the new codes in all seven languages

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

3.9 KiB

BUGS.md — open findings

Only the 2026-09-28 specification review (B-73 … B-100) still has open entries (B-92, B-93, B-96). A finding is removed from this file once it is fixed, and git log --all --grep 'B-nn' is the record of how it was closed. Per CLAUDE.md's convention each entry gets its own commit with its own regression test, and the B-nn marker goes in a comment next to the fix.

Audit of 2026-09-28 — specification review (B-73 … B-98)

Fourth audit, run against the new specification in docs/ rather than the code alone: four independent reviews (coin/chain mechanics, money state machine, DevOps, spec cross-validation), with the critical claims re-verified in code. The target behaviour of every entry below is now written into docs/ as requirements marked *Stato:* da implementare (B-nn); the Italian overview, the design decisions and the implementation order are in docs/piano-correzioni.md. The code has not been changed yet. Same convention as above: one commit per entry, with its own regression test, spec + code + test together.

Design decisions taken with the owner on 2026-09-28 (they change existing decisions, see docs/limiti-noti.md):

  • Draw by commit-reveal. The winner must not be computable by anyone who controls block production, since PLM's hashrate (~22 TH/s) is rentable.
  • Never release funds on doubt. A money-moving row is rolled back only on proof (an explicit rejection, or its inputs confirmed-spent elsewhere), never on a timeout or on one server's silence.
  • One pool address per round, so a round's money, history and payout are isolated from every other round's.
  • Deposits need 6 confirmations (configurable), everything else keeps 1: the deposit is the only input a user can double-spend.
  • Contain reorgs instead of pretending they don't happen: detect them, pause on deep ones, and pay out only what is verifiably on chain.

Medium

B-92 — No health, readiness or real audit browsing

/health always answers ok; nothing exposes Electrum state; the audit endpoint returns only the latest 500 rows unfiltered; registration writes no audit row; the access log records Caddy's IP; app logs go only to the file.

Fix: GET /health/ready (Electrum connected, tip age < 1800s, scheduler heartbeat < 30s) used as the container healthcheck; Electrum status in /admin; audit filters by event type, round, user and time, with pagination; user_registered audit row with index and address; uvicorn proxy headers from Caddy only; logs also to stdout.

B-93 — Deploys and rollbacks

A deploy with an open SSE stream never shuts down gracefully (uvicorn waits, Docker kills after 10s); an older image cannot start on a newer DB; images are untagged; SITE_ADDRESS passed per command reverts to localhost on the next plain docker compose up.

Fix: close SSE streams on shutdown, stop_grace_period: 30s, await task cancellation; tag images per release; SITE_ADDRESS in .env with ${SITE_ADDRESS:?}; documented pre-deploy backup and downgrade procedure.

B-96 — Smaller state inconsistencies

A reduced full-balance withdrawal is not re-checked against the minimum; an abandoned payout leaves winner_amount_sats/fee_amount_sats set; a promoted payout writes no payout_sent and a promoted bet no bet_placed; the reconciler pings SSE for every row it checks; concurrent refreshes of one user can collide on the unique outpoint and end the Electrum session; listener.client is read outside the per-user lock; a bet covered only by pending change gets insufficient_balance instead of balance_pending_confirmation. A round waiting in closing on a bet that never leaves the mempool gives no signal at all.

Fix: each as described, one regression test per item; for the closing wait, a closing_stalled audit row every 1800s past the deadline and closing_waiting_since on GET /rounds/current (spec: TX-29).