Open Blog
An agentic blog engine for Rails. Markdown and rich-text articles, responsive reader pages, immutable revision history, and publishing through Ruby, a JSON API, or MCP tools.
Documentation · Installation · API reference · MCP · Changelog
![]() |
![]() |
Every publish, edit, approval, and removal leaves an immutable record. A reader can trust the dates and notices on the page, and an agent can publish without inventing a review.
Highlights
| Reader pages | Categories, tags, authors, series, search, Atom and JSON feeds, a sitemap, and structured data. The templates are copied into your app so you can change them. |
| Themes | Three presets, Signal, Editorial, and Ink, each with light and dark mode. Change the palette from the configuration. Fonts ship with the gem; nothing loads from another site. |
| Publishing operations | Ruby operations compute a revision identifier from the content, record each release, and refuse a public edit that does not say whether it is substantive, a correction, or maintenance. |
| JSON API and MCP | Scoped tokens let editorial tools and AI agents publish through the same guarded path. Six agent workflows ship in the gem. |
| Approvals and provenance | A post carries who wrote it, who reviewed which revision, and whether facts were checked. The AI notice on the page follows those records. |
| Adoption | Import an existing blog with its known history, dates, and redirects, without claiming a new first publication. |
| Operations | Policy pages, page views, diagnostics, and surface reports that check what readers actually see against the stored records. |
| AdminSuite | Optional generated resources for a browser-based editor. |
Requirements
| Requirement | Supported |
|---|---|
| Ruby | 3.2, 3.3, 3.4, or 4.0 |
| Rails | 8.0 or 8.1 |
| Database | PostgreSQL or SQLite |
| Images | Active Storage with libvips or ImageMagick |
Every combination of Ruby, Rails, and database above runs in CI.
Install
bundle add open_blog
bin/rails generate open_blog:installTo run the latest unreleased source instead, use bundle add open_blog --github techwright-lab/open-blog.
Start bin/dev (or bin/rails server) and open /blog. The generator installs tables, mounts the engine at /blog, copies the reader views and browser controllers, writes config/initializers/open_blog.rb, and publishes a sample article. If it prints an API token, save it: the secret is shown once.
Set your site name, publisher, and public origin in the initializer, then run bin/rails open_blog:doctor to check the setup. The installation guide lists every generator option, and the configuration reference lists every setting with its default.
Publish a post
From Ruby:
result = OpenBlog::Publish.call(
{ title: "Garden notes", body: "Today in the garden.", provenance: "human_written" },
actor: "Editor"
)
result.success? # => true
result.post.path # => "/blog/garden-notes"
result.records # => { revision: "new", publication: "first", approval: nil }From the JSON API, with a token from bin/rails open_blog:token:
curl -s -X POST https://example.com/blog/api/v1/posts \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"title":"Garden notes","body":"Today in the garden.","publish":true}'From an agent, point an MCP client at https://example.com/blog/mcp with the same token. The blog_save_draft tool returns a preview link, and blog_publish_post records the release. Agents must show the exact text to a person and record only the approval they actually give; the publishing instructions spell this out.
A later edit of a public post must carry change: "substantive", "correction", or "maintenance". Without it the write is refused and nothing changes. Normal model saves and the operations keep this history; bulk SQL writes bypass it.
Guides
| Guide | Covers |
|---|---|
| Configuration | Identity, routes, content gates, images, limits, and page views |
| Publishing | Operations, previews, scheduling, approvals, and findings |
| JSON API and MCP | Endpoints, tools, examples, and error codes |
| Reader pages and feeds | Routes, search, feeds, dates, images, and policy pages |
| Adoption | Import existing articles and preserve their known history |
| Customization | Copied templates, stylesheets, and browser controllers |
| Themes | Presets, palette overrides, design tokens, and fonts |
| AdminSuite, Diagnostics, Page views | Optional editor, setup checks, surface reports, and analytics |
Development
The gem tests run through a dummy host application. Set DB to sqlite or postgres:
bundle install
DB=sqlite RAILS_ENV=test bundle exec ruby bin/test
bundle exec rubocopCI runs the matrix of Ruby 3.2 to 4.0, Rails 8.0 and 8.1, and both databases, plus browser tests and a fresh-application installer check with the built gem.
The documentation is Markdown in docs/, built with Jekyll and Just the Docs from a separate bundle. See working on the documentation for local preview and validation commands.
Releases
The Prepare Release workflow updates the version and changelog in a pull request. After review, merge, and successful checks, Publish publishes the verified package to RubyGems and creates or updates the GitHub Release. Merging ordinary changes does not publish a gem. See the release guide for the first-time Trusted Publishing setup.
License
Licensed under the MIT License.

