decrypt_master_key.py: prints the existing master xprv after an explicit
confirmation prompt, for disaster-recovery backups. Falls back to
./data/keys/master.xprv.enc (the docker-compose.yml bind-mount path) when
.env's configured MASTER_KEY_PATH doesn't exist locally.
encrypt_master_key.py: the reverse direction — takes an externally-generated
xprv (e.g. created offline/air-gapped) via a hidden getpass prompt, validates
it parses as a private extended key, and encrypts it with the same Fernet
scheme generate_master_key.py uses. Refuses to overwrite an existing key file
unless --overwrite is passed.
Neither script is reachable via any API endpoint or the admin panel, by
design — this is the one secret the entire custodial wallet derives from.
Documented in docs/setup.md (new "Recuperare o portare una xprv esistente"
section) and CLAUDE.md's Commands block.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CLAUDE.md and the user/admin guides only mentioned "a confirmed block" for
the draw, leaving the actual end-to-end timing (why it can take several
minutes after the countdown hits zero) unclear. Spell out the three distinct
confirmations in sequence — last pending bet, draw block, payout tx — and
the resulting best/worst-case wall-clock estimates.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The round timer relied on each client's own wall clock, so two browsers with
skewed local clocks showed different countdowns for the same round; the
server now also returns server_time so the frontend can correct for clock
skew. Also drop out-of-order /rounds/current responses (multiple independent
triggers could resolve late and revert the UI to a stale drawing/result
state) and prune per-round bookkeeping maps on round transitions.
Separately, place_bet only checked status == "open", leaving a window (up to
the scheduler's 5s tick interval) after a round's timer hit zero where a new
bet could still be accepted. place_bet now checks the round's own deadline
directly (round_accepts_bets), acting as an immediate "yellow light" for new
entries while still letting already-broadcast bets confirm before the round
closes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
RoundConfig gets a paused flag toggled via new POST /admin/pause and
/admin/resume endpoints (audit-logged, surfaced as a "Manutenzione" card in
the admin Parametri view). Pausing only stops the *next* round from opening
once the current one closes — rounds/service.py:open_new_round_if_needed
still lets an in-progress round finish, draw, and pay out its winner
normally. GET /rounds/current exposes lottery_paused so the user page shows
a maintenance banner (even while logged out) instead of silently going idle.
Also replaces the user dashboard's stacked account-bar card + bento-grid
menu with a single sticky navbar (identity row + Deposito/Bet/Prelievo
tabs), and moves the page content into a dedicated .app-shell container so
the navbar itself can span full width.
CLAUDE.md's DRAW architecture bullet now explains draw_animation_seconds
and its decoupling from the real block-wait timing. guida-utente.md
gets a new "Estrazione del vincitore" section describing what a player
sees and when. guida-admin.md's Parametri table and hardcoded-defaults
note include the new field.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ran through the init skill's checklist against the current codebase:
test count (49 -> 54), a new "Admin dashboard and test UI" architecture
section (the two static SPAs, their endpoints, and the deliberate
non-linking between them), two new non-obvious domain decisions (admin
privkey export is intentional not a bug; who pays an RBF fee bump), a
pointer to docs/ for the human-facing guides, and updated Known gaps
(deployment gap resolved and removed; admin-token blast radius and the
Docker auto-restart risk called out; user-facing history gap reworded
now that the admin side has one). MVP business parameters reworded to
distinguish what's admin-configurable (bet amount, min amount) from
what's genuinely hardcoded (70/30 split, 1-conf threshold).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CLAUDE.md and docs/guida-admin.md now describe RoundConfig as the sole
source of truth for business parameters, with no env var counterpart —
defaults live as hardcoded model column defaults, not app/config.py.
guida-admin.md documents the four new dashboard sections (Round,
Transazioni pendenti, Audit log alongside Parametri/Utenti). setup.md
points readers to the admin panel instead of .env for those values.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
open_new_round_if_needed now withholds opening the next round until
ROUND_COOLDOWN_SECONDS (default 30) have passed since the previous
round's closed_at, returning None in that window instead of a Round.
Without this, the next round opened within one scheduler tick (~5s) of
the previous payout confirming — not enough time for a player to
notice the round they were in actually resolved.
Callers updated: the scheduler treats None as "nothing to do this
tick", and place_bet raises a "try again shortly" BetError instead of
crashing on a None round.
Not in the original flowchart — a deliberate UX addition on top of it,
documented as such in CLAUDE.md.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Records the docker compose commands (master key bootstrap, up/down,
log tailing) and the SITE_ADDRESS dev-vs-production behavior, plus an
explicit warning: app's restart:unless-stopped means a crash mid-round
auto-restarts into the still-open scheduler-resume gap, so this isn't
unattended-safe yet.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Records the MVP build as code-complete and unit-tested, documents the
real install/run/test commands now that the project is scaffolded, and
lists known gaps (scheduler restart resume, payout retry, RBF fallback,
missing history endpoints, deployment, admin auth, rate limiting) to
address before treating this as production-ready.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>