feat: decouple read FeedId from inbound MailboxId
Separate the two feed identities so the public read URL never reveals the inbound address and vice-versa: - FeedId becomes an opaque high-entropy token (read id + KV key); MailboxId (noun.noun.NN) owns the inbound address and the untrusted-input boundary via MailboxId.parse. They map only through the inbound:<mailbox> secondary index, resolved solely at reception. - inbound index lifecycle is owned by FeedRepository: written by save/saveConfig, dropped by removeFromList(Bulk) — symmetric, never mirrored by hand (removes the manual delete in feed-service + the cron loop, and a silent empty-catch). - Feed.mailboxId exposes a MailboxId VO (symmetry with Feed.id); the mailbox@domain shape lives on MailboxId.emailAddress(domain). - Distinguish mailbox_unknown (no feed claims the address) from feed_not_found (dangling index) for observability; both forwardable, both 404. - Drop the redundant EmailParser.extractMailbox pass-through so MailboxId.parse is the single parse boundary. Docs (README/INSTALL/CLAUDE.md/landing) and tests updated; 439 tests green, tsc clean, build dry-run OK. Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
1 parent
f7f10779bc
commit
1a4a479190
43 files changed
+649
-149
No files matched your search
@@ -17,6 +17,7 @@ kill-the-news keeps the same workflow while avoiding shared domains and shared d
|
||||
- Inline double-confirm delete interactions with toast feedback in the admin dashboard
|
||||
- Resizable + sortable table columns in the admin dashboard (Table view)
|
||||
- Unique newsletter addresses per feed (for example `[email protected]`)
|
||||
- **Separate inbound address and feed URL** — the address you subscribe with (`[email protected]`) and the public feed URL (`/rss/<opaque-id>`) use **independent** ids, so you can share a feed without leaking the address that feeds it, and an address harvested by a newsletter can't be used to read your feed (`/rss/<your-address>` 404s)
|
||||
- Cloudflare Email Workers ingestion (no third-party service)
|
||||
- ForwardEmail webhook ingestion with source-IP verification (optional alternative)
|
||||
- Optional per-feed sender allowlist (`[email protected]` or `domain.com`)
|
||||
@@ -45,9 +46,9 @@ Two ingestion methods are supported — pick one or use both:
|
||||
|
||||
Common path:
|
||||
|
||||
1. Incoming email arrives at `[email protected]`.
|
||||
2. The Worker resolves the feed from the recipient address and stores the email in KV.
|
||||
3. `https://yourdomain.com/rss/:feedId` renders RSS from stored items.
|
||||
1. Incoming email arrives at `[email protected]` (the feed's inbound address).
|
||||
2. The Worker resolves the feed from the recipient address (via the `inbound:` index) and stores the email in KV.
|
||||
3. `https://yourdomain.com/rss/<opaque-feed-id>` renders RSS from stored items — note the feed id is a separate opaque token, not the inbound address.
|
||||
4. `/admin` provides feed management and email deletion.
|
||||
5. `https://yourdomain.com/` shows a public status page with monitoring counters and a link to the admin.
|
||||
|
||||
|
||||
Reference in new issue
Block a user