A vibe coded frontend and server for sending news articles to an e-reader.
Find a file
Stefan 99882bb80a Restrict readerEmail to allowlisted domains
Prevents the form being abused as an open email relay to arbitrary
addresses (which is the realistic abuse vector — disk fill, server
compromise, and device exploitation are all bounded already).

Default allowlist: kindle.com, pbsync.com. Suffix matching so
subdomains (e.g. free.kindle.com) are also accepted.

Configurable via ALLOWED_READER_DOMAINS env var so the list can be
extended without code changes.
2026-05-21 23:54:55 +03:00
public Restrict readerEmail to allowlisted domains 2026-05-21 23:54:55 +03:00
src Restrict readerEmail to allowlisted domains 2026-05-21 23:54:55 +03:00
.env.example Restrict readerEmail to allowlisted domains 2026-05-21 23:54:55 +03:00
.gitignore Rewrite the send-to-kindle app to work for at least pocketbook as well 2026-05-21 23:08:55 +03:00
.prettierrc.json Rewrite the send-to-kindle app to work for at least pocketbook as well 2026-05-21 23:08:55 +03:00
LICENSE Rewrite the send-to-kindle app to work for at least pocketbook as well 2026-05-21 23:08:55 +03:00
package-lock.json Add file upload (POST /send-file) and drag-and-drop UI 2026-05-21 23:43:17 +03:00
package.json Add file upload (POST /send-file) and drag-and-drop UI 2026-05-21 23:43:17 +03:00
README.md Restrict readerEmail to allowlisted domains 2026-05-21 23:54:55 +03:00
tsconfig.json Rewrite the send-to-kindle app to work for at least pocketbook as well 2026-05-21 23:08:55 +03:00

Send to E-Reader

Convert web articles to EPUB and email them straight to your e-reader. Works with any device that accepts emailed files — Kindle, PocketBook, or anything else with a similar service.

Built with Express 5 + TypeScript. Hosted at read.atanasov.fi.

How it works

Two ways to send something to your e-reader:

URL → EPUB

  1. You paste an article URL and your e-reader's email address.
  2. The server fetches the page, runs Mozilla Readability over it to strip ads/chrome, builds an EPUB, and emails it to the address you gave.
  3. The EPUB is deleted from the server immediately after sending.

Upload a file

  1. Drag (or pick) a file — EPUB, PDF, MOBI, anything your device accepts.
  2. The server attaches it to an email and sends it to your e-reader as-is, no conversion.
  3. The file is deleted from the server immediately after sending. Max size: 25 MB.

Supported devices

The destination email domain is checked against an allowlist (default: kindle.com, pbsync.com) to prevent the app being used as an open email relay. Subdomains count, so free.kindle.com works too.

Device Email pattern
Amazon Kindle *@kindle.com (or *.kindle.com subdomains)
PocketBook *@pbsync.com

You can extend the list via the ALLOWED_READER_DOMAINS env var (see below).

You'll also need to add the sender address to your device's approved-senders list — one-time per device.

Prerequisites

  • Node.js 20 or newer
  • An SMTP-capable email provider. Defaults are set up for Resend — sign up (no waitlist), verify your domain, grab an API key.

Local development

git clone <your-fork>
cd send_to_kindle
npm install
cp .env.example .env   # then fill in SMTP_PASSWORD (Resend API key) and MAIL_FROM
npm run dev

Server listens on http://localhost:3000 (override with PORT).

Production build

npm run build
npm run start

Environment variables

Variable Required Default Notes
SMTP_HOST no smtp.resend.com Any SMTP server works
SMTP_PORT no 587 587 = STARTTLS (preferred — port 465 is blocked by most VPS providers)
SMTP_USER yes For Resend, literally the string resend
SMTP_PASSWORD yes For Resend, your API key (re_…)
MAIL_FROM yes Sender address, e.g. read@atanasov.fi. Must be from a verified domain on your provider.
ALLOWED_READER_DOMAINS no kindle.com,pbsync.com Comma-separated allowlist of destination email domains. Suffix-matching, so subdomains are allowed.
PORT no 3000 Listen port

Resend setup (quick)

  1. Sign up at resend.com.
  2. Add your domain (e.g. atanasov.fi) and add the SPF/DKIM/return-path DNS records they generate. Verification usually completes in a few minutes.
  3. Create an API key.
  4. In .env:
    SMTP_USER=resend
    SMTP_PASSWORD=re_xxxxxxxxxxxxxxxxxx
    MAIL_FROM=send@read.atanasov.fi
    

For a different provider, just point SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASSWORD at it — any standard SMTP service works.

API

POST /send-article

Body (JSON):

{
  "url": "https://example.com/article",
  "readerEmail": "your-device@kindle.com"
}

Response on success:

{
  "success": true,
  "message": "Article sent to your e-reader",
  "title": "Article Title"
}

POST /send-file

Body (multipart/form-data):

  • file — the binary file (max 25 MB)
  • readerEmail — destination address

Response on success:

{
  "success": true,
  "message": "File sent to your e-reader",
  "title": "original-filename.epub"
}

Rate limit

Both endpoints share a limit of 20 sends per IP per hour.

Limitations

  • Articles must be publicly accessible — no paywalled or login-required pages.
  • Server-side fetch means JS-heavy SPAs may extract poorly (the page must render meaningful HTML without JavaScript).
  • Some sites block non-browser user-agents; the app sends a Chrome UA but can still be refused.

Stack

License

GPL-3.0-only. See LICENSE.