madcatalog — Production Readiness & Cheap AWS Plan

Evaluated 2026-06-09  ·  14 Lambda functions  ·  3 Grafana dashboards  ·  8 local services

Overall Production Readiness

Scores are across five axes. The backend runtime and observability instrumentation are well-advanced for a prototype; the missing piece is deployment infrastructure and secret management discipline before any real traffic can touch it.

API runtime
85%
Observability
75%
Test coverage
55%
Deployment pipeline
15%
Secret management
20%

Grafana Dashboard Assessment

Three provisioned dashboards — Overview, MPP Flow, and Latency Distribution — together give strong operational and business coverage. Gaps are mostly in alerting and DynamoDB-specific metrics rather than missing panels.

Operational Objectives Well Covered

RED Metrics

Rate, error rate (4xx/5xx split), and duration (p50/p95/p99) tracked per Lambda function and in aggregate. Matches the Google SRE RED model exactly.

Per-Endpoint Latency Heatmaps

Every REST endpoint and every MCP tool has its own heatmap + timeseries pair in the Latency Distribution dashboard — 50+ panels covering all 14 Lambdas and 9 MCP tools.

Webhook Processing

Stripe webhook duration (p50/p95) and event-type breakdown are tracked. Critical for payment settlement correctness.

Logs & Traces

Loki logs and Tempo traces are wired in. OTel bridges console to Loki. Spans are emitted per Lambda invocation. Trace sampling is currently 100% — fine at low volume, needs review at scale.

Lambda Cold Starts

Not tracked. Lambda cold start duration is a key operational signal for API Gateway + Lambda; it cannot be inferred from p50/p95 alone and deserves its own panel.

DynamoDB Throttling

Not tracked. On-demand mode absorbs small spikes but can still throttle. CloudWatch ThrottledRequests metric should be surfaced in Grafana via the CloudWatch datasource when deployed.

Business Objectives Well Covered

MPP Funnel (Sankey)

Catalog views → 402 challenge → bids placed → payment confirmed. Conversion rates (view→challenge, bid→payment success) shown as stat panels. This directly maps to the core product metric.

Revenue by Payment Rail

Stripe (fiat) vs Tempo (crypto) split across ad bids and subscriptions. Total all-rails revenue panel exists. Revenue rate timeseries per source.

Subscription Lifecycle

Created, canceled, revenue, invoices paid — all tracked over time and as 24h stat panels. Subscription revenue by interval (monthly/yearly) is visualised.

Payment Distribution

Average and p95 payment amounts shown. Payment events (succeeded/failed/refunded) on a single timeseries — important for spotting refund spikes.

Advertiser Spend vs Cap

MaxAdvertiserSpendUSDCents is a SAM parameter but spend-vs-cap utilisation is not visualised. When the cap goes live in production it should be dashboarded.

Alerting / SLOs

No Grafana alert rules exist on any dashboard. No SLO definitions or error-budget burn-rate panels. Dashboards are "observe and react" — not "be paged before customers notice".

Local Services → AWS Mapping

Every service that runs locally has a cheap or free AWS equivalent. The entire stack maps to serverless or managed services — no EC2 instances needed.

