Deploying a Multi-Tenant Event Management Platform: Lambda, S3, and Real-Time State Management for Charter Operations
Over the past development session, we deployed a complete event management system for QueenofSanDiego's charter operations, integrating AWS Lambda serverless compute, S3-based frontend hosting, CloudFront CDN distribution, and DynamoDB for persistent state. This post walks through the architecture decisions, deployment patterns, and infrastructure configuration that enables real-time checklist synchronization, role-based access control, and event lifecycle management across multiple charter types.
What Was Done
We built and deployed three core components:
- Lambda function (`/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py`) — RESTful event handler with JWT authentication, event CRUD operations, checklist state management, and role claim workflows
- Frontend SPA (`/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/frontend/index.html`) — Single-page application serving interactive event dashboards, checklist panels, and real-time role designation UI
- Infrastructure automation — EventBridge cron rules, DynamoDB table configuration, SES email triggers, and CloudFront cache invalidation pipelines
The system handles charter event creation, crew role assignment (captain, first mate, deckhand), waiver workflows, and departure/return timing synchronization. Magic link authentication enables passwordless invite-based access, with JWT tokens scoped to user identity and role.
Technical Architecture
Lambda Function Design
The Lambda function serves as the single API endpoint for all event operations. It's organized around a request router that dispatches based on HTTP method and path:
POST /events → Create new event with initial crew assignments
GET /events/{event_id} → Fetch event details + checklist state
PUT /events/{event_id} → Update event metadata (departure time, return time, etc.)
POST /events/{event_id}/checklists → Initialize checklist from template
PUT /events/{event_id}/checklists/{checklist_id} → Update checklist item state
POST /events/{event_id}/roles/{role}/claim → Crew member claims a role
POST /events/{event_id}/roles/{role}/release → Crew member releases assigned role
GET /events/{event_id}/guests → List all crew on event (waiver status, roles)
Key handlers within the Lambda codebase:
authenticate_request()— Extracts and validates JWT from Authorization header; rejects unauthenticated requests with 401create_event(body, user_id)— Validates required fields (vessel_id, event_date, captain_id), generates event_id, stores in DynamoDB with initial role assignmentsload_checklist(event_id, checklist_template_id)— Fetches checklist template from DynamoDB, instantiates items with pending state, binds to eventclaim_role(event_id, role, user_id)— Transitions role state from unassigned → claimed, updates crew roster, sends confirmation email via SESget_sunset_time(event_date)— Calls local timeserver API to compute sunset, used for departure/return timing validation
Environment variables configure:
DYNAMODB_EVENTS_TABLE— Table name for event documentsDYNAMODB_CHECKLISTS_TABLE— Table name for checklist templates and instancesSES_SENDER_EMAIL— Verified SES sender address (hardcoded to `admin@queenofsandiego.com`)JWT_SECRET— Symmetric key for HS256 token validation (stored in AWS Secrets Manager)FRONTEND_URL— Base URL for magic link generation
Frontend State Management
The SPA (`index.html`) implements a lightweight state machine for event lifecycle visibility:
- Event view — Displays crew roster with role badges, checklist progress bars, and timing panels (departure/return countdown)
- Checklist panel — Interactive checklist items with tri-state toggles (pending, in-progress, complete); real-time sync via AJAX
- Role claim modal — Allows authenticated crew to self-designate for unassigned roles; triggers Lambda claim handler
- Waiver modal — Renders waiver HTML from SES template, captures e-signature, POSTs acknowledgment to Lambda
JavaScript hooks into timing functions via onDeparture() and onReturn() callbacks, triggering state transitions in the backend when elapsed time matches computed sunset window.
Infrastructure & Deployment
S3 & CloudFront Configuration
Frontend assets are deployed to an S3 bucket configured for static website hosting:
S3 Bucket: queenofsandiego-shipcaptaincrew-frontend
Region: us-west-2
Versioning: Enabled
Block Public Access: All disabled (required for CloudFront public read)
CloudFront distribution handles edge caching and HTTPS termination:
CloudFront Distribution ID: E2ABCD1234567 (example)
Origin: S3 bucket website endpoint
Cache Behavior:
- Default TTL: 300 seconds (5 minutes) for index.html
- Max TTL: 86400 seconds (1 day) for versioned assets
- Compress: Enabled (gzip for HTML, CSS, JS)
After each frontend deployment, we invalidate the CloudFront cache for /index.html and /* to ensure fresh content:
aws cloudfront create-invalidation \
--distribution-id E2ABCD1234567 \
--paths "/index.html" "/*"
Lambda Deployment & Versioning
The Lambda function is zipped and deployed via the AWS CLI with alias management for safe rollback:
cd /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew
zip -r lambda_deployment.zip lambda_function.py
aws lambda update-function-code \
--function-name shipcaptaincrew-api \
--zip-file fileb://lambda_deployment.zip
aws lambda publish-version --function-name shipcaptaincrew-api
aws lambda update-alias \
--function-name shipcaptaincrew-api \
--name production \
--routing-config AdditionalVersionWeight=10 \
--function-version 42
Before deployment, syntax validation is performed to catch import errors and undefined references:
python3 -m py_compile lambda_function.py && echo "Syntax OK"
EventBridge Automation
Scheduled cron rules trigger reminder emails and state transitions:
Rule: ptb_nudge
Schedule: cron(0 18 * * ? *) #