No description
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-25 23:58:57 +01:00
config Route suspicious/spam to quarantine folders, tag work/personal mail 2026-10-25 23:58:14 +01:00
.gitignore Ignore credentials, runtime data and Python caches 2026-10-25 23:58:57 +01:00
mail_taxonomy.py Route suspicious/spam to quarantine folders, tag work/personal mail 2026-10-25 23:58:14 +01:00
README.md Initial commit 2026-10-10 19:21:07 +02:00

Local IMAP mail classifier

This is the existing mail_taxonomy.py scanner, refactored to separate editable policy from the executable. Dry-run is the default. The classifier talks to the locally configured Ollama server and to IMAP; only --apply moves mail.

Files

  • mail_taxonomy.py — IMAP, Ollama, report generation, SQLite checkpointing, manual-move detection, and opt-in IMAP MOVE.
  • config/prompt.md — complete system prompt used for Ollama classification.
  • config/categories.yaml — category definitions (also used to construct the JSON schema and folder names).
  • config/rules.yaml — ordered deterministic header-only rules, including global subject exclusions.

rules.yaml supports sender (exact lowercase address), subject_regex, list_id_contains, and subject_prefix. The Cryptocollectors mailing list has a List-ID match and a subject-tag fallback. For other rules, subject exceptions divert to Ollama. Header matching is not proof of sender authentication. Do not add broad rules for accounts or security correspondence.

Install

python3 -m pip install PyYAML

Keep config/ beside mail_taxonomy.py, or set MAIL_CLASSIFIER_CONFIG to an absolute path containing these three files. The program checks configuration at startup and stops on errors.

Run (fish)

set -x OLLAMA_MODEL qwen3.5:9b
set -x SAMPLE_UIDS_FILE /absolute/path/to/ai-treated-200-uids.json
set -x MAX_MESSAGES 200
python3 mail_taxonomy.py

IMAP_HOST, IMAP_USER, and optional IMAP_PORT, OLLAMA_URL, CLASSIFICATION_CACHE are the same variables as before. The SQLite cache and report paths are still relative to the working directory unless set explicitly; configuration is relative to the script directory. For a safe test of moves after reviewing the report:

python3 mail_taxonomy.py --apply --limit-moves 20

Only approved header-rule matches move by default; --include-model is additionally required to move eligible LLM classifications. Manual move detection is unchanged:

python3 mail_taxonomy.py --detect-manual-moves

Checkpointing and versioning

MAX_MESSAGES limits only messages that need a model call. Two kinds of INBOX message are always included in the report and don't count towards the limit: messages that already have a cached classification for the current model and policy, and messages that match a header rule. Finding header-rule matches means fetching the headers of every unclassified INBOX message in bulk (500 per IMAP command). Each run therefore sends up to MAX_MESSAGES new messages to Ollama. This doesn't apply when SAMPLE_UIDS_FILE is set.

The LLM cache fingerprint includes the prompt text, the categories, schema, and relevant Ollama settings. Modifying config/prompt.md or config/categories.yaml makes new classifications distinct from cached results. Changing only config/rules.yaml does not invalidate LLM cache entries, but header-rule matches are recalculated every run. Do not use --apply without reviewing newly edited rules and their matched messages.

The scanner is not tested against your running Dovecot or Ollama. The extracted prompt and all six matching rules passed local validation and targeted tests.