The Trinity Beast Infrastructure — CloudFormation Recovery Guide

Complete Infrastructure-as-Code Template — One Command to Recreate Everything
Corrected August 13, 2026 Region: us-east-2 (Ohio) 182 Resources 13 Parameters

Region scope: This template covers us-east-2 only. CloudFormation stacks are region-scoped, and a handful of resources are forced into us-east-1 by AWS service availability — SES inbound email receiving, the CloudFront WAF, and CloudFront's own metrics all only exist there. See Section 8 for the full list and the companion template name.

Table of Contents

List of Diagrams

1. What Is This?

The short version: This is your insurance policy. If every single piece of the us-east-2 footprint of The Trinity Beast Infrastructure were deleted tomorrow — every server, every database, every network — this one YAML file recreates all of it with a single command.

This is a disaster-recovery reference, not the live stack. The account's actual running infrastructure is hand-built — it was never deployed from this template, and it was never adopted into it. Only one CloudFormation stack genuinely exists in the account today (trinity-beast-ultimate-v3), and it owns only the base VPC/security-group layer, not the application layer. Running create-stack from this template builds a fresh, equivalent environment in parallel — it does not touch, adopt, or modify anything already running. Corrected 2026-08-13 after this file and its underlying template were found to describe infrastructure that no longer matches reality in several places (wrong cache engine, missing Lambdas, two deleted Lambdas still present, an entire feature's infrastructure missing) — see the correction notes throughout this document.

CloudFormation in Plain English

Think of CloudFormation as a blueprint for a building. When a contractor builds a house, they don't wing it — they follow a blueprint that describes every wall, every pipe, every electrical outlet. CloudFormation is the same idea, but for cloud infrastructure.

You write a YAML file that describes what you want: "I need a network, a database, three servers, a load balancer..." and AWS reads that file and builds everything for you. Every time. Exactly the same way. No clicking through consoles, no forgetting a step, no "it worked on my machine."

How It Works Infrastructure as Code
  1. You describe what you want in a YAML file (the "template")
  2. You run one commandaws cloudformation create-stack
  3. AWS builds everything — in the right order, with the right connections
  4. AWS tracks it all as a "stack" — you can update, monitor, or delete everything as a unit

The Trinity Beast template is 182 resources defined in a single file, covering everything in us-east-2 — networking, databases, servers, load balancers, DNS, CDN, monitoring, AI operations, and document translation. One file. One command. Everything except the us-east-1 footprint (Section 9), which lives in its own template because CloudFormation stacks are region-scoped.

Why this matters: Without this template, recreating The Trinity Beast would mean manually clicking through dozens of AWS console screens, remembering exact settings, and hoping you don't miss anything. With this template, it's one command and a 30-minute wait. That's the difference between a weekend of panic and a coffee break.

2. What's In the Template

The template defines 182 AWS resources organized into 11 layers. Each layer builds on the ones before it — you can't have servers without a network, and you can't have a load balancer without servers. CloudFormation handles the ordering automatically.

Total Resources
182
Infrastructure Layers
11
Parameters
13
Deploy Time
~30-45 min

Layer 1: Networking

VPC & Network Infrastructure ~30 Resources

The foundation. Think of the VPC as the building itself — it defines the walls and rooms. Subnets are the rooms. The Internet Gateway is the front door. Security groups are the locks on each room's door.

ResourceDetailsPurpose
VPC10.0.0.0/16 (65,536 IPs)The private network — your building
Public Subnets3 subnets across 3 AZsRooms with windows — internet-facing (load balancers live here)
Private Subnets3 subnets across 3 AZsInterior rooms — no direct internet access (databases, servers live here)
Internet Gateway1 IGW attached to VPCThe front door to the internet
Route Tables4 route tablesHallway signs — tell traffic where to go
Security Groups7 security groupsDoor locks — control who can talk to what
VPC Endpoints10 endpointsPrivate back doors to AWS services (no internet needed)
Flow LogsVPC Flow Logs → CloudWatchSecurity cameras — record all network traffic

Layer 2: IAM (Identity & Access Management)

Roles & Permissions 9 Roles

IAM roles are like employee badges — they define what each service is allowed to do. A server can read from the database but can't delete the network. Each role follows the principle of least privilege: only the permissions needed, nothing more.

RolePurpose
ECS Task Execution RoleLets ECS pull Docker images from ECR and write logs to CloudWatch
ECS Task RoleLets the running containers access Secrets Manager, S3, SES, SQS, and AWS Translate
EventBridge RoleLets the scheduler (EventBridge) launch ECS tasks for the nightly sync job
Flow Logs RoleLets VPC Flow Logs write to CloudWatch Logs
Receipt Lambda RoleLets the receipt Lambda read trinity-beast-receipt-secrets (Stripe keys only — isolated from the shared DB secret), send SES, and enqueue to the email queue
trinity-beast-email-sender-roleDedicated role for the "brainless" email-sender Lambda — SQS consume + SES send only, no DB, no shared secret
tbi-autonomous-ops-roleShared execution role for all 8 AutoOps Lambdas (ECS, WAF, CloudWatch, Bedrock, SES, SNS, SQS, Secrets Manager)
tbi-translate-roleTranslation worker ECS task role only (Bedrock, S3, CloudFront, SQS, SNS) — no Lambdas share it since the deploy/finalize Lambdas were removed 2026-06-27

Corrected 2026-08-13. tbi-translate-pipe-role and tbi-translate-orchestrator-role no longer exist — both were deleted along with the EventBridge Pipe and Step Function they served when the translation architecture was simplified to direct SQS fan-out (2026-06-27). Added the email-sender role, which was missing entirely.

Layer 3: Data Layer

Databases, Caches & Storage ~15 Resources

Where your data lives. Aurora is the main database (think: filing cabinets). ElastiCache is the speed cache (think: sticky notes on your desk for things you need instantly). ECR stores your Docker images. S3 stores your website files.

ResourceDetailsPurpose
Aurora Serverless v2PostgreSQL 17.7, 1.5–18 ACU, Optimized I/OMain database — writer + reader instances
ElastiCache Serverless for ValkeyValkey 9.0, auto-scaling storage (0–30 GB) and compute (0–10,000 ECPU/s), multi-AZ, TLS always-onIn-memory cache — sub-millisecond reads, no node to size or manage
ECR Repositories5 repositoriesDocker image storage — trinity-beast-lpo-server, trinity-beast-sync-job, tbi-translate-worker, plus 2 legacy repos (trinity-beast, trinity-beast-lpo) kept for template parity with the account's actual inventory
S3 Buckettrinity-beast-website-east2Website files, daily/cost reports, translated docs
Secrets Managertrinity-beast-secrets + trinity-beast-receipt-secretsDB creds/Stripe/API keys, and an isolated Stripe-keys-only secret for the receipt Lambda (no DB access, isolated 2026-07-24)

