Self-hosted newsletter
Run your own newsletter.
Kestrel is an open-source, self-hosted newsletter app. Write in Markdown, preview the exact email, schedule with a cancelable review window, and send to your subscribers. You keep the list, the consent record, the delivery record, and a permanent per-issue archive.
Runs on your own Cloudflare account. Your data never leaves it.

Ownership
You keep what matters
The pitch isn't another sending API. It's that the parts a newsletter is actually made of stay yours.
The list
Your subscribers
They live in your own D1 database, not a vendor's. Query or export them any time. Nobody rents you access to your own audience.
The consent
Proof of permission
Every subscription is double opt-in, with the confirmation and the unsubscribe recorded. The evidence you had consent stays with you.
The delivery
An auditable record
Each send records who received which issue, plus suppressions from hard bounces and spam complaints. You control the whole trail.
The archive
A permanent home
Every issue gets a lasting public page on your own domain. The links you send never rot behind someone else's platform.
How it works
Write, preview, schedule, send
One interface, an HTTP API, with two clients: a web editor and Claude. The steps are the same either way.
Write in Markdown
Draft an issue in the web editor or hand it to Claude. Same API underneath, same result. If a draft changed under you, a stale save is rejected rather than clobbered.
Preview the exact email
See precisely what lands in the inbox: the rendered email HTML and plain-text, not an approximation. Send yourself a test first.
Schedule with a review window
Queue a send behind a cancelable countdown. Change your mind and cancel before it fires; a Cron Trigger drives the sweep when it does.
Send to a double opt-in list
Only confirmed subscribers receive it. The send loop is idempotent, so it never double-sends, and bounces and complaints suppress on their own.
Archive it forever
Each sent issue becomes a permanent, indexable page on your domain. The same content stays readable long after the email is buried.
Under the hood
Built to be operated
Small, legible, and boring in the ways that matter. You run it on your own account and can read every line. It's open source under the MIT license.
- One API, two clients
- A single HTTP API drives everything; the web editor and Claude are just clients of it. Nothing the editor can do is off-API.
- Optimistic concurrency
- Edits are revision-checked (
If-Match/ETag), so a stale write is rejected with409instead of overwriting a newer draft. - Idempotent send loop
- The scheduled sweep is safe to retry. It resumes a send without re-mailing anyone who already received the issue.
- Three isolated environments
- dev, staging, and production each get their own database, storage, and mail transport, so development can never reach a real inbox.
- Swappable email provider
- SES or Resend in production; a fake in-memory transport for local dev and tests. It's one variable.
- Access-gated admin
- Authoring sits behind Cloudflare Access. The reader pages are public, gated only by unguessable per-subscriber tokens.
Self-host
From clone to a running instance
Local dev needs Node and npm. No Cloudflare account is required until you deploy. The shortest honest path:
git clone https://github.com/kurtbruns/kestrel
cd kestrel && npm install
cp .dev.vars.example .dev.vars
npm run dev # http://localhost:8787
npm run seed # load the demo publicationStanding up a real, deployed instance takes more: a Cloudflare account, one Access application, an email provider (SES or Resend), and sending-domain DNS. The operator setup guide covers it.
Your audience. Your data. Your newsletter.
Kestrel is self-hosted and runs entirely on your own Cloudflare account.