Skip to content

Repository files navigation

HeadRSS

A self-hosted, headless RSS sync backend on Cloudflare Workers + D1.

Overview

HeadRSS is built with these principles in mind:

  • Decoupled: feed fetching and sync are separate. The fetcher runs on your machine, the sync server on Cloudflare, connected only by HTTP.
  • Extensible: the fetch pipeline is open to customization (e.g. full-text extraction) without touching the sync server.
  • Data ownership: your subscriptions, read state, and content stay in your own database.
  • Client freedom: you can use any Google Reader-compatible app (Reeder, NetNewsWire, etc.) on any platform.

Features

  • Google Reader API for RSS client sync
  • Native REST API with OpenAPI spec for web frontends
  • OPML import/export
  • Authenticated feed support (Basic Auth, Bearer token, custom headers), encrypted at rest
  • Multi-user with per-device app passwords
  • Automatic exponential backoff for failing feeds
  • Separate CLI fetcher that runs on your machine via cron
  • CLI for admin and subscription management
  • Runs on Cloudflare free tier for personal use

Architecture

HeadRSS consists of two components:

  • Worker: Cloudflare Workers + D1. Handles API requests and stores all data (feeds, items, users, read state).
  • CLI (headrss): runs on your machine. Works as:
    • Fetcher: fetches RSS/Atom feeds on a cron schedule and pushes items to the Worker.
    • Admin/user client: manages users, feeds, subscriptions, folders, and OPML.
┌─────────────────────────────────┐ ┌──────────────────────────────────────┐
│ Server │ │ Cloudflare (Worker + D1) │
│ │ │ │
│ CLI (headrss): │ │ API endpoints: │
│ headrss feed fetch │ │ /api/google/* ← RSS clients │
│ ├─ GET /admin/feeds ─────┼────▶│ /api/native/* ← web frontend │
│ ├─ fetch RSS/Atom feeds │ │ /ingest/* ← fetcher push │
│ └─ POST /ingest/items ─────┼────▶│ /admin/* ← admin/fetcher │
│ │ │ │ │
│ headrss feed purge ─────┼────▶│ ▼ │
│ │ │ D1 (SQLite) │
│ headrss admin user/feed/opml │ │ │
│ headrss subscription/folder │ │ │
│ │ │ │
└─────────────────────────────────┘ └──────────────────────────────────────┘

See Architecture for details.

Packages

PackageDescription
@headrss/coreDomain types, port interfaces, commands, queries, Zod schemas
@headrss/adapter-d1D1 implementation of EntryStore + CredentialStore
@headrss/adapter-greaderGoogle Reader protocol adapter (Hono sub-app)
@headrss/adapter-apiNative REST API adapter (Hono sub-app, OpenAPI)
@headrss/workerCloudflare Worker entry point — wires everything together
@headrss/cliCLI tool for admin + feed fetching (Bun runtime)

Setup

Prerequisites

Install & Build

pnpm install
pnpm build

Deploy the Worker

# Create D1 databasecd packages/worker
npx wrangler d1 create headrss
# Copy wrangler.toml.example and fill in your database_id
cp wrangler.toml.example wrangler.toml
# Apply migrations
npx wrangler d1 migrations apply headrss --local # local dev
npx wrangler d1 migrations apply headrss --remote # production# Set secrets
npx wrangler secret put TOKEN_KEY
npx wrangler secret put CREDENTIAL_KEY
npx wrangler secret put INGEST_API_KEY
npx wrangler secret put FETCH_API_KEY
npx wrangler secret put ADMIN_API_KEY
# Deploy
npx wrangler deploy

Configure the CLI

All configuration is via environment variables:

# Requiredexport HEADRSS_URL="https://headrss.your-domain.com"# Admin operationsexport ADMIN_API_KEY="your-admin-key"# Feed fetchingexport FETCH_API_KEY="your-fetch-key"export INGEST_API_KEY="your-ingest-key"# Per-user operations (or use `headrss login`)export HEADRSS_USER="your-username"export HEADRSS_PASSWORD="your-app-password"

Optional tuning:

VariableDefaultDescription
FETCH_CONCURRENCY8Max parallel feed fetches
FETCH_INTERVAL900Seconds between fetches per feed
FETCH_TIMEOUT30HTTP request timeout (seconds)
RETENTION_DAYS90Item retention for purge
LOG_LEVELinfodebug, info, warn, error

See Setup Guide and Configuration for details.

Usage

Initial setup

# Create a user
headrss admin user add alice
# Create an app password (displays the plaintext password once)
headrss admin password add <userId> --label "CLI"# Login (caches token locally)
headrss login

Import feeds

# Import from OPML file (as logged-in user)
headrss subscription import subscriptions.opml
# Export subscriptions as OPML
headrss subscription export -o subscriptions.opml
# Or add individually
headrss subscription add https://example.com/feed.xml
headrss subscription add https://example.com/feed.xml --folder Technology

Fetch & sync

# Fetch all due feeds (one-shot, for cron)
headrss feed fetch
# Dry run — show what would be fetched
headrss feed fetch --dry-run
# Purge old items (keeps starred and explicitly unread)
headrss feed purge

Recommended cron

*/15****headrss feed fetch03***headrss feed purge

Manage subscriptions & folders

headrss subscription list
headrss subscription add <url>
headrss subscription rm <id>
headrss subscription import <file>
headrss subscription export [-o <file>]
headrss folder list
headrss folder add <name>
headrss folder rm <id>

Admin commands

headrss admin user list
headrss admin user add <username>
headrss admin user rm <id>
headrss admin password list <userId>
headrss admin password add <userId>
headrss admin password rm <id>
headrss admin feed list
headrss admin feed rm <id>
headrss admin opml export<userId>
headrss admin opml import <file> --user <id>

See CLI Reference for details.

Connecting RSS clients

Point your RSS client at the Worker URL with the Google Reader base path:

https://headrss.your-domain.com/api/google

Login with your username and an app password. Tested with:

  • Reeder (iOS/Mac)
  • NetNewsWire (Mac/iOS)

API endpoints

PathAuthDescription
/api/google/*ClientLogin tokenGoogle Reader-compatible API
/api/native/v0/*Bearer tokenNative REST API (OpenAPI)
/ingest/*INGEST_API_KEYFeed item ingestion
/admin/*FETCH_API_KEY / ADMIN_API_KEYSystem administration
/healthNoneHealth check
/api/openapi.jsonNoneOpenAPI spec

See API Reference and Authentication for details.

Development

# Install dependencies
pnpm install
# Build all packages
pnpm build
# Run tests
pnpm test# Type check
pnpm typecheck
# Lint
pnpm lint
# Format
pnpm format
# Local dev servercd packages/worker && npx wrangler dev
# Run CLI in development
bun packages/cli/src/index.ts --help

Building the CLI binary

# Buildcd packages/cli
bun build --compile src/index.ts --outfile headrss
# Use it directly
./headrss feed fetch
# Or, copy to bin
cp headrss ~/.local/bin/

Documentation

License

MIT

About

Self-hosted, headless Google Reader compatible RSS sync backend on Cloudflare Workers

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages