digitalocean/ghost-writer

Python

Open original ↗

Captured source

source ↗
published Mar 5, 2026seen 5dcaptured 15hhttp 200method plain

digitalocean/ghost-writer

Description: Agentic Blogging

Language: Python

Stars: 4

Forks: 0

Open issues: 5

Created: 2026-03-05T21:09:49Z

Pushed: 2026-05-13T16:30:54Z

Default branch: master

Fork: no

Archived: no

README:

Ghost Writer Agent

An AI-powered blog writing agent that researches topics, writes articles, and publishes to Ghost or WordPress. It operates in two modes: Autonomous Mode (default), where it generates and publishes posts on a schedule with no human interaction, and Chat Mode, where you brainstorm ideas, review drafts, and approve posts through a web UI.

How It Works

Ghost Writer uses a LangChain agent backed by an LLM (via DigitalOcean's Gradient API) to write blog posts. The agent has access to two tools:

  • search_web -- Searches the web via DuckDuckGo to gather current information and data points for articles.
  • publish_to_blog -- Publishes a post to your Ghost or WordPress blog. Draft content is automatically captured from the LLM's response, so the model only needs to pass the title and tags.

The server runs on FastAPI with an APScheduler background job for autonomous publishing.

Modes

Autonomous Mode (default)

When ENABLE_CHAT is false (the default), Ghost Writer runs headless. The web UI shows a simple splash screen indicating autonomous mode is active. The scheduler generates and publishes posts automatically based on BLOGS_PER_DAY:

1. Picks a topic from BLOG_TOPIC 2. Researches the topic using web search 3. Writes a full article with HTML formatting 4. Publishes directly to your blog

No human interaction is required.

Chat Mode

Set ENABLE_CHAT=true to enable the interactive chat UI. In this mode you can:

1. Suggest a topic -- Discuss ideas with the agent 2. Brainstorm -- Ask the agent for blog post ideas 3. Review drafts -- The agent writes the article and shows it to you before publishing 4. Approve or revise -- Say "publish it" to go live, or request changes

The agent will never publish without your explicit confirmation in chat mode. The autonomous scheduler still runs in the background alongside the chat UI.

Configuration

Set the following environment variables:

| Variable | Default | Description | |---|---|---| | ENABLE_CHAT | false | Set to true to enable the interactive chat UI | | BLOG_URL | | URL of your blog (e.g. https://myblog.com) | | BLOG_TYPE | | CMS platform: Ghost or Wordpress | | BLOG_API_KEY | | API credentials (see below) | | BLOG_TOPIC | General | Comma-separated topics (e.g. AI, Cloud Computing, DevOps) | | GRADIENT_MODEL_ACCESS_KEY | | DigitalOcean Gradient model access key (required) | | GRADIENT_MODEL | anthropic-claude-4.6-sonnet | LLM model to use | | BLOGS_PER_DAY | 1 | Number of autonomous posts per day (0-24, 0 to disable scheduler) | | GW_API_KEY | | Optional API key for endpoint authentication (see Security below) | | GW_RATE_LIMIT | 20 | Max POST requests per IP per minute |

API Key Formats

  • Ghost: id:secret format from Ghost Admin > Integrations > Custom Integration
  • WordPress: username:application_password format from Users > Application Passwords

Running with Docker

docker network create agents-net

docker compose up --build

The UI is available at http://localhost:5003. To enable chat mode:

ENABLE_CHAT=true docker compose up --build

Running Locally

cd ghost-writer
pip install -e .
python src/__main__.py

The UI is available at http://localhost:8000.

TUI Chat

Ghost Writer includes a terminal-based chat interface built with Textual. Install the package and launch it with a single command:

cd ghost-writer
pip install -e .
gw-tui

This opens a full TUI with a scrollable chat pane, input box, and status bar. You can brainstorm topics, review drafts, and publish posts -- all from the terminal. No server required; the agent runs directly in-process.

You can also launch it via the CLI subcommand:

python src/__main__.py chat

API

When GW_API_KEY is set, POST / and GET /status require a Bearer token:

Authorization: Bearer

Unauthenticated requests to protected endpoints return 401 Unauthorized. The GET / UI page is always accessible so users can enter their key through the browser prompt.

Status

GET /status returns the current configuration, chat mode state, and scheduler status.

JSON-RPC (chat mode only)

POST /
{
"jsonrpc": "2.0",
"id": "1",
"method": "message/send",
"params": {
"message": {
"role": "user",
"parts": [{"kind": "text", "text": "Write about Kubernetes security"}]
}
}
}

Returns a 403 error if ENABLE_CHAT is not true.

Security

  • Authentication -- Set GW_API_KEY to require a Bearer token on POST / and GET /status. Strongly recommended for any non-local deployment.
  • Rate limiting -- POST requests are limited to GW_RATE_LIMIT per IP per minute (default 20).
  • Input limits -- Chat messages are capped at 50,000 characters.
  • Security headers -- Responses include Content-Security-Policy, X-Content-Type-Options, X-Frame-Options, and Referrer-Policy.
  • Non-root container -- The Docker image runs as an unprivileged appuser.

For production deployments, place the service behind a TLS-terminating reverse proxy (nginx, Caddy, Traefik) and always set GW_API_KEY.

Architecture

ghost-writer/
├── src/
│ ├── __main__.py # FastAPI server, scheduler, CLI (serve/chat subcommands)
│ ├── agent.py # LangChain agent (chat + autonomous modes)
│ ├── tools.py # search_web, publish_to_blog, auto-draft capture
│ ├── tui.py # Textual TUI chat interface (gw-tui entry point)
│ ├── models.py # A2A Pydantic models
│ ├── blog_clients.py # Ghost and WordPress API clients
│ └── static/
│ └── index.html # Chat UI (served only when ENABLE_CHAT=true)
├── pyproject.toml # Package config with gw-tui entry point
├── AgentCard.json
├── Dockerfile
├── docker-compose.yml
└── .do/
└── app.yaml # DigitalOcean App Platform deployment config

Notability

notability 2.0/10

Low stars, routine repo release