Corrected 2026-08-13. The bucket name was wrong throughout this document — it's trinity-beast-website-east2, not trinity-beast-website-east2 (which does not exist). The tier_catalog table row was removed from this list — it's real and important, but it's created by application migrations against Aurora, not by this CloudFormation template, which only provisions the empty database engine.

Layer 4: Compute

ECS Fargate & Lambda ~20 Resources

The workers. ECS Fargate runs your containers without you managing servers. Think of it as hiring workers and only paying for the hours they work — no building maintenance. Lambda runs small, one-off tasks (like processing a receipt after a purchase).

ResourceDetailsPurpose
ECS Clustertrinity-beast-fargate-clusterThe factory floor — organizes all services
Main Servicetrinity-beast-main-service — 2 vCPU / 6 GBLPO + LRS (SERVER_TYPE: APP_REPORT_SERVER)
Mirror Servicetrinity-beast-mirror-service — 2 vCPU / 6 GBLPO + LRS (SERVER_TYPE: APP_REPORT_SERVER)
LRS Servicetrinity-beast-lrs-service — 2 vCPU / 6 GBLPO + LRS (SERVER_TYPE: APP_REPORT_SERVER)
Webhook Servicetrinity-beast-webhook-service — 2 vCPU / 6 GBWebhook Push delivery engine + Webhook Push Test Receiver (SERVER_TYPE: WEBHOOK_SERVER) — see Layer 5 for the test-receiver's inbound ports
Translate Worker Servicetbi-translate-worker-service — 2 vCPU / 6 GB, persistentBedrock translation worker — polls SQS directly, no orchestrator
Sync Job Task Deftrinity-beast-sync-job — 0.5 vCPU / 1 GBNightly database sync task definition (BeastReconciler)
Lambda Functiontrinity-beast-receipt — Go, 1770 MB, 180sPost-checkout receipt processing; enqueues to the email queue (falls back to direct SES if enqueue fails)
SQS Queuetrinity-beast-queued-usage-logs — StandardDecoupled usage log write pipeline
Lambda Functiontrinity-beast-queued-writer — Go, 256 MB, in VPCSQS consumer — batch-inserts usage logs into Aurora
SQS Queuetrinity-beast-email-queueDecouples email delivery from the receipt Lambda's response path
Lambda Functiontrinity-beast-email-sender — Go, 1770 MB, 60s"Brainless" SQS consumer — receives pre-built emails, sends via SES. No DB, no Valkey, no Bedrock.

Corrected 2026-08-13. The Webhook service was sized 1 vCPU / 2 GB in this document — it's actually 2 vCPU / 6 GB, standardized to match every other ECS service on 2026-08-03. The translate worker's persistent ECS service and the email queue/sender pair were entirely missing.

Layer 5: Load Balancing

ALB & NLB ~12 Resources

Load balancers are traffic cops. They stand at the front door and direct incoming requests to the right server. The ALB handles web traffic (HTTP/HTTPS). The NLB handles UDP traffic (real-time price feeds).

ResourceListenersPurpose
ALB (Trinity-Beast-TCP-ALB)Port 80, 443, 8080, 9090 + host-header rules on 443 for lrs. and webhook.Web traffic — API, LRS reports, HTTPS redirect, webhook test receiver
NLB (Trinity-Beast-UDP-NLB)UDP 2679, 2680, 2681Real-time UDP price/report feeds + webhook test receiver
Target Groups6 target groupsRoute traffic to the right ECS service

Listener breakdown: Port 80 redirects to 443 (HTTPS). Port 443 serves the API (default action) and carries two additional host-header rules — priority 10 for lrs.cpmp-site.org, priority 20 for webhook.cpmp-site.org. Port 8080 routes to the main service. Port 9090 routes to LRS services. Ports 8081, 9091, and 8092 serve dedicated health checks (isolated from production traffic). UDP 2679 and 2680 deliver real-time price/report data.

Webhook Push Test Receiver Added 2026-08-07

The Webhook service (BeastWebhook) is not purely outbound anymore. Alongside its outbound delivery engine (UDP datagrams + signed HTTPS POSTs to subscribers), it also runs a permanent, health-checked, in-process test receiver — built specifically to test the outbound engine end-to-end against a real network endpoint, both transports, without standing up a throwaway receiver each time. It was briefly a standalone Lambda for a few hours the same day it was built, then migrated in-process specifically to gain a persistent UDP socket, which a Lambda Function URL cannot provide.

TransportReachable atPath
HTTPShttps://webhook.cpmp-site.org/ALB host-header rule (priority 20) → trinity-beast-webhook-test-tg (container port 8090)
UDPudp.cpmp-site.org:2681New NLB listener → Trinity-Beast-UDP-2681-TG (container port 2681)
HealthDedicated health server, container port 8092 — gates both target groups above

Gated behind WEBHOOK_TEST_RECEIVER_ENABLED=true — off by default, structurally. The HTTPS side uses an additional SNI certificate on the existing 443 listener (ACMCertificateArnWebhook) — the default certificate serving api/lrs/www is never touched.

Corrected 2026-08-13. The webhook test receiver's entire infrastructure — 2 target groups, 1 ALB listener rule, 1 NLB listener, 1 additive ACM cert, the DNS record for webhook.cpmp-site.org, and 3 new security-group rules — was missing from the template entirely. It has been live since 2026-08-07.

Layer 6: DNS (Route 53)

Domain Names & Records 10+ Records

DNS is the phone book of the internet. When someone types cpmp-site.org, DNS tells their browser which server to talk to. Route 53 manages all of this.

RecordPoints ToPurpose
cpmp-site.orgCloudFrontMain website
www.cpmp-site.orgCloudFrontWWW alias for website
api.cpmp-site.orgALBREST API endpoint
lrs.cpmp-site.orgALBLRS report server
udp.cpmp-site.orgNLBUDP price/report feed + webhook test receiver (port 2681)
webhook.cpmp-site.orgALBWebhook Push test receiver (HTTPS) — added 2026-08-13, was missing
MX recordSES inboundEmail receiving
SPF recordTXT recordEmail authentication — "yes, we're allowed to send email"
DMARC recordTXT recordEmail policy — tells receivers how to handle our email
SES verificationTXT/CNAME recordsProves we own the domain for sending email

Layer 7: CDN (CloudFront)

Content Delivery Network 1 Distribution

CloudFront is like having copies of your website in cities around the world. Instead of everyone connecting to Ohio, visitors get served from the nearest location. Faster for them, less load on your servers.

SettingValue
OriginS3 bucket (trinity-beast-website-east2)
HTTP → HTTPSAutomatic redirect
TLS VersionTLS 1.2 minimum
CertificateACM certificate in us-east-1 (CloudFront requirement)

