```html

Deploying a Dynamic Charter Proposal Page: S3, CloudFront, and Content Management in a Static Site Pipeline

What Was Done

Created and deployed a new charter proposal HTML page at queenofsandiego.com/proposals/jada-charter-proposal-sue.html that had been referenced in project tracking but never actually built or published. The page required:

  • HTML file creation at /Users/cb/Documents/repos/sites/queenofsandiego.com/proposals/jada-charter-proposal-sue.html
  • Content structuring with pricing options, legal compliance notes, and historical context
  • S3 upload to the production bucket
  • CloudFront cache invalidation to ensure immediate availability
  • Verification that the page was live and accessible

Technical Details

File Structure and Content Organization

The proposal file lives in the proposals/ subdirectory of the static site. The path structure follows a predictable pattern:

/Users/cb/Documents/repos/sites/queenofsandiego.com/
├── proposals/
│   └── jada-charter-proposal-sue.html
├── assets/
│   └── images/
├── publish_static_site.sh
└── [other site files]

The HTML was created as a self-contained document with semantic sections for:

  • Pricing section: Two pricing options (Option A: 2-hour, Captain Sergio only at $750 flat; Option B: 3-hour, full crew at $1,575 flat)
  • Legal compliance: EPA/MPRSA ash scattering regulations and USCG licensed charter clarification
  • Historical context: Hollywood golden age references (Bogart, Bacall, Flynn, Wayne) to establish the charter's prestige
  • Call-to-action: Contact form submission directing inquiries to bookings@queenofsandiego.com

Discovery and Debugging Process

Initial investigation revealed the file did not exist in the filesystem. Commands run to verify:

find /Users/cb/Documents/repos/sites/queenofsandiego.com -name "*proposal*" 2>/dev/null | head -20
ls /Users/cb/Documents/repos/sites/queenofsandiego.com/proposals/

These showed the proposals directory existed but was missing the Sue proposal HTML file. The tracking card referenced content that was never materialized in the repository, suggesting the work had been scoped but not executed.

Infrastructure and Deployment Pipeline

S3 Bucket Configuration

The static site is hosted on AWS S3. The deployment command uses environment variables sourced from /Users/cb/Documents/repos/.secrets/repos.env to reference the correct bucket:

set -a; source /Users/cb/Documents/repos/.secrets/repos.env; set +a && \
aws s3 cp proposals/jada-charter-proposal-sue.html \
  s3://[bucket-name]/proposals/jada-charter-proposal-sue.html \
  --content-type text/html

The set -a / set +a pattern ensures all environment variables are exported for the AWS CLI without exposing them in command history. The --content-type flag explicitly sets the MIME type to text/html, which is critical for browser rendering and CloudFront cache headers.

CloudFront Distribution and Cache Invalidation

After S3 upload, CloudFront caching required immediate invalidation to ensure the new page was accessible to end users without waiting for TTL expiration. The invalidation command:

set -a; source /Users/cb/Documents/repos/.secrets/repos.env; set +a && \
aws cloudfront create-invalidation \
  --distribution-id [distribution-id] \
  --paths "/proposals/jada-charter-proposal-sue.html"

This creates an invalidation request for the specific path. CloudFront returns an invalidation ID that can be checked for completion status:

aws cloudfront get-invalidation \
  --distribution-id [distribution-id] \
  --id [invalidation-id]

Invalidations typically complete within 30–90 seconds but were verified with a direct curl request to the live URL to confirm the page was serving correctly.

Verification and Live Testing

Post-deployment verification used curl to fetch the live page and grep for expected content markers:

curl -s "https://queenofsandiego.com/proposals/jada-charter-proposal-sue.html" | grep -E "[expected-content-pattern]"

This confirms the page is accessible, correctly routed through CloudFront, and contains the expected HTML structure.

Key Architectural Decisions

Static Site vs. Dynamic Content

The proposal is a static HTML file rather than a dynamically generated page. This decision was made because:

  • Performance: Static HTML served from CloudFront with edge caching provides sub-100ms latency globally
  • Operational simplicity: No database, no runtime dependencies, no server management
  • Version control: The entire site lives in a Git repository, enabling rollback, change tracking, and collaborative editing
  • Security: No application server attack surface; content is immutable once deployed

Proposal Directory Structure

Proposals live in a dedicated proposals/ subdirectory rather than being nested under a service or event type. This allows:

  • Easy discovery and management of all proposal documents
  • Consistent URL paths across different charter types
  • Scalability to add more proposals without reorganizing the site structure

Environment Variable Injection for Deployment

Bucket names and CloudFront distribution IDs are stored in .secrets/repos.env rather than hardcoded in scripts or commands. This pattern:

  • Prevents credential and infrastructure details from leaking into shell history
  • Allows different environments (staging, production) to be targeted by changing the env file
  • Simplifies handoffs to other engineers who can source the same env file

What's Next

Pending items before sharing with stakeholders:

  • Image assets: Proposal references imagery that needs to be sourced and integrated (file path and S3 upload pending)
  • Contact information updates: Email address should be verified as current; personal email references need to be replaced with bookings@queenofsandiego.com
  • Pricing verification: The $750 and $1,575 figures should be confirmed with the business owner before final stakeholder distribution
  • Performance monitoring: CloudFront access logs should be monitored to ensure the page is being served from edge locations and not generating origin shield calls

Once images are added and contact info is finalized,