- GET /health/ready (Electrum connected, header < 1800s, scheduler heartbeat < 30s) is the
container healthcheck; the scheduler beats on every tick and draw-wait iteration
- GET /admin/electrum and an Electrum indicator in /admin (server, header age, last rotation)
- GET /admin/audit-log: before_id, event_type, round_id, user_id, from, to; the panel gets filters
- registration writes user_registered in its transaction
- the app's own logs also go to stdout; uvicorn trusts X-Forwarded-For from Caddy's fixed
address only, so the access log shows the client's IP
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- 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>
Caddy's site address comes from SITE_ADDRESS (defaults to "localhost").
Left at that default, Caddy detects it isn't a public hostname and
issues a self-signed cert from its own internal CA — no domain needed
for local/dev testing. Set to a real domain, it gets a genuine Let's
Encrypt certificate automatically instead.
DB, encrypted master key and logs are bind-mounted from ./data/ on the
host (not opaque Docker-managed volumes), so they survive container
restarts/rebuilds and stay reachable for manual inspection/backup
directly from the repo root. ./data/ is gitignored.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>