Local service (tmuxinator pane) What it does AWS replacement Cost signal
localstack — LocalStack DynamoDB Emulates DynamoDB at :4566 AWS DynamoDB (same table names, already PAY_PER_REQUEST; just remove DYNAMODB_ENDPOINT) Free tier 25 GB + 25 WCU/RCU free forever
sam-api — SAM local API 14 Lambda functions behind sam local start-api on :3001 Lambda + API Gateway HTTP API via sam deploy. SAM template is already complete and parameterised. Free tier 1M invocations/month free forever
stripe — stripe listen Forwards Stripe webhooks to localhost Configure Stripe dashboard to POST directly to the API Gateway /webhooks/stripe endpoint. No AWS resource needed. $0
swagger — Swagger UI (watchexec) Redocly/Swagger UI on :8082 for API exploration Build static Redocly bundle → include in Hugo docs site → deploy to S3/CloudFront. Alternatively keep as a developer-only tool. $0 (static file)
site-up — Hugo catalog site Human-readable catalog at :1317 hugo build → S3 bucket (static website) + CloudFront distribution. Hugo already produces clean static HTML. ~$0 CloudFront free tier covers 1 TB/month + 10M requests
docs-up — Hugo docs site ADRs, proposal, prototype at :1318 Same S3 + CloudFront deployment as catalog site. Use a separate S3 prefix or a second CloudFront behaviour. A single distribution serves both. ~$0 Shared with catalog CDN
mcp — MCP server (Go) Model Context Protocol HTTP server on :8083 Option A (cheapest): Lambda Function URL — package Go binary, cold starts acceptable for a dev-tool.
Option B: App Runner — managed, scales to zero, ~$5/month minimum.
MVP shortcut: Omit from initial deployment; MCP is a developer tool, not customer-facing.
$0–$5/mo
obs — LGTM stack (Grafana, Prometheus, Loki, Tempo, OTelCol) Full observability at :3000/:9090/:3100/:3200/:4317/:4318 Grafana Cloud free tier — 10,000 series, 50 GB logs, 50 GB traces, 50 GB profiles per month. Change OTEL_EXPORTER_OTLP_ENDPOINT to the Grafana Cloud OTLP endpoint. All three existing dashboard JSON files import without changes. $0 Free tier is generous for startup scale

Production Readiness Gaps

Ordered by severity. Critical items must be resolved before accepting real payments. Important items should be resolved within the first week of live traffic.

Target AWS Architecture

Every service maps to either a serverless AWS primitive or the Grafana Cloud free tier. No EC2 instances. No NAT gateways. No load balancers. Monthly cost at startup scale: under $1.
DNS
Route 53 hosted zone — madcatalog.com + api.madcatalog.com + docs.madcatalog.com
TLS
AWS Certificate Manager — free wildcard cert, auto-renewed, attached to CloudFront and API Gateway
Static Sites
S3 bucket (private) + CloudFront distribution — serves Hugo catalog site (madcatalog.com) and docs site (docs.madcatalog.com or /docs behaviour). hugo build in CI uploads to S3.
API
API Gateway HTTP API (api.madcatalog.com) → 14 Lambda functions. Deployed via sam deploy. Throttling already configured in SAM template (burst 200, rate 100 rps).
Database
DynamoDB on-demand — madcatalog-ads, madcatalog-bids, madcatalog-advertisers, madcatalog-subscriptions. Same tables as LocalStack. PITR enabled.
Payments
Stripe sends webhooks directly to POST api.madcatalog.com/webhooks/stripe. No forwarder. Stripe Restricted Key limited to the exact permissions required.
Secrets
SSM Parameter Store SecureString — /madcatalog/prod/stripe-secret-key, /madcatalog/prod/stripe-webhook-secret, /madcatalog/prod/mpp-secret-key, /madcatalog/prod/stripe-network-id. Resolved by SAM at deploy time.
OTel / Obs
Grafana Cloud free tier — OTLP endpoint replaces local OTel Collector. Same Prometheus metrics, Loki logs, Tempo traces. Existing dashboard JSON imports without changes. Alerting configured in Grafana Cloud.
MCP server
Lambda Function URL (deferred from MVP) — Go binary packaged as Lambda, function URL exposed. No API Gateway needed. Or skip entirely; MCP is a developer tool.

Cost Estimate — Startup Scale

Assumes low initial traffic (hundreds to low thousands of requests per day). All AWS free tier limits are perpetual unless noted. Estimates are conservative — actual costs will likely be lower.

Service Usage assumption Monthly cost Note
AWS Lambda <100K invocations/month $0 1M requests + 400K GB-seconds free forever
API Gateway HTTP API <100K requests/month $0 $1.00/million; first 300M/month discounted
DynamoDB on-demand <1M reads + <200K writes/month $0 25 GB storage + 25 WCU + 25 RCU free forever
DynamoDB PITR <1 GB of data ~$0 $0.20/GB/month; negligible at <1 GB
S3 (static sites) <500 MB storage $0 5 GB storage + 20K PUTs + 200K GETs free (12 months)
CloudFront <10 GB transfer/month $0 1 TB/month + 10M requests free forever
ACM (TLS certs) 1 wildcard cert $0 Free when used with CloudFront or API Gateway
SSM Parameter Store ~6 standard SecureString params $0 Standard parameters are free; Advanced is $0.05/param/month
Route 53 1 hosted zone, ~10K queries/month ~$0.50 $0.50/zone/month + $0.40/million queries
Grafana Cloud Free tier $0 10K series, 50 GB logs, 50 GB traces per month
GitHub Actions (CI/CD) <2,000 minutes/month $0 2,000 free minutes/month on public repos; 500 MB artifacts storage
Total estimated monthly cost ~$0.50 Dominated by Route 53 hosted zone fee

