branch:
README.md
10274 bytesRaw
# Email Agent Example
This example demonstrates how to build an email-processing agent using the email integration features in the agents framework.
## Features
- **Email Routing**: Routes emails to agents based on email addresses (e.g., `agent+id@domain.com`)
- **Secure Reply Routing**: HMAC-signed headers for secure reply flows
- **Email Parsing**: Uses PostalMime to parse incoming emails
- **Auto-Reply**: Automatically responds to incoming emails with loop prevention
- **State Management**: Tracks email count and stores recent emails
- **API Interface**: REST API for testing and management
- **Security Tests**: Comprehensive test suite including attack bypass attempts
## Quick Start
1. Install dependencies:
```bash
npm install
```
2. Configure the secret (required):
Update `wrangler.jsonc` with a unique secret for local development:
```jsonc
"vars": {
"EMAIL_SECRET": "your-unique-secret-here"
}
```
For production, use Wrangler secrets:
```bash
wrangler secret put EMAIL_SECRET
```
3. Start development server:
```bash
npm start
```
4. Run tests:
```bash
npm test
```
## Testing
### Automated Test Suite
The example includes a comprehensive test suite with functional and security tests:
```bash
# Run all tests (starts server automatically)
npm test
# Run with verbose output
npm run test:verbose
# Run only security tests
npm run test:security
```
Sample output:
```
═════════════════════════════════════════════════════════════════
FUNCTIONAL TESTS
═════════════════════════════════════════════════════════════════
✅ PASS Basic Email (12ms)
✅ PASS Unicode Content (6ms)
✅ PASS Long Subject (6ms)
✅ PASS Multiline Body (7ms)
✅ PASS Special Characters (11ms)
Subtotal: 5/5 passed
═════════════════════════════════════════════════════════════════
SECURITY TESTS (Attack Bypass Attempts)
═════════════════════════════════════════════════════════════════
🛡️ BLOCKED Forged headers (no signature) (4ms)
🛡️ BLOCKED Fake signature (random) (4ms)
🛡️ BLOCKED Expired signature (31 days) (2ms)
...
Subtotal: 15/15 attacks blocked
🎉 All tests passed! Security defenses are working.
```
### Manual Testing
Send test emails with different scenarios:
```bash
# Run all test scenarios
npm run test-email
# Run specific scenario
npm run test-email -- --scenario basic
# Use custom agent ID
npm run test-email -- --scenario unicode --id my-custom-id
# Show help
npm run test-email -- --help
```
Available scenarios: `basic`, `unicode`, `long-subject`, `multiline`, `special-chars`
### API Endpoints
#### Send Test Email
```bash
curl -X POST http://localhost:8787/api/test-email \
-H "Content-Type: application/json" \
-d '{
"from": "user@example.com",
"to": "EmailAgent+test123@example.com",
"subject": "Test Email",
"body": "Hello from test!"
}'
```
#### Security Test Endpoint
For testing security defenses with custom headers:
```bash
curl -X POST http://localhost:8787/api/test-security \
-H "Content-Type: application/json" \
-d '{
"from": "attacker@evil.com",
"to": "victim@example.com",
"subject": "Attack attempt",
"body": "Trying to bypass security",
"headers": {
"X-Agent-Name": "EmailAgent",
"X-Agent-ID": "admin-secret",
"X-Agent-Sig": "fake-signature",
"X-Agent-Sig-Ts": "1234567890"
},
"secureOnly": true
}'
```
## Email Routing
The agent supports multiple routing strategies. Import the resolvers from `agents/email`:
```typescript
import { routeAgentEmail } from "agents";
import {
createSecureReplyEmailResolver,
createAddressBasedEmailResolver,
createCatchAllEmailResolver
} from "agents/email";
```
### 1. Secure Reply Routing (Recommended for replies)
Uses HMAC-signed headers to securely route email replies back to the correct agent instance:
```typescript
const secureResolver = createSecureReplyEmailResolver(env.EMAIL_SECRET, {
maxAge: 7 * 24 * 60 * 60, // 7 days
onInvalidSignature: (email, reason) => {
console.warn(`Invalid signature from ${email.from}: ${reason}`);
}
});
```
Security features:
- HMAC-SHA256 signatures prevent header forgery
- Timestamp validation prevents replay attacks (default: 30 day expiry)
- Future timestamp rejection prevents validity extension attacks
- Constant-time comparison prevents timing attacks
### 2. Address-Based Routing
Routes based on email address patterns:
```typescript
const resolver = createAddressBasedEmailResolver("EmailAgent");
// EmailAgent+user123@domain.com → { agentName: "EmailAgent", agentId: "user123" }
// john.doe@domain.com → { agentName: "EmailAgent", agentId: "john.doe" }
```
#### Routing Rules
For emails with **sub-addresses** (using `+`):
- `localpart+subaddress@domain.com` → `{ agentName: "localpart", agentId: "subaddress" }`
For emails **without sub-addresses**:
- `localpart@domain.com` → `{ agentName: defaultAgentName, agentId: "localpart" }`
### 3. Catch-All Routing
Routes all emails to a single agent:
```typescript
const resolver = createCatchAllEmailResolver("EmailAgent", "main");
// All emails route to EmailAgent:main
```
### Composing Resolvers
Combine multiple resolvers for flexible routing:
```typescript
await routeAgentEmail(email, env, {
resolver: async (email, env) => {
// Try secure reply routing first (for replies)
const replyRouting = await secureReplyResolver(email, env);
if (replyRouting) return replyRouting;
// Fall back to address-based routing (for new emails)
return addressResolver(email, env);
}
});
```
## Agent Implementation
The `EmailAgent` class demonstrates:
- **Email Processing**: Parses emails with PostalMime
- **State Management**: Tracks emails and statistics
- **Auto-Reply**: Sends automated responses with secure signatures
- **Loop Prevention**: Detects auto-reply headers to prevent infinite loops
```typescript
class EmailAgent extends Agent<Env, EmailAgentState> {
async onEmail(email: AgentEmail) {
// Parse email content
const parsed = await PostalMime.parse(await email.getRaw());
// Update agent state
this.setState({
emailCount: this.state.emailCount + 1,
emails: [...this.state.emails.slice(-9), emailData]
});
// Send auto-reply (with secure signature for reply routing)
if (!this.isAutoReply(parsed)) {
await this.replyToEmail(email, {
fromName: "My Email Agent",
body: "Thank you for your email!",
secret: this.env.EMAIL_SECRET
});
}
}
}
```
### Auto-Reply Loop Prevention
The agent detects auto-replies by checking:
- `Auto-Submitted` header (RFC 3834)
- `X-Auto-Response-Suppress` header (Microsoft)
- `Precedence` header values (`bulk`, `junk`, `list`, `auto_reply`)
- Subject line patterns ("auto-reply", "out of office", "automatic reply")
## Security
### Secure Reply Flow
When sending outbound emails, the agent signs headers with HMAC-SHA256:
```
X-Agent-Name: EmailAgent
X-Agent-ID: customer123
X-Agent-Sig: <HMAC signature>
X-Agent-Sig-Ts: <Unix timestamp>
```
When a reply comes back, the signature is verified before routing. This prevents:
| Attack | Protection |
| ---------------- | ------------------------ |
| Forged headers | Signature verification |
| Replay attacks | Timestamp expiration |
| Future timestamp | Clock skew limit (5 min) |
| Timing attacks | Constant-time comparison |
### Security Tests
The test suite includes 15 attack scenarios:
- Forged headers without signature
- Random/fake signatures
- Expired signatures
- Future timestamps
- Malformed timestamps
- SQL injection payloads
- Path traversal attempts
- Header injection (newlines)
- Unicode normalization attacks
- Case manipulation
- Long payload DoS attempts
- Null byte injection
Run security tests: `npm run test:security`
## Deployment
1. Set production secret:
```bash
wrangler secret put EMAIL_SECRET
```
2. Deploy:
```bash
npm run deploy
```
3. Configure email routing in Cloudflare Dashboard:
- Go to `https://dash.cloudflare.com/<account-id>/<domain>/email/routing/routes`
- Add routing rules to point to your worker
4. Send emails to addresses like:
- `support@yourdomain.com` → EmailAgent with ID "support"
- `EmailAgent+urgent@yourdomain.com` → EmailAgent with ID "urgent"
## Project Structure
```
email-agent/
├── src/
│ └── index.ts # Main agent and worker code
├── run-tests.ts # Automated test runner (functional + security)
├── test-mail.ts # Manual test script with CLI
├── wrangler.jsonc # Cloudflare Worker configuration
├── package.json
└── README.md
```
## Scripts
| Script | Description |
| ----------------------- | ------------------------------------- |
| `npm start` | Start development server |
| `npm test` | Run all tests (functional + security) |
| `npm run test:verbose` | Run tests with detailed output |
| `npm run test:security` | Run only security tests |
| `npm run test-email` | Manual test script with CLI options |
| `npm run deploy` | Deploy to Cloudflare |
## Next Steps
- Add email templates for different types of auto-replies
- Implement AI-powered response generation
- Add email attachment processing
- Integrate with external services (CRM, ticketing systems)
- Add email scheduling and delayed sending
- Implement conversation threading