AvaMail
A self-hosted email management system built on Cloudflare Workers. Receive emails on your custom domain, store them in a database, and send emails through Brevo - all running on Cloudflare's edge network.
Install / Use
/learn @MrOplus/AvaMailREADME
AvaMail
A self-hosted email management system built on Cloudflare Workers. Receive emails on your custom domain, store them in a database, and send emails through Brevo - all running on Cloudflare's edge network.
Features
- Receive Emails: Use Cloudflare Email Routing to receive emails on your custom domain
- Send Emails: Send emails through Brevo's reliable SMTP API
- Attachment Support: Send and receive emails with attachments (up to 5MB per file)
- Modern UI: Vue 3 SPA with a clean, responsive interface
- Secure: PBKDF2 password hashing, rate limiting, security headers
- Serverless: Runs entirely on Cloudflare Workers with D1 database
- Optional Forwarding: Optionally forward received emails to your personal inbox
Architecture
┌─────────────────────────────────────────────────────────────┐
│ Cloudflare Edge │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Email │ │ Worker │ │ D1 │ │
│ │ Routing │───▶│ (Hono) │◀──▶│ Database │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Incoming │ │ Vue SPA │ │
│ │ Emails │ │ (Frontend) │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────┐
│ Brevo │
│ (Sending) │
└─────────────┘
Prerequisites
Before you begin, make sure you have:
- Cloudflare Account with a domain added
- Brevo Account (free tier available at brevo.com)
- Node.js 18+ and pnpm installed
- Wrangler CLI installed (
npm install -g wrangler)
Quick Start
1. Clone and Install
git clone https://github.com/MrOplus/AvaMail.git
cd AvaMail
pnpm install
2. Configure Cloudflare
Login to Wrangler:
wrangler login
Create a D1 database:
cd packages/worker
wrangler d1 create avamail-db
Copy the example config and update with your database ID:
cp wrangler.toml.example wrangler.toml
Then edit wrangler.toml and replace YOUR_DATABASE_ID_HERE with the ID from the previous command:
[[d1_databases]]
binding = "DB"
database_name = "avamail-db"
database_id = "your-actual-database-id"
3. Initialize Database
Run the database migrations:
wrangler d1 execute avamail-db --remote --file=src/db/schema.sql
4. Build Frontend
cd ../../frontend
pnpm run build
5. Deploy
cd ../packages/worker
pnpm run deploy
Your application will be deployed to https://avamail.<your-subdomain>.workers.dev
Wrangler Configuration
The wrangler.toml file is gitignored to prevent accidentally committing your database ID. Copy the example file to get started:
cd packages/worker
cp wrangler.toml.example wrangler.toml
The configuration file contains:
name = "avamail" # Worker name (can be customized)
main = "src/index.ts"
compatibility_date = "2024-01-01"
# D1 Database - Replace with your actual database ID
[[d1_databases]]
binding = "DB"
database_name = "avamail-db"
database_id = "YOUR_DATABASE_ID_HERE" # From: wrangler d1 create avamail-db
# Static assets from Vue build
[assets]
directory = "../../frontend/dist"
# Environment variables
[vars]
APP_NAME = "AvaMail"
Required Configuration
| Setting | Description | How to Get |
|---------|-------------|------------|
| database_id | Your D1 database UUID | Run wrangler d1 create avamail-db and copy the ID |
Optional Customization
| Setting | Default | Description |
|---------|---------|-------------|
| name | avamail | Worker name (affects the URL: name.subdomain.workers.dev) |
| APP_NAME | AvaMail | Application display name |
Important: Never commit sensitive data to
wrangler.toml. API keys and secrets are stored securely in the D1 database after initial setup through the UI.
Configuration Steps
Step 1: Initial Setup
- Open your deployed application URL
- Create your admin password (minimum 8 characters)
- You'll be logged in automatically
Step 2: Configure Cloudflare Email Routing
-
Go to Settings → Cloudflare tab
-
Create a Cloudflare API Token with the required permissions:
Creating an API Token:
- Go to Cloudflare Dashboard → API Tokens
- Click Create Token
- Select Create Custom Token
- Add the following permissions:
Account Permissions: | Permission | Access Level | |------------|--------------| | Email Routing Addresses | Edit |
Zone Permissions: | Permission | Access Level | |------------|--------------| | Zone | Read | | DNS | Edit | | Email Routing Rules | Edit | | Zone Settings | Edit |
- Set Zone Resources to:
Include→All zones(or select specific zone) - Set Account Resources to:
Include→ your account - Click Continue to summary → Create Token
- Copy the token (you won't see it again!)
-
Enter your Cloudflare credentials in AvaMail:
| Field | Description | Where to Find | |-------|-------------|---------------| | API Token | Custom API token | Created above with required scopes | | Account ID | Your account identifier | Dashboard sidebar or any zone URL | | Domain | Your email domain | e.g.,
example.com| | Forwarding Email | (Optional) Backup email | Your personal email for backup forwarding | -
Click Save Cloudflare Settings
-
Click Fix Email Routing to ensure emails route to the worker
Step 3: Configure Brevo (Sending)
- Go to Settings → Brevo tab
- Get your Brevo API key:
- Log into Brevo
- Go to SMTP & API → API Keys
- Create a new API key or use existing one
- Enter your API key and save
Step 4: Verify Domain in Brevo
To send emails from your domain:
- Go to Brevo Senders & Domains
- Add your domain
- Add the required DNS records (SPF, DKIM, DMARC)
- Verify the domain
Step 5: Add Email Addresses
- Go to Settings → Addresses tab
- Add email addresses you want to use (e.g.,
contact@yourdomain.com) - Set a default sending address
Step 6: Complete Setup
- Once Cloudflare and Brevo are configured, click Complete Setup
- You're ready to send and receive emails!
DNS Records
For email to work properly, ensure these DNS records are set:
Cloudflare Email Routing (MX Records)
Cloudflare will automatically configure these, but verify:
| Type | Name | Value | Priority |
|------|------|-------|----------|
| MX | @ | route1.mx.cloudflare.net | 69 |
| MX | @ | route2.mx.cloudflare.net | 15 |
| MX | @ | route3.mx.cloudflare.net | 21 |
| TXT | @ | v=spf1 include:_spf.mx.cloudflare.net ~all | - |
Brevo (for sending)
Add the records provided by Brevo for your domain:
| Type | Name | Purpose | |------|------|---------| | TXT | @ | SPF record | | TXT | mail._domainkey | DKIM record | | TXT | _dmarc | DMARC record |
Usage
Receiving Emails
Emails sent to any address at your domain (e.g., anything@yourdomain.com) will be:
- Received by Cloudflare Email Routing
- Processed by the worker
- Stored in the D1 database
- (Optional) Forwarded to your personal email
Sending Emails
- Click Compose in the sidebar
- Select your "From" address
- Enter recipient, subject, and message
- Add attachments if needed (max 5MB per file, 10MB total)
- Click Send
Managing Emails
- Inbox: View received emails
- Sent: View sent emails
- Star: Mark important emails
- Delete: Remove emails
Development
Local Development
# Start frontend dev server
cd frontend
pnpm run dev
# In another terminal, start worker dev server
cd packages/worker
pnpm run dev
Project Structure
AvaMail/
├── frontend/ # Vue 3 SPA
│ ├── src/
│ │ ├── api/ # API client
│ │ ├── stores/ # Pinia stores
│ │ ├── views/ # Page components
│ │ └── router/ # Vue Router
│ └── dist/ # Built assets
│
├── packages/
│ ├── worker/ # Cloudflare Worker
│ │ ├── src/
│ │ │ ├── api/ # API routes
│ │ │ ├── db/ # Database queries
│ │ │ ├── lib/ # Utilities
│ │ │ └── email-handler.ts
│ │ └── wrangler.toml
│ │
│ ├── cloudflare-email-api/ # Cloudflare API wrapper
│ └── mailgun-api/ # Brevo API wrapper
│
└── README.md
Security
Features
- PBKDF2 Password Hashing: 100,000 iterations with SHA-256
- Rate Limiting: Login attempts limited to 5 per 15 minutes
- Security Headers: X-Frame-Options, X-Content-Type-Options, etc.
- Session Management: Secure token-based authentication
- Input Validation: All inputs validated and sanitized
- Timing-Safe Comparisons: Prevents timing attacks
Recommendations
For production use:
- Custom Domain: Use your own domain instead of
workers.dev - HTTPS Only: Cloudflare Workers are HTTPS by default
- Strong Password: Use a strong, unique admin password
- Regular Updates: Keep dependencies updated
- Backup: Regularly export your D1 database
Troubleshooting
Emails Not Being Received
- Verify DNS records are correct
- Check Cloudf
