-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile.api
More file actions
65 lines (57 loc) · 4.48 KB
/
Copy pathDockerfile.api
File metadata and controls
65 lines (57 loc) · 4.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# I-5 — production API container for the Veridex demo surface (veridex.api.server).
# Serves on 0.0.0.0 with CORS for the web origin, a generic /healthz, and a DURABLE, fail-closed
# Postgres backend (init_db invoked once + pooled connections). Secrets are NEVER baked in — pass
# DATABASE_URL / CORS_ORIGINS / PRIVY_* / OPERATOR_TOKEN as env at run time (COM-001).
# D-1 owns the full Coolify compose stack; this image is the container/CORS/healthz scaffold only.
FROM python:3.11-slim AS base
WORKDIR /app
# Install the package with the extras the serving import path needs:
# api — FastAPI + uvicorn + PyJWT[crypto] (the Privy ES256 auth boundary the app imports)
# postgres — psycopg[binary] + psycopg-pool (the durable pooled PostgresStore)
# live — httpx (the live TxLINE/venue async HTTP the router imports)
COPY pyproject.toml ./
COPY veridex ./veridex
COPY veridex_agent ./veridex_agent
RUN pip install --no-cache-dir ".[api,postgres,live,agent]"
# R-2 — bake the pinned, GENUINE seed pack (demo_pack_real: the verified WC /odds/updates backfill)
# into the image so the ReplayPack catalog is SELF-CONTAINED: a bare `docker run` (no compose
# bind-mount) still builds a hash-verified catalog and /readyz passes. COPY was chosen over relying on
# the compose read-only mount alone: the image is then trustworthy on its own (no missing-mount failure
# mode), and the packs are ~600KB — negligible bloat. NOTE: this COPYed layer is NOT a runtime read-only
# boundary for the bare image — the container runs as root, so the overlay is writable at this path. The
# ENFORCED read-only boundary is (a) the compose `:ro` seed-pack bind mount below and (b) the catalog
# module itself, which only READS the seed root and never writes it (`register_pack` publishes to the
# SEPARATE writable capture volume, never here). In compose, REPLAY_PACK_ROOT is overridden to the
# operator's provisioned :ro seed mount.
# NOTE: the catalog derives pack_id from the leaf dir name, so this seed catalogs as pack_id="demo_pack_real".
# That id is STABLE across both the image-bake path and the compose :ro mount (both leaf-named "demo_pack_real"),
# so R-3's pack-bound competition identity references the same id in either deployment, and the F1 seed's
# phase-1 assert_pack (which requires pack_id="demo_pack_real") resolves on the bare image and under compose.
COPY scripts/fixtures/demo_pack_real /opt/veridex/replay-packs/demo_pack_real
ENV REPLAY_PACK_ROOT=/opt/veridex/replay-packs/demo_pack_real
# F1 — bake the Official Replay League seed ENTRYPOINT so the running container can seed itself: a
# SEPARATE `docker run … python /opt/veridex/scripts/seed_official_replay_league.py --seed-revision <r>`
# process drives the D3 run_seed state machine over the SAME durable Postgres the API serves. Importing
# this module has NO side effects (all work is under the __main__ guard), so baking it is inert until run.
COPY scripts/seed_official_replay_league.py /opt/veridex/scripts/
# Quick-honest-enrichment: bake ONLY the two sealed maker cp1 files (NOT the rest of
# scripts/txline_live/) so GET /maker/arena-result resolves in-image. maker_router._REPO_ROOT =
# Path(__file__).resolve().parents[2]; `python -m veridex.api.server` runs from WORKDIR /app with
# /app/veridex on sys.path[0], so the module resolves to /app/veridex/... → _REPO_ROOT=/app and the
# sealed artifacts resolve under /app/scripts/txline_live/cp1/. Task 2 verifies this empirically.
COPY scripts/txline_live/cp1/maker-arena-result.json /app/scripts/txline_live/cp1/maker-arena-result.json
COPY scripts/txline_live/cp1/fixtures.json /app/scripts/txline_live/cp1/fixtures.json
# Network binding (overridable). The process is the isolation boundary, so bind all interfaces.
ENV HOST=0.0.0.0 \
PORT=8000
EXPOSE 8000
# WAL_DIR: the persistent, mounted spool directory the I-4 WAL writes to. It MUST be backed by a
# durable mounted volume (NOT the ephemeral container layer, which is lost on restart — AC-13). The
# WAL itself is I-4; this image only makes the path/volume ready. D-1's compose mounts a real volume
# here; the VOLUME declaration documents the contract and prevents accidental writes to the image layer.
ENV WAL_DIR=/data/wal
VOLUME ["/data/wal"]
# ASGI-factory form: the app is built from the environment INSIDE the process, so a misconfiguration
# (e.g. missing CORS_ORIGINS, or an unreachable DATABASE_URL) fails startup loudly (fail-closed) —
# never a silent InMemory fallback that would lose state on restart.
CMD ["python", "-m", "veridex.api.server"]