Open source · Self-hosted · MIT license

From structured rows to
polished articles — automatically.

A source-agnostic article publishing pipeline for Framer, GitHub, and modern CMS workflows. Open source, self-hosted, MIT licensed.

CSV
JSON
NocoDB
Sheets
Context
brand · tone · glossary
Generate
AI · prompt templates
Validate
judge · rewrite loop
Markdown
GitHub
Framer CMS

Five steps from row to published article

Each step is configurable. You control the source, the prompt, the model, and the destination.

01

Connect your source

Point the pipeline at any structured data source. Each row becomes one article run — no scripting required.

csv json nocodb google-sheets
02

Add your context

Drop in a positioning document, messaging guide, product glossary, or evaluation rubric. Every prompt gets the full context — not just the row.

positioning.md glossary.md rubric.yaml
03

Configure AI

Set your provider and API key in .env. Switch between Anthropic, OpenAI-compatible endpoints, OpenRouter, or any local model without touching the pipeline logic.

Anthropic OpenAI OpenRouter local endpoint
04

Generate & validate

The pipeline writes the article, then runs it through local validators and an AI judge. If quality thresholds aren't met, it rewrites automatically before moving on.

local validators AI judge rewrite loop
05

Publish everywhere

Output to local Markdown files, commit directly to a GitHub repository folder, or push to a Framer CMS collection — including section-mapped mode for structured field mapping.

Markdown GitHub Framer CMS section-mapped

Article production breaks at scale

Most teams hit the same three walls once they try to publish more than a handful of articles.

"Prompting one-off articles doesn't scale"
Every article is a separate session. Context gets copy-pasted, quality varies, and there's no audit trail.
Pipeline approach: one config runs every row through the same generation logic, with consistent context and logging.
"Brand context gets ignored"
Positioning docs and messaging guides live in a Notion page. They never make it into the prompt — unless someone remembers to paste them.
Context files are injected into every prompt automatically. Positioning, glossary, and tone guidance travel with the pipeline.
"Publishing still requires copy/paste"
After generation comes the manual grind: copy into CMS, format headings, paste into GitHub. One article is fine; fifty is a workflow problem.
Direct publish to Markdown, GitHub, and Framer CMS — including field-level section mapping for structured CMS entries.

Everything the pipeline needs.
Nothing it doesn't.

Source-driven generation
Each row in your data source becomes one article. Scale by adding rows, not by writing more prompts.
Bring your own context
Drop in positioning docs, tone guides, glossaries, or evaluation rubrics. They're injected into every prompt automatically.
❰ ❱
Custom prompt templates
Edit article prompts directly in the web UI or as plain files. No code changes needed to adjust tone, structure, or depth.
Model-agnostic AI
Set AI_PROVIDER in your .env. Swap providers without touching the pipeline. Works with any OpenAI-compatible endpoint.
Deterministic validators
Local rule-based checks run before and after generation. Catch structural problems without spending tokens on another AI call.
AI judge + rewrite loop
An AI judge scores each article against your rubric. Articles that fall below threshold are automatically rewritten and re-evaluated.
Markdown export
Output clean Markdown files with YAML frontmatter to any local directory. Compatible with any static site generator.
GitHub folder publishing
Commit articles directly into a folder in any GitHub repository via the Contents API. No local git checkout needed.
Framer CMS publishing
Push articles to a Framer CMS collection as structured entries. Single-body or section-mapped mode, configurable per run.
Framer section mapping
Map each H2 section in your article to a separate Framer CMS field. Structured publishing without copy/paste.
Web UI included
A local web app for managing runs, reviewing output, editing prompts, and triggering regeneration — no terminal required.
Open-source & self-hosted
MIT licensed. Runs entirely on your machine. API keys stay in your .env file. No data leaves your environment.

Publish where your content lives

Three built-in publishers, each configurable via your project config. Add your own by implementing the publisher interface.

Markdown

Local files

Generate clean .md files with YAML frontmatter. Works with Astro, Hugo, Jekyll, Eleventy, or any file-based CMS.

  • Configurable output directory
  • Slug derived from source row data
  • YAML frontmatter with title, date, and custom fields
  • Compatible with any static site generator
GitHub

Repository folder

Commit articles directly to a folder in any GitHub repository using the Contents API. No local git checkout required.

  • Works with any public or private repo
  • Dry-run mode previews changes without committing
  • Commit message templating
  • File path and naming configurable per project
Framer CMS

CMS collections

Push structured article data directly to a Framer CMS collection. Supports single-body entries or section-mapped fields.

  • Single-body mode: full article as one field
  • Section-mapped mode: each H2 maps to a CMS field
  • Slug, title, and metadata fields auto-populated
  • Dry-run support for previewing without publishing

One H2 section. One CMS field.

