| # AmanPay — Hugging Face Space (Docker SDK). HF terminates HTTPS at *.hf.space, | |
| # so we serve plain HTTP on the Space port (7860). Weights auto-download from the | |
| # Hub on boot (amanpay/models/hf_backbone.py); WebAuthn rp_id/origin auto-derive | |
| # from the request host, so no domain is hardcoded. | |
| # --- Stage 1: build the React/TS frontend --- | |
| FROM node:20-slim AS web | |
| WORKDIR /web | |
| COPY web/package.json web/package-lock.json ./ | |
| RUN npm ci --no-audit --no-fund | |
| COPY web/ ./ | |
| RUN npm run build # -> /web/dist (hashed assets) | |
| # --- Stage 2: Python app --- | |
| FROM python:3.11-slim | |
| # Writable caches (Spaces run with a non-root user; keep everything under /app). | |
| ENV PYTHONUNBUFFERED=1 \ | |
| PIP_NO_CACHE_DIR=1 \ | |
| HOME=/app \ | |
| HF_HOME=/app/.cache/huggingface \ | |
| TORCH_HOME=/app/.cache/torch \ | |
| AMANPAY_HF_REPO=MHamdan/amanpay-encoders \ | |
| AMANPAY_HF_AUTO_DOWNLOAD=1 | |
| # --- Public demo activation (this image IS the public simulation Space) --- | |
| # Enables Programme D2 (accounts + passkeys) and D3 (simulated wallet) in SYNTHETIC mode with a | |
| # self-seeding demo dataset + one-tap passkey enrollment, so the Space works out-of-the-box with | |
| # no manual configuration. All money is simulated; no bucket and no real participant data (this | |
| # does NOT set AMANPAY_D2_PERSISTENCE_ENABLED). RP/origin default to the direct Space domain. | |
| # Override any of these in the Space "Settings -> Variables" (which take precedence at runtime), | |
| # e.g. set AMANPAY_DEMO_OPEN_ENROLL=0 to require invitation codes again. | |
| ENV AMANPAY_D2_ENABLED=1 \ | |
| AMANPAY_D3_ENABLED=1 \ | |
| AMANPAY_D3_SYNTHETIC_MODE=1 \ | |
| AMANPAY_DEMO_BOOTSTRAP=1 \ | |
| AMANPAY_DEMO_OPEN_ENROLL=1 \ | |
| AMANPAY_D2_STORAGE_DEV_UNKNOWN_FS=1 \ | |
| AMANPAY_D2_COOKIE_SAMESITE=none \ | |
| AMANPAY_D3_STEP_UP_MAX_AGE_SECONDS=86400 | |
| RUN apt-get update && apt-get install -y --no-install-recommends \ | |
| libgl1 libglib2.0-0 libsndfile1 \ | |
| && rm -rf /var/lib/apt/lists/* | |
| WORKDIR /app | |
| # CPU PyTorch wheels first (smaller image). | |
| RUN pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu | |
| COPY requirements.txt . | |
| RUN grep -vE '^(torch|torchvision|torchaudio)\b' requirements.txt > /tmp/reqs.txt \ | |
| && pip install -r /tmp/reqs.txt | |
| COPY amanpay/ ./amanpay/ | |
| COPY api/ ./api/ | |
| COPY configs/ ./configs/ | |
| COPY frontend/ ./frontend/ | |
| COPY examples/ ./examples/ | |
| # results/ is local-only (gitignored, absent from a fresh checkout) — deliberately | |
| # NOT copied. The report-card snapshot is optional; build_report_card() falls back | |
| # to None for any missing result file, so /report-card still works. | |
| COPY setup.py requirements.txt ./ | |
| RUN pip install -e . | |
| # The built React SPA (served by api/main.py at / with hashed assets under /assets). | |
| COPY --from=web /web/dist ./web/dist | |
| # Non-sensitive build metadata for GET /version (CI overwrites with the real commit). | |
| COPY build_info.json ./ | |
| # D1.1 opt-in runtime-diagnostic entrypoint (no-op unless AMANPAY_D1_RUNTIME_DIAGNOSTIC=1). | |
| COPY scripts/d1_entrypoint.sh scripts/d1_space_diagnostic.py scripts/d1_space_proof.py ./scripts/ | |
| # D2 operator bootstrap CLIs + in-Space synthetic proof (run in-Space; opt-in, dormant by default). | |
| COPY scripts/d2_initialize_tenant.py scripts/d2_issue_operator_invite.py \ | |
| scripts/d2_issue_customer_invites.py scripts/d2_list_accounts.py \ | |
| scripts/d2_revoke_invitation.py scripts/d2_space_proof.py ./scripts/ | |
| # D3 finance operator CLIs (initialize/seed/verify/control-totals/rebuild; opt-in, dormant). | |
| COPY scripts/d3_initialize_finance.py scripts/d3_seed_demo_finance.py \ | |
| scripts/d3_verify_ledger.py scripts/d3_list_control_totals.py \ | |
| scripts/d3_rebuild_balances.py ./scripts/ | |
| RUN chmod +x scripts/d1_entrypoint.sh | |
| # Writable dirs for weights + caches (owned so the Space's non-root user can write). | |
| RUN mkdir -p /app/checkpoints /app/.cache && chmod -R 777 /app/checkpoints /app/.cache | |
| EXPOSE 7860 | |
| # Routed through the D1.1 entrypoint wrapper: one-shot diagnostic ONLY when | |
| # AMANPAY_D1_RUNTIME_DIAGNOSTIC=1, then `exec`s uvicorn (signals + clean shutdown preserved). | |
| CMD ["sh", "-c", "exec scripts/d1_entrypoint.sh uvicorn api.main:app --host 0.0.0.0 --port ${PORT:-7860}"] | |