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. Work in the editor, or just ask Claude. You keep the list, the consent record, the delivery record, and a permanent per-post archive.
Free and open source. You pay only for Cloudflare and Amazon SES.

How it works
Write, preview, schedule, send
Every post follows the same path, from a draft in Markdown to an email in your subscribers' inboxes and a permanent page on your own domain.
Write in Markdown
Draft a post in the web editor. 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. Cancel it or move it any time before it fires; when the window closes, it sends on its own.
Send to confirmed subscribers
Only subscribers whose consent is on record receive it. Nobody is mailed twice, even if a send has to resume. A bounce stops mail to that address, and a spam complaint unsubscribes the reader, on their own.
Archived automatically
Each sent post becomes a permanent, indexable page on your domain, unless you send it as email only. Either way, the email's own link keeps working, so the post stays readable long after the email is buried.
Working with robots
Ask Claude to do it
Kestrel is built on an API. Everything the app does, it does through that API. Connect Claude to your instance and it works through that same API, as you, from proofreading a draft to scheduling its send. The same guardrails hold either way: a scheduled post waits out its review window, and you can still move it or cancel it.
Can you proofread my draft titled “First frost” and schedule it to go out Friday at 7am?
- Read draft · First frost
- Scheduled · Friday, 7:00 AM
One typo: “kestral” in the third paragraph, now “kestrel”. The rest reads clean.
Scheduled for Friday at 7:00 AM, cancelable until it sends.
Add Kestrel as a connector in Claude Desktop or claude.ai, or sign Claude Code in. Either way Claude signs in as you, and its changes show as yours, marked as made through Claude. How to connect Claude →
Ownership
You keep what matters
When you have something to share, you should be able to reach the people who signed up to hear from you. With Kestrel you own that relationship: the list, the proof they opted in, the record of what you sent, and the archive it lives in.
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
Signups are double opt-in, and every import that adds subscribers carries your word that they opted in. Confirmations and unsubscribes are recorded, so the evidence you had consent stays with you.
The delivery
An auditable record
Each send records who received which post, and every bounce and spam complaint it drew. You control the whole trail.
The archive
A permanent home
Every post gets a lasting page on your own domain: in your public archive, or at an unlisted link if you send it as email only. The links you send never rot behind someone else's platform.
Moving from another service? Bring your list with you: import it
in the dashboard, or ask Claude to move it with its /migrate-list skill.
Your readers arrive confirmed, on your word that they opted in, and the import
emails no one.
Bring your existing list →
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 post.
- Access-gated admin
- Authoring sits behind Cloudflare Access. The reader pages are public, gated only by unguessable per-subscriber tokens.
Get started
See if Kestrel is right for you
Kestrel is for publishers who want to own their list and don't mind running a small app to do it. Try it first, in your browser or on your computer.
Step 1 · Try it
Try the demo
Your own private copy of Kestrel, filled with a demo publication. Write a post, preview the email, and schedule a send. Nothing reaches a real inbox, and your copy is cleared after a while.
Step 2 · Run your own
Deploy your own
Kestrel installs from npm as
@kurtbruns/kestrel. Your instance is a small repository of your own that names the release it runs, so upgrading is installing a newer one. A step-by-step guide takes you from an empty repository to a live newsletter on your own domain. You'll need:- A Cloudflare account, with your domain's DNS on Cloudflare
- An AWS account, to send through Amazon SES
- Node.js 22 or later, and Git
- A GitHub account, or another Git host, for your instance's repository
No per-subscriber fees. You pay Amazon SES's rates per email. Cloudflare's free Workers plan suits a list of up to about 1,000 subscribers, and sends to all of them in about 5 minutes once AWS raises your SES send rate to 50 a second, which costs nothing. A larger list belongs on Cloudflare's $5 Workers Paid plan, for the database's daily writes as well as faster sends.
Running an instance already, or want one hosted for you? Get in touch.
Your audience. Your data. Your newsletter.
Open source under the MIT license, and yours to run.