# infra/ Local development stack and provider-neutral production images. Root-owned dirs (`backend/`, `frontend/`) are owned by their respective workstreams; this directory owns only orchestration, images and proxy configuration. ``` infra/ ├── docker-compose.yml # dev stack: api, worker, frontend proxy, pg, redis, minio, mailpit ├── .env.example # copy to infra/.env; placeholders only, no real secrets ├── docker/ │ ├── Dockerfile.backend # provider-neutral API/worker image (context: backend/) │ └── Dockerfile.frontend # provider-neutral SPA+nginx image (context: frontend/) └── nginx/ └── nginx.conf # same-origin /api/v1 reverse proxy + SPA static ``` ## Quickstart ```sh cp infra/.env.example infra/.env # fill placeholders (no real secrets locally) docker compose -f infra/docker-compose.yml up -d --wait ``` | Service | URL / port | Notes | | --- | --- | --- | | Web app (nginx proxy) | http://127.0.0.1:8080 | serves SPA, proxies `/api/v1` → api:8000 | | API (direct) | http://127.0.0.1:8000 | FastAPI; `GET /healthz` | | Mailpit UI | http://127.0.0.1:8025 | test SMTP sink; SMTP on 127.0.0.1:1025 | | MinIO console | http://127.0.0.1:9001 | S3 API on 127.0.0.1:9000 | | PostgreSQL | 127.0.0.1:5432 | named volume `postgres_data` | | Redis | 127.0.0.1:6379 | AOF persistence, named volume `redis_data` | Until `backend/` and `frontend/` exist, start infra services only: ```sh docker compose -f infra/docker-compose.yml up -d --wait postgres redis minio mailpit ``` ## Contracts with backend/frontend workstreams These entry points are referenced by the compose file and CI; coordinate changes through both sides: **Backend** (`backend/`, build context of `Dockerfile.backend`): - uv-managed project with `pyproject.toml` + `uv.lock`; lint `ruff`, types `ty`, tests `pytest`. - API: `uvicorn app.main:app`, health endpoint `GET /healthz`. - Worker: Celery app importable as `app.worker.celery`; tasks idempotent, `acks_late`, `prefetch=1`. - Statement parsing happens **only** in the worker; the local compose worker is attached only to the internal backend network, and production must enforce the equivalent no-egress policy. **Frontend** (`frontend/`, build context of `Dockerfile.frontend`): - pnpm project with `pnpm-lock.yaml`; scripts: `lint`, `typecheck`, `test` (non-watch), `build` → `dist/`. - Calls the same-origin `/api/v1`; **no secrets in `VITE_*`** — none are required. ## CI `.github/workflows/ci.yml` runs backend lint/type/test/build and frontend lint/type/test/build once the workstreams provide the plan-required lockfiles (`backend/uv.lock`, `frontend/pnpm-lock.yaml`; auto-detected), and always validates the compose file, boots the infra services against their healthchecks, and `nginx -t`-checks the proxy config. ## Production Production runs the same images on a platform in the Russian Federation that supplies PostgreSQL, Redis, S3-compatible storage, SMTP and a secret manager. See `docs/adr/0004-provider-neutral-encrypted-data-boundary.md` and `docs/operations/beta-release-runbook.md`.