```html

Building a Centralized Hub for Multi-City Domains: CloudFront, Lambda@Edge Routing, and DNS Architecture

What Was Done

The Queen's Fleet needed a consolidated hub domain to serve as the entry point for a network of city-specific sites (queenof[city].com). Rather than duplicating infrastructure for each city, we registered thequeensfleet.com and wired it into an existing hub+spokes architecture using CloudFront, Lambda@Edge routing functions, and centralized DNS management via Namecheap API.

Technical Details

Domain Registration and ACM Certification

We registered thequeensfleet.com programmatically via the Namecheap API from a Lightsail instance (API access is restricted to instances in that account for security). The registration used existing domain contact information already stored in Namecheap, avoiding manual steps and drift.

Immediately after registration, we requested an ACM certificate for thequeensfleet.com and *.thequeensfleet.com in the us-east-1 region (required for CloudFront Lambda@Edge functions). DNS validation was automated: the validation CNAME records were added to the Namecheap zone via API, and we polled the ACM API until the certificate reached ISSUED status before proceeding to CloudFront distribution creation.

Hub Page Build Pipeline

The hub page is generated by build/build_hub.py, which mirrors the pattern used for city-specific pages. The build process:

  • Reads the site registry (stored as YAML in the project root) to discover all registered city domains
  • Generates an HTML template that lists each city as a clickable destination, with links to queenof[city].com endpoints
  • Outputs the built hub to an S3 origin bucket (referenced by CloudFront as the primary origin for the hub distribution)
  • Includes test coverage via tests/test_hub.py, which verifies the build output contains expected city links and valid HTML structure

The build is deterministic and reproducible—running it twice produces identical output (exact byte match), which allows us to verify deployments via ETag comparison before CloudFront invalidation.

CloudFront Distribution and Lambda@Edge Routing

We created a new CloudFront distribution (ID: E12G09B4XHLS8Y) with thequeensfleet.com and www.thequeensfleet.com as alternate domain names, backed by the ACM certificate. The distribution has two origins:

  • S3 origin: Serves static hub content (the built HTML from build_hub.py)
  • City-router behavior: Uses a Lambda@Edge function (qos-city-router) to intelligently route requests to city-specific backends

The qos-city-router function lives in build/router.js and runs at CloudFront edge locations. Its logic:

  • Intercepts requests for subdomains (e.g., newyork.thequeensfleet.com)
  • Strips the subdomain and rewrites the origin to the corresponding queenof[city].com CloudFront distribution
  • Caches responses at the edge, avoiding repeated rewrites for the same city
  • Falls back to the hub origin for apex and www requests

This pattern eliminates the need for DNS A/CNAME records per city—all routing logic is centralized in one Lambda function deployed to 500+ edge locations globally via Lambda@Edge.

DNS Architecture

Namecheap DNS records for thequeensfleet.com:

  • Apex (@) and www: ALIAS records pointing to the CloudFront distribution domain name
  • All subdomains (*): CNAME or wildcard ALIAS records, also pointing to CloudFront (Lambda@Edge handles the subdomain-to-city mapping)

We used Namecheap's ALIAS record type (DNS flattening) rather than traditional CNAME at the apex, which allows the root domain to resolve correctly while avoiding the "CNAME at apex" restriction.

Infrastructure and Deployment

Site Registry Integration

The site registry (site-registry.yml) is the source of truth for which cities are available. When we added thequeensfleet.com, we updated the registry to mark it as a "ventures" entry (distinct from individual city domains). The hub build process reads this registry to generate accurate city listings.

The registry format allows us to:

  • Enable/disable cities without code changes
  • Tag cities by region or theme
  • Version the registry alongside code for auditability

Deployment Process

Deploying changes to the hub involves:

  1. Run python build/build_hub.py to generate updated HTML
  2. Sync output to the S3 origin bucket: aws s3 sync build/output/ s3://queenof-hub-origin/ --delete
  3. Verify the S3 object ETag matches the expected build checksum
  4. Invalidate the CloudFront cache for the hub distribution: aws cloudfront create-invalidation --distribution-id E12G09B4XHLS8Y --paths "/*"
  5. Poll CloudFront status until Deployed is reported
  6. Verify the live site serves the new content via HTTPS

Each step has a corresponding assertion in tests/test_hub.py, which means we can run the full test suite before and after deployment to catch regressions.

Key Decisions

Why Lambda@Edge over DNS per-city: Each city previously had its own DNS CNAME chain. Consolidating routing into a Lambda@Edge function reduced DNS lookup overhead and eliminated the need to manage dozens of DNS records. Edge caching means the rewrite overhead is paid once per cached response, not per request.

Why S3 + CloudFront for the hub: Static HTML + CDN scales to any traffic volume with zero infrastructure management. The hub is read-only content (cities don't change mid-day), so caching is simple and safe. Build-time generation means we validate the output locally before it reaches users.

Why Namecheap API over manual DNS: Programmatic DNS management is reproducible and auditable. The domain registration and certificate validation run in CI/CD without manual intervention, reducing toil and human error.

Why build-time testing: Tests run against the generated HTML artifact before deployment, not against production. This catches broken city links at build time, not when users click them.

What's Next

Once DNS propagation completes (monitored via background polling), the hub will be live at https://thequeensfleet.com. Subsequent work includes:

  • Monitoring CloudFront cache hit ratios and Lambda@Edge invocation metrics in CloudWatch
  • Adding city-specific landing pages (currently the hub lists cities, but each needs custom copy/imagery)
  • Integrating with avatar generation and asset management infrastructure
  • Setting up CI/CD to rebuild and redeploy the hub automatically when the site registry changes

The infrastructure is now ready to scale: adding a new city is a single-line registry change + rebuild, no CloudFront or DNS reconfiguration needed.

```