InboxFly Documentation
Complete guide for the self-hosted edition (open source · MIT): from local trials to production, daily management and advanced setup.
1. Quick start locally (3 min)
Local environments cannot receive real mail, but you can exercise the full panel and pipeline (seed data via "Simulate"):
git clone https://github.com/xusteve/inboxfly.email
cd inboxfly && npm install
npm run dev # starts dev server on http://localhost:8788
- First visit opens the setup wizard; the local SETUP_TOKEN is
inboxfly-local-setup (see .dev.vars).
- Wizard step 3 (Enable Email Routing) is skipped locally.
- In the panel click "Simulate": it seeds test mail through the exact production pipeline (match → filter → decide → store → stats), only skipping real delivery.
- To reset:
rm -rf .wrangler and restart, then walk the wizard again.
2. Deploy to Cloudflare
Deploy to Cloudflare
npx wrangler login
npx wrangler deploy # Worker + static panel + D1 + R2 + KV + Cron
npx wrangler secret put SETUP_TOKEN # setup token for the first-run wizard
- D1 tables are created automatically on first panel visit — no manual SQL needed (or run
npx wrangler d1 execute inboxfly --remote --file schema.sql as a fallback).
- The panel defaults to
https://inboxfly.<your-subdomain>.workers.dev; bind a custom domain (e.g. panel.yourdomain) under Workers → Domains & Routes.
- The Cron Trigger (daily 03:00 UTC R2 cleanup) is registered automatically with the deployment.
robots.txt ships with Disallow: / so your private panel is not indexed.
3. First-run wizard (5 steps)
Open the panel URL after deploying and follow the wizard. Every step explains "why" and "risks":
- Verify deployer identity: enter the SETUP_TOKEN (appears only in deploy output, invalidated after setup) to prevent panel takeover.
- Create admin credentials: username + ≥8-char password (PBKDF2-hashed, non-recoverable).
- Enable Email Routing: skip locally; in production follow the guide in the Cloudflare dashboard (next section).
- Add a destination address: enter your real mailbox (e.g. Gmail) and complete Cloudflare confirmation.
- Create the first rule: defaults to
*@yourdomain → forward → verified destination, and sets it as the fail-open fallback.
4. Enable mail routing (per domain)
Three things must be in place: the Email Routing master switch, catch-all pointing at the Worker, and a verified destination. Two ways:
Option A · Panel automation (recommended)
After saving a CF API Token in Settings → Domains, an "Enable forwarding" button appears per domain. The panel automatically: detects MX conflicts (lists them for per-record confirmation) → enables Email Routing → adds recommended MX/SPF DNS records → points catch-all at the InboxFly Worker.
| Scope | Permission group | Access | Used for |
| Zone | Zone | Read | Domain discovery & sync |
| Zone | Zone Settings | Read + Edit | Email Routing status & enable |
| Zone | DNS | Read + Edit | MX conflict check, add MX/SPF records |
| Zone | Email Routing Rules | Read + Edit | Catch-all → Worker, native rule import |
| Account | Email Routing Addresses | Read + Edit | Destination addresses: add & verified-status sync |
Note: enabling/querying Email Routing requires the "Zone Settings" group, not "Email Routing Rules" — tick both or you get an Authentication error. The panel never calls the Workers / D1 / KV / R2 APIs (runtime bindings) — those permissions are not needed.
Option B · Manual in Cloudflare dashboard
Dashboard → your zone → Email → Email Routing → Enable (confirm MX replacement) → Catch-all address → action "Send to a Worker" → pick inboxfly. Refresh the panel afterwards; all domain health checks turn green.
5. Destination addresses
- Add your real mailbox on the Destinations page → Cloudflare automatically sends a confirmation email → once the recipient clicks, the panel flips to "Verified" automatically.
- Destinations are account-level: verify once, use on every domain.
- The first verified address becomes the fail-open fallback: the Worker tries it on internal errors to avoid silent loss.
- Addresses referenced by rules cannot be deleted (you will be asked to edit the rule first).
6. Routing rules
The rules page mirrors Cloudflare's native routing page: one unified rules table with a fixed "📌 Catch-all" fallback row at the end.
Creating a rule
- Email match: name on the left (e.g.
info, support) + @ + domain dropdown on the right; empty name = whole domain.
- Action: Send to an email (forward to a verified destination) / Drop (silent) / Bounce (InboxFly enhancement — SMTP-level rejection, not available natively).
- Advanced filters (optional enhancements): sender blacklist (one per line: address /
@domain / /regex/), subject regex, size cap (≤25MB), attachment condition.
Evaluation order
Exact rules → catch-all rules, sorted within group, first match wins. Unmatched mail goes to the Catch-all row (forward to default / bounce / drop).
Important: do not create routing rules manually in the Cloudflare dashboard — native rules take precedence over catch-all and bypass InboxFly (no copies, no stats). Import existing native rules via Settings → Domains.
7. Daily management
- Mail list: filter by domain / status / keyword; batch multi-select delete supported.
- Mail detail: sandboxed HTML rendering (remote images blocked), plain-text display, attachment downloads, raw MIME (.eml).
- Stats: by status (forwarded/blocked/dropped/bounced/error) and domain, with 14-day trends. "Forwarded" = Cloudflare accepted the forward; final placement is unobservable.
- Layout: the top-right button toggles sidebar / header mode; mobile always uses the header layout.
8. Storage & quotas
- Copies (HTML/text/raw MIME) and attachments live in your own R2, retained 30 days by default with daily 03:00 UTC auto-cleanup (adjustable in Settings; manual cleanup available).
- Filter-blocked mail stores metadata only by default (spam-storm safe); enable full bodies when needed.
- A warning banner appears when the 30-day R2 estimate exceeds 9GB, with one-click switch to metadata-only mode (forwarding unaffected).
9. Security
- Cloudflare Turnstile: Settings → Bot protection, paste Site/Secret keys and enable — a human check appears on sign-in (server-side siteverify). For local testing use the official test keys: Site
1x00000000000000000000AA / Secret 1x0000000000000000000000000000000AA (always passes).
- Admin account: Settings → Admin account; changing username/password invalidates all other sessions.
- Sessions: 24h default, 30 days with "keep me signed in"; CSRF protection on mutations, rate-limited login.
- Mail content safety: bodies render in an iframe sandbox (scripts disabled), server-side sanitization strips dangerous markup, remote images blocked by default.
10. Themes / language / PWA
- 3 themes × light/dark: Fluid Glass (default) / Minimal / Playful, each with a full dark variant; follows system by default.
- 10 languages: auto-detected from the browser; switch anytime via the globe icon.
- PWA install: Android Chrome → menu "Install app" (or the Settings button); iOS Safari → Share → "Add to Home Screen".
11. Known limits
- Local cannot receive real mail (Email Routing is a production capability) — use "Simulate" locally.
- "Forwarded" means Cloudflare accepted it; spam-folder placement is unobservable. Whitelist your domain in the recipient mailbox if needed.
- Do not mix native Cloudflare rules with InboxFly on the same domain (they bypass the panel); coexistence at zone level is fine.
- 25MiB per message (Cloudflare platform limit).
12. Uninstall & cleanup
- Revoke the panel's CF API Token (dashboard → API Tokens) — the forwarding pipeline runs independently in your account and keeps working; panel automation degrades.
- Stop forwarding: dashboard → Email Routing → disable Catch-all or the whole service.
- Delete resources: Workers → inboxfly; D1 → inboxfly; R2 → inboxfly; KV → the namespace.