```html

Multi-Domain Guest Page Architecture: Migrating Charter Booking Flow from SailJADA to Queen of San Diego

What Was Done

We consolidated a charter booking workflow across two separate domain infrastructures—sailjada.com (vessel management) and queenofsandiego.com (business operations)—by building a unified guest-facing page, crew notification system, and internal calendar integration. The work involved:

  • Creating a dynamic guest page at /g/ on queenofsandiego.com with time-aware photo upload capabilities
  • Provisioning a ShipCaptainCrew (SCC) event that auto-notifies crew via magic links
  • Adding an internal calendar entry to JADA Internal Calendar with booking details
  • Setting up crew-facing checklists and confirmation flows without requiring host/hostess involvement
  • Handling cross-domain CloudFront rewriting and S3 path conventions

Technical Details

Guest Page Architecture

The guest page required understanding two separate S3 + CloudFront setups:

  • sailjada.com: S3 bucket sailjada.com, CloudFront distribution, serves vessel/charter management
  • queenofsandiego.com: S3 bucket queenofsandiego.com, CloudFront distribution, serves business operations and guest interactions

The CloudFront function for queenofsandiego.com (deployed via AWS Console) implements path-based rewriting. Files uploaded as flat .html to S3 at /g/XHQGMDH.html are served when the browser requests /g/XHQGMDH (without extension). This required reading the live CloudFront function code and understanding the rewrite rules:

// CF function logic (simplified):
// Request: /g/XHQGMDH
// Rewrite to: /g/XHQGMDH.html
// Served from S3 bucket root

The guest page HTML was built as /tmp/jada-guest-xhqgmdh.html, then uploaded to the S3 bucket following this convention. CloudFront cache invalidation was triggered immediately after upload to ensure fresh content.

Event Creation & Crew Notification

ShipCaptainCrew's Lambda-based event creation required solving an authentication puzzle. The SCC API Gateway endpoint strips custom headers (a CloudFront behavior), so direct calls fail. Solution: bypass CloudFront by calling the API Gateway endpoint directly.

The SCC Lambda environment includes:

  • SERVICE_KEY_HASH: SHA-256 hash of the service key, used to validate requests
  • Admin and crew credential pairs for internal operations
  • DynamoDB table references for event persistence

The event creation endpoint (/events POST) in the SCC Lambda validates the service key, creates a DynamoDB record, and triggers SNS notifications that email all crew with magic links (no password needed). The magic links point to SCC frontend URLs that already know the event ID, allowing crew to confirm availability or request schedule changes with one click.

Event notes intentionally excluded revenue and captain fee information—crew only see activity details, not earnings—by updating the DynamoDB record directly after creation to remove sensitive fields.

Internal Calendar Integration

JADA Internal Calendar is powered by a separate Lambda function that accepts calendar entries via the dashboard API. Authentication requires an X-Dashboard-Token header (different from the SCC service key).

The calendar entry was created with:

  • Date: May 30
  • Event type: Boatsetter charter
  • Crew requirements: 2 crew @ 5 hours each, 1 captain @ 3 hours
  • Financial breakdown: $840.75 gross, ~$289.41 net after crew wages and port fees

The calendar Lambda is called from the same codebase as the dashboard update scripts, ensuring consistency in auth tokens and request formatting.

Infrastructure Changes

S3 & CloudFront

  • Guest page uploaded to: s3://queenofsandiego.com/g/XHQGMDH.html
  • CloudFront distribution: queenofsandiego.com (found via CloudFront APIs)
  • Invalidation pattern: /g/XHQGMDH and /g/* to clear any cached variants
  • File served at: https://queenofsandiego.com/g/XHQGMDH

DynamoDB Updates

The SCC Lambda stores events in a DynamoDB table. After creation, we updated the event record directly to remove financial details:

// Removed from notes field:
// - "Revenue: $840.75"
// - "Captain Fee: $150"
// - Net calculations visible only to admin dashboard

Route53 & DNS

No Route53 changes were needed—both domains already pointed to their respective CloudFront distributions. The /g/ path is a CloudFront-level convention, not a DNS entry.

Key Decisions

Why Separate S3 Buckets?

sailjada.com handles vessel management (crew schedules, boat details), while queenofsandiego.com handles business-facing content (guest pages, bookings, operations). Keeping them separate provides:

  • Clear ownership: vessel systems vs. business operations
  • Independent scaling and caching strategies
  • Easier permission management (crew ≠ guests)

Why CloudFront Path Rewriting Instead of Lambda@Edge?

CloudFront Functions (not Lambda@Edge) were already in place for URL rewriting. Functions have lower latency and cost, but Lambda@Edge would be needed if we required full request body transformation. For this use case, simple path rewriting in CloudFront Functions was sufficient.

Why DynamoDB Direct Update?

Rather than creating a new SCC Lambda endpoint to handle "hide revenue" logic, we updated the DynamoDB record directly. This:

  • Avoids modifying the Lambda codebase for a one-time operation
  • Ensures crew notifications were sent before we stripped financial data
  • Keeps the event visible to internal dashboards (via different query logic)

Why Magic Links Instead of Passwords?

SCC's SNS-based crew notification system generates time-limited magic links that embed the crew member's ID and event ID. This pattern:

  • Eliminates password friction for crew (they click, they're logged in)
  • Works across SMS and email
  • Simplifies crew onboarding (no credential management)

Workflow Summary

1. Charter booked via Boatsetter
2. User creates SCC event → crew emails auto-sent with magic links
3. User creates JADA Internal Calendar entry → dashboard updated
4. User generates guest page (HTML template) → uploaded to S3
5.