Files
postal-mailer/README.md

69 lines
3.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Postal
Ollama-personalized email campaign sender for drjones' homelab. Windows-native (VPS), SQLite-backed,
LLM personalization via gemma4 on nightmare over Tailscale, delivery via SMTP (Proton) or Amazon SES.
## Architecture
```
[VPS 64.224.17.129: C:\Mailer] [nightmare 100.103.34.0]
Flask GUI :8899 (localhost) <--TS--> ollama-shim :11440 (non-chunked proxy)
engine.py (send loop, SQLite) └─> Ollama :11434 mgraffam/gemma4-heretic:12b
delivery: SMTP (Proton) or SES (boto3)
```
- **ollama-shim** exists because Ollama's chunked streaming stalls over this tailnet path;
the shim returns one plain body. systemd unit `ollama-shim.service` on nightmare.
- All state in `mailer.db` (subscribers, sends, campaigns, ab_variants, suppression).
## Features
- CSV import: `email,firstname,interest` per line
- Per-subscriber intro written by gemma4 (thinking-model aware: extracts quoted greeting from CoT)
- A/B subject variants — gemma4 rewrites the subject 2×; rotated evenly; per-variant sent counts
- Send-hours window (`send_hours: 9-18` server-local) — sleeps outside the window
- Segment targeting (`segment_interest` LIKE match)
- Suppression list (imports + public `/unsub/<email>` endpoint)
- Rate limiting (emails/min), batch pauses, STOP switch
- Crash resume: queued sends persist in SQLite; auto-resume on boot/startup
- Watchdog task (`PostalWatchdog`, every 5 min) restarts the app if :8899 stops answering
- Test-send button (personalizes + sends one real email)
- Tunables UI with hover explanations: model, rate, batch, hours, send mode, SMTP creds, prompt
## Setup (Windows VPS)
```
cd C:\Mailer
python -m venv --system-site-packages venv
venv\Scripts\pip install flask requests boto3
schtasks /create /tn MailerApp /tr "C:\Mailer\start.bat" /sc onstart /ru SYSTEM /rl HIGHEST /f
schtasks /create /tn PostalWatchdog /tr "C:\Mailer\watchdog.bat" /sc minute /mo 5 /ru SYSTEM /rl HIGHEST /f
```
## Config (Tunables in the UI)
| key | default | note |
|---|---|---|
| ollama_url | http://100.103.34.0:11440 | nightmare via shim |
| ollama_model | mgraffam/gemma4-heretic:12b | thinking model, handled |
| send_hours | 9-18 | server-local window |
| rate_per_minute | 60 | keep ≤60 while warming reputation |
| send_mode | smtp | `smtp` (Proton, ~2000/day paid) or `ses` (needs AWS IAM key + verified domain) |
| smtp_host/user/pass | proton | pass = Proton SMTP token (Settings → SMTP tokens), paid plan required |
## Scaling to 100K+
- Use SES (~$1/10K emails), verify the sending domain (SPF/DKIM via Cloudflare), start in sandbox,
request production access, then warm up: 50/day → double daily → cap ~10K/day before the big push.
- Bounce/complaint webhook handling is the next build item; until then monitor SES dashboard daily.
## Files
| file | purpose |
|---|---|
| app.py | Flask GUI + API |
| engine.py | send loop, Ollama client, Sender (smtp/ses), SQLite schema |
| start.bat | launcher (opens browser, runs app) |
| watchdog.bat | health-check + auto-restart |
| ollama_shim.py | deploys to nightmare — non-chunked Ollama proxy on tailnet |