```html

Building a Real-Time Technical Blog Pipeline for Four Sailing & Event Sites

What Was Done

I built an automated technical blog generation system that captures development work across four distinct domains—queenofsandiego.com, sailjada.com, dangerouscentaur.com, and burialsatseasandiego.com—and publishes granular, detailed technical posts to dedicated tech blogs at tech.[domain].com. The system integrates with Claude Code's session hooks to automatically generate blog posts the moment a development session ends, ensuring complete visibility into all technical work performed.

Technical Architecture

The solution consists of three primary components:

  • Session Hook Integration — A Stop hook (/Users/cb/.claude/hooks/tech_blog_stop.sh) that fires when any Claude Code session completes
  • Infrastructure Initialization — Python scripts that provision S3 buckets, CloudFront distributions, and DNS records for each tech blog domain
  • Blog Generator — A Python tool that parses session transcripts in JSONL format, extracts work details, and generates HTML blog posts with technical granularity

Infrastructure Setup

Each tech blog required identical AWS infrastructure:

  • S3 Buckets
    • qos-sites — Hosts tech.queenofsandiego.com content
    • jada-sites — Hosts tech.sailjada.com content
    • dc-sites — Hosts tech.dangerouscentaur.com content (reusing existing wildcard distribution)
    • bats-sites — Hosts tech.burialsatseasandiego.com content
  • CloudFront Distributions
    • QOS: New distribution with ACM wildcard cert *.queenofsandiego.com
    • JADA: New distribution with ACM wildcard cert *.sailjada.com
    • DC: Reused existing wildcard distribution (E2Q4UU71SRNTMB) pointing to dc-sites
    • BATS: New distribution with new ACM cert for tech.burialsatseasandiego.com
  • DNS Configuration
    • QOS & JADA: Route53 alias records pointing to respective CloudFront distributions
    • DC: Namecheap CNAME record (existing wildcard infrastructure)
    • BATS: GoDaddy CNAME record (domain already at GoDaddy)

The infrastructure init script (/Users/cb/Documents/repos/tools/tech_blog_init.py) handles all provisioning with environment-aware defaults. It reads from ~/.aws/credentials and repos.env to locate the correct AWS accounts and region configurations.

Session Capture & Blog Generation

The Stop hook executes when a Claude Code session ends. It:

  1. Reads the session transcript from ~/.claude/sessions/[session-id].jsonl
  2. Determines which site(s) were worked on by parsing file paths and git repositories referenced
  3. Calls the blog generator with the session ID and detected site context
  4. Generator parses JSONL tool use entries to extract:
    • Exact file paths created/modified
    • Specific functions, classes, or configuration sections changed
    • AWS resource operations (S3 uploads, CloudFront invalidations, Route53 updates)
    • Infrastructure decisions and their rationale
    • Command examples (with credentials redacted)
  5. Outputs formatted HTML blog post with <h2>, <h3>, <code>, and <pre><code> tags
  6. Uploads post to appropriate S3 bucket and invalidates CloudFront cache

The JSONL transcript format contains structured tool use records. The generator extracts relevant entries based on tool type (file operations, git, AWS CLI, HTTP requests) and reconstructs the sequence of work performed.

Credential & Secret Redaction

A critical design decision was enforcing zero credential exposure in published posts. The redaction layer:

  • Strips AWS access keys, secret keys, and session tokens from command examples
  • Removes API credentials (GoDaddy, Namecheap) from DNS configuration examples
  • Filters environment variable values while preserving variable names
  • Removes email addresses and personal identifiers from quoted output
  • Replaces ACM certificate ARNs and private key references with placeholders

Posts reference resource names (S3 bucket names, CloudFront distribution IDs, Route53 hosted zone IDs) in full detail—these are infrastructure names and are safe to publish—while all sensitive credentials are filtered before upload.

Navigation Integration

Each site's main navigation includes a "Ship's Papers" dropdown menu. This was updated to include a "Technical Blog" link pointing to the respective tech blog domain. For example, on queenofsandiego.com, the Ship's Papers menu now includes:

<a href="https://tech.queenofsandiego.com">Technical Blog</a>

This ensures stakeholders like Sergio can access granular technical documentation directly from the public site navigation.

Key Decisions

  • Separate S3 Buckets & CloudFront Distributions — Each domain gets its own static hosting infrastructure rather than consolidating to a single bucket. This provides blast radius isolation and allows independent cache invalidation strategies.
  • Reuse of Existing Wildcard Certs*.queenofsandiego.com and *.sailjada.com already had wildcard ACM certificates. The dangerouscentaur domain had a wildcard CloudFront distribution. These were leveraged to minimize certificate provisioning time.
  • GoDaddy for BATS DNS — The burialsatseasandiego.com domain was already at GoDaddy DNS, so a CNAME record was added there rather than migrating to Route53.
  • Granular Post Content — Posts capture exact file paths, function names, and specific changes rather than high-level summaries. This allows technical stakeholders to verify what was done and identify impacts.
  • Automatic Trigger via Stop Hook — Publishing happens immediately after session completion, ensuring no development work falls through the cracks. The hook runs in the background and logs success/failure to ~/.claude/logs/tech_blog_generation.log.

What's Next