All apps · 0 apps
crosspoint-newsletter
Docker app from tisanina's Repository
Overview
Readme
View on GitHubCrossPoint Newsletter Server
Newsletter-to-EPUB pipeline for e-ink readers.
A self-hosted server that connects to an IMAP mailbox, ingests email newsletters, converts them into optimized EPUB 3.0 books for e-ink displays, and serves them through an integrated OPDS 1.2 catalog and web dashboard. Designed specifically for the CrossPoint e-ink reader (Waveshare ESP32-S3 e-paper) but compatible with any OPDS-capable e-reader or reading app.
Features
- Automated IMAP Ingestion: Connects over IMAP/SSL with periodic background polling and zero external broker dependencies.
- HTML Sanitization: Cleans bloated email HTML by stripping tracking pixels, advertisement beacons, tracking redirects, scripts, and unsubscribe footers.
- E-Ink Image Optimization: Downscales and processes embedded and inline (CID) images into 8-bit grayscale with optional Floyd-Steinberg dithering optimized for e-paper screens (up to 480×800 / 600×800).
- Procedural Cover Generation: Automatically generates stylish, readable SVG/PNG book covers with issue numbers, titles, and publication dates.
- EPUB 3.0 Packaging: Assembles clean, validated EPUB 3.0 books embedded with Calibre-compatible series metadata and navigation tables.
- Built-in OPDS 1.2 Catalog: Complete Atom XML catalog featuring navigation by recent issues, publication series, full-text search, and direct EPUB download.
- Responsive Web Dashboard: Clean modern WebUI to manage subscriptions, review incoming issues, configure credentials, and trigger manual ingestion.
- Triage Inbox & Discovery: Automatically detects emails from new or unknown senders and places them in a triage queue for one-click approval or dismissal.
- Sender Blacklist: Drop unwanted spam or notifications before processing.
- Configurable Retention Policies: Set automated rolling cleanup policies based on issue count or maximum retention age per newsletter series.
- Zero-Maintenance Storage: Embedded SQLite database with optional FTS5 search and straightforward filesystem storage—no external database servers required.
- Docker-First Deployment: Lightweight container ready for Unraid, Docker Compose, TrueNAS, or standard Linux servers.
Architecture
Email (IMAP/SSL) → Ingest → Transform (HTML→EPUB) → Storage (SQLite + filesystem)
↓
OPDS 1.2 Server + WebUI
↓
E-ink Reader (Wi-Fi → microSD → offline reading)
The system is organized into four modular components:
- Ingest (
ingest/): Connects to the dedicated IMAP mailbox, polls for unread messages, extracts MIME structures, and maps incoming emails to configured newsletter series. Unrecognized senders are saved to a triage queue. - Transform (
transform/): Sanitizes HTML markup, resolves inline images, optimizes media for e-ink displays (grayscale dithering and downscaling), generates covers, and compiles valid EPUB 3.0 files. - Storage (
storage/): Manages the SQLite database for catalog state and the local filesystem storage structure (conf/,epub/,raw/,covers/), applying retention rules and consistency audits. - Serve (
serve/): Fast, asynchronous FastAPI application serving the OPDS 1.2 feed, REST API endpoints, and Jinja2-powered administrative web dashboard.
Quick Start — Docker (Recommended)
The fastest way to deploy CrossPoint Newsletter Server is using Docker Compose.
# 1. Clone the repository
git clone https://github.com/tisanina/crosspoint-newsletter.git
cd crosspoint-newsletter
# 2. Configure environment and subscriptions
cp .env.example .env
# Edit .env with your IMAP credentials and settings
cp config/newsletters.example.yaml config/newsletters.yaml
# Add your newsletter subscriptions
# 3. Build and launch containers
cd docker
docker compose build
docker compose up -d
Once running, access the Web Dashboard at:
http://your-server:8400/ui/
Quick Start — Local Development
For local development or testing without Docker:
# 1. Create and activate a Python 3.12+ virtual environment
python3 -m venv .venv
source .venv/bin/activate
# 2. Install package in editable mode with development dependencies
pip install -e '.[dev]'
# 3. Initialize configuration files
cp .env.example .env
cp config/newsletters.example.yaml config/newsletters.yaml
# 4. Run linters and tests
ruff check src/
pytest
# 5. Start the server
cn serve
Configuration
Environment Variables
Configuration is loaded from environment variables or the .env file located at the project root.
| Variable | Description | Default |
|---|---|---|
TZ / CN_TIMEZONE |
Timezone for logs, dates, and cover generation | Europe/Rome |
CN_IMAP_HOST |
IMAP mail server hostname (e.g. imap.gmail.com) |
None |
CN_IMAP_PORT |
IMAP SSL port | 993 |
CN_IMAP_USER |
Dedicated email address for newsletter subscriptions | None |
CN_IMAP_PASSWORD |
IMAP password or app-specific password | None |
CN_IMAP_FOLDER |
Remote IMAP mailbox folder to inspect | INBOX |
CN_POLL_INTERVAL_MINUTES |
Frequency of background email ingestion checks (in minutes) | 15 |
CN_ENABLE_AUTO_POLL |
Whether to automatically poll IMAP in the background | true |
CN_KEEP_RAW |
Retain original .eml files on disk for debugging or triage |
true |
CN_CONF_DIR |
Path to persistent configuration and SQLite database | /app/conf or ./data |
CN_DATA_DIR |
Base directory for generated content (EPUBs, covers, raw email) | /app/data or ./data |
CN_OPDS_HOST |
Bind address for OPDS feed and WebUI | 0.0.0.0 |
CN_OPDS_PORT |
Port for OPDS feed and WebUI | 8400 |
CN_OPDS_USERNAME |
Optional HTTP Basic Auth username for OPDS & WebUI | None |
CN_OPDS_PASSWORD |
Optional HTTP Basic Auth password for OPDS & WebUI | None |
CN_NEWSLETTERS_CONFIG |
Path to newsletter series definition YAML file | ./config/newsletters.yaml |
Newsletter Subscriptions
Newsletters can be managed via the WebUI or declared in config/newsletters.yaml:
newsletters:
- name: "The Pragmatic Engineer"
sender: "newsletter@pragmaticengineer.com"
enabled: true
retention:
max_issues: 10 # Keep only the latest 10 issues
max_age_days: null # null = unlimited
- name: "Platformer"
sender: "casey@platformer.news"
enabled: true
sender_rules:
- type: "header_from_contains"
value: "platformer"
retention:
max_issues: null
max_age_days: 30 # Prune issues older than 30 days
Unraid Deployment
CrossPoint Newsletter Server is optimized for Unraid and supports tiered storage:
- Unraid Template: An Unraid XML template is provided in
docker/crosspoint-newsletter.xml. - Installation: Copy the template into
/boot/config/plugins/dockerMan/templates-user/or install it via the Docker tab (Community Applications integration coming soon). - Split Volume Architecture:
- Cache SSD (
/app/conf): Stores the active SQLite database (newsletter.db) and credentials for fast read/writes without spinning up the main array. - Array (
/app/data): Houses large EPUB archives, generated cover art, and raw.emlfiles.
- Cache SSD (
- Refer to
docker/crosspoint-newsletter.xmland the Operations Guide for detailed configuration paths and variables.
OPDS E-Reader Setup
The server publishes a compliant OPDS 1.2 catalog feed compatible with the CrossPoint e-paper reader, KOReader, Moon+ Reader, Foliate, and any OPDS client.
- OPDS Catalog Endpoint:
http://<your-server-ip>:8400/opds - Authentication: Supports standard HTTP Basic Authentication if
CN_OPDS_USERNAMEandCN_OPDS_PASSWORDare configured. - Offline Reading: EPUB files downloaded over Wi-Fi are stored on the reader's local storage or microSD card and remain accessible offline without requiring continuous server connectivity.
CLI Reference
The project includes a command-line utility cn for local maintenance, ingestion triggers, and storage inspection:
# Start the web server and OPDS catalog
cn serve [--host 0.0.0.0] [--port 8400] [--reload]
# Run the ingestion and EPUB conversion pipeline manually
cn pipeline run [--limit 10] [--no-mark-seen]
cn pipeline run --local-eml /path/to/email.eml
# Manage unrecognized incoming emails in the triage queue
cn inbox list
cn inbox approve <id> [--name "My Newsletter"] [--slug "my-newsletter"]
cn inbox dismiss <id>
# Inspect and manage newsletter publications
cn newsletter list
cn newsletter add "Tech Weekly" --sender "news@techweekly.com" --max-issues 20
cn newsletter update <slug> --max-age-days 60 --apply-retention
# Inspect issues and storage metrics
cn issue list [--newsletter <slug>] [--status done]
cn issue stats
# Enforce retention rules and prune expired issues
cn retention run [--dry-run] [--newsletter <slug>]
# Audit and verify consistency between SQLite DB and disk storage
cn storage check
Project Structure
crosspoint-newsletter/
├── config/
│ └── newsletters.example.yaml # Newsletter subscription rules template
├── docker/
│ ├── Dockerfile # Multi-stage production container
│ ├── docker-compose.yml # Container orchestration
│ ├── crosspoint-newsletter.xml # Unraid Community Applications template
│ └── entrypoint.sh # Container startup script
├── docs/ # Architecture and operational documentation
│ ├── ARCHITECTURE.md # Detailed end-to-end architecture
│ ├── DATA_MODEL.md # Database schemas and entities
│ ├── OPDS_SERVER.md # OPDS 1.2 feed specification
│ ├── OPERATIONS.md # Production deployment & operations guide
│ ├── PRIVACY.md # Privacy considerations & tracking removal
│ └── WEBUI.md # WebUI endpoints & REST API specification
├── src/crosspoint_newsletter/
│ ├── __init__.py
│ ├── cli.py # CLI management interface (`cn`)
│ ├── config.py # Environment and runtime configuration
│ ├── ingest/ # IMAP polling, email parsing, sender matching
│ ├── transform/ # HTML sanitization, e-ink image processing, EPUB 3.0 packaging
│ ├── storage/ # SQLite database, file store, retention engine
│ └── serve/ # FastAPI OPDS 1.2 catalog, REST API, Web dashboard
├── tests/ # Unit and integration test suite
├── .env.example # Environment configuration template
├── pyproject.toml # Build configuration and dependencies
├── LICENSE # MIT license
└── README.md # Project documentation
Documentation
For in-depth details on design, development, and maintenance, see the documentation in docs/:
- Architecture — System components, boundaries, and data flow.
- Data Model — Database schema, SQLite migrations, and entity relations.
- OPDS Server — Atom XML catalog endpoints and e-reader contract.
- Operations Guide — Deployment, backup, logging, and troubleshooting.
- Privacy Policy — Tracker stripping, telemetry removal, and sanitization rules.
- WebUI & API — Web interface design and REST API endpoints.
Contributing
Contributions are welcome! Please follow these steps:
- Fork the repository and create a feature branch (
git checkout -b feature/amazing-feature). - Make your changes following PEP 8 guidelines and existing architectural patterns.
- Run tests and linters to ensure everything passes:
ruff check src/ pytest - Commit your changes with descriptive commit messages (
git commit -m "Add support for XYZ"). - Push to your branch (
git push origin feature/amazing-feature) and open a Pull Request.
License
This project is licensed under the terms of the MIT License.
Install crosspoint-newsletter on Unraid in a few clicks.
Find crosspoint-newsletter in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.
Related apps
Explore more like this
Explore allDetails
ghcr.io/tisanina/crosspoint-newsletter:latestRuntime arguments
- Web UI
http://[IP]:[PORT:8400]/ui/- Network
bridge- Shell
sh- Privileged
- false
- Extra Params
--restart unless-stopped
Template configuration
Porta principale per la WebUI di gestione e il catalogo OPDS
- Target
- 8400
- Default
- 8400
- Value
- 8400
Volume su Cache SSD: database SQLite newsletter.db, credenziali e configurazione
- Target
- /app/conf
- Default
- /mnt/user/appdata/crosspoint-newsletter
- Value
- /mnt/user/appdata/crosspoint-newsletter
Volume su Array RAID: archivio libri EPUB, raw email (.eml) e copertine
- Target
- /app/data
- Default
- /mnt/user/data/crosspoint-newsletter
- Value
- /mnt/user/data/crosspoint-newsletter
Server IMAP della casella di posta (es. imap.gmail.com)
- Target
- CN_IMAP_HOST
- Default
- imap.gmail.com
- Value
- imap.gmail.com
Porta SSL IMAP (default: 993)
- Target
- CN_IMAP_PORT
- Default
- 993
- Value
- 993
Indirizzo email su cui ricevi le newsletter
- Target
- CN_IMAP_USER
Password per le app di Gmail (16 caratteri)
- Target
- CN_IMAP_PASSWORD
Cartella da monitorare (default: INBOX)
- Target
- CN_IMAP_FOLDER
- Default
- INBOX
- Value
- INBOX
Esegue il controllo periodico della posta in background
- Target
- CN_ENABLE_AUTO_POLL
- Default
- true
- Value
- true
Intervallo in minuti tra un controllo e l'altro (default: 15)
- Target
- CN_POLL_INTERVAL_MINUTES
- Default
- 15
- Value
- 15
Conserva i file originali .eml su RAID per triage o rielaborazione
- Target
- CN_KEEP_RAW
- Default
- true
- Value
- true
Fuso orario del container (default: Europe/Rome)
- Target
- TZ
- Default
- Europe/Rome
- Value
- Europe/Rome
User ID Unraid (default: 99 nobody)
- Default
- 99
- Value
- 99
Group ID Unraid (default: 100 users)
- Default
- 100
- Value
- 100