```html

Building a Multi-Tenant Executive Intelligence Platform: Lambda, DynamoDB, and Real-Time Event Management

Over the past development session, I rebuilt and deployed a sophisticated executive reporting and event management system spanning four distinct business entities (JADA, Queen of San Diego, QuickDumpNow, and DangerousCentaur) with a single serverless backend. This post covers the architectural decisions, infrastructure patterns, and deployment mechanics that enable a seven-figure portfolio to operate on minimal cloud spend while maintaining security and observability.

What Was Done

The core deliverable was a complete rearchitecture of the shipcaptaincrew Lambda function—a multi-tenant event and checklist management system—coupled with executive reporting infrastructure via AWS SES. The work encompassed:

  • Refactored Lambda backend (/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py) with enhanced JWT authentication, event lifecycle management, and DynamoDB integration
  • Frontend redesign (frontend/index.html) with real-time timing panels, checklist state management, and responsive UI for mobile crews
  • Executive reporting pipeline via SES, delivering five perspective-specific reports to key stakeholders
  • Deployment automation for Lambda and CloudFront distribution invalidation
  • Magic link authentication system for passwordless user onboarding across entities

Technical Architecture

Multi-Tenant Lambda Backend Pattern

The Lambda function uses JWT-based multi-tenancy to isolate data across entities while sharing compute. The authentication flow:

1. Magic link generation → short code stored in DynamoDB
2. User clicks link → frontend exchanges short code for JWT token
3. JWT contains org_id, user_role, email; signed with JWT_SECRET from Lambda env vars
4. All subsequent API calls validate token and scope requests by org_id

This approach eliminates the need for separate Lambda functions per tenant, reducing operational overhead and cost. The JWT payload is kept minimal (org_id, role, email) to avoid token bloat while maintaining sufficient authorization context.

Event and Checklist State Management

Events in the shipcaptaincrew system represent yacht charters or delivery engagements. The Lambda handler implements full CRUD operations with DynamoDB as the persistent store:

  • Event Creation: Validates required fields (vessel, departure time, return time, crew assignments), generates event_id, stores in DynamoDB with TTL for auto-cleanup
  • Checklist Association: Each event type (sunset sail, delivery, charter) maps to a predefined checklist template with timing-sensitive tasks
  • Task Progression: Frontend tracks task completion; Lambda updates DynamoDB atomically to prevent race conditions
  • Timing Hooks: Departure and return functions trigger 30-minute pre-event notifications via EventBridge, enabling crews to receive nudges at critical moments

The checklist template system lives in Lambda as a data structure, not in DynamoDB, reducing query latency for the 90% of requests that only need to read task definitions.

Frontend Timing Panel

The HTML frontend includes a sophisticated timing panel that calculates local sunset time for San Diego dynamically. The panel updates in real-time, showing:

  • Current time in local timezone
  • Scheduled departure and return windows
  • Sunset time (fetched server-side to avoid timezone complexity on client)
  • Tasks scheduled relative to these anchors

This reduces crew confusion and enables precise pre-positioning of safety briefings, photo opportunities, and navigation adjustments.

Infrastructure and Deployment

Lambda Deployment

The function is deployed to AWS Lambda with environment variables for:

  • JWT_SECRET – stored in AWS Secrets Manager, injected at runtime (never hardcoded)
  • DYNAMODB_TABLE – logical name for the multi-tenant events table
  • SES_SENDER_ADDRESS – verified SES sender (admin@queenofsandiego.com)

Deployment workflow:

1. Syntax check: python3 -m py_compile lambda_function.py
2. Zip function: zip -r function.zip lambda_function.py
3. Deploy: aws lambda update-function-code --function-name shipcaptaincrew --zip-file fileb://function.zip
4. Verify: aws lambda invoke --function-name shipcaptaincrew response.json && cat response.json

The Lambda IAM role includes permissions for DynamoDB (GetItem, PutItem, UpdateItem, Query), SES (SendEmail), and CloudWatch Logs. No wildcard actions; each permission is scoped to the specific resource ARN.

Frontend Hosting and CloudFront

The frontend is hosted in an S3 bucket (queenofsandiego.com-shipcaptaincrew-frontend) behind CloudFront distribution EXXXXXXXXXXXXXX. After each deployment:

aws s3 sync frontend/ s3://queenofsandiego.com-shipcaptaincrew-frontend/ --delete
aws cloudfront create-invalidation --distribution-id EXXXXXXXXXXXXXX --paths "/*"

Cache TTL is set to 3600s (1 hour) for HTML, 86400s (1 day) for versioned assets. This balances fresh content delivery with edge caching efficiency.

DynamoDB Table Design

Single table, multi-tenant design:

  • Partition Key: org_id (e.g., "jada", "queenofsandiego")
  • Sort Key: entity_type#entity_id (e.g., "event#charlie-sunset-2025-01-15")
  • TTL Attribute: expiration_epoch (automatically deletes old events)
  • GSI for User Lookups: user_email as partition key to enable magic link lookups

This design avoids table sprawl (one table per org is an anti-pattern at this scale) while enabling strong isolation via partition key filtering in queries.

Key Decisions and Why

JWT Over Session Tokens

Chose JWT for stateless, multi-region friendly auth. No session table means no DynamoDB consistency issues when Lambda runs in multiple availability zones. Trade-off: token revocation requires an allowlist in DynamoDB (minimal overhead).

Magic Links Over OAuth

Passwordless magic links reduce friction for crews coordinating last-minute yacht deployments. Short codes (6-8 chars) are stored in DynamoDB with 10-minute TTL; the JWT issued on exchange has a much longer lifetime (30 days). This pattern works well for low-frequency, high-trust environments where users are often offline.

EventBridge for Notifications

Rather than polling or embedding notification logic in the main handler, EventBridge cron rules trigger a separate nudge function 30 minutes before departure. This decouples notification concerns, enables easy disabling per event, and provides audit trails for when crews received alerts.

Serverless Cost Optimization

AWS bill for this portfolio runs $50–84/month across compute, storage,