# Email Agent — System Overview ## Purpose The Email Agent triages the family inbox. All mail lands in one Gmail account (`samuelrolfe@gmail.com`). PurelyMail forwards every domain address there. Sam opens one page. He clears the inbox in a few minutes. He knows that nothing important was missed. The build spec is `AGENTS.md`. The outcome statement is in `AGENTS.md` section 1. ## Architecture ``` Sam (phone or desktop browser) -> Caddy on .35 (mail.lab.audasmedia.com.au) -> Email Agent container on .13 :8790 |- HTMX dashboard (Go, server-rendered HTML) |- SQLite (/data/email_agent.db, own volume) |- IMAP read -> imap.gmail.com:993 (IPv4 only) |- SMTP send -> smtp.gmail.com:587 (IPv4 only, STARTTLS) |- JEV -> https://api.typesafe.ai (classification) |- OmniRoute -> http://192.168.20.13:20129/v1 (reply drafts) |- Apprise -> http://192.168.20.35:8210 (daily summary email) \- Hub API -> read-only /ask, /capabilities, /notify Hub on .13 :8788 asks this service with a shared bearer token. n8n runs the CLI jobs (-fetch, -classify, -summary-send) on a schedule. ``` ## Services and connections | Service | Host | Address | Role | | :--- | :--- | :--- | :--- | | `email-agent` | `.13` | `:8790` | Web app, API, and CLI jobs. | | Caddy | `.35` | `:443` | TLS reverse proxy for `mail.lab.audasmedia.com.au`. | | Gmail IMAP | cloud | `imap.gmail.com:993` | Read mail. Force IPv4. | | Gmail SMTP | cloud | `smtp.gmail.com:587` | Send replies. Force IPv4. | | JEV | cloud | `api.typesafe.ai` | Classify each message. | | OmniRoute | `.13` | `:20129` | Draft reply replies. Not JEV. | | Apprise | `.35` | `:8210` | Deliver the daily summary by email. | | Central AI Hub | `.13` | `:8788` | Ask questions. Read-only. | | n8n | `.13` | — | Trigger the CLI jobs. | ## Functional requirements in use | ID | Requirement | Where | | :--- | :--- | :--- | | FR-1 | Fetch mail with the Gmail path on a schedule. | `-fetch`, `/actions/fetch` | | FR-2 | Record the source address from `X-Pm-Original-To`. | `internal/mailfetch` | | FR-3 | Classify with JEV: category, importance, action, confidence. | `internal/classify` | | FR-4 | Apply user rules over the classification. | `internal/classify/rules.go` | | FR-5 | Show a triage list grouped by category. | `internal/server/inbox.go` | | FR-6 | Bulk delete, move, and mark. | `internal/server/actions.go` | | FR-7 | Detect unsubscribe links. Keep an ignore list. | `internal/unsubscribe` | | FR-8 | Draft a reply with the LLM. Edit, then send. | `internal/reply`, `internal/mailops` | | FR-9 | Move mail to Gmail labels. Deletes are recoverable. | `internal/mailops` | | FR-10 | Build a daily summary and send it by email. | `internal/summary`, `internal/apprise` | | FR-11 | Edit rules as plain text. Save and version. | `/rules`, `internal/server/rules.go` | | FR-12 | Expose `GET /capabilities`, `POST /ask`, `POST /notify`. | `internal/hub` | | FR-13 | Log every action. | `internal/store` `actions_log` | | FR-14 | Edit the category list in the UI. | `/categories` | | FR-15 | Send as the address the mail arrived at. | `internal/reply`, FR-2 value | ## Build phase status Phases 1 to 7 are complete and deployed. Phase 8 documents the project. | Phase | Scope | State | | :--- | :--- | :--- | | 1 | Go skeleton, SQLite, session login, Docker, Caddy. | Done. | | 2 | Gmail IMAP read, FR-2 source address. | Done. | | 3 | JEV classification, rules table, editable categories. | Done. | | 4 | Triage actions: delete, move, mark, bulk select. | Done. | | 5 | Reply drafts, edit, send. | Done. | | 6 | Unsubscribe detection and the rules editor. | Done. | | 7 | Daily summary by email and the read-only Hub API. | Done. | | 8 | Documentation across Gitea, Obsidian, Outline, Vikunja, and the map. | Done. | ## Key rules - Force IPv4 for every Gmail connection. IPv6 to Gmail failed from `.13`. - The source address header is `X-Pm-Original-To`. Never use `Delivered-To`. - `/ask` is read-only. It never changes state. - Spam is recommended, never auto-deleted. - Deletes are recoverable. The app uses a Delete label. - Sending a reply needs Sam's action in the UI. No auto-send. - Every action is logged. - Message bodies are kept for one month. The `-retain` job clears older bodies and caps `actions_log`. Metadata stays. ## Related documents - `AGENTS.md` — the build spec and the phase plan. - `README.md` — the technical repository README. - `docs/phase7-summary-hub.md` — the Phase 7 evidence. - `docs/outline/` — these AI-context documents. - `docs/email-agent-map.html` — the interactive architecture map.