Layer 8: Monitoring & Alerts

CloudWatch Alarms & SNS ~25 Alarms in this Template

Monitoring is your early warning system. CloudWatch watches metrics (CPU usage, error rates, database health) and SNS sends you a text or email when something goes wrong. You find out about problems before your users do.

CategoryAlarmsWhat They Watch
ECS CPUCPU alarms per serviceContainer CPU usage — alerts if consistently high
ECS Service CountRunning task count (main/mirror/lrs/webhook/translate-worker)Alerts if a service has zero running tasks (it's down)
AuroraCPU, connections (threshold 250)Database health — CPU spikes, connection exhaustion
ElastiCache ServerlessECPU (>6,000 of 10,000, 3 consecutive periods), storage (>24 of 30 GB), throttled commands (>0)Cache health — processing-unit consumption, capacity pressure
ALB/NLB target-group health6 target-group alarms — one per target group across both load balancersUnhealthy hosts on any of the 6 target groups, including the webhook test receiver
S3Bucket size on the real website bucketUnusual growth on trinity-beast-website-east2
Translation DLQMessages visible > 0Failed, already-paid customer translation work sitting undelivered

Corrected 2026-08-13, several changes: (1) The 3 ElastiCache alarms previously watched AWS/MemoryDB metrics that don't exist for this cluster — rewritten against the real AWS/ElastiCache Serverless metrics. The ECPU alarm requires 3 consecutive breaching periods (not 2), added after two consecutive nights of a harmless nightly-sync bulk-write burst tripping it on a bucket-boundary coincidence. (2) ALB/NLB health alarms now use TreatMissingData: missing instead of notBreaching — a healthy load balancer always publishes a zero, so absence of data can only mean the alarm itself is blind (this exact setting hid the real ALB alarm for 3.5 months in 2026, per go-live.md). (3) Added the S3 size and Translation-DLQ alarms, which were both entirely absent. (4) All alarms route to the AutoOps notification pipeline (Section 10 in autonomous-ops.md), not the legacy critical-alerts SNS topic this document previously described — see Layer 10 below.

Layer 9: Scheduling (EventBridge)

Automated Tasks 8 Rules in this Template

EventBridge is your cron job in the cloud. It runs tasks on a schedule without you lifting a finger. The AutoOps rules (bedrock-analyze schedule, GuardDuty finding, digests) live in the AutoOps section of the template and are listed together with this one for completeness.

RuleScheduleWhat It Does
trinity-beast-nightly-synccron(0 5 * * ? *) = 1:00 AM EDTLaunches the sync job ECS task (BeastReconciler) to synchronize database data nightly
tbi-ops-alarm-triggerEvent pattern — any CloudWatch alarm state changeTriggers the self-heal Step Function
tbi-ops-honeypot-queue-processorrate(5 minutes)Drains the honeypot auto-block queue
tbi-ops-bedrock-analyze-schedulerate(30 minutes)AI threat correlation via Bedrock
tbi-ops-guardduty-high-findingEvent pattern — GuardDuty severity ≥ 7Immediate AI threat analysis on a real finding
tbi-ops-daily-digestcron(0 11 * * ? *) = 6:00 AM ESTConcise daily operational summary
tbi-ops-weekly-digestcron(0 12 ? * MON *) = 7:00 AM EST Mondays"Week in Review" + subscriber newsletter
tbi-cpmp-monthly-newslettercron(0 15 ? * 1#1 *) = 10:00 AM EST, 1st SundayCPMP ministry newsletter

Corrected 2026-08-13. 7 of these 8 rules were missing from the template entirely. The nightly-sync schedule was also off by an hour — cron(0 6 * * ? *) is 1 AM EST, but the live rule runs cron(0 5 * * ? *) (1 AM EDT, daylight time, in effect most of the year). Adjust by one hour each November when clocks fall back, same as the live rule.

Layer 10: Autonomous Operations (AutoOps)

Self-Healing AI Infrastructure 40+ Resources

The AutoOps layer adds intelligent, self-healing automation across 5 layers. It monitors the infrastructure, detects problems, fixes them automatically, generates AI-powered reports, and handles support tickets — notifying you after the fact.

ResourceTypePurpose
tbi-ops-notifyLambda (Go, 1770 MB, 180s)Send severity-tagged SNS notifications
tbi-ops-self-healLambda (Go, 1770 MB, 180s)ECS task restart, force-deploy, health checks
tbi-ops-waf-actionLambda (Go, 1770 MB, 180s)Block/unblock IPs in WAF IP set
tbi-ops-honeypot-processorLambda (Go, 1770 MB, 180s)Drain honeypot queue → WAF block → notify
tbi-ops-bedrock-analyzeLambda (Go, 1770 MB, 180s)AI threat analysis via Bedrock (Qwen 3 235B)
tbi-rhema-supportLambda (Go, 1770 MB, 180s)AI ticket categorization + draft responses (Qwen 3 235B)
tbi-ops-digestLambda (Go, 1770 MB, 540s)Daily/weekly/monthly operational digests via Bedrock
tbi-ops-health-check-healStep FunctionMulti-step heal workflow: check → wait → recheck → deploy → verify → notify
tbi-autoops-blocked-ipsWAF IP SetAuto-blocked IPs from honeypot + AI analysis
tbi-ops-notificationsSNS TopicSingle funnel for every operational alert with severity levels
tbi-autonomous-ops-roleIAM RoleShared execution role for the 5 core AutoOps Lambdas + Rhema + digest
trinity-beast-queued-writerLambda (Go, 256 MB, VPC)SQS → Aurora batch usage log writer
trinity-beast-queued-usage-logsSQS QueueDecoupled usage log pipeline
trinity-beast-email-queueSQS QueueDecoupled email delivery pipeline
trinity-beast-email-senderLambda (Go, 1770 MB, 60s)"Brainless" SQS consumer — pre-built email → SES, own dedicated role

Corrected 2026-08-13, several changes. (1) tbi-rhema-support, tbi-ops-digest, and trinity-beast-email-sender were entirely missing from this template — 3 of the account's 10 live Lambdas were never declared. (2) The Bedrock model reference was wrong throughout — the standing model for every AutoOps/Rhema call is Qwen 3 235B (qwen.qwen3-235b-a22b-2507-v1:0), not Claude Haiku. (3) The 4 anomaly detectors row has been removed — 2 of the original 4 (CacheHitRate, Latency) were deleted 2026-07-30 after being diagnosed as structurally unmeasurable at pre-launch volume (see go-live.md); the remaining 2 (RequestRate, ErrorRate) exist live but anomaly detectors are not currently declarable via this CloudFormation template resource type in a way that matches the live configuration, so they are intentionally not represented here — recreate them via the AWS CLI per the procedure in go-live.md if rebuilding from scratch. (4) The agent_profiles/agent_profile_events table row was removed — like tier_catalog above, these are Aurora application tables created by migrations, not by this template.

⚠️ Known Gap — WAF Web ACLs Not in This Template

The WAF Web ACL protecting the ALB (trinity-beast-api-waf) and the CloudFront-managed ACL are not declared in this template — this is a known, documented gap, not a regression (it was never here). Building all 8 live rule groups correctly (IP Reputation, Common Rules, Known Bad Inputs, SQL Injection, two rate-limit rules, the AutoOps IP-set rule, and the honeypot auto-block rule) in CloudFormation is substantial, and shipping it half-verified would be worse than being explicit about the gap. The WAF IP Set that AutoOps writes to (tbi-autoops-blocked-ips) is declared and correct — only the ACL that references it via a rule is not. Recreating the WAF ACLs is the current manual step in a full disaster-recovery scenario; see security.py in the KCC helper scripts for the live rule inventory to replicate.

Layer 11: Translation Engine

Custom Bedrock-Powered Document Translation 10 Resources

A custom translation pipeline that uses Amazon Bedrock (Qwen 3 235B) with sentinel preprocessing to translate documents while preserving code blocks, Mermaid diagrams, and brand terms. AWS Translate is NOT used — it corrupts technical content.

ResourceTypePurpose
tbi-translate-workerECR RepositoryPython 3.11 container image for the translation worker
tbi-translate-roleIAM RoleWorker task role only — Bedrock, S3, CloudFront, SQS, SNS
trinity-beast-translation-queueSQS QueueOne message per language — the worker polls this directly
trinity-beast-translation-queue-dlqSQS Queue (DLQ)Failed translation messages after 3 receive attempts
trinity-beast-translation-cost-queueSQS QueueTranslation cost events
tbi-translate-worker-taskECS Task DefinitionFargate task def (2 vCPU / 6 GB, no timeout ceiling)
tbi-translate-worker-serviceECS Service (persistent)Always-on worker polls SQS directly — one container per language message

Architecture, simplified 2026-06-27: POST /admin/translate → N SQS messages (1 per language) → N persistent worker containers, auto-scaled to match → each worker handles sentinel preprocessing, Bedrock translation, validation, S3 write, and CloudFront invalidation directly (boto3 — no Lambda) → the last worker to finish calls POST /admin/translate/complete/{job_id} on the LPO server, which handles state update, search rebuild, cost computation, and notification in-process. Zero Lambdas, zero Step Functions, zero EventBridge Pipes.

Corrected 2026-08-13 — this section was describing a dead architecture. tbi-translation-orchestrator (Step Function), tbi-translate-deploy and tbi-translate-finalize (both Lambdas), and the EventBridge Pipe that fed the orchestrator were all deleted from AWS on 2026-06-27 when the pipeline was simplified to direct SQS fan-out — but this template and this document both still declared/described all four as if they were live. None of them exist. The worker's task sizing was also wrong (1 vCPU / 3 GB instead of the real 2 vCPU / 6 GB), and the translation DLQ and cost queue were both missing entirely.

3. Parameters — What You Need to Provide

Parameters are the blanks you fill in when deploying. Think of them as the customization options on an order form. Most have sensible defaults — you only must provide two values.

Only 2 required: DBPassword and SecretValue are the only parameters you absolutely must provide. Everything else has a default that matches the current production setup.

ParameterPurposeDefaultSecret?Required?
DBPassword PostgreSQL master password for Aurora cluster None — you must provide this Yes (NoEcho) YES
SecretValue JSON string for trinity-beast-secrets (DB creds, Stripe keys, API keys) None — you must provide this Yes (NoEcho) YES
DBUsername PostgreSQL master username postgres No No
DBName Aurora database name CPMP_Backend_Aurora No No
DomainName Primary domain name cpmp-site.org No No
ACMCertificateArnEast2 ACM certificate ARN in us-east-2 (for ALB HTTPS) Current production cert ARN No No
ACMCertificateArnEast1 ACM certificate ARN in us-east-1 (for CloudFront) Current production cert ARN No No
ACMCertificateArnWebhook Single-SAN ACM cert ARN for webhook.cpmp-site.org — added 2026-08-13, was missing Current production cert ARN No No
AlertEmail Email address for critical alerts [email] No No
AlertSMS Phone number for SMS alerts [phone] No No
SESFromAddress SES sender address for receipt emails CPMP Mission <No-Reply@CPMP-Site.org> No No
LPOImageTag Docker image tag for LPO server latest No No
SyncImageTag Docker image tag for sync job latest No No

About SecretValue: This is a JSON string containing all the secrets your application needs. Format: {"DB_PASSWORD":"...","STRIPE_SECRET_KEY":"...","STRIPE_WEBHOOK_SECRET":"...",...}. Keep this stored securely outside of AWS — in a password manager, encrypted file, or similar. If you lose this, you'll need to regenerate all the API keys and passwords it contains.

4. How Resources Are Wired Together

CloudFormation doesn't just create resources in isolation — it wires them together using two key mechanisms: !Ref (reference another resource's ID) and !GetAtt (get a specific attribute like an endpoint URL). Here's what that looks like in plain English.

The Big Picture

Diagram 4.1 — Resource Wiring Overview
graph TB
    Internet["Internet"]

    subgraph DNS_CDN["DNS & CDN"]
        Route53["Route 53
cpmp-site.org"] CloudFront["CloudFront
Static Website"] S3["S3 Bucket
trinity-beast-website-east2"] end subgraph LoadBalancers["Load Balancers"] ALB["ALB
Trinity-Beast-TCP-ALB
TCP: 80, 443 → 8080, 9090"] NLB["NLB
Trinity-Beast-UDP-NLB
UDP: 2679, 2680"] end subgraph ECS["ECS Fargate Cluster — 4 of 5 Services Shown (BeastTranslate omitted, no ALB/NLB path)"] Main["BeastMain · AZ 2a
APP_REPORT_SERVER
2 vCPU / 6 GB"] Mirror["BeastMirror · AZ 2b
APP_REPORT_SERVER
2 vCPU / 6 GB"] LRS["BeastLRS · AZ 2c
APP_REPORT_SERVER
2 vCPU / 6 GB"] Webhook["BeastWebhook
WEBHOOK_SERVER
2 vCPU / 6 GB"] end subgraph Data["Data Layer"] Aurora["Aurora PostgreSQL
Serverless v2
1.5–18 ACU"] MemDB["ElastiCache Serverless
Valkey 9.0
Auto-scaling · Multi-AZ"] Secrets["Secrets Manager
trinity-beast-secrets"] end Lambda["Lambda
trinity-beast-receipt
Go / provided.al2023"] EventBridge["EventBridge
Nightly Sync
1 AM EDT"] SyncJob["Sync Job
0.5 vCPU / 1 GB"] SNS["SNS
AutoOps Notifications
tbi-ops-notify → SES"] CW["CloudWatch
~25 Alarms in Template"] Internet --> Route53 Route53 -->|"cpmp-site.org"| CloudFront Route53 -->|"api / lrs"| ALB Route53 -->|"udp"| NLB CloudFront --> S3 Internet -->|"Stripe Webhook"| Lambda ALB -->|"TCP 8080"| Main ALB -->|"TCP 8080"| Mirror ALB -->|"TCP 9090"| LRS NLB -->|"UDP 2679"| Main NLB -->|"UDP 2679"| Mirror NLB -->|"UDP 2680"| LRS Main --> Aurora Main --> MemDB Mirror --> Aurora Mirror --> MemDB LRS --> Aurora LRS --> MemDB Webhook --> Aurora Webhook --> MemDB Main -.->|"!GetAtt Endpoint"| Aurora Main -.->|"!GetAtt Endpoint"| MemDB Main -.->|"Reads"| Secrets Lambda --> Aurora Lambda --> MemDB Lambda -.->|"Reads"| Secrets EventBridge -->|"cron 0 6 * * ?"| SyncJob SyncJob --> Aurora SyncJob --> MemDB CW -.->|"ALARM"| SNS %% Internet → DNS — white linkStyle 0 stroke:#e2e8f0,stroke-width:2px %% DNS → CDN — pink (website path) linkStyle 1 stroke:#f472b6,stroke-width:2px %% DNS → ALB — blue (TCP) linkStyle 2 stroke:#60a5fa,stroke-width:2px %% DNS → NLB — orange (UDP) linkStyle 3 stroke:#FF9900,stroke-width:2px %% CloudFront → S3 — pink linkStyle 4 stroke:#f472b6,stroke-width:2px %% Internet → Lambda — violet (Stripe) linkStyle 5 stroke:#a78bfa,stroke-width:2px %% ALB → ECS — blue (TCP) linkStyle 6 stroke:#60a5fa,stroke-width:2px linkStyle 7 stroke:#60a5fa,stroke-width:2px linkStyle 8 stroke:#60a5fa,stroke-width:2px %% NLB → ECS — orange (UDP) linkStyle 9 stroke:#FF9900,stroke-width:2px linkStyle 10 stroke:#FF9900,stroke-width:2px linkStyle 11 stroke:#FF9900,stroke-width:2px %% ECS → Aurora — red (database) linkStyle 12 stroke:#f87171,stroke-width:2px linkStyle 14 stroke:#f87171,stroke-width:2px linkStyle 16 stroke:#f87171,stroke-width:2px linkStyle 18 stroke:#f87171,stroke-width:2px %% ECS → ElastiCache — green (cache) linkStyle 13 stroke:#10b981,stroke-width:2px linkStyle 15 stroke:#10b981,stroke-width:2px linkStyle 17 stroke:#10b981,stroke-width:2px linkStyle 19 stroke:#10b981,stroke-width:2px %% !GetAtt / !Ref wiring — cyan (dashed) linkStyle 20 stroke:#22d3ee,stroke-width:1.5px linkStyle 21 stroke:#22d3ee,stroke-width:1.5px %% Secrets reads — rose linkStyle 22 stroke:#fca5a5,stroke-width:1.5px %% Lambda → Aurora — red linkStyle 23 stroke:#f87171,stroke-width:2px %% Lambda → ElastiCache — green linkStyle 24 stroke:#10b981,stroke-width:2px %% Lambda → Secrets — rose linkStyle 25 stroke:#fca5a5,stroke-width:1.5px %% EventBridge → SyncJob — yellow linkStyle 26 stroke:#facc15,stroke-width:2px %% SyncJob → Aurora — red linkStyle 27 stroke:#f87171,stroke-width:2px %% SyncJob → ElastiCache — green linkStyle 28 stroke:#10b981,stroke-width:2px %% CloudWatch → SNS — rose (alerts) linkStyle 29 stroke:#fca5a5,stroke-width:1.5px style Internet fill:#FF9900,color:#0f172a,stroke:#FF9900 style ALB fill:#1e293b,stroke:#60a5fa,color:#e2e8f0 style NLB fill:#1e293b,stroke:#FF9900,color:#e2e8f0 style Main fill:#064e3b,stroke:#10b981,color:#e2e8f0 style Mirror fill:#064e3b,stroke:#10b981,color:#e2e8f0 style LRS fill:#064e3b,stroke:#10b981,color:#e2e8f0 style Webhook fill:#064e3b,stroke:#10b981,color:#e2e8f0 style Aurora fill:#1e293b,stroke:#f87171,color:#e2e8f0 style MemDB fill:#1e293b,stroke:#10b981,color:#e2e8f0 style Lambda fill:#1e293b,stroke:#a78bfa,color:#e2e8f0 style CloudFront fill:#1e293b,stroke:#f472b6,color:#e2e8f0 style S3 fill:#1e293b,stroke:#f472b6,color:#e2e8f0 style Route53 fill:#1e293b,stroke:#e2e8f0,color:#e2e8f0 style Secrets fill:#7f1d1d,stroke:#fca5a5,color:#e2e8f0 style SNS fill:#7f1d1d,stroke:#fca5a5,color:#e2e8f0 style CW fill:#1e293b,stroke:#60a5fa,color:#e2e8f0 style SyncJob fill:#1e293b,stroke:#facc15,color:#e2e8f0 style EventBridge fill:#1e293b,stroke:#facc15,color:#e2e8f0
TCP Path (ALB) UDP Path (NLB) Website / CDN Lambda (Stripe) Aurora (Database) ElastiCache (Cache) !GetAtt / !Ref Wiring Secrets / Alerts EventBridge → Sync Job

Key Connections (What !Ref and !GetAtt Do)

Database → ECS Tasks

The Aurora cluster endpoint is automatically passed to all 4 ECS task definitions as the DB_HOST environment variable. When CloudFormation creates the Aurora cluster, it gets an endpoint like trinity-beast-aurora.cluster-xxxxx.us-east-2.rds.amazonaws.com. That endpoint is injected into every container so they know where the database is — no hardcoding needed.

Cache → ECS Tasks

The ElastiCache Serverless endpoint is passed to all 4 ECS task definitions as the CACHE_URL environment variable. Same idea — CloudFormation creates the cache, gets the endpoint, and passes it to the containers automatically. With Serverless, there is no node to size or manage — it scales transparently behind that single endpoint.

Security Groups → Each Other

Security groups reference each other to create a chain of trust:

  • The ALB security group allows traffic from the internet (ports 80, 443)
  • The ECS tasks security group allows traffic only from the ALB security group — not from the internet directly
  • The Aurora security group allows traffic only from the ECS tasks security group — only your servers can reach the database
  • The ElastiCache security group allows traffic only from the ECS tasks security group — same protection for the cache

This is defense in depth. Even if someone bypasses the load balancer, they can't reach the database directly because the security group only trusts traffic from the ECS containers.

DNS → Load Balancers → Services

Route 53 records use !GetAtt to point at the load balancers:

  • api.cpmp-site.org → ALB's DNS name (automatically resolved)
  • udp.cpmp-site.org → NLB's DNS name
  • cpmp-site.orgCloudFront distribution domain

If the load balancer's address changes (e.g., after a rebuild), the DNS records update automatically because they reference the resource, not a hardcoded IP.

Secrets Manager → ECS Tasks

The Secrets Manager secret ARN is passed to ECS task definitions. At runtime, ECS pulls the secret values and injects them as environment variables. Your application code never sees the raw secret — it just reads environment variables like DB_PASSWORD and STRIPE_SECRET_KEY.

5. How to Deploy

Deploying the stack means telling CloudFormation: "Build everything described in this template." Here's how, step by step.

Prerequisites

Before You Start Checklist
  1. AWS CLI installed and configured — run aws sts get-caller-identity to verify you're authenticated to account 211998422884
  2. ACM certificates — you need two SSL certificates already created and validated:
    • One in us-east-2 (for the ALB)
    • One in us-east-1 (for CloudFront — this is an AWS requirement, CloudFront only uses us-east-1 certs)
  3. Docker images in ECR — the template references ECR repos, but you need to push images to them after the repos are created (or have them ready to push)
  4. The secret values — your database password and the JSON secrets string (Stripe keys, etc.)

The Deploy Command

Important: Replace the placeholder values below with your actual secrets. Never commit real secrets to version control.

aws cloudformation create-stack \
  --stack-name trinity-beast-stack \
  --template-body file://trinity-beast-stack.yaml \
  --capabilities CAPABILITY_NAMED_IAM \
  --region us-east-2 \
  --parameters \
    ParameterKey=DBPassword,ParameterValue='YOUR_DB_PASSWORD_HERE' \
    ParameterKey=SecretValue,ParameterValue='{"DB_PASSWORD":"...","STRIPE_SECRET_KEY":"...","STRIPE_WEBHOOK_SECRET":"..."}' \
    ParameterKey=DBUsername,ParameterValue=postgres \
    ParameterKey=DBName,ParameterValue=CPMP_Backend_Aurora \
    ParameterKey=DomainName,ParameterValue=cpmp-site.org \
    ParameterKey=AlertEmail,ParameterValue='[email]' \
    ParameterKey=AlertSMS,ParameterValue='[phone]' \
    ParameterKey=LPOImageTag,ParameterValue=latest \
    ParameterKey=SyncImageTag,ParameterValue=latest

What's CAPABILITY_NAMED_IAM? This flag tells CloudFormation: "Yes, I know this template creates IAM roles, and I'm okay with that." It's a safety check — AWS wants you to explicitly acknowledge that you're granting permissions.

What Happens During Deployment

Deployment Timeline ~30-45 Minutes

CloudFormation reads the template, figures out the dependency order, and starts creating resources. Here's the rough timeline:

TimeWhat's Happening
0–2 minVPC, subnets, internet gateway, route tables created
2–5 minSecurity groups, VPC endpoints, IAM roles created
5–15 minAurora cluster spinning up (this is the slowest part)
5–15 minElastiCache Serverless cache creating (runs in parallel with Aurora)
10–20 minECR repos, S3 bucket, Secrets Manager created
15–25 minECS cluster, task definitions, ALB, NLB created
20–30 minECS services start (4 services — they'll fail health checks until images are pushed)
25–35 minRoute 53 records, CloudFront distribution, CloudWatch alarms
30–45 minStack complete — all 182 resources created

Post-Deployment Steps

After the Stack Is Created Action Required

The stack creates the infrastructure, but some things need to be done manually after:

  1. 1Push Docker images to ECR — your ECS services are running but have no code yet
    # Authenticate Docker to ECR
    aws ecr get-login-password --region us-east-2 | docker login --username AWS --password-stdin 211998422884.dkr.ecr.us-east-2.amazonaws.com
    
    # Tag and push the LPO server image
    docker tag trinity-beast-lpo-server:latest 211998422884.dkr.ecr.us-east-2.amazonaws.com/trinity-beast-lpo-server:latest
    docker push 211998422884.dkr.ecr.us-east-2.amazonaws.com/trinity-beast-lpo-server:latest
    
    # Tag and push the sync job image
    docker tag trinity-beast-sync-job:latest 211998422884.dkr.ecr.us-east-2.amazonaws.com/trinity-beast-sync-job:latest
    docker push 211998422884.dkr.ecr.us-east-2.amazonaws.com/trinity-beast-sync-job:latest
  2. 2Deploy Lambda code — upload the Go binary to the Lambda function
    aws lambda update-function-code \
      --function-name trinity-beast-receipt \
      --zip-file fileb://bootstrap.zip \
      --region us-east-2
  3. 3Upload website to S3
    aws s3 sync ./website/ s3://trinity-beast-website-east2/ --delete
  4. 4Invalidate CloudFront cache
    aws cloudfront create-invalidation \
      --distribution-id E110PRKEIYQVLL \
      --paths "/*"
  5. 5Verify DNS resolution
    dig api.cpmp-site.org
    dig udp.cpmp-site.org
    dig cpmp-site.org
    curl -I https://api.cpmp-site.org/health

How to Check Stack Status

# Check overall stack status
aws cloudformation describe-stacks --stack-name trinity-beast-stack --region us-east-2

# Watch events in real-time (useful during creation)
aws cloudformation describe-stack-events \
  --stack-name trinity-beast-stack \
  --region us-east-2 \
  --query 'StackEvents[0:10].[Timestamp,ResourceType,LogicalResourceId,ResourceStatus]' \
  --output table

# List all resources in the stack
aws cloudformation list-stack-resources \
  --stack-name trinity-beast-stack \
  --region us-east-2

Success looks like: "StackStatus": "CREATE_COMPLETE". If you see CREATE_FAILED or ROLLBACK_COMPLETE, check the events for the specific resource that failed — CloudFormation will tell you exactly what went wrong.

6. How to Update

Need to change something? Edit the YAML file and tell CloudFormation to update. It's smart enough to figure out what changed and only touch those resources.

The Update Command

aws cloudformation update-stack \
  --stack-name trinity-beast-stack \
  --template-body file://trinity-beast-stack.yaml \
  --capabilities CAPABILITY_NAMED_IAM \
  --region us-east-2 \
  --parameters \
    ParameterKey=DBPassword,UsePreviousValue=true \
    ParameterKey=SecretValue,UsePreviousValue=true

Notice UsePreviousValue=true — for secret parameters, you don't need to re-enter them on every update. CloudFormation remembers the values from the last deployment.

How Updates Work

CloudFormation Change Detection

CloudFormation compares your new template against the current state and categorizes each change:

Change TypeWhat HappensExample
No Interruption Resource is updated in place — no downtime Changing a CloudWatch alarm threshold
Some Interruption Resource is briefly interrupted during update Changing an ECS task definition (rolling deploy)
Replacement Old resource is deleted and a new one is created Changing VPC CIDR block, Aurora engine version

⚠️ Replacement Warning: Some changes force CloudFormation to delete and recreate a resource. This can cause data loss. Before updating, always preview changes with a Change Set:

aws cloudformation create-change-set \
  --stack-name trinity-beast-stack \
  --template-body file://trinity-beast-stack.yaml \
  --change-set-name my-changes \
  --capabilities CAPABILITY_NAMED_IAM \
  --region us-east-2

# Review what will change
aws cloudformation describe-change-set \
  --stack-name trinity-beast-stack \
  --change-set-name my-changes \
  --region us-east-2

This shows you exactly what will be modified, added, or replaced — before anything happens. Always use Change Sets for production updates.

Common Safe Updates

Dangerous Updates (Require Replacement)

7. How to Recover from Disaster

The scenario: everything is gone. The AWS account was compromised, someone deleted the stack, or you need to rebuild from scratch in a new account. Here's the step-by-step playbook.

Estimated recovery time: 1–2 hours from "everything is gone" to "everything is running." The stack itself takes 30–45 minutes. The rest is pushing code and verifying.

Step-by-Step Recovery Playbook Disaster Recovery

1 Run create-stack with the template

This is the big one. One command creates all 182 resources (the us-east-2 footprint — see Section 9 for the us-east-1 pieces):

aws cloudformation create-stack \
  --stack-name trinity-beast-stack \
  --template-body file://trinity-beast-stack.yaml \
  --capabilities CAPABILITY_NAMED_IAM \
  --region us-east-2 \
  --parameters \
    ParameterKey=DBPassword,ParameterValue='YOUR_DB_PASSWORD' \
    ParameterKey=SecretValue,ParameterValue='YOUR_SECRETS_JSON'

Then wait. Monitor progress:

aws cloudformation describe-stacks --stack-name trinity-beast-stack --region us-east-2 \
  --query 'Stacks[0].StackStatus' --output text

2 Wait for stack to complete

Watch for CREATE_COMPLETE. This takes 30–45 minutes. Aurora and ElastiCache are the slowest. Go get coffee.

3 Push Docker images to the new ECR repos

The ECR repos are empty — they're just containers waiting for images. Build and push:

# Authenticate
aws ecr get-login-password --region us-east-2 | \
  docker login --username AWS --password-stdin \
  211998422884.dkr.ecr.us-east-2.amazonaws.com

# Build and push LPO server
docker build -t trinity-beast-lpo-server:latest .
docker tag trinity-beast-lpo-server:latest \
  211998422884.dkr.ecr.us-east-2.amazonaws.com/trinity-beast-lpo-server:latest
docker push 211998422884.dkr.ecr.us-east-2.amazonaws.com/trinity-beast-lpo-server:latest

# Build and push sync job
docker build -t trinity-beast-sync-job:latest -f Dockerfile.sync .
docker tag trinity-beast-sync-job:latest \
  211998422884.dkr.ecr.us-east-2.amazonaws.com/trinity-beast-sync-job:latest
docker push 211998422884.dkr.ecr.us-east-2.amazonaws.com/trinity-beast-sync-job:latest

ECS will automatically pick up the new images and start the services.

4 Deploy Lambda code (all 9 us-east-2 functions)

Repeat this build-and-deploy pattern for every Lambda in the template: trinity-beast-receipt, trinity-beast-queued-writer, trinity-beast-email-sender, and the 5 AutoOps functions (tbi-ops-notify, tbi-ops-self-heal, tbi-ops-waf-action, tbi-ops-honeypot-processor, tbi-ops-bedrock-analyze) plus tbi-rhema-support and tbi-ops-digest. See deployment.md for the exact source directory of each.

# Build the Go binary for Lambda — GOARCH must be amd64, matching the
# template's x86_64 Architectures setting (NOT arm64)
GOOS=linux GOARCH=amd64 go build -o bootstrap ./cmd/handler/
zip -j function.zip bootstrap

# Deploy to Lambda
aws lambda update-function-code \
  --function-name trinity-beast-receipt \
  --zip-file fileb://function.zip \
  --region us-east-2

5 Upload website files to S3

aws s3 sync ./cpmp-redesign/ s3://trinity-beast-website-east2/ --delete

6 Invalidate CloudFront

aws cloudfront create-invalidation \
  --distribution-id E110PRKEIYQVLL \
  --paths "/*"

7 Update Route 53 nameservers at domain registrar

If the hosted zone was recreated, it gets new nameservers. You must update them at your domain registrar (wherever you bought cpmp-site.org).

# Get the new nameservers
aws route53 get-hosted-zone --id YOUR_HOSTED_ZONE_ID \
  --query 'DelegationSet.NameServers' --output text

Copy those 4 nameserver values and update them at your registrar. DNS propagation can take up to 48 hours, but usually completes within 1–2 hours.

8 Verify all services are healthy

# Check ECS services
aws ecs describe-services \
  --cluster trinity-beast-fargate-cluster \
  --services trinity-beast-main-service trinity-beast-mirror-service trinity-beast-lrs-service trinity-beast-webhook-service tbi-translate-worker-service \
  --region us-east-2 \
  --query 'services[].{name:serviceName,running:runningCount,desired:desiredCount,status:status}'

# Check API health
curl -s https://api.cpmp-site.org/health | jq .

# Check ALB target health
aws elbv2 describe-target-health \
  --target-group-arn YOUR_TARGET_GROUP_ARN \
  --region us-east-2

# Check Aurora
aws rds describe-db-clusters \
  --db-cluster-identifier trinity-beast-aurora-cluster \
  --region us-east-2 \
  --query 'DBClusters[0].Status'

# Check ElastiCache Serverless
aws elasticache describe-serverless-caches \
  --serverless-cache-name trinity-beast-cache-serverless \
  --region us-east-2 \
  --query 'ServerlessCaches[0].Status'

Database note: The stack creates an empty Aurora database. You'll need to run your database migrations to recreate the schema and seed data. If you have a database backup (RDS snapshot), you can restore from that instead — but that's a manual step outside the CloudFormation template.

8. What's NOT in the Template

Honesty time. The CloudFormation template is powerful, but it doesn't cover everything. Some things require manual steps, either because AWS doesn't support them in CloudFormation or because they involve external services.

These items require manual action after the stack is deployed. The template creates the infrastructure, but these pieces must be configured separately.

ItemWhy It's Not in the TemplateWhat You Need to Do
ACM Certificates Certificates require DNS validation — a chicken-and-egg problem (you need DNS to validate, but DNS is in the template) Request certificates in ACM for us-east-2 (ALB) and us-east-1 (CloudFront). Validate via DNS. Pass the ARNs as parameters.
SES Domain Verification & DKIM SES verification involves external DNS records and waiting for AWS to verify Verify cpmp-site.org in SES. Set up DKIM signing. Move out of SES sandbox if needed.
Stripe Webhook Configuration Stripe is an external service — CloudFormation can't configure it Log into Stripe Dashboard. Create webhook endpoint pointing to https://api.cpmp-site.org/webhook/stripe. Copy the webhook secret into your SecretValue parameter.
Database Schema & Seed Data CloudFormation creates the database engine, not the tables inside it After Aurora is up, connect and run your migrations: go run cmd/migrate/main.go. Seed any required reference data.
Docker Images The template creates ECR repos (the shelves) but not the images (the books) Build your Docker images locally or in CI/CD, then push to ECR. See Section 7, Step 3.
Website Content The template creates the S3 bucket (the filing cabinet) but not the files inside it Upload your website files: aws s3 sync ./cpmp-redesign/ s3://trinity-beast-website-east2/
Compute Savings Plan Savings Plans are billing commitments, not infrastructure — they can't be defined in CloudFormation Purchase a Compute Savings Plan through the AWS Cost Explorer console after your infrastructure is stable.
WAF Web ACLs Building all 8 live rule groups correctly is substantial and was deliberately deferred rather than shipped half-verified — see the callout in Layer 10 above Recreate trinity-beast-api-waf via console or CLI, replicating the live rule inventory (IP Reputation, Common Rules, Known Bad Inputs, SQL Injection, 2 rate-limit rules, the AutoOps IP-set rule, honeypot auto-block). The IP Set itself IS in the template.
Actual Secret Values Secrets should never be stored in a template file — that would be a security risk You provide secrets as parameters at deploy time. Store them securely in a password manager or encrypted vault.

Corrected 2026-08-13 — ElastiCache Serverless row removed from this list. AWS::ElastiCache::ServerlessCache is a real, well-supported CloudFormation resource type and IS declared in the template now (Section 2, Layer 3) — this document previously claimed otherwise and had the template quietly using the wrong service (MemoryDB, not ElastiCache) as a workaround. That workaround is gone; the template creates the real cache.

Think of it this way: The template builds the house — walls, plumbing, electrical, locks on the doors. But you still need to move in your furniture (Docker images), hang your pictures (website content), set up your mail forwarding (SES), and give the locksmith your key preferences (secrets). The house is ready, but it needs to be lived in.

9. The us-east-1 Footprint (Separate Template)

Added 2026-08-13. CloudFormation stacks deploy to a single region — you cannot mix resources from two regions in one create-stack call. This template targets us-east-2. A handful of real, necessary resources are forced into us-east-1 by AWS service availability, not by choice, and they live in a companion template (trinity-beast-stack-us-east-1.yaml) rather than being squeezed into this one.

Why these specific resources have to be in us-east-1: SES inbound email receiving is not available in us-east-2 at all — it's a hard region restriction, not a preference. The CloudFront WAF and all AWS/CloudFront metrics are global-service resources that AWS only exposes through us-east-1.

ResourceTypeWhy us-east-1
tbi-support-inboundLambda (Go, 1770 MB, 60s)Threads inbound customer email replies back into support tickets. Triggered by S3, not SES directly.
tbi-support-inbound-roleIAM RoleS3 read + Aurora-facing Lambda invoke for the above
tbi-support-inboundS3 Bucket (prefix emails/)SES receipt-rule destination — must be co-located with the receipt rule
support-reply-inboundSES Receipt RuleRoutes inbound mail on reply.cpmp-site.org to the S3 bucket above
reply.cpmp-site.org, cpmp-site.orgSES IdentitiesVerified sender/receiver domains for the inbound path
CreatedByCloudFront-449feaa5WAF Web ACL (global scope)CloudFront WAF is global/us-east-1 by definition
aws-waf-logs-trinity-beast-use1S3 Bucketus-east-1 unified logs bucket — home for CloudFront WAF logs (region-locked) and any future us-east-1 vended logs
tbi-ops-notificationsSNS Topic (us-east-1, same name as the us-east-2 one — a distinct resource)The notification entry point for us-east-1 alarms (e.g. CloudFront 5xx). A CloudWatch alarm can only publish to a same-region SNS topic.
Trinity-Beast-CloudFront-5XX-HighCloudWatch AlarmCloudFront metrics are us-east-1-only, so the alarm publishing on them must live there too

The bridge back to the normal notification pipeline: the us-east-1 tbi-ops-notifications topic has the us-east-2 tbi-ops-notify Lambda as a confirmed cross-region subscriber — SNS permits a Lambda subscription across regions. So a us-east-1 alarm still ends up as one formatted SES email, same as every other alert, with no forwarder Lambda needed.

Do not add regions to this list casually. Every other cross-region reach in this system is transient (the translation engine's Bedrock failover chain tries us-east-1 and then us-west-2 only if us-east-2 Bedrock is unavailable — it creates no resources in those regions, it only invokes Bedrock there). The resources above are the only resident us-east-1 infrastructure in the account, and they exist because AWS requires it, not because anyone chose it.

10. Template Location

The CloudFormation template and supporting files are stored in multiple locations for redundancy.

Primary Locations

Local Repository Source of Truth
trinity-beast-lpo-server/deployments/cloudformation/trinity-beast-stack.yaml

This is the canonical version. All edits should be made here and then synced to S3.

S3 Backup Cloud Copy
s3://trinity-beast-website-east2/cloudformation/trinity-beast-stack.yaml

A copy in S3 for redundancy. You can deploy directly from S3 using --template-url instead of --template-body:

aws cloudformation create-stack \
  --stack-name trinity-beast-stack \
  --template-url https://trinity-beast-website-east2.s3.us-east-2.amazonaws.com/cloudformation/trinity-beast-stack.yaml \
  --capabilities CAPABILITY_NAMED_IAM \
  --region us-east-2 \
  --parameters ...
Resource Inventory 75 JSON Files
trinity-beast-lpo-server/deployments/cloudformation/inventory/

This directory contains 75 JSON files — one for every resource's current configuration as captured from the live AWS environment at the time of capture. These serve as a reference if you need to verify that the CloudFormation template matches what's actually deployed. They're snapshots, not live data — some predate the 2026-08-13 correction pass and may themselves be stale; treat them as a starting point for comparison, not as ground truth.

Keep these in sync: When you update the template locally, remember to upload the new version to S3: aws s3 cp trinity-beast-stack.yaml s3://trinity-beast-website-east2/cloudformation/