v0.2.0
This release adds a versioned REST API, full attachment support (R2), per-feed favicons, one-click unsubscribe, and a redesigned landing/status page — on top of a large internal refactor toward a clean domain-driven architecture.
✨ Features
REST API & OpenAPI
- New versioned REST API (
/api/v1/feeds*) with an OpenAPI 3.1 spec (/api/openapi.json) and rendered reference docs via Scalar (/api/docs). /api/v1/statsis now the canonical public stats endpoint (JSON + CORS); the deprecated/api/statshas been removed.
Attachments (Cloudflare R2)
- Optional R2 attachment storage with a config toggle, storage metrics on the status page, and a demo configuration.
- Attachments are listed with download links on the email view and the admin email detail page; a paperclip indicator flags emails that carry attachments.
- Inline
cid:images are now rendered in place inside the email/feed body instead of being shown as separate attachments.
Favicons
- Project favicon served from the envelope logo (
/favicon.svg,/favicon.ico). - Per-feed favicon derived from the last sender's domain (
/favicon/:feedId), falling back to the project icon.
Feeds
- RFC 8058 one-click unsubscribe dispatched when a feed is deleted.
Landing & status page
- Redesigned status page: hero, themed sections, responsive layout, FAQ.
- Live counter moved above the demo banner; CTAs point to the demo; feature cards for the REST API and auto-expiring feeds.
Admin UX
- Status-page link added to the dashboard header.
- Create-feed form collapsed into an accordion to declutter the dashboard.
🐛 Fixes
- Render inline
cid:images correctly in emails and feeds. - Purge R2 attachments when bulk-deleting emails via the no-JS path.
- Add a landing-page favicon to fix the
/favicon.ico404. - Close type-check gaps in client scripts and tooling.
📚 Documentation
- Extracted setup/deploy/config into
INSTALL.md. - Added
SECURITY.mdandCONTRIBUTING.md. - README: continuous-deployment section (CI secrets) and a note on the R2 permission needed for scoped deploy tokens.
- License: added Julien Herr copyright alongside the original author.
🏗️ Internal / Architecture
A substantial refactor toward domain-driven design (no behavior change for users):
- Split
src/intodomain/(framework-agnostic),application/(use-cases),infrastructure/(KV/R2, HTTP, logging) androutes/(HTTP edge). - Introduced the Feed aggregate as the single write path for feed config + the email index, with domain events (
FeedCreated,EmailIngested) driving side effects (counters, WebSub, favicon). - Consolidated all KV access behind repository adapters (
FeedRepository,IconRepository,WebSubSubscriptionRepository,CountersRepository) and a single key schema. - Added value objects (
FeedId,EmailAddress,Domain,SenderPolicy,Lifetime) and a domain/persistence translation seam (feed-mapper).
Full changelog: v0.1.0...v0.2.0