You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Security: add auth to all container routes, production env validation
(fails startup if secrets missing), tighten CORS methods/headers,
catch-all exception handler, fix OAuth redirect to use APP_BASE_URL.
Infrastructure: complete production docker-compose with docker socket,
skills mount, health checks, resource limits, required POSTGRES_PASSWORD.
Non-root API container, multi-worker uvicorn, nginx security headers.
Resilience: graceful shutdown stops running containers, boot-time
reconciliation marks stale sessions as FAILED, configurable container
TTL via CONTAINER_TTL_SECONDS setting.
Observability: health check verifies DB connectivity (503 if degraded),
complete .env.example with all production variables documented.
Copy file name to clipboardExpand all lines: CLAUDE.md
+9-5Lines changed: 9 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -56,6 +56,9 @@ infra/
56
56
-**Admin panel**: SQLAdmin at `/admin`, configured in `admin/views.py`
57
57
-**Dashboard**: user-facing installation management at `/installations` -- installation list, session history, session replay. Authenticated users redirect from `/` to `/installations`. SQLAdmin remains at `/admin` as superadmin escape hatch.
58
58
-**Cross-module queries**: installation module queries `ContainerSession` model directly (inline import in service functions) for session counts and lists. This avoids circular imports while keeping the API surface on the installation router.
59
+
-**Auth on all REST routes**: identity and installation routers use `Depends(get_current_user)`, container router uses it too. The webhook handler bypasses REST routes entirely — it calls `create_session()` directly (DB record only, no container start). Container start happens when the authenticated frontend calls the REST endpoint.
60
+
-**Production env validation**: `Settings` has a `model_validator` that enforces non-empty secrets when `ENVIRONMENT=production`. Tests use `ENVIRONMENT=test` to skip this.
61
+
-**Graceful lifecycle**: lifespan reconciles stale RUNNING/PENDING sessions on boot (marks FAILED), and stops all running containers on shutdown. Periodic cleanup uses configurable `CONTAINER_TTL_SECONDS` from settings.
59
62
60
63
## Skills
61
64
@@ -88,11 +91,8 @@ cd apps/api && uv run alembic revision --autogenerate -m "description" # New mi
88
91
89
92
## Environment
90
93
91
-
Required `.env` at repo root (see docker-compose.yml):
-`FERNET_KEY` — encryption key for stored credentials
94
+
Required `.env` at repo root — see `.env.example` for all variables with generation instructions.
95
+
Key additions for production: `ENVIRONMENT=production`, `ADMIN_PASSWORD`, `CORS_ORIGINS`, `CONTAINER_TTL_SECONDS`, `UVICORN_WORKERS`.
96
96
97
97
## Gotchas
98
98
@@ -113,6 +113,8 @@ Required `.env` at repo root (see docker-compose.yml):
113
113
-**Shallow clone + `gh pr checkout --detach`**: `gh pr checkout` (without `--detach`) fails on `--depth=1` clones because git can't set up tracking branches from shallow refs. Always use `--detach` — containers don't need tracking branches, just files on disk.
114
114
-**Installation IDs in URLs**: frontend routes (`/installations/:id`) use `github_installation_id` (integer, e.g. `123093268`), NOT the internal UUID. The API installation endpoints also expect the GitHub integer ID.
115
115
-**Fast-failing container race**: if a container exits before the SSE stream fully drains, the client may disconnect before `mark_completed()` runs, leaving the session stuck as RUNNING with 0 persisted events. The 5-minute cleanup task marks these as TIMEOUT. Root cause: generator cancellation on client disconnect skips the post-stream `mark_completed` call in `_event_stream()`.
116
+
-**Flaky dispatcher tests**: `test_issues_opened_is_ignored_and_logged` and `test_pull_request_closed_is_ignored` fail when run as part of the full suite due to structlog `configure_logging()` state contamination from `create_app()` in earlier tests. They pass in isolation.
117
+
-**Multi-worker background tasks**: with `--workers N`, each uvicorn worker runs its own lifespan (webhook reaper + container cleanup). Both are idempotent: reaper uses atomic row-level claim (`mark_processing`), cleanup suppresses double-stop exceptions.
116
118
117
119
## Key Decisions
118
120
@@ -122,3 +124,5 @@ Required `.env` at repo root (see docker-compose.yml):
122
124
-**Dashboard over SQLAdmin**: user-facing operations (installation list, session history, token config) go through the dashboard UI; SQLAdmin is the superadmin escape hatch
123
125
-**Open source target**: designed for self-hosting with own Claude licenses
124
126
-**Post-results to PR**: after session completion, the API can post score card as a PR comment — opt-in per installation via `post_results_to_pr` boolean; extraction and formatting in `container/pr_comment.py`, triggered in `_event_stream()` after `mark_completed()`
127
+
-**Coolify deployment**: TLS terminates at the Coolify reverse proxy, not in nginx. Nginx serves the SPA with security headers but no HTTPS config. SSE `X-Accel-Buffering: no` header is set by the API, not nginx.
128
+
-**Non-root API container**: production Dockerfile uses `appuser`. Port 8000 > 1024 so no privilege needed. Docker socket mount still grants Docker access regardless of USER.
0 commit comments