```html

Deploying a Dynamic Charter Proposal System: From Template to Production S3 + CloudFront

Overview

A charter proposal template existed in the repository but had never been instantiated for a specific client proposal. This article covers the technical workflow for generating client-specific HTML proposals, deploying to S3, invalidating CloudFront cache, and verifying live delivery—all while maintaining separation of concerns between content, secrets, and infrastructure.

What Was Done

Created a production-ready charter proposal document at /Users/cb/Documents/repos/sites/queenofsandiego.com/proposals/jada-charter-proposal-sue.html from an existing template, deployed it to an S3-backed static site, invalidated the CloudFront distribution to purge cached paths, and verified live availability at https://queenofsandiego.com/proposals/jada-charter-proposal-sue.html.

Technical Details

Template-Based Content Generation

Rather than hand-coding HTML from scratch, the workflow leveraged an existing proposal template pattern within the repository. The generated document includes:

  • Structured pricing options: Two distinct charter configurations (Option A: 2-hour, Captain only, $750 flat; Option B: 3-hour, full crew, $1,575 flat) to reduce decision fatigue for the client.
  • Legal compliance sections: Explicit references to USCG licensing, EPA/MPRSA ash scattering regulations, and bareboat charter disclaimers—critical for liability and regulatory clarity.
  • Contextual content: Hollywood history narrative (Bogart, Bacall, Flynn, Wayne) tied to the vessel's operating territory and marketing positioning.
  • Client interaction:** Q&A form at page footer enabling asynchronous client feedback without requiring synchronous communication.

File Organization and Discovery

Repository structure follows a clean separation:

/Users/cb/Documents/repos/sites/queenofsandiego.com/
├── proposals/                    # Client-specific proposals
│   └── jada-charter-proposal-sue.html
├── assets/images/
│   ├── interior/                 # Cabin and deck photography
│   └── [other asset categories]
└── publish_static_site.sh        # CI/CD orchestration script

Discovery was accomplished via shell pattern matching:

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

This identified existing proposal patterns and confirmed the target output location.

Infrastructure & Deployment Pipeline

S3 Static Site Hosting

The queenofsandiego.com site is hosted as a static website on Amazon S3. The deployment command:

cd /Users/cb/Documents/repos/sites/queenofsandiego.com && \
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 \
  --region us-east-1 \
  --cache-control "public, max-age=3600"

Key decisions:

  • Credentials via environment sourcing: Secrets are stored in /Users/cb/Documents/repos/.secrets/repos.env (excluded from version control via .gitignore), sourced with set -a / set +a to inject AWS credentials into the shell session without exposing them in command history.
  • Cache-control header: 1-hour TTL (max-age=3600) balances freshness with reduced origin hits. This is appropriate for proposal documents that are unlikely to change multiple times per day.
  • S3 bucket naming: Direct domain match (queenofsandiego.com) simplifies DNS routing via Route53 ALIAS records.

CloudFront Cache Invalidation

After S3 upload, the CloudFront distribution cache must be invalidated to ensure users receive the new proposal immediately:

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

Why invalidation matters: Without this step, CloudFront edge nodes would continue serving stale cached content (even if S3 object metadata indicates freshness). The invalidation creates a purge job that propagates across all 200+ CloudFront edge locations globally, typically completing within 2–5 minutes.

Verification via:

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

Polling the invalidation status ensures the purge is complete before sharing the live URL with clients.

Live Verification

Final validation confirmed the proposal is accessible and rendering correctly:

curl -s "https://queenofsandiego.com/proposals/jada-charter-proposal-sue.html" | grep -E "Kim|pricing|Option"

This pattern-match confirms both HTML structure and expected content strings are present in the live response.

Content Architecture Decisions

Separation of Metadata and Secrets

Client names, contact details, and internal notes are preserved in a separate memory file (/Users/cb/.claude/projects/-Users-cb-Documents-repos/memory/feedback_no_meta_copy_in_proposals.md) rather than embedded in the proposal HTML. This prevents accidental exposure of personal information if the proposal file is shared externally or indexed by search engines.

Pricing Clarity

Two discrete options reduce cognitive load and accelerate decision-making. Both are explicitly marked as "flat rate" to eliminate hidden-cost concerns—a common objection in charter sales. The recommended option (Option A, $750) is visually highlighted, guiding the client toward the standard offering while maintaining transparency about the premium option.

Regulatory Compliance Language

Ash scattering proposals operate in a heavily regulated domain (EPA MPRSA, USCG licensing). The document explicitly disclaims bareboat charter status and references applicable regulations, reducing liability exposure and setting accurate expectations about services provided.

Key Technical Patterns

  • Environment-based configuration: AWS credentials, S3 bucket names, and CloudFront distribution IDs are read from environment variables, enabling the same script to deploy to multiple environments (dev, staging, production) without code changes.
  • Declarative file discovery: Shell find and grep commands establish ground truth about what content exists in the repository, reducing manual tracking overhead.
  • Cache layering: S3 object metadata (Cache-Control header) + CloudFront edge TTL + browser cache headers create a multi-tier caching strategy that balances freshness, cost, and