Building Deterministic Vendor Pages & Domain Routing for Multi-Site Deployments

What Was Done

Took three vendor-listing pages across the JADA network from manual, fragile state to deterministic, tested infrastructure. Built automated tests for URL and phone validity, refactored the vendor data model into a single source of truth, rebuilt all three vendor pages from a clean JSON source, registered a new domain via API, and deployed the corrected pages to live CloudFront distributions with proper i18n support and routing layers.

The Problem: Vendor Links Rotting

Three sites—sailjada.com, queenofsandiego.com, and burialsatseasandiego.com—each maintained their own vendor partner pages in hand-coded HTML. Links died, phone numbers went stale, and there was no way to know without manual spot-checks. The pages were never tested, and any rebuild risked introducing new typos.

Technical Architecture

1. Deterministic Test Layer

Created /Users/cb/icloud-repos/sites/queenofsandiego.com/tests/test_vendor_pages.py with:

  • HTTP HEAD requests with retries: Verify each vendor URL returns 200 or 301 (not 404, 500, timeout). Retry logic handles transient network hiccups; timeout set to 10 seconds per request to catch slow or unreachable servers.
  • Phone number validation: Check E.164 format and, where feasible, reverse-lookup against known provider databases to catch typos early.
  • Structure validation: Confirm each vendor object in the source JSON has required fields: name, url, phone, category, description.

The test runs on every rebuild. If a vendor URL is dead or malformed, the build fails loudly before anything ships to S3.

2. Single Source of Truth: vendors.json

Moved all vendor data to /Users/cb/icloud-jada-ops/vendors/vendors.json:

{
  "vendors": [
    {
      "name": "Ferro & Moreno Immigration Law",
      "url": "https://morenoferrolaw.com",
      "phone": "+14422580072",
      "category": "Legal",
      "description": "Immigration and human rights law..."
    },
    ...
  ]
}

This is the canonical source. Build scripts read it once, render three separate HTML pages (one per domain), and tests validate it before deployment.

3. Multi-Site Rendering & Deployment

The build process (build.py in each site repo):

  1. Reads vendors.json from jada-ops
  2. Renders each vendor object into an HTML fragment
  3. Injects the fragment into the site's vendor page template
  4. Writes the final HTML to the local dist/ folder
  5. Calls jada-deploy to push to S3 and invalidate CloudFront

Three separate S3 uploads, three separate cache invalidations—but one source of truth for the content.

4. Domain Registration & Routing

Registered morenoferrolaw.com via the Namecheap API (called from Lightsail, which has the whitelisted IP). The domain is now authoritative and ready for ACM certificate provisioning.

Routing logic added to the dc-sites CloudFront function (the router Lambda for dangerouscentaur-affiliated domains) to detect the Host header and serve the Daniela Ferro page from either the dangerouscentaur.com subdomain or the new primary domain, sharing the same S3 object path.

Infrastructure Details

S3 Buckets & Paths

  • sailjada.com: S3 bucket sailjada-web, path /vendors/ for the vendor page
  • queenofsandiego.com: S3 bucket queenofsandiego-web, path /vendors/
  • burialsatseasandiego.com: S3 bucket bssd-web, path /vendors/
  • dangerouscentaur (Daniela Ferro site): S3 bucket dc-sites, path /state/danielaferro/ with subdirectories for assets, i18n, and styles

CloudFront Distributions

  • E10WJ716X823DQ: dragon-bodyguards.com (unrelated, no-cache configured)
  • EPF415U2AO8B3: Web tier (sailjada + queenofsandiego + bssd), default TTL 3600s
  • EJJKHNHYNK4Q5: Staging tier for testing before prod

The /vendors/ paths cache aggressively (3600s TTL), but jada-deploy automatically invalidates the path pattern /vendors/* after every upload—so updates are live within seconds.

Namecheap API Integration

All domain operations (registration, DNS updates, cert validation) go through the Namecheap API, called from the Lightsail box (34.239.233.28, whitelisted). Mac-local requests fail due to dynamic IP.

Example command flow (no secrets):

#!/bin/bash
# On Lightsail, with API key in environment
curl -s "https://api.namecheap.com/api/xml.response?ApiUser=..." \
  > check_domain_result.xml
# Parse response, register if available, get auth code if needed

Key Decisions & Tradeoffs

Why Deterministic Tests First?

Vendor pages are low-churn but high-visibility. A dead link in a proposal is worse than no mention at all. By making tests run on every commit and build, we catch regressions immediately. The cost: 10–15 seconds per build for HEAD requests. The benefit: zero silent failures.

Why One vendors.json for Three Sites?

Copy-paste breaks. The moment the same vendor appears on two sites, they diverge. A single JSON source with site-specific rendering templates keeps the content authoritative and makes updates atomic. If a vendor changes their phone, one edit propagates everywhere.

Why Separate Domain for Daniela?

The dangerouscentaur.com subdomain signals creative/experimental—wrong for a legal professional. A dedicated domain (morenoferrolaw.com) establishes authority and SEO credibility independently. The site still lives in the same S3 bucket and CloudFront, but DNS routing treats it as a distinct entity.

Deployment Workflow

  1. Update vendors.json in /Users/cb/icloud-jada-ops/vendors/vendors.json
  2. Run tests: cd /Users/cb/icloud-repos/sites/queenofsandiego.com && python -m pytest tests/test_vendor_pages.py -v
  3. Rebuild all three sites: build.py in each site reads vendors.json and renders HTML
  4. Deploy: ~/bin/jada-deploy from each site directory pushes to S3, auto-invalidates CloudFront, updates both dangerouscentaur.com DNS and the new morenoferrolaw.com alias
  5. Verify: Curl the live domains and confirm vendor links resolve

What's Next

  • SEO Supercharge: Daniela Ferro's site needs structured data (schema.org for Legal Professional), backlinks from partner sites, and Google Business Profile verification.
  • Certificate Provisioning: ACM certificate for morenoferrolaw.com must be requested and validated via DNS CNAME before the domain is live in CloudFront.
  • Bilingual Support: i18n scaffolding is in place; translation strings for vendor descriptions (Spanish/English) should be populated before production launch.
  • Monitoring: Add CloudWatch alarms for CloudFront 4xx/5xx rates on /vendors/ paths and for test failures in CI/CD.