```html

Building the Queen's Fleet Hub: Wiring a Multi-Site Network with CloudFront, Route53, and Automated Validation

What Was Done

We launched thequeensfleet.com as a central hub for 16 regional destination sites (queenof[city].com), discovered that three destinations were missing from DNS and CloudFront routing, and implemented automated regression testing to prevent this from happening again.

The discovery came through QA: card images for the Pacific, Atlantic, and Mazatlán destinations rendered broken on the hub, while others loaded correctly. Root cause was infrastructure configuration — the S3 objects existed, but the domains had never been wired into CloudFront or Route53.

Technical Details: The Architecture

Domain Registration and Certificates

We registered thequeensfleet.com via the Namecheap API (called from the Lightsail ops box; API credentials stored in environment, not committed). The domain was provisioned with a wildcard ACM certificate in us-east-1 covering *.thequeensfleet.com, validated via CNAME records created in Namecheap's API-managed DNS.

The Hub Structure

The hub page is generated by build/build_hub.py, which templates card metadata (destination name, image URL, slug) and outputs static HTML. Each card references a card image at https://[slug].queenofsandiego.com/assets/0.jpg — this is critical because it decouples the hub domain from destination image hosting.

The hub itself is served by CloudFront distribution E176JRMB92GPAJ, which was configured with:

  • An S3 origin (qos-city-sites bucket) pointing to the hub's index.html
  • A wildcard origin behavior for all other paths, routing to the CloudFront function qos-city-router
  • Aliases for thequeensfleet.com and www.thequeensfleet.com

The qos-city-router CloudFront function (in build/router.js) inspects the Host header and route the request to the appropriate S3 prefix — so a request to thepacific.queenofsandiego.com/assets/0.jpg retrieves from s3://qos-city-sites/city-dist/thepacific/assets/0.jpg.

The Missing Piece: DNS and Distribution Aliases

The Pacific, Atlantic, and Mazatlán S3 objects and HTML existed, but their hostnames were never added to CloudFront's alias list or Route53. This meant:

  • Direct HTTPS requests to thepacific.queenofsandiego.com failed (no cert match, no distribution alias)
  • The hub's card image references couldn't resolve
  • Card tiles rendered with broken image placeholders

The Fix

We added three aliases to CloudFront distribution E176JRMB92GPAJ:

thepacific.queenofsandiego.com
theatlantic.queenofsandiego.com
mazatlan.queenofsandiego.com

Created matching Route53 A and AAAA records (in the queenofsandiego.com hosted zone) pointing to the distribution's CloudFront domain ([dist-id].cloudfront.net).

Validated from the Lightsail box that all three hostnames returned 200 with valid card images, and that the hub now displays all 16 destination photos correctly.

Infrastructure Changes

CloudFront Configuration

  • Distribution: E176JRMB92GPAJ
  • New aliases (3): thepacific, theatlantic, mazatlan (all under queenofsandiego.com)
  • Total aliases after fix: 18 (including hub domain, www variant, and 16 destination slugs)
  • Certificate: wildcard ACM cert covering *.queenofsandiego.com (already in place, no redeployment needed)
  • Cache behaviors: origin for hub root + HTML, CloudFront function for routing

Route53

Added to the queenofsandiego.com hosted zone (3 new records, 1 per missing destination):

  • A record: thepacific → CloudFront distribution alias
  • AAAA record: thepacific → CloudFront distribution IPv6 alias
  • Repeated for theatlantic and mazatlan

S3 Structure

All destination content was already in S3 at s3://qos-city-sites/city-dist/[slug]/. No changes needed; the router function + CloudFront aliases were the missing pieces.

Regression Testing

We wrote a new test in tests/test_hub.py that runs nightly:

test_every_hub_card_host_serves_its_image()

This test curls the card image URL for all 16 destinations from the hub HTML and asserts 200 responses. It distinguishes real misconfigurations (test fails, alerts human) from transient network drops (test skips, no false positive).

The test catches the scenario that QA had to manually report: if a new slug is deployed without wiring its DNS, the regression suite fails immediately instead of waiting for someone to notice broken images.

Key Decisions

Why wildcard certificate instead of per-destination certs?
A single wildcard cert covers all 16 destination hostnames now and future destinations without redeployment. The tradeoff is that all destinations must live under the same domain (*.queenofsandiego.com), which we already owned.

Why CloudFront function routing instead of separate origins?
Separate origins per destination would require 16+ CloudFront distributions or complex cache behavior rules. A single function inspecting the Host header and prefixing the S3 path keeps the distribution lean, reduces DNS records, and scales without CloudFront changes. The router is stateless and runs in us-east-1 (all requests, microsecond latency).

Why CNAME/alias records in Route53 instead of CNAME flattening?
Route53 alias records (AWS-native) evaluate at zone apex and serve Route53's managed nameservers directly. Flattening would be necessary only if the zone were on another registrar; keeping it AWS-native for operational consistency.

Why mandatory regression test?
The broken image issue was silent — QA had to visually inspect 16 cards to catch three failures. Automated validation catches the misconfiguration automatically, reducing mean time to detection from "whenever someone visits" to "nightly test run."

Documentation and Ops Handoff

Updated the new-slug deployment checklist in:

  • sites/queenof-cities/CLAUDE.md — system context
  • sites/queenof-cities/infra.md — infrastructure diagram and resource reference
  • sites/queenof-cities/CONTEXT.md — domain, distribution, and Route53 IDs
  • decisions/2026-07-05-hub-broken-cards-dns-fix.md — incident and fix for future reference

What's Next

The hub is now fully operational with all 16 destination card images rendering and linked. Future destinations (new slugs) must follow the checklist: add S3 content, add CloudFront alias, add Route53 A/AAAA records, and the regression test validates automatically. The infrastructure can scale to additional cities without architectural changes.

```