# Email Agent — Code Map

## Folder structure

```
email_agent/
├── AGENTS.md              Build spec (phases 1-8, requirements, rules).
├── README.md              Technical repository README.
├── cmd/email-agent/
│   └── main.go            Entry point. HTTP server and every CLI flag.
├── internal/
│   ├── apprise/           Apprise API client for the daily summary email.
│   ├── classify/          JEV client, rule engine, classification orchestration.
│   ├── config/            Environment-driven configuration.
│   ├── hub/               Hub contract: capabilities, /ask answerer, notify.
│   ├── mailfetch/         Stdlib-only IMAP client, MIME parse, FR-2 source address.
│   ├── mailops/           Gmail label moves, flag changes, and SMTP sending.
│   ├── reply/             OmniRoute LLM drafts and reply templates.
│   ├── server/            HTTP routes, auth middleware, HTMX handlers.
│   ├── session/           Server-side session store and signed cookie values.
│   ├── store/             SQLite open, schema, migrations, all queries.
│   ├── summary/           Deterministic daily-summary builder.
│   ├── unsubscribe/       RFC 2369/8058 and body unsubscribe-link detection.
│   └── version/           Build metadata (-ldflags).
├── web/
│   ├── embed.go           Embeds the static and template files.
│   ├── static/            tokens.css, app.css, Inter fonts, htmx.min.js.
│   └── templates/         login, dashboard, inbox, message, categories, rules,
│                          unsubscribe, reports.
├── docs/                  Phase evidence, AI context, map, diagrams.
├── Dockerfile             Multi-stage, CGO-free build.
├── docker-compose.yml     Own container, image, port, and volume.
└── Makefile               Build, test, and run targets.
```

## Entry points

| Entry point | Language | Role |
| :--- | :--- | :--- |
| `cmd/email-agent/main.go` | Go | Service and CLI. |
| `internal/server/server.go` | Go | HTTP router and middleware. |
| `internal/store/schema.sql` | SQL | The AGENTS.md section 7 tables. |

## CLI flags

| Flag | Effect |
| :--- | :--- |
| `-version` | Print version, commit, and build time. |
| `-healthcheck` | Probe the local `/health` endpoint and exit. |
| `-fetch` | Run one Gmail IMAP scan and exit (FR-1). |
| `-classify` | Classify pending messages with JEV and exit. |
| `-classify-all` | Re-classify every stored message. |
| `-check-smtp` | Verify SMTP IPv4, STARTTLS, and login. Sends no mail. |
| `-check-thread <id>` | Confirm a stored thread has a sent reply. Read-only. |
| `-summary` | Build today's summary, store it, and print it. Sends nothing. |
| `-summary-send` | Build the summary and deliver it by email via Apprise. |
| `-notify` | Send one notification to the Hub (`POST /notify`). |
| `-retain` | Clear old message bodies and cap `actions_log` (AGENTS.md section 7). |

## HTTP routes

| Method | Path | Auth | Purpose |
| :--- | :--- | :--- | :--- |
| `GET` | `/health` | public | Liveness and database check. |
| `GET` | `/readyz` | public | Readiness. |
| `GET` | `/login` | public | Sign-in form. |
| `POST` | `/login` | public | CSRF-checked sign-in. |
| `POST` | `/logout` | session | End the session. |
| `GET` | `/` | session | Dashboard. |
| `GET` | `/partials/inbox` | session | HTMX inbox fragment. |
| `POST` | `/actions/fetch` | session + CSRF | One IMAP scan. |
| `POST` | `/actions/classify` | session + CSRF | Classify pending messages. |
| `POST` | `/actions/reclassify` | session + CSRF | Re-classify every message. |
| `POST` | `/actions/bulk` | session + CSRF | Bulk delete, move, mark, restore. |
| `GET` | `/partials/message` | session | Message detail fragment. |
| `POST` | `/actions/draft` | session + CSRF | Generate and store a reply draft. |
| `POST` | `/actions/draft/save` | session + CSRF | Save draft edits. |
| `POST` | `/actions/send` | session + CSRF + confirm | Send a reply over SMTP. |
| `GET` | `/categories` | session | Category editor (FR-14). |
| `POST` | `/categories/add` | session + CSRF | Add a category. |
| `POST` | `/categories/rename` | session + CSRF | Rename and cascade. |
| `POST` | `/categories/remove` | session + CSRF | Remove and reassign. |
| `GET` | `/rules` | session | Rules editor (FR-11). |
| `POST` | `/rules/save` | session + CSRF | Save a new rules version. |
| `POST` | `/rules/restore` | session + CSRF | Restore an older version. |
| `POST` | `/rules/reclassify` | session + CSRF | Re-classify with current rules. |
| `GET` | `/unsubscribe` | session | Unsubscribe links and ignore list (FR-7). |
| `POST` | `/unsubscribe/ignore` | session + CSRF | Add a domain to the ignore list. |
| `POST` | `/unsubscribe/unignore` | session + CSRF | Remove a domain record. |
| `POST` | `/unsubscribe/mark` | session + CSRF + confirm | Record a domain. No link is followed. |
| `GET` | `/reports` | session | Daily summary and delete-report. |
| `GET` | `/capabilities` | Hub bearer | Hub capability list (FR-12). |
| `POST` | `/ask` | Hub bearer | Read-only short answer. |
| `GET` | `/static/...` | public | Embedded CSS, fonts, htmx. |

