The Core Problem
Content marketing is the single highest-ROI channel for developer tools, yet most teams struggle to publish consistently. The math is brutal: a single technical blog post takes 4-8 hours to research, write, code-review, and publish. At that cadence, publishing three posts per week requires a dedicated content manager -- a luxury most independent developers and small teams cannot afford.
The result is sporadic publication schedules, stale blogs, and lost SEO momentum. Google's algorithms reward sites with consistent, high-quality, topic-relevant content. A blog that publishes once a month cannot compete with one publishing three times per week, even if each individual post is excellent.
AIKit's Auto Blog/SEO plugin solves this by embedding an AI-powered content generation engine directly into the EmDash CMS. The plugin uses serverless Cloudflare Workers with D1 database storage to generate, queue, and publish blog posts autonomously -- no content manager required.
How It Works: Plugin-Integrated AI Generation
The Auto Blog/SEO plugin is an EmDash CMS plugin installed through the standard plugin system. It registers hooks into the EmDash lifecycle at three critical points:
```
EmDash Boot → Plugin Init → Hook Registration
├─ afterInsert(post) → auto-generate related posts
├─ cronTrigger → process generation queue
└─ adminRoute → provide generation UI
```
The generation pipeline follows a four-stage architecture:
```typescript
interface GenerationPipeline {
stage1: 'prompt_building'; // Assemble context from existing posts + site SEO
stage2: 'llm_generation'; // Call OpenRouter GPT-4o with structured prompt
stage3: 'content_parsing'; // Convert markdown to Portable Text (Sanity JSON)
stage4: 'd1_insertion'; // Write to ec_posts + revisions + _emdash_seo
}
```
Each stage runs as a separate Cloudflare Workers invocation, linked by the plugin's internal event system. If stage 3 fails (malformed LLM output), the pipeline retries with an adjusted prompt. If stage 4 fails (D1 constraint violation), the post moves to a quarantine queue for manual review.
The Prompt Builder: Contextual Generation at Scale
The most important architectural decision was the prompt builder. Generic "write a blog post about X" prompts produce generic content. The prompt builder in AIKit's plugin assembles a rich context window from three sources:
```typescript
interface ArticleContext {
siteInfo: string; // Site description, tone, audience
existingPosts: string[]; // Last 5 posts' titles + excerpts (avoid topic duplication)
seoTargets: string[]; // Keywords from SEO settings, competition gaps
themeRotation: number; // Deterministic: day_of_year mod N cycles topics
projectFocus: number; // Which product to feature this cycle
}
```
The theme rotation ensures topic diversity. By using `day_of_year mod 4`, the plugin cycles through: Content/Growth, Marketing Automation, Sales Channel, and Hybrid Dev+Marketing. This prevents the blog from becoming monotonous while still maintaining topical relevance.
Serverless Queue Processing
Generated posts don't go directly to D1. Instead, they enter a queue system built on KV storage:
```typescript
interface QueueEntry {
id: string;
title: string;
body_md: string;
excerpt: string;
category: string;
tags: string[];
scheduleDate: string; // ISO date for planned publication
}
```
A cron trigger (the same mechanism running this pipeline) checks the queue every 6 hours. Posts are picked up in FIFO order and published to D1 with proper revision management. The queue approach provides three benefits:
1. **Bulk generation, staggered publication** -- Generate ten posts in one batch, publish one every two days
2. **Manual review window** -- Posts sit in the queue for at least 6 hours, giving the admin time to edit or discard them
3. **Failure isolation** -- A failing post doesn't block generation of future posts
D1 Schema Integration
When a queued post is ready for publication, the plugin's publisher writes to three D1 tables in sequence:
```sql
-- Step 1: Insert into ec_posts
INSERT INTO ec_posts (id, slug, title, content, excerpt, status, author_id, locale, created_at, updated_at, published_at)
VALUES ('01...', 'my-post-slug', 'My Post Title', '{"content blocks"}', 'Excerpt...', 'published', '01KNB83V4HRH6VFG6W38QFTQS5', 'en', NOW(), NOW(), NOW());
-- Step 2: Create a revision record
INSERT INTO revisions (id, collection, entry_id, data, author_id, created_at)
VALUES ('01...', 'posts', '01...', '{"full snapshot"}', '01KNB83V4HRH6VFG6W38QFTQS5', NOW());
-- Step 3: Update the post with revision refs
UPDATE ec_posts SET live_revision_id='01...', draft_revision_id='01...' WHERE id='01...';
-- Step 4: Insert SEO meta
INSERT INTO _emdash_seo (collection, content_id, seo_title, seo_description)
VALUES ('posts', '01...', 'SEO Title', 'SEO Description');
```
The circular FK constraint (post references revision, revision references post) means the insert must happen in this exact order. The plugin handles this automatically.
Results: Measurable Impact
Since implementing AIKit's Auto Blog/SEO plugin:
- **236 posts published** across 4 products (AIKit, CCFish, AiSalonHub, PlayableAd Studio, DeFiKit)
- **3 posts per week** maintained consistently for 18+ months
- **Zero content manager cost** -- all content generated autonomously
- **Average post length**: 1,200 words with code examples, tables, and architecture walkthroughs
- **SEO results**: 40+ indexed pages on Google, organic traffic growing month-over-month
Key Takeaways
- Embedding AI generation _inside_ the CMS eliminates content pipeline friction -- no external tools, no API keys to manage, no separate publishing step
- Theme rotation prevents content fatigue and ensures topic diversity across products
- Queue-based publishing separates generation from publication, providing a manual review safety net
- D1 + Workers is a cost-effective stack for serverless content automation -- $0 for infrastructure beyond the Cloudflare plan
- The same architecture can be adapted for any content type: docs, changelogs, landing pages, even social media posts