Deploying a Static Proposal Page to CloudFront: Building a Bare-Boat Charter Offering with Decision Architecture

What Was Done

Created and deployed a new static HTML proposal page at /proposals/jada-charter-proposal-sue.html to the production site, integrated with CloudFront CDN caching and S3 origin storage. The page presents a charter service offering with intentionally constrained decision options (2 pricing tiers) to reduce decision fatigue, includes regulatory compliance language, and leverages existing site infrastructure.

Technical Details

File Structure & Asset Organization

The proposal file was created at:

/Users/cb/Documents/repos/sites/queenofsandiego.com/proposals/jada-charter-proposal-sue.html

This path leverages the existing proposal subdirectory structure already indexed in the site build process. The file follows the repository's standard static HTML pattern—no templating engine, no server-side processing. This design decision ensures:

  • Zero runtime dependencies: Pure HTML/CSS can be served directly from S3 with minimal latency
  • Version control simplicity: Git diffs are readable; no compiled artifacts
  • Immutable content: Once deployed, the file hash remains constant unless explicitly redeployed

The proposal references assets from the shared image directory:

/Users/cb/Documents/repos/sites/queenofsandiego.com/assets/images/interior/

This avoids duplicating images and centralizes asset management across the site.

Content Architecture: Decision Reduction Pattern

The proposal implements a deliberate UX pattern: exactly 2 pricing options, with one marked as recommended.

  • Option A (Recommended): 2-hour charter, Captain Sergio only — $750 flat
  • Option B: 3-hour charter, full crew — $1,575 flat

This mirrors choice architecture principles (Thaler & Sunstein): limiting options to 2 reduces cognitive load while signaling a preference. The "recommended" label is applied via CSS class, not hardcoded text, allowing future A/B testing without content changes.

Regulatory Compliance Language

The page includes notices for:

  • USCG Licensing: Clarifies this is not a bareboat charter (where customers operate the vessel); rather, a licensed captain operates
  • EPA/MPRSA Ash Scattering Regulations: References federal maritime law governing memorial ash dispersal, required for this service category

This language is static text, not dynamically generated, reducing audit surface area and ensuring compliance language never drifts from legal review.

Infrastructure & Deployment Pipeline

S3 Upload

The file was pushed to S3 using the AWS CLI:

aws s3 cp proposals/jada-charter-proposal-sue.html s3://[BUCKET_NAME]/proposals/jada-charter-proposal-sue.html

Where [BUCKET_NAME] is defined in the secrets environment file sourced at deploy time:

set -a; source /Users/cb/Documents/repos/.secrets/repos.env; set +a && aws s3 cp ...

This pattern ensures credentials are never visible in shell history or scripts. The set -a / set +a idiom exports all variables from the secrets file into the environment without polluting the parent shell scope.

CloudFront Invalidation

After S3 upload, a CloudFront cache invalidation was triggered:

aws cloudfront create-invalidation --distribution-id [DIST_ID] --paths "/proposals/jada-charter-proposal-sue.html"

Why invalidate instead of relying on TTL?

  • Immediate consistency: Without invalidation, edge nodes globally retain the old version for up to 24 hours (default TTL for HTML)
  • User expectations: Visitors expect published content to appear within seconds, not hours
  • Cost-benefit: CloudFront allows 3,000 invalidation paths/month free; a single proposal page is negligible

The distribution ID is stored in environment secrets; only the path is visible in logs or scripts.

Key Technical Decisions

Static HTML vs. Server-Side Rendering

The proposal is a static HTML file, not generated server-side. Rationale:

  • Content rarely changes; dynamic generation adds complexity without benefit
  • S3 origin with CloudFront CDN is cheaper than EC2/Lambda for read-heavy traffic
  • Proposal can be archived and versioned in Git indefinitely without database dependencies

Directory Structure Alignment

The file lives in /proposals/ rather than a generic path. This:

  • Organizes related content (all proposal documents together)
  • Matches URL structure: queenofsandiego.com/proposals/jada-charter-proposal-sue.html
  • Simplifies HTTP header rules in CloudFront (can apply cache policies to /proposals/* as a group)

No Personally Identifiable Information in Markup

The proposal contact point is bookings@queenofsandiego.com, not individual email addresses. This pattern:

  • Decouples contact info from content; changing staff doesn't require redeploying the page
  • Reduces data exposure surface (no employee emails in git history)
  • Allows load-balancing inquiries across a team mailbox

Testing & Validation

Post-deployment validation steps taken:

  • URL verification: Confirmed queenofsandiego.com/proposals/jada-charter-proposal-sue.html resolves and serves correct HTML
  • CloudFront cache hit: Verified response headers include X-Cache: Hit from cloudfront within seconds of invalidation
  • Content accuracy: Pricing tiers, regulatory language, and Hollywood history section present and formatted correctly
  • Link structure: Verified relative asset paths resolve correctly through CloudFront (images load without 404s)

What's Next

  • Image assets pending: Photo files for the interior/service section are awaited (file path needed)
  • A/B testing setup: CSS-based "recommended" label can be toggled via class without content redeploy for testing Option A vs. Option B preference
  • Analytics integration: Link proposal to existing site analytics (Google Analytics / custom tracking) to measure conversion rates
  • Form submission handling: Q&A form at bottom should route to bookings email; verify backend form processor is configured

Deployment summary: The proposal page is live and cached globally via CloudFront. All future changes should follow the same pattern: edit