## Data model

SQLite. The schema is in `internal/store/schema.sql`. Migrations run at start
and are recorded in `schema_migrations`.

| Table | Holds |
| :--- | :--- |
| `messages` | Gmail id, thread id, headers, from, to, subject, body, source address. |
| `classifications` | Category, importance, action, confidence, model. |
| `categories` | The editable category list (FR-14). |
| `rules` | Plain-text rules, versioned (FR-11). |
| `actions_log` | Append-only action log (FR-13). Capped by `-retain`. |
| `unsubscribe` | Detected domains, status, and ignore list (FR-7). |
| `drafts` | Reply draft body, template, and sent state (FR-8). |
| `summaries` | Daily summary text and delivery target (FR-10). |

Migrations: v2 adds IMAP UID fields. v3 seeds the first categories and rules.
v4 adds `folder` and `mailbox`. v5 adds `message_id_header` and `drafts.sent_at`.
v6 adds `list_unsubscribe` fields and `unsubscribe.url`.

## Key modules

| Module | Role |
| :--- | :--- |
| `internal/mailfetch/imap.go` | Minimal IMAP client. Forces `tcp4`. |
| `internal/mailfetch/message.go` | MIME parse and the FR-2 source address. |
| `internal/mailfetch/imap_write.go` | IMAP MOVE, COPY, and flag changes. |
| `internal/classify/jev.go` | JEV `/v1/systemone` client. Three Choice questions. |
| `internal/classify/rules.go` | Rule parse and first-match-wins engine (FR-4). |
| `internal/mailops/mailops.go` | App folder to Gmail label mapping. |
| `internal/mailops/smtp.go` | SMTP send with IPv4 and STARTTLS. |
| `internal/reply/draft.go` | Reply prompt and OmniRoute call. |
| `internal/reply/templates.go` | Built-in reply templates. |
| `internal/store/retention.go` | Body clearing and `actions_log` cap. |
| `internal/hub/ask.go` | Deterministic, read-only answerer. |

## Tests

All Go tests run in a container:

```sh
timeout 600 docker run --rm -v "$PWD":/src -w /src golang:1.25-alpine go test ./...
```

| Package | Coverage |
| :--- | :--- |
| `internal/classify` | JEV client, rule engine, orchestration. |
| `internal/hub` | Answer intents, auth, read-only proof, notify. |
| `internal/mailfetch` | IMAP parse, MIME, FR-2, write ops. |
| `internal/mailops` | Label mapping, SMTP. |
| `internal/reply` | Draft prompts and templates. |
| `internal/server` | Auth, routes, CSRF, handlers. |
| `internal/store` | Schema, retention, triage, categories, summaries. |
| `internal/summary` | Counts, ordering, text. |
| `internal/unsubscribe` | Link detection. |
| `internal/apprise`, `internal/config`, `internal/session` | Client and config. |

## Dependencies

Go 1.25. The only database driver is `modernc.org/sqlite` (pure Go, no CGO).
The mail path uses the Go standard library plus a minimal IMAP client. The UI
uses vendored HTMX and the Family Home Lab `tokens.css`. No JavaScript build
step.
