The API: FastAPI on ECS Fargate
The complete SaaS API surface: job lifecycle, tier gating, auth, billing, webhooks. And two packaging landmines you won’t find in any docs.
Plan 2 delivered the entire backend surface. Python 3.12, FastAPI, SQLAlchemy 2 async, asyncpg, Alembic migrations, passlib+bcrypt for auth, slowapi for rate limiting.
Job lifecycle — the full path
POST /api/jobs → creates DB record, returns presigned S3 PUT URL
Browser uploads directly → S3
POST /api/jobs/{id}/confirm → validates upload exists, enqueues to SQS FIFO
Lambda job_dispatcher → SQS message → Batch submit_job
Batch reframer container → runs 9-module pipeline
docker-entrypoint.sh → POST /internal/jobs/{id}/start
→ runs pipeline
→ POST /internal/jobs/{id}/complete OR /fail
webhook_delivery Lambda → customer's configured endpointTier gating
Every tier-restricted operation returns { code: "tier_limit", feature, required_plan, upgrade_url }. This is not just a guard against UI bugs — the API is the enforcement point. The UI checks tier to avoid bad UX; the API checks tier to prevent bypasses.
The bcrypt/passlib landmine
bcrypt >= 4.0 changed how it handles passwords longer than 72 bytes — it raises a ValueError. Passlib internally calls detect_wrap_bug() which hashes a 74-byte test string. This call fails with bcrypt 4. The fix is pinning bcrypt>=3.2.0,<4.0 in requirements.
You will not find this in any deprecation notice. You find it when your auth service crashes after a routine pip install --upgrade.
The pydantic EmailStr import bomb
pydantic.EmailStr requires email-validator as a separate package. If it's missing, uvicorn crashes at startup before serving a single request. The error is an ImportError deep in pydantic internals — not in your code, not at the field definition, only at runtime.
This is one of those production incidents that feels embarrassing but is completely non-obvious from reading the pydantic docs. The fix is one line in requirements.txt: email-validator.