Section-mapped mode lets each H2 heading in your article target a specific Framer CMS field — no copy/paste required.

Article H2 section
Framer CMS field
What the output looks like
outputSection
How it works
howItWorksSection
Pricing comparison
pricingSection
FAQ
faqSection

Configure the section-to-field mapping in your project config. The pipeline extracts each H2 section from the generated article and sends it to the corresponding Framer CMS field.

Model flexibility

Use the model you trust.
Keep the pipeline.

No lock-in. Set your provider in AI_PROVIDER and swap models at any time.

Anthropic

Claude models via the Anthropic API. Configurable model name and max tokens.

OpenAI-compatible

Any provider that implements the OpenAI chat completions API. Set AI_BASE_URL to point anywhere.

OpenRouter

Access dozens of models through a single API key. Model selection stays in config.

Local endpoint

Point AI_BASE_URL at Ollama, LM Studio, or any local OpenAI-compatible server.

# .env — change the model without touching pipeline code
AI_PROVIDER=anthropic
AI_MODEL=claude-opus-4-5
AI_API_KEY=sk-ant-…

# Switch to a local endpoint
AI_PROVIDER=openai-compatible
AI_BASE_URL=http://localhost:11434/v1

Built for repeatable content workflows

Any time a row in a spreadsheet or database should become a published article, the pipeline fits.

01

SEO comparison pages

Maintain a spreadsheet of product pairs. The pipeline turns each row into a structured comparison article published directly to your site or CMS.

02

Blog post production at scale

Define topics in a CSV or NocoDB table. Generate, validate, and publish a full editorial article for each one in a single pipeline run.

03

Programmatic landing pages

Turn a data sheet of keywords, use cases, or segments into complete landing page copy published as Markdown or Framer CMS entries.

04

Framer CMS content ops

Keep content in Google Sheets or NocoDB, then publish structured entries directly to Framer collections — including section-mapped field output.

05

GitHub-based static sites

Commit Markdown articles directly into a content folder in your GitHub repository. Trigger deployments from the same pipeline run.

06

Agency content workflows

Run pipelines per client with separate context files, prompt templates, and publish targets. Repeatability at the account level, not just the article level.

Self-hosted. No vendor lock-in. Bring your own keys.

PublishRail runs entirely on your machine. Your content, your context, and your API keys never leave your environment.

The publisher architecture is extensible — implement the interface and add your own output target without modifying core pipeline code.

MIT licensed — use it, fork it, modify it
.env.example included — no guessing which keys are needed
Dry-run mode for every publisher — preview before you commit
Extensible publisher pattern — add your own output target
Web UI runs locally on localhost:3737
.env.example
# Source
SOURCE_TYPE=csv
CSV_PATH=./inputs/articles.csv

# AI provider
AI_PROVIDER=anthropic
AI_MODEL=claude-opus-4-5
AI_API_KEY=your_api_key_here

# Publisher
PUBLISHER=markdown
OUTPUT_DIR=./outputs

# GitHub publisher (optional)
GITHUB_TOKEN=your_github_token_here
GITHUB_REPO=your-org/your-repo
GITHUB_CONTENT_PATH=content/articles

# Framer publisher (optional)
FRAMER_API_KEY=your_framer_key_here
FRAMER_COLLECTION_ID=collection_id_here
FRAMER_MODE=section-mapped

Common questions

Is it open source?
Yes. PublishRail is MIT licensed. You can use it, modify it, and deploy it however you like. The full source is on GitHub.
What data sources are supported?
CSV files, JSON files, NocoDB tables, and Google Sheets are supported out of the box. Set SOURCE_TYPE in your .env to switch between them.
Can I use my own prompts?
Yes. Prompt templates are editable directly in the web UI or as plain files in your project directory. Adjust tone, structure, length, or article format without changing pipeline code.
Does it publish to Framer?
Yes. Set PUBLISHER=framer and configure your Framer API key and collection ID. Supports both single-body mode (full article as one field) and section-mapped mode (each H2 section maps to a separate CMS field).
Can I map H2 sections to specific Framer fields?
Yes — that's section-mapped mode. Set FRAMER_MODE=section-mapped and define the heading-to-field mapping in your project config. The pipeline extracts each H2 section from the generated article and sends it to the corresponding field.
Can I use local models?
Yes. Set AI_PROVIDER=openai-compatible and point AI_BASE_URL at any local OpenAI-compatible server — Ollama, LM Studio, or similar. The pipeline doesn't care where the completions come from.
Does it store my API keys?
No. API keys live in your local .env file and never leave your machine. The pipeline reads them at runtime. Nothing is sent to any external service other than the AI provider you configure.
Can agencies use it for multiple clients?
Yes. The pipeline is designed for repeatable, source-driven workflows. Run separate pipeline configurations per client — each with its own context files, prompt templates, source, and publish target.