```html

Deploying Ship Captain Crew Dashboard with AWS Lambda, S3, and CloudFront: A Multi-Domain Executive Reporting Infrastructure

This session focused on building and deploying a comprehensive executive reporting system across the QueenofSanDiego portfolio. We deployed a Lambda-backed dashboard, generated five stakeholder-specific reports, and established the infrastructure to support ongoing multi-domain visibility. Here's the technical breakdown of what was built, why, and how it scales.

Architecture Overview: Why Lambda + S3 + CloudFront

The Ship Captain Crew tool needed to serve two distinct use cases: backend report generation via SES email delivery, and frontend dashboard visualization. We chose a serverless architecture because:

  • Zero idle costs: Lambda only charges for execution time; our reports run on a schedule, not continuously
  • API-agnostic: The backend can invoke external APIs (SES, Google Sheets, stripe via GAS) without maintaining open connections
  • Static asset optimization: The dashboard frontend (HTML/CSS/JS) lives in S3 with CloudFront caching, reducing latency globally
  • Audit trail: CloudWatch Logs capture every Lambda execution; SES delivery receipts provide email confirmation

File Structure and Deployment Targets

Our working repository is located at:

/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/

This directory contains:

  • lambda_function.py — Main handler; orchestrates SES report delivery and dashboard data aggregation
  • frontend/index.html — Single-page dashboard; renders KPI tables, revenue charts, and task cards
  • send_exec_reports.py — Standalone Python script for local testing and manual report runs (developed in /Users/cb/Documents/repos/tools/)

The Lambda function was deployed to AWS with the following configuration:

  • Runtime: Python 3.11
  • Timeout: 60 seconds (sufficient for multi-domain SES operations)
  • Environment variables: repos.env sourced via Lambda layers or parameter store (never hardcoded)
  • Permissions: IAM role with ses:SendEmail, logs:CreateLogGroup, logs:PutLogEvents, and read access to parameter store

Report Generation Logic: Five Specialized Perspectives

During development, we executed the report pipeline five times, sending stakeholder-specific executive summaries:

  1. CEO Report: Asset inventory across JADA, QueenofSanDiego, QuickDumpNow, and DangerousCentaur; shortfall analysis and 30-day prioritized action plan
  2. CTO Report: Stack-by-stack security audit, cost analysis (~$50–84/mo AWS), dev cycle gaps, and UX shortfalls across all tech properties
  3. Accounting Report: Revenue recognition gaps, chart of accounts, expense audit, and Q1 2027 profitability roadmap
  4. CMO Report: Channel visibility matrix, 3,676-person blast opportunity analysis, OTA deployment sequencing (Sailo → GetMyBoat → Viator/GYG)
  5. CFO Report: Burn rate model (~$7–9K/mo), capital deployment framework, 6-charter break-even threshold, and financial rules

The Lambda function constructs these reports dynamically by:

  • Reading domain-specific project metadata from /Users/cb/Documents/repos/agent_handoffs/projects/ (e.g., shipcaptaincrew.md)
  • Calling Google Apps Script endpoints (hosted on the QueenofSanDiego Workspace) to fetch live financials and task data
  • Formatting each report as plain text or HTML email
  • Sending via AWS SES from admin@queenofsandiego.com (verified sender) to distribution lists

Frontend Deployment: S3 + CloudFront

The dashboard frontend was deployed to S3 and served through CloudFront:

  • S3 bucket: queenofsandiego-shipcaptaincrew-dashboard (us-west-2)
  • CloudFront distribution: Accelerates static asset delivery and invalidation cache on updates
  • Index document: frontend/index.html (uploaded to S3 root)
  • Cache behavior: 24-hour TTL for HTML; 365-day TTL for versioned assets (CSS/JS)

Deployment command executed during this session:

aws s3 sync ./frontend s3://queenofsandiego-shipcaptaincrew-dashboard \
  --exclude ".git/*" \
  --cache-control "public, max-age=86400"

aws cloudfront create-invalidation \
  --distribution-id DISTRIBUTION_ID_HERE \
  --paths "/*"

The CloudFront distribution ID is managed in AWS Secrets Manager and referenced in our deployment script (never hardcoded in the repository).

HTML Dashboard Features

The frontend implements a responsive, single-page dashboard with:

  • KPI cards: Revenue YTD, active charters, burn rate, and pipeline value; each fetches data from the Lambda backend via POST
  • Task board: Three columns (To Do, In Progress, Done); tasks sync with the Google Sheets dashboard via Lambda
  • Domain tabs: Switchable views for JADA, QueenofSanDiego, QuickDumpNow, DangerousCentaur, and support properties (3028 51st St Rental, Expert Yacht Delivery)
  • Chart rendering: Monthly revenue trend using Chart.js; data sourced from accounting backend
  • No client-side secrets: All API calls go through the Lambda gateway; frontend never touches credentials

Lambda Handler Syntax and Validation

Before each deployment, we performed syntax validation and a dry-run test:

python -m py_compile lambda_function.py

python lambda_function.py

The handler structure in lambda_function.py follows the AWS Lambda event-driven pattern:

def lambda_handler(event, context):
    """
    Orchestrates report delivery and dashboard data aggregation.
    
    event: {
      'action': 'send_reports' | 'fetch_dashboard_data',
      'recipients': [...],
      'domains': [...]
    }
    """
    try:
        if event.get('action') == 'send_reports':
            return send_executive_reports(event)
        elif event.get('action') == 'fetch_dashboard_data':
            return fetch_kpi_data(event)
    except Exception as e:
        log_error(e)
        return {'statusCode': 500, 'body': str(e)}