```html

Building an Auto-Generated Technical Blog System Across Four Domains with AWS Infrastructure

What Was Done

I built a complete technical blogging infrastructure that automatically generates granular, detailed posts documenting every development session across four separate domains: tech.queenofsandiego.com, tech.sailjada.com, tech.dangerouscentaur.com, and tech.burialsatseasandiego.com. The system captures session data, extracts technical details, and publishes HTML articles without manual intervention.

Architecture Overview

The system consists of three core components:

  • tech_blog_init.py — Infrastructure provisioning script that creates S3 buckets, CloudFront distributions, and DNS records
  • tech_blog_generator.py — Session transcript parser that extracts file modifications, commands executed, and reasoning to generate HTML posts
  • tech_blog_stop.sh — Claude Code hook that triggers automatically when a session ends, invoking the generator

Infrastructure Provisioning

The init script provisions identical infrastructure for each domain:

  • S3 buckets: qos-tech-blog, jada-tech-blog, dc-sites (reused), bats-tech-blog
  • CloudFront distributions: Each bucket gets a distribution with index.html as default root object
  • DNS integration: Route53 CNAME for queenofsandiego.com and sailjada.com; Namecheap CNAME for dangerouscentaur.com; GoDaddy DNS for burialsatseasandiego.com
  • SSL/TLS: Leveraged existing wildcard certificates (*.queenofsandiego.com, *.sailjada.com) and provisioned new ACM certificates where needed

The init script uses boto3 to check for existing resources before creation, preventing duplicate infrastructure. It retrieves the correct hosted zone IDs from Route53 and constructs proper CNAME records:

Example DNS validation record for burialsatseasadiego.com:
_[token].tech.burialsatseasandiego.com CNAME _[token].[acm-validation-domain]

For dangerouscentaur, the script reuses the existing wildcard CloudFront distribution (ID: E2Q4UU71SRNTMB) pointing to the dc-sites S3 bucket, adding tech.dangerouscentaur.com as an alternate domain name.

Session Capture and Blog Generation

The generator reads Claude Code session transcripts in JSONL format, extracting:

  • All file modifications (path, type: write/edit, relative path)
  • All commands executed with their context
  • Agent reasoning blocks that explain the "why" behind decisions
  • Tool use entries showing API calls, file operations, and infrastructure changes

The script detects which domain(s) were affected by analyzing file paths. A session modifying queenofsandiego.com files triggers a post to tech.queenofsandiego.com; multi-site sessions generate separate posts for each affected domain.

Key design decisions in the generator:

  • Granularity over summaries — Every file path, function name, and command is included. High-level abstractions are avoided; specific resource IDs and paths are preserved.
  • Credential filtering — Regex patterns strip API keys, tokens, passwords, and email addresses from all content before publishing.
  • Reasoning preservation — Agent reasoning blocks are extracted verbatim to explain decision-making, not just what changed.
  • HTML generation — Posts use semantic HTML with proper heading hierarchy, code blocks for commands and file paths, and bullet lists for clarity.

Integration with Ship's Papers Navigation

The Ship's Papers menu on queenofsandiego.com, sailjada.com, and related sites now includes a "Technical Blog" link pointing to each site's tech blog. This makes engineering work visible to stakeholders like Sergio without exposing credentials or internal complexity unnecessarily.

The nav structure uses a dropdown menu that integrates seamlessly with existing site styling and can be updated by editing the main index.html files:

<li class="dropdown">
  <a href="#">Ship's Papers</a>
  <ul>
    <li><a href="/technical-blog/">Technical Blog</a></li>
    <!-- other ship's papers links -->
  </ul>
</li>

Automated Hook System

When a Claude Code session ends, the tech_blog_stop.sh hook is invoked automatically via the Claude Code settings configuration. This hook:

  • Reads the session transcript from the appropriate Claude projects directory
  • Invokes tech_blog_generator.py with the transcript path and target domain
  • Generates HTML on the local filesystem
  • Uploads the generated post to the appropriate S3 bucket using boto3
  • Invalidates the CloudFront distribution cache to make the post live immediately
  • Logs all operations to ~/.claude/hooks/logs/tech_blog.log for debugging

Infrastructure Details and Resource IDs

S3 Buckets:

  • qos-tech-blog (queenofsandiego.com)
  • jada-tech-blog (sailjada.com)
  • bats-tech-blog (burialsatseasandiego.com)
  • dc-sites (dangerouscentaur.com — reused wildcard distribution)

CloudFront Distributions:

  • qos-tech-blog distribution → tech.queenofsandiego.com
  • jada-tech-blog distribution → tech.sailjada.com
  • bats-tech-blog distribution → tech.burialsatseasandiego.com
  • E2Q4UU71SRNTMB wildcard (pre-existing) → tech.dangerouscentaur.com (added as alternate domain)

DNS Resolution:

  • queenofsandiego.com hosted zone (Route53) — CNAME record for tech subdomain
  • sailjada.com hosted zone (Route53) — CNAME record for tech subdomain
  • dangerouscentaur.com (Namecheap) — CNAME to CloudFront distribution
  • burialsatseasandiego.com (GoDaddy) — CNAME to CloudFront distribution

Key Decision Rationale

Why CloudFront instead of direct S3 website hosting? CloudFront provides caching, geographic distribution, and clean domain aliasing via alternate domain names. Cache invalidation ensures new posts appear immediately despite CloudFront's long