Step-by-Step Deployment Plan

Ordered from unblocked-now to needs-runtime-feedback. Each step is independently deployable. The whole sequence can be completed in a single day.

  1. Create SSM parameters for all secrets Put STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, MPP_SECRET_KEY, STRIPE_NETWORK_ID as SecureString under /madcatalog/prod/. Update SAM parameters to use resolve:ssm-secure:/madcatalog/prod/stripe-secret-key syntax. IAM role for the deployer needs ssm:GetParameter on those paths.
  2. Create samconfig.toml with production defaults Set AllowedOrigins=https://madcatalog.com, MaxAdvertiserSpendUSDCents=1000000, OtelExporterOtlpEndpoint=https://otlp-gateway-prod-us-east-0.grafana.net/otlp, TempoTestnet=false for production. Keep DynamoDBEndpoint empty (uses real AWS).
  3. Run sam build && sam deploy --guided The SAM template is complete and already passes sam validate --lint. First deploy uses --guided to populate samconfig.toml. Subsequent deploys use sam deploy (no flags). Note the API Gateway URL from Outputs.
  4. Deploy Hugo sites to S3 + CloudFront hugo build outputs static HTML. Create an S3 bucket, CloudFront distribution, ACM cert, and Route 53 records. Use aws s3 sync public/ s3://madcatalog-site. CloudFront cache invalidation on deploy: aws cloudfront create-invalidation --paths "/*".
  5. Configure Stripe webhook endpoint in Stripe Dashboard Add https://api.madcatalog.com/webhooks/stripe as a webhook endpoint. Select events: payment_intent.succeeded, payment_intent.payment_failed, customer.subscription.*, invoice.*. Copy the signing secret to SSM.
  6. Set up Grafana Cloud and wire OTel Create a free Grafana Cloud account. Generate an OTLP endpoint + API key. Set OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS (Bearer token) in samconfig.toml and redeploy. Import the three dashboard JSON files. Add Prometheus, Loki, and Tempo data sources from the Grafana Cloud connection wizard.
  7. Enable DynamoDB PITR on all four tables aws dynamodb update-continuous-backups --table-name madcatalog-ads --point-in-time-recovery-specification PointInTimeRecoveryEnabled=true (repeat for each table). Alternatively add PointInTimeRecoverySpecification to each table in the SAM template and redeploy.
  8. Add a GitHub Actions deploy workflow New .github/workflows/deploy.yml triggered on push to main. Steps: checkout → setup-node → setup-sam → sam build → sam deploy (using OIDC role, no long-lived AWS keys) → hugo build → aws s3 sync → CloudFront invalidation.
  9. Add Grafana alert rules for critical signals Minimum viable alerts: 5xx rate > 1% for 5 minutes (page), payment failure rate > 5% for 10 minutes (page), p99 latency > 5s for 5 minutes (warn). Route to email or Slack via Grafana Cloud contact points.

What to Skip for MVP

MCP server deployment

The MCP server is a developer tool. It adds no value to end users or advertisers. Skip it for the initial launch; deploy when you need Claude to operate against production data.

WAF

Add WAF after you see real traffic patterns. At launch, API Gateway throttling (already configured) is sufficient. WAF costs ~$5/month base plus $0.60/million requests.

Lambda reserved concurrency

Set limits once you have a baseline. Premature limits cause unnecessary 429s during legitimate traffic spikes. The DynamoDB on-demand mode absorbs reasonable burst natively.

Tempo crypto payment rail

Leave TempoTestnet=true until Tempo mainnet integration is validated. Real money should not flow through an untested crypto payment path. Stripe fiat path is production-ready.

Summary: The backend Lambda code and observability instrumentation are solid. The blocking items before production are purely operational — deploy pipeline, secret management, and five config parameters that need non-default values. With one focused day of work, this stack can be live on AWS for under $1/month.

Generated 2026-06-09 · madcatalog production readiness audit