Building Multi-Site Auto-Generated Technical Blog Infrastructure with Session-Based Post Generation
What Was Done
Implemented a comprehensive technical blogging system across four domain properties (queenofsandiego.com, sailjada.com, dangerouscentaur.com, and burialsatseasandiego.com) that automatically captures development session activity and generates granular technical posts in real-time. The system captures exact file modifications, command execution, and infrastructure changes—providing complete transparency into all work performed on these properties.
System Architecture Overview
The solution consists of three core components:
- Session Capture Hook — A Claude Code Stop hook that intercepts session completion and extracts all activity metadata
- Blog Generator — Python utility that parses session transcripts and generates HTML posts with granular technical details
- Infrastructure Layer — S3 buckets, CloudFront distributions, and DNS configuration for four independent tech blog subdomains
Infrastructure Implementation
S3 and CloudFront Setup
Created dedicated S3 buckets and CloudFront distributions for each tech blog:
- queenofsandiego.com: S3 bucket `qos-tech-blog`, CloudFront distribution backed by wildcard cert `*.queenofsandiego.com`, deployed at `tech.queenofsandiego.com`
- sailjada.com: S3 bucket `jada-tech-blog`, CloudFront distribution backed by wildcard cert `*.sailjada.com`, deployed at `tech.sailjada.com`
- dangerouscentaur.com: S3 bucket `dc-sites`, reuses existing wildcard CloudFront distribution (E2Q4UU71SRNTMB), deployed at `tech.dangerouscentaur.com`
- burialsatseasandiego.com: S3 bucket `bats-tech-blog`, CloudFront distribution with ACM certificate validation via GoDaddy DNS, deployed at `tech.burialsatseasandiego.com`
Each CloudFront distribution is configured with index.html as the default root object, automatic gzip compression, and 24-hour cache TTL for HTML content (versioned posts maintain indefinite cache).
DNS Configuration
Route53-managed domains (queenofsandiego.com, sailjada.com): Created alias records routing `tech.*` subdomains directly to their CloudFront distribution domain names.
GoDaddy-managed domain (burialsatseasandiego.com): Added CNAME record for `tech.burialsatseasandiego.com` pointing to CloudFront distribution endpoint. ACM certificate validation required adding DNS validation CNAME record to GoDaddy, which was automated via GoDaddy API integration.
Session Capture and Post Generation
Stop Hook Mechanism
The Stop hook script at `/Users/cb/.claude/hooks/tech_blog_stop.sh` executes when a Claude Code session ends. It:
- Extracts the session transcript from Claude's internal session directory
- Identifies which domain the session was working on (via file paths modified in `/Users/cb/Documents/repos/sites/[domain]/`)
- Passes the transcript to the blog generator with domain context
- Uploads the generated HTML post to the appropriate S3 bucket
- Invalidates the CloudFront distribution cache to immediately publish the post
The hook is registered in Claude Code settings at `/Users/cb/.claude/settings.json` under the `hooks` configuration, ensuring it runs on every session completion.
Blog Generator Logic
The generator at `/Users/cb/Documents/repos/tools/tech_blog_generator.py` parses JSONL-formatted session transcripts and extracts:
- File modifications: All Write/Edit operations with exact file paths
- Command execution: Every CLI command run during the session with output
- Tool usage: All Claude tool invocations (AWS CLI, Python scripts, etc.) with parameters and results
- Reasoning: Agent notes that explain architectural decisions and implementation approach
The generator then synthesizes this metadata into a structured HTML post following a consistent template with sections for What Was Done, Technical Details, Infrastructure Changes, Key Decisions, and Next Steps. Critically, all credentials, API keys, tokens, and secrets are automatically redacted before post generation—ensuring sensitive data never reaches the public blog.
Post Structure and Content Filtering
Each post is rendered as standalone HTML with:
- Semantic HTML5 tags (
<h2>, <h3>, <code>, <pre>) for readability and SEO - Exact file paths: `/Users/cb/Documents/repos/sites/queenofsandiego.com/index.html`
- Exact command examples: `aws s3 cp file.html s3://qos-tech-blog/`
- Infrastructure details: CloudFront distribution IDs (e.g., `E2Q4UU71SRNTMB`), S3 bucket names, Route53 hosted zone IDs
- Architectural patterns and rationale: Why wildcard certs vs. single-domain certs, why CloudFront invalidation strategy, etc.
A content filter removes:
- AWS access keys and secret keys (pattern matching and known credential formats)
- GoDaddy API keys and Namecheap tokens
- Database passwords and connection strings
- Email addresses, phone numbers, personal identifiable information (PII)
- Any environment variable values that appear sensitive
Integration with Site Navigation
Updated the Ship's Papers dropdown menu in `queenofsandiego.com/index.html` to include a "Technical Blog" link pointing to `https://tech.queenofsandiego.com`. This same pattern was replicated for the other three properties, making tech blog posts discoverable and transparent to stakeholders (particularly Sergio, who can now review exact implementation details).
Multi-Domain Coordination
The infrastructure init script at `/Users/cb/Documents/repos/tools/tech_blog_init.py` detects which domain(s) are being modified in a session by analyzing the file paths in the transcript. A single session can touch multiple domains—for example, updating DNS settings that affect both queenofsandiego.com and sailjada.com. The script intelligently routes posts to the correct S3 bucket and CloudFront distribution based on which files were modified.
A configuration file (`/Users/cb/.claude/projects/-Users-cb-Documents-repos/memory/project_tech_blogs.md`) maintains mapping of:
- Domain → S3 bucket name
- Domain → CloudFront distribution ID
- Domain → Route53 hosted zone ID (or external DNS provider)
- Domain → Primary contact/approver (stored separately from posts)
Key Architectural Decisions
Why Session