Multi-Domain Guest Booking Workflow: Coordinating S3, CloudFront, Lambda, and DynamoDB for Charter Event Management
This post documents the architecture and implementation decisions behind a unified guest booking system that coordinates multiple AWS services, custom Lambda handlers, and domain routing logic to create a seamless experience for charter crew coordination.
Problem Statement
The core challenge: when a new charter booking arrives (via Boatsetter or direct), we need to simultaneously:
- Create an internal calendar entry for operations planning
- Notify crew automatically with authenticated magic links
- Generate and host a guest-facing confirmation page
- Provide crew with a detailed checklist page for prep
- Track financials and crew assignments
The workflow had to work across two separate domains (sailjada.com and queenofsandiego.com), use existing CloudFront distributions with custom function rewrites, and maintain secure authentication without leaking sensitive data to frontend consumers.
Technical Architecture
Domain and S3 Organization
Guest-facing pages are hosted on queenofsandiego.com (CloudFront distribution ID: E***), with the S3 origin bucket queenofsandiego.com. This separation from sailjada.com serves multiple purposes:
- Brand separation: Guests see a branded interface separate from crew tools
- Subdomain routing: Pages are served at
/g/{BOOKING_ID}.htmlpaths - Friendly URLs: Custom mapping from booking ID to human-readable slugs (e.g.,
/g/boatsetter-may30.html)
The CloudFront function (attached to viewer request) implements path rewriting logic:
// CloudFront Function: queenofsandiego.com path rewriting
// Maps /g/ requests to flat .html files per S3 convention
if (request.uri.startsWith('/g/')) {
request.uri = request.uri + '.html'; // if not already .html
}
This allows clean URLs while maintaining S3 flat-file structure without folder hierarchies.
Lambda Event Coordination
The ShipCaptainCrew Lambda (/tmp/scc-lambda-src/lambda_function.py) handles event creation via two distinct pathways:
- CloudFront path: Via
queenofsandiego.comdomain (header stripping issue) - Direct API Gateway: Bypassing CloudFront to preserve auth headers
The Lambda service key authentication uses environment variable SERVICE_KEY_HASH (bcrypt hash of actual key). Requests include:
POST /event
X-Service-Key: {SERVICE_KEY}
Content-Type: application/json
{
"event_name": "May 30 Boatsetter Charter",
"crew_ids": ["crew-001", "crew-002"],
"captain_id": "captain-001",
"date": "2024-05-30T09:00:00Z",
"duration_hours": 3,
"notes": "Guest count: 6, Dock: Sheraton Marina"
}
The Lambda validates the key, creates a DynamoDB record in the scc_events table, and auto-generates magic link tokens for crew notifications.
Guest Page Generation and Hosting
Guest pages are generated as static HTML files and uploaded directly to s3://queenofsandiego.com/g/. The file naming convention:
// Booking ID example: xhqgmdh (7-char alphanumeric)
s3://queenofsandiego.com/g/boatsetter-may30.html
// Also kept as fallback:
s3://queenofsandiego.com/g/xhqgmdh.html
CloudFront caching is invalidated immediately after upload using invalidation requests against the distribution.
Authentication and Security Decisions
Why Hash Service Keys in Lambda Env
Storing plaintext keys in Lambda environment variables is a security anti-pattern. Instead:
- The actual service key is stored in AWS Secrets Manager
- Lambda env var
SERVICE_KEY_HASHcontains the bcrypt hash - Incoming requests are hashed on-the-fly and compared:
bcrypt.checkpw(request_key, env_hash) - Logs never reveal the actual key
Why Bypass CloudFront for API Calls
CloudFront functions strip non-standard headers (including custom auth headers like X-Service-Key). Solution: hit the API Gateway endpoint directly instead of routing through the CDN:
// Instead of:
POST https://queenofsandiego.com/event
// Use:
POST https://{API_GATEWAY_ID}.execute-api.us-west-2.amazonaws.com/event
This preserves auth headers while API Gateway resource policies ensure only internal services (dashboard Lambda, scheduled tasks) can invoke it.
Data Flow and Crew Notification
When an event is created, the Lambda:
- Validates the service key
- Writes event record to DynamoDB (
scc_eventstable) - Generates unique magic link tokens for each crew member
- Invokes SES or sends via integrated notification system
- Email includes deep link:
https://queenofsandiego.com/crew/xhqgmdh?token={MAGIC_TOKEN}
Magic tokens are single-use and time-limited (24–48 hours) to prevent unauthorized access.
Financial Data Sensitivity
A critical security decision: revenue and captain fee information must NOT appear in crew-facing event notes. During booking creation, notes are sanitized before storage:
// In Lambda event handler:
event_notes = sanitize_for_crew_visibility(notes)
// Removes: revenue, captain_fee, net_margin
// Keeps: guest count, special requests, dock location
If historical events had sensitive data, a DynamoDB direct update can remove it retroactively (for PATCH operations).
Key Infrastructure Resources
- S3 buckets:
sailjada.com(internal tools),queenofsandiego.com(guest pages) - CloudFront distributions:
E***for queenofsandiego.com with viewer request function - Lambda functions: ShipCaptainCrew handler, dashboard update trigger, calendar integration
- DynamoDB tables:
scc_events,crew,bookings - API Gateway: Resource-based policy restricting to internal callers
- Route53:
queenofsand