Sets up the project skeleton:
- FastAPI app factory with lifespan, request-id middleware, and RFC 9457
problem+json error handlers
- Async SQLAlchemy 2.0 over MySQL (asyncmy), with a constraint naming
convention in place before the first migration and async Alembic
- Keycloak as a pure resource server: OIDC discovery, cached JWKS with
rotation-aware refresh, and require_roles dependencies
- Devcontainer running MySQL 8.4 and Keycloak 26.7 as compose siblings,
with the realm (clients, roles, test users) imported on first boot
- Test suite covering the endpoints plus the token validator itself,
exercised against a locally generated RSA keypair
- uv packaging, ruff, mypy --strict, pre-commit, Gitea CI, prod Dockerfile
Two Keycloak-in-containers traps are handled explicitly and documented in
the README: the issuer/internal-URL split (the browser sees localhost:8080,
the API sees keycloak:8080) and the audience mapper that stops Keycloak
issuing tokens with aud=account.
The devices resource is a placeholder proving the routing -> auth -> ORM ->
migration path end to end; replace it with the real domain.
Co-Authored-By: Claude Opus 5 <[email protected]>