```html

Building a Multi-Tenant Executive Intelligence Platform: Infrastructure, Security, and Reporting Automation

Over the past development session, we built and deployed a comprehensive executive reporting system across a four-entity portfolio (JADA, QueenofSanDiego, QuickDumpNow, DangerousCentaur) with automated SES delivery, Lambda-based report generation, and strategic dashboard integration. This post details the architectural decisions, infrastructure patterns, and technical implementation.

What Was Built

We constructed an automated intelligence pipeline that generates role-specific executive reports tailored to five distinct C-suite personas:

  • CEO Report: Asset inventory, revenue gaps, operational shortfalls, KPI framework
  • CTO Report: Stack audit, security hardening roadmap, cost optimization, dev cycle analysis
  • CFO Report: Burn rate modeling, capital deployment framework, break-even analysis
  • CMO Report: Channel visibility matrix, OTA sequencing, blast campaign ROI modeling
  • Accounting Report: Chart of accounts, revenue recognition, expense categorization

This expanded to eight total reports by adding domain-specific audits: 3028 51st St Rental operations, Expert Yacht Delivery fulfillment, and DangerousCentaur client portfolio billing.

Technical Architecture

Report Generation & Delivery

We created two parallel Python modules to handle report generation and SES delivery:

  • /Users/cb/Documents/repos/tools/send_exec_reports.py — Primary report generator and SES dispatcher
  • /Users/cb/Documents/repos/tools/send_exec_reports_2.py — Secondary variant for A/B testing delivery logic

The system uses AWS SES (Simple Email Service) with verified sender identity admin@queenofsandiego.com to dispatch reports to c.b.ladd@gmail.com with BCC configuration. This approach leverages SES's 50,000 daily free tier for development while maintaining audit trails through BCC delivery.

Why SES over alternatives? SES integrates directly with IAM roles, requires no external API keys in application code, and provides DKIM/SPF authentication out-of-the-box. For production multi-tenant delivery, this pattern scales to thousands of recipients without rate-limiting concerns.

Lambda Integration & Frontend

The core application logic lives in:

  • /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py — Event handler, authentication, checklist state management
  • /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/frontend/index.html — React-based SPA with JWT auth, event creation, checklist UI

The Lambda function underwent 10+ iterations during this session, adding:

  • Magic link authentication: Time-limited JWT tokens generated server-side, validated on every protected endpoint
  • Role-based access control: Captain, Crew, Guest, Admin roles with claim/release workflows
  • Timing logic: Departure/return calculation hooks, sunset time integration for San Diego events
  • Waiver page integration: On-hold status enforcement, guest eligibility checks
  • EventBridge cron triggering: Automated nudge notifications via ptb_nudge rule

Deployment Pipeline

Deployments followed a two-phase pattern:

Phase 1: Lambda Function

# Syntax validation
python -m py_compile lambda_function.py

# Packaging
zip -r lambda_deployment.zip lambda_function.py dependencies/

# AWS CLI deployment (no credentials shown)
aws lambda update-function-code \
  --function-name shipcaptaincrew-prod \
  --zip-file fileb://lambda_deployment.zip

Phase 2: Frontend Static Assets

# S3 sync with public-read ACL
aws s3 sync frontend/ s3://queenofsandiego-shipcaptaincrew-assets/ \
  --delete --cache-control "max-age=3600"

# CloudFront invalidation (distribution ID redacted)
aws cloudfront create-invalidation \
  --distribution-id [DIST_ID] \
  --paths "/*"

This two-phase approach ensures Lambda updates don't break in-flight requests while S3/CloudFront invalidation guarantees fresh assets reach end-users within seconds.

Security & Infrastructure Decisions

Authentication Pattern

Magic link auth replaces traditional password flows. The implementation:

  1. Admin generates short codes (6–8 character alphanumeric), stored in DynamoDB with TTL (time-to-live) set to 24 hours
  2. SES sends invitation email containing magic link (e.g., queenofsandiego.com/auth?code=ABC123)
  3. Frontend exchanges code for JWT token via POST /api/auth/claim
  4. Subsequent requests include JWT in Authorization header; Lambda validates signature and expiry

This eliminates password storage, phishing vectors, and credential stuffing attacks. The JWT includes role claims, enabling client-side permission checks before API calls.

Environment Variable Management

Secrets are stored in Lambda environment variables and referenced at runtime:

  • JWT_SECRET — Used for token signing/verification
  • STRIPE_API_KEY — Billing processor integration (hardcoded reference identified as security gap in CTO report)
  • SES_FROM_ADDRESS — Verified sender identity

Identified gap: The CTO audit found hardcoded Stripe keys in repos.env and plaintext secret files. Remediation requires AWS Secrets Manager integration: store sensitive values there, retrieve via boto3.client('secretsmanager') at Lambda startup, cache for session duration.

DynamoDB Optimization

Event state and checklists live in single DynamoDB table with composite keys:

  • PK: event_id (partition key)
  • SK: type#timestamp (sort key enabling GSI queries)

This design supports:

  • O(1) event lookups by ID
  • Range queries for time-ordered checklist items
  • On-hold status enforcement via conditional writes (fail if crew member already assigned)

Key Decisions & Trade-offs

Decision 1: Single Lambda vs. Microservices

The monolithic Lambda function (~500 lines) handles auth, event CRUD, checklist state, and email dispatch. This pattern works because cold-start time (~1s with 128MB reserved concurrency) is acceptable for low-traffic tools. If throughput exceeds 10 concurrent