- 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>
84 lines
2.5 KiB
YAML
84 lines
2.5 KiB
YAML
services:
|
|
# B-91 (SEC-51): one-shot, as root, so the bind-mounted ./data belongs to the non-root
|
|
# user the app runs as (uid 1000) and is closed to everyone else — also the first time
|
|
# after an older root-owned install, with no sudo on the host.
|
|
data-permissions:
|
|
build: .
|
|
image: plm-lottery-app:local
|
|
user: "0:0"
|
|
entrypoint: ["/bin/sh", "-c", "chown -R 1000:1000 /data && chmod -R go-rwx /data"]
|
|
volumes:
|
|
- ./data:/data
|
|
restart: "no"
|
|
|
|
app:
|
|
build: .
|
|
image: plm-lottery-app:local
|
|
restart: unless-stopped
|
|
env_file: .env
|
|
environment:
|
|
- DATABASE_URL=sqlite+aiosqlite:////app/db_data/plm_lottery.db
|
|
- MASTER_KEY_PATH=/app/key_data/master.xprv.enc
|
|
- XPRV_ENCRYPTION_KEY_FILE=/run/secrets/xprv_encryption_key
|
|
# B-92 (ARC-02): uvicorn trusts X-Forwarded-For from Caddy's fixed address only, so the access
|
|
# log records the client's IP instead of Caddy's.
|
|
- FORWARDED_ALLOW_IPS=172.28.0.10
|
|
secrets:
|
|
- xprv_encryption_key
|
|
volumes:
|
|
- ./data/db:/app/db_data
|
|
- ./data/keys:/app/key_data
|
|
- ./data/logs:/app/logs
|
|
expose:
|
|
- "8123"
|
|
# B-92 (ARC-61): ready = Electrum connected, a recent header, a live scheduler. Docker does not
|
|
# restart an unhealthy container (the process exiting does); the state is for monitoring.
|
|
healthcheck:
|
|
test:
|
|
- CMD
|
|
- python
|
|
- -c
|
|
- "import urllib.request as u; u.urlopen('http://127.0.0.1:8123/health/ready', timeout=4)"
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 60s
|
|
depends_on:
|
|
data-permissions:
|
|
condition: service_completed_successfully
|
|
|
|
caddy:
|
|
image: caddy:2-alpine@sha256:d8542f48d34a9cf4e4c11a478865229840e87e4c96ea3f439101f31a5d35f75f
|
|
restart: unless-stopped
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
environment:
|
|
- SITE_ADDRESS=${SITE_ADDRESS:-localhost}
|
|
volumes:
|
|
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
|
- caddy_data:/data
|
|
- caddy_config:/config
|
|
networks:
|
|
default:
|
|
ipv4_address: 172.28.0.10
|
|
depends_on:
|
|
- app
|
|
|
|
# A fixed subnet and a fixed address for Caddy, so uvicorn can trust exactly that one proxy (ARC-02).
|
|
networks:
|
|
default:
|
|
ipam:
|
|
config:
|
|
- subnet: 172.28.0.0/24
|
|
|
|
# The Fernet key that protects the master key lives in its own file, outside ./data (a backup of
|
|
# the data does not carry both) and in no environment variable (SEC-53).
|
|
secrets:
|
|
xprv_encryption_key:
|
|
file: ./secrets/xprv_encryption.key
|
|
|
|
volumes:
|
|
caddy_data:
|
|
caddy_config:
|