Summary
Terraform for the be/ + admin stack on AWS, two independent roots sharing one module.
-
infra/modules/app-stack/— VPC (no NAT), SGs, RDS PostgreSQL 16 (force_ssl, write-only password), ElastiCache Redis (TLS + AUTH, noeviction), S3 uploads + deploy bucket, ECR (immutable, scan), Secrets Manager placeholders, IAM (app role, CI user / optional GitLab OIDC), EC2 + EIP + fixed SSM deploy document, optional Route 53. -
infra/terraform-staging/— targets LocalStack on holapc (create_compute=false, app keeps running on holapc). -
infra/terraform-prod/— real AWS (Multi-AZ RDS, 2-node Redis, deletion protection, backups); hostnames/ACME email still TODO inprod.tfvars. -
infra/bootstrap-prod/— prod state bucket (S3 native lockfile). -
infra/runtime/—deploy.sh(lock → secrets → pull → cert → snapshot → migration → up → health, automatic runtime/image rollback), compose files, nginx template. - Also removes 16 stale lending docs (
chore(docs)commit).
Staging is applied and live: LocalStack on holapc (36 resources), images 62610d98 served at ahad.holadev.xyz / admin-ahad.holadev.xyz via deploy.sh staging. Prod is not applied. Post-apply fixes: Redis ignore_changes = [port] (LocalStack assigns its own port), staging runtime.env uses LocalStack's real ECR endpoint plus explicit DOCKER_HOST/DOCKER_CONFIG for holapc.
Verification
-
terraform fmt -check,validateon all 3 roots - tflint, trivy (no HIGH/CRITICAL), shellcheck clean
- Read-only
planagainst LocalStack: staging 36 to add, prod 55 to add; validations reject missing Redis token / prod hostnames; no secret values in plan JSON - Codex adversarial gate: 3 rounds; 4/5 round-1 findings fixed, 2 findings accepted as documented risks (IMDS hop limit 2 exposes the instance role to containers; rollback reuses the failed release's env/secrets)
Known gaps
- LocalStack Redis enforces neither TLS nor AUTH; staging cannot exercise either.
- S3 uploads on staging fail until the be/ S3 endpoint change lands.
- Pairs with !74 (merged) (drops the old holapc deploy jobs); CI deploy to LocalStack (S11) is still to do.
Follow-ups (separate MRs)
- be/: Redis TLS, S3 endpoint/path-style, stable file URL + authenticated presign, default AWS credential chain
- fe/: send Authorization on image loads (ship with the be/ presign change)