{"id":51025397,"url":"https://github.com/proverbiallemon/emdash-inbox","last_synced_at":"2026-06-21T19:01:55.762Z","repository":{"id":352362805,"uuid":"1214856549","full_name":"proverbiallemon/emdash-inbox","owner":"proverbiallemon","description":"Inbox-by-Google-style mailbox UI for EmDash CMS, with Cloudflare Email Service transport built in","archived":false,"fork":false,"pushed_at":"2026-05-26T05:18:14.000Z","size":669,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-26T07:17:55.213Z","etag":null,"topics":["cloudflare","cloudflare-workers","cms","email","emdash","emdash-plugin","inbox"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/proverbiallemon.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-19T06:20:41.000Z","updated_at":"2026-05-26T05:18:18.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/proverbiallemon/emdash-inbox","commit_stats":null,"previous_names":["proverbiallemon/emdash-inbox"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/proverbiallemon/emdash-inbox","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/proverbiallemon%2Femdash-inbox","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/proverbiallemon%2Femdash-inbox/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/proverbiallemon%2Femdash-inbox/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/proverbiallemon%2Femdash-inbox/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/proverbiallemon","download_url":"https://codeload.github.com/proverbiallemon/emdash-inbox/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/proverbiallemon%2Femdash-inbox/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34622271,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-21T02:00:05.568Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloudflare","cloudflare-workers","cms","email","emdash","emdash-plugin","inbox"],"created_at":"2026-06-21T19:01:54.141Z","updated_at":"2026-06-21T19:01:55.754Z","avatar_url":"https://github.com/proverbiallemon.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# emdash-inbox\n\n**The mailbox UI for [EmDash CMS](https://github.com/emdash-cms/emdash).** Read, thread, reply, pin, snooze, and mark-done the email your site sends and receives, right inside the admin panel.\n\nOutbound goes through the native Cloudflare Email Sending Workers binding — no API token to manage. Inbound arrives via a small Cloudflare Email Worker sidecar that POSTs to a webhook-secured endpoint. The whole UI is one EmDash plugin.\n\n---\n\n## Status\n\n**Pre-alpha (v0.7.0).** The plugin works end-to-end for outbound + inbound + threading + reply + grouped inbox with per-message read state. M1–M7 shipped: outbound and inbound email work end-to-end via the native CF Email Sending binding; the admin page is a card-based Inbox with pin / snooze / done, filter tabs, date buckets, and a cron-driven wake path for snoozed messages; clicking a card opens a thread-grouped detail view with sanitized HTML body rendering and thread-level bulk actions; the thread view has an inline TipTap-based reply form (pre-filled To / Subject / quoted body, Cmd+Enter to send); the inbox list collapses messages to one card per thread with participant chips, message-count badge, and a faded second snippet when the thread has history; and an admin-auth MCP route (`messages/mcp`) exposes 7 inbox tools over JSON-RPC 2.0. Inbox list aggregates threads client-side over all messages on every list-view fetch — fine for personal mailboxes (\u003c5K messages), revisit before v1.0 if running at higher volumes.\n\nBuilt against EmDash v0.14.0 (bumped from 0.12 — see CHANGELOG). Expect breaking changes between commits as EmDash itself matures.\n\n## Why this exists\n\nEmDash (Cloudflare's WordPress successor, released April 2026) ships with a plugin system, a media library, content types, and an MCP server — but not with email. Cloudflare Email Service (public beta, April 2026) provides a native Workers binding for sending and a receive pipeline via Email Workers.\n\n`emdash-inbox` is the missing piece: one plugin that makes EmDash a CMS *and* an email client, using the platform Cloudflare stack underneath.\n\n## Operator setup\n\n1. **Onboard your sender domain to Cloudflare Email Sending** (Dashboard -\u003e Compute \u0026 AI -\u003e Email Service -\u003e Email Sending -\u003e Onboard Domain). One-time.\n2. **Add the `send_email` binding to your host's `wrangler.jsonc`:**\n   ```jsonc\n   \"send_email\": [\n     { \"name\": \"EMAIL\", \"remote\": true }\n   ]\n   ```\n3. **Wire the plugin into `astro.config.mjs`** alongside the existing `ssr.noExternal: [\"emdash-inbox\"]` entry. Under EmDash 0.12 the plugin's runtime deps (TipTap, dompurify, postal-mime) also want to be listed in `vite.ssr.noExternal` to avoid Vite optimizer cascades during dev — the browser still serves correctly without it, the cascades are just noisy.\n4. **Configure plugin settings** in the admin: `senderAddress` (your verified sender) and `inboundSecret` (a long random string shared with the inbound sidecar worker).\n5. **Deploy the inbound sidecar Worker** under `examples/inbound-email-worker/` and bind it to your domain via Cloudflare Email Routing. The sidecar POSTs raw RFC822 to `POST /_emdash/api/plugins/emdash-inbox/inbound`, gated by `X-Inbound-Secret` matching the value you configured in step 4.\n\nOperators upgrading from 0.6.x: the `accountId` and `apiToken` fields are gone — existing rows for those settings are cleared automatically on first request after upgrade. The CF API token they referenced can be revoked.\n\n### Troubleshooting\n\n- **`No email provider configured` / `EMAIL_NOT_CONFIGURED` after install.** Tail the host worker (`wrangler tail`) and look for `[hooks] Plugin \"emdash-inbox\" declares email:deliver hook without hooks.email-transport:register capability — skipping`. That message means your host is on EmDash 0.14+ and is bundling an older `definePlugin` from `emdash-inbox`'s nested `node_modules`. Make sure `emdash-inbox`'s `devDependencies.emdash` matches your host's installed version (≥0.14) and rebuild the plugin with `pnpm install \u0026\u0026 pnpm build`. The current main branch is already set up for 0.14.\n- **Magic-link URL contains `localhost:4321`.** EmDash stores the base URL under the `emdash:site_url` option in the database, set during initial setup. Setting `SITE_URL` in `wrangler.jsonc` afterwards does not back-fill that row. Update it directly: `wrangler d1 execute \u003cdb\u003e --remote --command \"UPDATE options SET value='\\\"https://your.domain\\\"' WHERE name='emdash:site_url';\"`\n\n## Roadmap\n\n| Milestone | Deliverable |\n|---|---|\n| **M1** ✅ | `email:provide` claimed; `email:deliver` hook sends via Cloudflare Email Service (REST path in M1; migrated to the native binding in M7). Outbound proven end-to-end. |\n| **M2** ✅ | Inbound via Cloudflare Email Worker; basic list-view admin page. |\n| **M3** ✅ | Inbox-by-Google UX: card-based list, pin / snooze / done, filter tabs, date buckets, cron wake path for snoozed messages. |\n| **M4** ✅ | Threading (derived from In-Reply-To / References at ingest), message detail / thread view, sanitized HTML body rendering with external-image gating, thread-level bulk actions. |\n| **M5** ✅ | Inline reply / compose in the thread view (TipTap StarterKit editor, pre-filled To / Subject with Re-prefix dedup, quoted-body seed, Cmd+Enter to send, Esc to discard); shared `deliverEmail()` extraction so both the `email:deliver` hook and the new `messages/reply` route dispatch through one path. |\n| **M6** ✅ | Thread-grouping in the inbox list (one card per thread with participant chips, message-count badge, two-snippet preview when N≥2); per-message read state with auto-mark-read on thread open; latest-message-wins filter behavior; new `\u003cThreadCard\u003e` with fan-out hover actions matching `\u003cThreadView\u003e`'s bulk-action pattern. |\n| **M7** ✅ | REST-to-native binding migration for outbound (drops the `accountId` / `apiToken` settings + the `network:fetch` capability); admin-auth `messages/mcp` route exposing 7 inbox tools over JSON-RPC 2.0 (`list_threads`, `get_thread`, `search_messages`, `mark_read`, `pin_thread`, `snooze_thread`, `mark_done`); typed `EmailBinding` + `DeliverError` + `wrapBindingError()` helper module. |\n| **M8** | Compose-from-scratch + reply-all + CC / BCC, attachments (unblocked by M7's binding migration), drafts, signatures, toast undo, pagination for `messages/list`. Compose surfaces in both the admin UI **and** the `messages/mcp` MCP route, so AI clients can send mail (not just triage existing threads). |\n| **M8b** | Complete the `messages/mcp` triage suite so anything possible in the admin UI is also possible through Claude / other MCP clients: compose, reply, reply-all, draft save, attachment upload, plus the existing read + status tools. Target: \"full inbox in a Claude chat.\" |\n| **M9** | Bundle classification (Orders, Shipping, Commissions, Fans, Promos, Updates) + highlights — structured field extraction surfaced as inline cards. Reminders, content linking. **v1.0.** |\n\n## Attribution\n\nInformed by patterns from [SaasMail](https://github.com/choyiny/saasmail) (Apache License 2.0) — particularly around Cloudflare Email Workers inbound handling, MIME parsing, and the rich-text composer. See [NOTICE](./NOTICE).\n\n## License\n\n[Apache License 2.0](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fproverbiallemon%2Femdash-inbox","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fproverbiallemon%2Femdash-inbox","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fproverbiallemon%2Femdash-inbox/lists"}