docs: add comprehensive AWS ECS Fargate deployment guide

⛔ closed · #153 · Open-Legal-Products/mike ← amaingot/mike-aws · opened 2mo ago by amaingot · closed 2mo ago · +49,115-50,873 across 188 files · ↗ on GitHub

From the PR description

Summary

  • Adds docs/aws-deployment.md - a full end-to-end production deployment guide for Mike on AWS
  • Covers every AWS service required: VPC/networking, Cognito+SES, RDS Postgres, S3, Bedrock, ACM, Route 53, Secrets Manager, IAM, ECS Fargate, and ALB
  • Includes app-specific gotchas not obvious from the existing README (SSE streaming idle-timeout, TRUST_PROXY_HOPS=2 behind ALB, NEXT_PUBLIC_* build-time baking requirement, unnecessary SES task-role grant)

What's included

  • Architecture diagram and per-service resource sizing rationale
  • Step-by-step AWS CLI commands for every resource (VPC, subnets, SGs, Cognito User Pool + app client, RDS with force-TLS, S3 with CORS + versioning, Bedrock model access, Secrets Manager, scoped IAM task/execution roles, ECS cluster + log groups, ALBs with correct idle timeouts, ECS task definitions and services, autoscaling)
  • Database migration pattern using ecs run-task with a command override
  • Day-2 operations: image deploys, secret rotation, scaling, backups
  • Observability: CloudWatch alarms, Container Insights, cost estimate
  • Security checklist (pre-launch + post-launch)
  • Troubleshooting section for the most common failure modes
  • Appendix A: optional improvements (ECR mirror, WAFv2, blue/green, Multi-AZ)
  • Appendix B: private GHCR image credential wiring

Test plan

  • Follow §16 Smoke Test end-to-end on a fresh AWS account
  • Verify all AWS CLI commands parse without error (--dry-run where supported)
  • Confirm Bedrock model ARNs match current model ids in backend/src/lib/llm/claude.ts

🤖 Generated with Claude Code

Our analysis

Re-platform from Cloudflare/Supabase to AWS — read the full analysis →

Frontend lint cleanups surfaced by new CI — read the full analysis →

Think the analysis missed something the PR description covers?

Commits in this PR (2)

SHA Subject Author Date
2dc27676 Re-platform from Cloudflare/Supabase to AWS Alex Maingot 2026-05-14 ↗ GitHub
commit body
Auth: Supabase Auth → AWS Cognito. Frontend uses amazon-cognito-identity-js
behind a supabase-shaped wrapper so existing call sites only changed import
paths. Backend uses aws-jwt-verify (CognitoJwtVerifier for real AWS,
JwtRsaVerifier with a custom HTTP fetcher for cognito-local). Signup page
now handles Cognito's email-confirmation step.

DB: Supabase PostgREST → RDS Postgres via Drizzle ORM. ~191 query sites
across 14 backend files (~8000 lines) rewritten. Drizzle schema mirrors
the original 17 tables minus the auth.uid()-based RLS helpers - access is
already enforced in lib/access.ts via service-role queries (defense in
depth note in the original schema.sql:1052). Initial migration committed
under backend/drizzle/. A new public.users table mirrors Cognito identities
(replaces the Postgres on-signup trigger).

Storage: R2 → S3 envs (S3_BUCKET_NAME, S3_ENDPOINT_URL, AWS_*). Endpoint
and forcePathStyle are conditional - set for MinIO locally, unset for real
AWS so the SDK uses the ECS task-role credential chain.

LLM: Anthropic SDK → @aws-sdk/client-bedrock-runtime for Claude only.
OpenAI and Gemini still call their providers directly. Per-user Claude
keys are dropped (Bedrock uses IAM); a migration purges existing rows and
narrows the user_api_keys.provider check constraint.

Account deletion: routes/user.ts now calls AdminDeleteUserCommand on the
Cognito User Pool plus a cascading delete on public.users.

Frontend runtime: Cloudflare Workers / OpenNext → Next.js standalone in
Docker (output: "standalone" + `node server.js`). Removes @opennextjs/
cloudflare, wrangler, @openrouter/sdk, @supabase/*.

Backend runtime: nixpacks → node:20-bookworm-slim with libreoffice +
fontconfig baked in for DOC/DOCX → PDF conversion.

Infra artifacts:
- frontend/Dockerfile, backend/Dockerfile (multi-stage, healthchecked)
- docker-compose.yml: postgres, cognito-local (ghcr.io/amaingot/cognito-local),
  MinIO + minio-init for bucket bootstrap, smtp4dev, backend, frontend
- scripts/bootstrap-local.sh: pre-seeds cognito-local's clients.json so the
  fork's auto-generated client id doesn't clash with the env-pinned id,
  then runs Drizzle migrations
- .github/workflows/ci.yml: PR build + lint + db:migrate against a postgres
  service
- .github/workflows/build-and-publish.yml: multi-arch (amd64/arm64) push to
  ghcr.io/<owner>/mike-{frontend,backend} on main and v* tags

End-to-end verified locally:
- docker compose up brings postgres/auth/minio/smtp to healthy
- bootstrap-local.sh provisions pool + client + bucket + schema
- Cognito signup → confirm → InitiateAuth → backend JWKS verify → users
  row auto-created (with deterministic UUID derivation for cognito-local's
  non-UUID subs)
- POST /projects, GET /projects, POST /single-documents all return 200
  with the expected DB rows and MinIO object

Operator notes captured in README.md: required AWS resources (Cognito
pool, RDS, S3, SES, Bedrock model access, ECS task role permissions),
ghcr.io → ECR re-tag flow, Bedrock model-ID verification step before
first deploy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
e7593aa7 Fix frontend lint errors surfaced by new CI Alex Maingot 2026-05-14 ↗ GitHub
commit body
The Re-platform commit introduced .github/workflows/ci.yml which runs
`npm run lint` for the first time on this codebase. That surfaced 38
pre-existing errors in code inherited from upstream willchen96/mike,
which never ran ESLint in CI.

Breakdown of fixes:

- 16 react-hooks/set-state-in-effect (new in eslint-plugin-react-hooks 7
  / Next 16): converted to the React "adjusting state during render"
  pattern, useSyncExternalStore for localStorage-backed state, or
  deferred initial measurements via requestAnimationFrame/setTimeout.
- 2 react-hooks/refs in ChatView: dropped a no-op .current entry from a
  dep array that was already covered by an eslint-disable comment.
- 1 react-hooks/immutability in DocView: hoisted scrollToHighlightOnPage
  above its first useCallback caller.
- 1 react-hooks/static-components in WFColumnViewModal: switched the
  dynamic icon to React.createElement so we don't bind a component to a
  PascalCase local during render.
- 11 @typescript-eslint/no-explicit-any: removed redundant casts
  (MikeMessage already typed files/error/workflow), typed the
  caret-from-point document shape, used unknown + narrowing in catch.
- 5 react/no-unescaped-entities: escaped apostrophes/quotes in JSX text.
- 2 @typescript-eslint/no-require-imports: ignored src/scripts/** in
  eslint config - the one file there is a CJS build-time conversion
  script, not part of the app bundle.

next build still succeeds. Zero behavior changes intended - the
React Compiler-aware rules were all addressed by restructuring effects
into pure-render derivations rather than disabling them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Capture this PR into my fork

Download a Markdown prompt that tells Claude how to port every commit in this PR into your working tree. Run it via claude -p < capture-pull-153.md from inside the repo you want the changes in.

⬇ Download capture-pull-153.md