Self-hosted email tracking for red teams and security research. Send emails with tracking pixels, tracked links, and calendar invites. Log opens, clicks, and recipient fingerprints.
Tracking Methods
- Tracking pixel (1x1 transparent GIF)
- Tracked links with JS fingerprinting (screen, GPU, WebRTC local IPs, timezone, fonts, etc.)
- Attachment beacon (HTML file with embedded pixel)
- Calendar invite tracking (ICS with RSVP callback)
- MDN/DSN receipt logging
What Gets Logged
- IP address + reverse DNS
- User-Agent, Accept-Language, referrer
- Client hints (Sec-CH-UA headers)
- Extended JS data: screen resolution, device memory, CPU cores, battery status, canvas/WebGL fingerprint, installed fonts, WebRTC leak, connection type
Infrastructure
- FastAPI + SQLite
- Postfix for outbound SMTP and bounce/receipt ingestion
- Caddy for automatic HTTPS
- Single
docker compose up
git clone https://github.com/youruser/baitops.git
cd baitops
# Edit docker-compose.yml with your domain and credentials
vim docker-compose.yml
# Deploy
docker compose up -d --buildDefault login: admin@local / plz-change-me (change these in docker-compose.yml)
Edit docker-compose.yml:
services:
app:
environment:
ADMIN_EMAIL: you@domain.com
ADMIN_PASSWORD: your-password
PUBLIC_BASE_URL: https://domain.com
SMTP_FROM: noreply@domain.com
TRACKING_MAIL_DOMAIN: domain.comFor full functionality (DSN/MDN/ICS replies), configure:
| Type | Name | Value |
|---|---|---|
| A | @ | your-server-ip |
| A | your-server-ip | |
| MX | @ | mail.domain.com (priority 10) |
Keep MX records DNS-only (no proxy) if using Cloudflare.
- Login at
https://your-domain.com - Go to Compose
- Write email, enable tracking options
- Insert tracked links via toolbar:
{{tracked_link:https://target.com|Click here}} - Send and monitor on Dashboard
Click any email to see event timeline with full fingerprint data.
┌─────────┐ ┌─────────┐ ┌──────────┐
│ Caddy │────▶│ App │────▶│ Postfix │
│ :443 │ │ :8000 │ │ :25 │
└─────────┘ └─────────┘ └──────────┘
│
▼
┌──────────┐
│ SQLite │
└──────────┘
- Caddy: TLS termination, auto HTTPS via Let's Encrypt
- App: FastAPI, serves UI and tracking endpoints
- Postfix: Outbound relay, routes DSN/MDN/ICS replies back to app via webhook
| Port | Service | Purpose |
|---|---|---|
| 80 | Caddy | HTTP → HTTPS redirect |
| 443 | Caddy | HTTPS (web UI, tracking endpoints) |
| 25 | Postfix | SMTP (inbound DSN/MDN/ICS, outbound mail) |
GET /t/pixel/{token} → 1x1 GIF, logs open
GET /t/link/{token} → JS fingerprint page → redirect
GET /t/attachment/{token} → 1x1 GIF for attachment beacon
POST /t/mdn/{token} → MDN receipt webhook
POST /t/dsn/{token} → DSN bounce webhook
POST /t/ics/{token} → Calendar reply webhook
├── app/
│ ├── main.py # FastAPI routes
│ ├── models.py # SQLAlchemy models
│ ├── email_utils.py # Email rendering, ICS generation
│ ├── templates/ # Jinja2 templates
│ └── static/ # CSS, JS
├── mail/
│ ├── Dockerfile # Postfix container
│ ├── entrypoint.sh # Postfix config
│ └── virtual_regexp # Webhook routing rules
├── caddy/
│ └── Caddyfile
└── docker-compose.yml
BSD 3-Clause License