From b6d98e55ac6024657ccf175899d852c554e2e61b Mon Sep 17 00:00:00 2001 From: Yvonne Yao Date: Mon, 3 Aug 2026 14:29:24 +1000 Subject: [PATCH] docs: finalize deployment readiness evidence --- README.md | 16 ++++++- docs/architecture.md | 70 +++++++++++++++++++++++++++---- docs/implementation-backlog.md | 15 ++++--- docs/operations.md | 14 +++++-- docs/requirements-traceability.md | 2 +- infra/terraform/README.md | 13 ++++++ 6 files changed, 110 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 8d4b8d4..9030279 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,12 @@ Happy Post is a cloud-native application with a Next.js frontend and FastAPI backend. The delivery architecture uses two independently deployable container images and ECS Fargate services. +## Start here + +- [Architecture](docs/architecture.md): primary solution architecture, AWS runtime, delivery/control plane, Terraform ownership, and deployed-state summary. +- [Live sandbox application](https://happy-post.asksafe.ai): deployed Happy Post MVP. +- [Operations](docs/operations.md): smoke tests, CloudWatch logs, database migration, restart persistence, and recovery procedures. + ## Current status The MVP application source is present: a FastAPI daily-entry API and a warm Next.js bedtime page where each signed-in user records at least three small happy things, with optional extra happy items when they have more to save. Local containerisation runs frontend, backend, and local PostgreSQL together through Docker Compose. P4's Terraform network, private RDS PostgreSQL data, ECR/ECS platform, edge, and both digest-pinned ECS service roots are applied. Terraform, application, security, image-publication, initial-service bootstrap, post-bootstrap deployment, and rollback workflows are implemented. The deployed frontend and backend services have been updated through the controlled ECS deployment workflow and their public HTTPS smoke tests pass. @@ -19,9 +25,17 @@ The MVP application source is present: a FastAPI daily-entry API and a warm Next ## Deployed service verification +| Check | URL | +| --- | --- | +| Application | [https://happy-post.asksafe.ai](https://happy-post.asksafe.ai) | +| Frontend health check | [https://happy-post.asksafe.ai/healthz](https://happy-post.asksafe.ai/healthz) | +| Frontend version check | [https://happy-post.asksafe.ai/version](https://happy-post.asksafe.ai/version) | +| Backend health check | [https://happy-post.asksafe.ai/backend/healthz](https://happy-post.asksafe.ai/backend/healthz) | +| Backend version check | [https://happy-post.asksafe.ai/backend/version](https://happy-post.asksafe.ai/backend/version) | + The controlled ECS deployment workflow has successfully deployed both services. Backend smoke tests passed at `/backend/healthz` and `/backend/version`; the backend version response reports the deployed immutable image digest. Frontend smoke tests passed at `/healthz` and `/version`; the frontend version response currently reports the application version `0.1.0`. The rollback workflow is available for component-scoped recovery and should be rehearsed only if time permits before assessment submission. -Database-changing releases require one extra controlled step before the backend deployment. After publishing the backend image from `main`, run the manual database migration workflow with the same backend `image_digest` and `source_commit`, confirm `migrate-backend`, verify success, and only then run the backend ECS deployment workflow with that digest. After this workflow is merged, update the `happy-post-sandbox-bootstrap` CloudFormation stack before running it so the deployment role has the scoped `ecs:RunTask` permission for migration tasks. +Database-changing releases require one extra controlled step before the backend deployment. After publishing the backend image from `main`, run the manual database migration workflow with the same backend `image_digest` and `source_commit`, confirm `migrate-backend`, verify success, and only then run the backend ECS deployment workflow with that digest. The migration workflow and required bootstrap permissions are now in place; keep future database-changing releases on the same migration-before-backend-deploy sequence. ## Run locally with containers diff --git a/docs/architecture.md b/docs/architecture.md index 06f1cb5..21dd50b 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -5,18 +5,22 @@ Happy Post is a single-environment AWS sandbox with two independently deployable ## High-Level Solution Architecture ~~~mermaid -flowchart LR - users[Internet users] --> cloudflare[Cloudflare DNS
asksafe.ai] - cloudflare -->|NS delegation| route53[Route 53
happy-post.asksafe.ai] - route53 -->|A alias resolution| alb[Internet-facing ALB
HTTPS :443] +flowchart TB + users[Internet users] --> cloudflare[Cloudflare DNS
asksafe.ai parent zone] + cloudflare -->|delegates happy-post.asksafe.ai| route53[Route 53 public hosted zone] + route53 -->|A/AAAA alias resolution| alb[Internet-facing ALB
HTTPS :443] acm[ACM public certificate] -. "TLS certificate" .-> alb - alb -->|/*| frontend[Frontend ECS Fargate
Next.js] - alb -->|/api/*| backend[Backend ECS Fargate
FastAPI] - backend -->|PostgreSQL 5432| rds[Private RDS PostgreSQL] - frontend -. logs .-> logs[CloudWatch Logs] - backend -. logs .-> logs + alb -->|default /* and /frontend/*| frontend_tg[Frontend target group] + alb -->|higher-priority /api/* and /backend/*| backend_tg[Backend target group] + frontend_tg --> frontend[Frontend ECS Fargate service
Next.js] + backend_tg --> backend[Backend ECS Fargate service
FastAPI] + backend -->|PostgreSQL 5432 only| rds[Private RDS PostgreSQL] + frontend --> frontend_logs[CloudWatch Logs] + backend --> backend_logs[CloudWatch Logs] ~~~ +Source: [solution-architecture.mmd](diagrams/solution-architecture.mmd). + Cloudflare remains the parent DNS provider for asksafe.ai and delegates happy-post.asksafe.ai to Route 53. Route 53 resolves the application domain to the public ALB; ACM terminates HTTPS at the ALB. ## AWS Sandbox Network and Runtime @@ -87,6 +91,54 @@ The sandbox environment is an OIDC/configuration boundary, not an approval gate: ## Terraform and Deployment Ownership +~~~mermaid +flowchart TB + oidc[Existing GitHub OIDC provider] --> bootstrap[CloudFormation bootstrap] + bootstrap --> state[S3 Terraform state bucket
private and versioned] + bootstrap --> lock[DynamoDB Terraform state lock table] + bootstrap --> roles[Separate GitHub OIDC roles
sandbox plan; sandbox apply/destroy; sandbox ECS deploy] + + state --> root[Terraform root configuration] + lock --> root + roles --> root + root --> foundations[Foundations] + root --> workloads[Workload stacks] + + subgraph foundations + network[network
VPC, subnets, routes, IGW, NAT] + identity[security-identity
IAM, security groups, Secrets Manager] + platform[platform
ECR, ECS cluster, ALB] + scaling[ECS service scaling
CPU target tracking: 1–2 tasks, 65%] + edge[edge
Route 53 records and ACM] + end + + subgraph workloads + data[data
RDS PostgreSQL and backups] + backend[backend-service
task definition and ECS service] + frontend[frontend-service
task definition and ECS service] + observability[observability
CloudWatch log groups and alarms] + end + + network --> identity + identity --> platform + platform --> edge + platform --> scaling + network --> data + identity --> data + platform --> backend + scaling --> backend + edge --> backend + data --> backend + platform --> frontend + scaling --> frontend + edge --> frontend + edge --> observability + backend --> observability + frontend --> observability +~~~ + +Source: [terraform.mmd](diagrams/terraform.mmd). + Terraform owns foundations and stable configuration: networking, private RDS PostgreSQL and database subnets/security group, Secrets Manager database credentials, ECR, ECS cluster, runtime IAM roles, logging, ALB/listeners, Route 53 and ACM records, ECS services, and initial task definitions. The mandatory data stack has its own state and lifecycle. CloudFormation bootstrap owns the S3 state bucket, DynamoDB lock table, runtime permissions boundary, and GitHub OIDC roles. Terraform uses the S3 backend with DynamoDB as the only locking mechanism. The version-controlled bootstrap source is [`infra/bootstrap/happy-post-terraform-bootstrap.yaml`](../infra/bootstrap/happy-post-terraform-bootstrap.yaml). The state bucket is retained; the lock table has deletion protection and is retained on bootstrap stack deletion or replacement. diff --git a/docs/implementation-backlog.md b/docs/implementation-backlog.md index 0918599..4bcadc9 100644 --- a/docs/implementation-backlog.md +++ b/docs/implementation-backlog.md @@ -1,6 +1,6 @@ -# Implementation Backlog +# Implementation Backlog and Delivery Status -The documentation baseline and application MVPs are complete. The remaining work is deliberately ordered so that local containers precede infrastructure and automation. +The documentation baseline, application MVPs, AWS sandbox foundation, CI/CD controls, controlled ECS deployment, and P6 database-backed Happy Post MVP are complete. Remaining items are future hardening or optional operational rehearsals, not blockers for the current sandbox demonstration. ## P0 — Documentation baseline (complete) @@ -40,7 +40,6 @@ The documentation baseline and application MVPs are complete. The remaining work - [x] Define frontend and backend CPU target-tracking scaling: minimum one task, maximum two tasks, 65% CPU target. - [x] Apply the network HTTP redirect ingress, rotate the database secret to include `database_url`, and apply the edge root. - [x] Publish and scan frontend and backend images, then bootstrap each component service root with its real immutable digest. -- [ ] Perform the required isolated RDS restore test before the first database-changing release or migration. ## P5 — CI/CD and operational validation @@ -53,7 +52,6 @@ The documentation baseline and application MVPs are complete. The remaining work - [x] Implement post-bootstrap component-selected ECS deployment, health verification, and known-good rollback workflows. - [x] Restrict the single GitHub environment, `sandbox`, to deployment branch `main` while retaining no required reviewers; then validate plan, apply, publish, and deployment role separation. - [x] Execute controlled backend and frontend ECS deployments and public HTTPS smoke tests through the post-bootstrap deployment workflow. -- [ ] Rehearse component rollback only if time permits before assessment submission. ## P6 — Database-backed Three Happy Things MVP @@ -66,8 +64,13 @@ The documentation baseline and application MVPs are complete. The remaining work - [x] Publish new immutable frontend and backend images from merged P6 `main`. - [x] Merge the controlled database migration workflow, update the CloudFormation bootstrap stack, then run `alembic upgrade head` as a one-off private ECS task from the verified backend image digest. - [x] Deploy the P6 backend, deploy the P6 frontend, then verify entry persistence through RDS. -- [ ] Re-run the restart persistence demo: create an entry, restart backend, and confirm the happy things remain. -- [ ] For real users, replace MVP app-level auth with managed identity such as Amazon Cognito, Auth0, or Clerk. +- [x] Re-run the restart persistence demo: create an entry, restart backend, and confirm the happy things remain. + +## Future hardening and optional rehearsals + +- [ ] Perform an isolated RDS restore test before any further database-changing release or migration. +- [ ] Rehearse component rollback with a safe no-op or previous known-good revision when time permits. +- [ ] Replace sandbox MVP app-level auth with managed identity such as Amazon Cognito, Auth0, or Clerk before serving real users. ## Deferred options diff --git a/docs/operations.md b/docs/operations.md index 27de645..f695c15 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -28,6 +28,14 @@ The latest controlled ECS deployment verification passed: The rollback workflow is available for component-scoped recovery. Rehearse rollback only if time permits before assessment submission; otherwise keep it as a documented follow-up because the deployment and public smoke path is already validated. +## CloudWatch logging + +- The applied platform foundation created separate CloudWatch log groups for the two ECS workloads: `/ecs/happy-post/backend` and `/ecs/happy-post/frontend`. +- Both log groups use fourteen-day sandbox retention. +- Backend and frontend task definitions use the `awslogs` driver and publish only to their matching log group. +- The backend execution role can publish backend logs only; the frontend execution role can publish frontend logs only. +- The ECS cluster has Container Insights enabled for sandbox runtime visibility. + ## Database operations - RDS PostgreSQL remains private and is reachable only from the backend service. @@ -39,10 +47,10 @@ The rollback workflow is available for component-scoped recovery. Rehearse rollb - Deletion protection is disabled for sandbox. Intentional destroy creates a uniquely named final DB snapshot, does not retain automated backups, and requires the snapshot owner to review and delete it within seven days unless retention is explicitly approved. - The Three Happy Things MVP uses the backend `DATABASE_URL` to persist user-scoped daily entries in PostgreSQL. The frontend never receives database credentials. - MVP auth uses an HttpOnly SameSite=Lax cookie containing an opaque random token, while PostgreSQL stores only the token hash in `user_sessions`. Sign-out deletes the current database session. Expired sessions are rejected by the backend. There is no shared session-signing secret to rotate in the MVP design. -- Restore testing is required before a release and after every database-changing migration. Restore to an isolated temporary private RDS instance, verify availability, approved private connectivity, `SELECT 1`, migration version, and row-count sanity checks for `users`, `user_sessions`, `daily_entries`, and `daily_entry_items`, record evidence, then remove the temporary restore resources. -- A database change requires a backward-compatible migration plan and either a restore plan or a compensating migration before deployment. For the first P6 release, run the append-only Alembic chain through `0002_create_users_daily_entries`, which replaces the temporary `posts` table with `users`, `user_sessions`, `daily_entries`, and `daily_entry_items`, before deploying the backend image that requires it. +- Restore testing remains required operational hygiene for database-changing releases. Restore to an isolated temporary private RDS instance, verify availability, approved private connectivity, `SELECT 1`, migration version, and row-count sanity checks for `users`, `user_sessions`, `daily_entries`, and `daily_entry_items`, record evidence, then remove the temporary restore resources. The isolated restore rehearsal was not completed before the P6 migration because of time and cost constraints; it remains a follow-up and must be completed before any further database-changing release. +- A database change requires a backward-compatible migration plan and either a restore plan or a compensating migration before deployment. The P6 release ran the append-only Alembic chain through `0002_create_users_daily_entries`, which replaces the temporary `posts` table with `users`, `user_sessions`, `daily_entries`, and `daily_entry_items`, before deploying the backend image that requires it. - The controlled migration workflow derives its subnet and security-group placement from the active backend ECS service, so the migration task runs in the same private application network path as the backend. It uses the backend execution role for `DATABASE_URL` secret injection and must not print connection strings or secret values. -- After merging migration-workflow changes, update the `happy-post-sandbox-bootstrap` CloudFormation stack before the first migration run. The deployed ECS deploy role must include scoped `ecs:RunTask` permission for the `happy-post-sandbox-backend-migration` task-definition family. +- The `happy-post-sandbox-bootstrap` CloudFormation stack has been updated with the scoped ECS migration permissions required by the database migration workflow. The deployed ECS deploy role includes scoped `ecs:RunTask` permission for the `happy-post-sandbox-backend-migration` task-definition family. ## Terraform state-backend operations diff --git a/docs/requirements-traceability.md b/docs/requirements-traceability.md index c9cb919..fb7a7b8 100644 --- a/docs/requirements-traceability.md +++ b/docs/requirements-traceability.md @@ -19,4 +19,4 @@ | Least privilege | OIDC trust, separate operational roles, and narrow runtime permissions | [Security decisions](security-decisions.md) | | End-to-end TLS | Deferred optional enhancement, not baseline | [Architecture](architecture.md) | -This table records the approved design, completed application MVPs, local containerisation, applied Terraform network, RDS data, platform, edge, service roots, CI controls, and successful controlled ECS deployment smoke tests. The P6 workload implementation adds database-backed user accounts, opaque sessions, and daily entries; deploying it requires running the controlled migration workflow through `0002_create_users_daily_entries` before the backend image that depends on it. +This table records the approved design, completed application MVPs, local containerisation, applied Terraform network, RDS data, platform, edge, service roots, CI controls, successful controlled ECS deployment smoke tests, and the completed P6 database migration and restart persistence demo. The P6 workload implementation adds database-backed user accounts, opaque sessions, and daily entries; the deployed release ran the controlled migration workflow through `0002_create_users_daily_entries` before the backend image that depends on it. diff --git a/infra/terraform/README.md b/infra/terraform/README.md index 58725e6..5076f50 100644 --- a/infra/terraform/README.md +++ b/infra/terraform/README.md @@ -64,6 +64,19 @@ revision, and updates only the selected existing service. Terraform ignores the service task-definition field after initial creation so it does not undo that delivery revision. +## Why there is no modules directory yet + +This repository intentionally keeps each Terraform root explicit instead of +introducing a shared `modules/` directory too early. The current sandbox has one +environment and one instance of each major component, so root-level code is +easier to review for ownership, state boundaries, IAM scope, and assessment +traceability. + +A `modules/` directory should be introduced when a pattern is reused across +multiple environments, accounts, regions, or repeated services. At that point, +modules should preserve the same state boundaries and least-privilege ownership +model instead of hiding cross-stack dependencies. + ## State backend The network root uses this non-secret backend configuration: