# CI-only docker compose override — the sandbox E2E backend. # # Merged by .github/workflows/e2e-sandbox.yml: # docker compose -f docker-compose.yml -f docker-compose.ci.yml \ # --env-file .env.production up -d --wait postgres go-api # # Do NOT use this on the prod VPS. docker-compose.yml is canonical; this # file only ever loosens what prod deliberately keeps tight. # # SCOPE: postgres + go-api ONLY. next-app runs on the host under # `next dev` (see the workflow for why), nginx and ws-server never start. # This file used to carry overrides for `app` and `mongodb` — services # that were retired from docker-compose.yml at the P9 cutover. Nothing # noticed for months because no workflow ever merged the two files, and # `docker compose config` had been failing outright with # `service "app" has neither an image nor a build context specified`. # So: no override here for a service CI does not start. An override # nobody runs is an override nobody maintains. services: postgres: # The sandbox fixtures run on the HOST, outside the compose network. # e2e/fixtures/pg.ts seeds users / anime_cache / subscriptions over # localhost:5432, and the base file publishes no port at all. ports: - "5432:5432" go-api: # `next dev` rewrites /api/:path* to http://localhost:8080 # (next-app/next.config.ts:25 — dev only; under NODE_ENV=production # it returns [] because nginx owns that hop). That rewrite runs in # the Next server process on the host, so :8080 has to be published. ports: - "8080:8080" environment: # storageState is minted once in globalSetup and reused by every # sandbox worker. The prod default (15m) can expire mid-suite and # produce "logged out" failures that look like auth regressions. JWT_EXPIRES_IN: 2h # Auth limiter is 10 attempts / 15 min per IP by default # (cmd/server/main.go:335). auth.spec.ts alone does register + # logout + login + two more logins, and retries replay all of it. AUTH_RATELIMIT_MAX: "1000" # 0 disables the global /api/* limiter (cmd/server/main.go:618). # This is the 2026-06-05 detail-page incident in miniature: go-api # meters per IP, but behind SSR every read arrives from ONE client # address, so the bucket is shared by all traffic rather than by # user. In prod that collapsed /anime/{id} into 429 → 500 and the # fix was to move rate limiting to the Cloudflare edge. Here the # single address is the runner, shared by three parallel Playwright # workers plus every RSC fetch they trigger — leave the limiter on # and tests fail for a reason unrelated to the code under test. API_RATELIMIT_BURST: "0" # .env.production carries real Gmail credentials on the VPS. CI # generates a throwaway env file with these unset, but pin them # empty so a locally-run sandbox suite can never send real mail to # the `@animego.test` fixture addresses either. Empty creds select # email.NoopSender (cmd/server/main.go:321); ForgotPassword still # writes the reset token to Postgres before it attempts the send # (auth/handlers.go:642), which is the only part the spec reads. GMAIL_USER: "" GMAIL_APP_PASSWORD: "" # The image warm job sends every AniList image the database references # to nginx's internal warm location. nginx never starts here, so there # is nothing to warm and every pass would only fail to connect. Empty # turns the job off. IMAGE_WARM_BASE_URL: "" # The variant warm job asks the same nginx's internal image location, # for the same reason off here. IMAGE_VARIANT_WARM_BASE_URL: ""