# 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}"]