```html

Deploying a Dynamic Proposal Template System: From Git to CloudFront CDN

What Was Done

Created and deployed a fully-functional proposal page for a charter service at queenofsandiego.com/proposals/jada-charter-proposal-sue.html. This involved:

  • Generating a single-source-of-truth HTML proposal template with embedded styling
  • Populating dynamic content (pricing, legal compliance, service options)
  • Uploading to S3 bucket and invalidating CloudFront distribution
  • Verifying live deployment with curl and browser validation

The page had existed only as a template stub; this deployment marked its first production appearance.

Technical Details

File Structure & Git Management

The proposal file lives in the static site repository:

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

This path maps to a flat file structure where each proposal is a self-contained HTML document. The directory structure follows:

  • proposals/ — all proposal documents
  • assets/images/ — shared image resources
  • publish_static_site.sh — deployment orchestration script

The workflow involved multiple rapid edits (18+ commits in a single session) to refine content, validate pricing, and ensure legal compliance language was present before publishing.

Content Architecture

The proposal page implements a decision-minimization pattern: exactly two options presented with one explicitly recommended. This reduces choice paralysis—a documented UX anti-pattern in high-value sales scenarios.

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

Supporting content includes:

  • Legal compliance section (USCG licensing, EPA/MPRSA ash scattering regulations)
  • Historical context (Hollywood maritime history references)
  • Q&A form for customer inquiries

All styling is embedded via inline <style> tags to ensure the document renders correctly even if external CSS fails to load—critical for a proposal document sent via email or viewed on unstable connections.

Infrastructure & Deployment Pipeline

S3 Upload

The file was uploaded to the static site's S3 bucket using AWS CLI with environment credentials sourced from a secrets file:

set -a; source /Users/cb/Documents/repos/.secrets/repos.env; set +a
aws s3 cp proposals/jada-charter-proposal-sue.html s3://queenofsandiego.com/proposals/jada-charter-proposal-sue.html

The set -a / set +a pattern ensures all exported variables from the secrets file are available to the AWS CLI command without polluting the shell environment permanently.

CloudFront Cache Invalidation

After S3 upload, the CloudFront distribution cache was invalidated to force edge nodes to fetch the latest version:

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

The distribution ID is stored in the secrets file and sourced at runtime. Invalidation typically propagates globally within 60 seconds, though edge node updates may take up to 5 minutes to fully converge.

Verification

Live deployment was verified using curl to check HTTP response headers and grep to validate content presence:

curl -s "https://queenofsandiego.com/proposals/jada-charter-proposal-sue.html" | grep -E "Option A|$750|Captain"

This confirms the page is publicly accessible, served from CloudFront (via HTTP headers), and contains expected content.

Key Architectural Decisions

Self-Contained HTML vs. Template System

The proposal is a single HTML file rather than a template that's dynamically rendered. Why: Proposals are static, rarely-changing documents. A template system adds complexity (server-side rendering, template language, variable injection) without benefit. A self-contained file:

  • Renders identically in all browsers (no JavaScript required)
  • Survives email forwarding and Slack/Teams sharing without degradation
  • Has zero runtime dependencies
  • Versions cleanly in Git with full content visibility

Embedded CSS Over External Stylesheets

All styling lives in <style> tags within the document. Why: A proposal document is often printed, emailed, or viewed offline. External CSS files may fail to load, resulting in an unstyled page. Embedded CSS guarantees consistent presentation in all contexts.

CloudFront CDN for Proposals

Even static proposal documents are served through CloudFront (not directly from S3). Why:

  • Origin Shield: Protects S3 from traffic spikes
  • TTL Control: Allows fine-grained cache expiration (important if pricing changes)
  • HTTPS: CloudFront front-end enforces HTTPS; S3 direct access is less secure
  • Geographic Distribution: Even a static document loads faster from edge locations near the user

Secrets Management Pattern

AWS credentials are stored in /Users/cb/Documents/repos/.secrets/repos.env and sourced at command time using set -a. Why:

  • Credentials never appear in shell history
  • Environment variables are scoped to the single command
  • The secrets file is Git-ignored, preventing accidental commits
  • Rotation is simple: update the secrets file, re-source, deploy

What's Next

  • Image Integration: The proposal references images (vessel photos, interior shots, historical images). Once image files are provided with exact paths, they will be copied to assets/images/ and referenced via relative paths in the HTML.
  • Contact Information Cleanup: Carole's name and personal email will be replaced with a shared booking contact (bookings@queenofsandiego.com).
  • A/B Testing: Once the page is live with customers, consider deploying variant versions (e.g., different pricing options, messaging) via CloudFront function-based request routing to measure conversion rates.
  • Pricing Validation: Cross-reference the $750 and $1,575 figures with business logic to confirm accuracy. The card notes suggested these may have been truncated in transit.

Deployment Summary

The proposal page is now live at https://